Skip to content

Commit

Permalink
Implement adaptive preconditioning for reactor networks
Browse files Browse the repository at this point in the history
Adds PreconditionerBase and derives AdaptivePreconditioner from this
base class. PreconditionerBase is intended to be an abstract class so
other types of preconditioners can be extended. AdaptivePreconditioner
is a specific type of preconditioning based on a tolerance.

The preconditioned integrator works with all Sundials versions supported
by Cantera.

The ILUT algorithm is used to limit fill-in when factorizing the preconditioner.

Adds access to CVODES integrator statistics in C++ and Python

Adds reactor classes "IdealGasConstPressureMoleReactor" and "IdealGasMoleReactor"
that use a mole-based state vector to increase the sparsity of the preconditioner.
  • Loading branch information
anthony-walker authored and speth committed Jul 20, 2022
1 parent d60e59e commit c361a5e
Show file tree
Hide file tree
Showing 34 changed files with 2,052 additions and 37 deletions.
1 change: 1 addition & 0 deletions doc/example-keywords.txt
Expand Up @@ -26,6 +26,7 @@ plasma
plotting
plug flow reactor
pollutant formation
preconditioner
premixed flame
reaction path analysis
reactor network
Expand Down
138 changes: 138 additions & 0 deletions include/cantera/numerics/AdaptivePreconditioner.h
@@ -0,0 +1,138 @@
/**
* @file AdaptivePreconditioner.h Declarations for the class
* AdaptivePreconditioner which is a child class of PreconditionerBase
* for preconditioners used by sundials
*/

// This file is part of Cantera. See License.txt in the top-level directory or
// at https://cantera.org/license.txt for license and copyright information.

#ifndef ADAPTIVEPRECONDITIONER_H
#define ADAPTIVEPRECONDITIONER_H

#include "cantera/numerics/PreconditionerBase.h"
#include "cantera/numerics/eigen_sparse.h"
#include "cantera/base/global.h"
#include <iostream>

namespace Cantera
{

//! AdaptivePreconditioner a preconditioner designed for use with large
//! mechanisms that leverages sparse solvers. It does this by pruning
//! the preconditioner by a threshold value. It also neglects pressure
//! dependence and thirdbody contributions in its formation and has a
//! finite difference approximation for temperature.
class AdaptivePreconditioner : public PreconditionerBase
{
public:
AdaptivePreconditioner() {}

void initialize(size_t networkSize);

void reset() {
m_precon_matrix.setZero();
m_jac_trips.clear();
};

void setup();

void solve(const size_t stateSize, double *rhs_vector, double* output);

PreconditionerType preconditionerType() { return PreconditionerType::LEFT_PRECONDITION; }

void setValue(size_t row, size_t col, double value);

virtual void stateAdjustment(vector_fp& state);

//! Transform Jacobian vector and write into
//! preconditioner
void transformJacobianToPreconditioner();

//! Prune preconditioner elements
void prunePreconditioner();

//! Function used to return semi-analytical jacobian matrix
Eigen::SparseMatrix<double> getJacobian() {
Eigen::SparseMatrix<double> jacobian(m_dim, m_dim);
jacobian.setFromTriplets(m_jac_trips.begin(), m_jac_trips.end());
return jacobian;
}

//! Get the threshold value for setting elements
double threshold() { return m_threshold; }

//! Get ilut fill factor
double ilutFillFactor() { return m_fill_factor; }

//! Get ilut drop tolerance
double ilutDropTol() { return m_drop_tol; }

//! Set the threshold value to compare elements against
//! @param threshold double value used in setting by threshold
void setThreshold(double threshold) {
m_threshold = threshold;
m_prune_precon = (threshold <= 0) ? false : true;
}

//! Set drop tolerance for ILUT
//! @param droptol double value used in setting solver drop tolerance
void setIlutDropTol(double droptol) {
m_drop_tol = droptol;
m_solver.setDroptol(droptol);
}

//! Set the fill factor for ILUT solver
//! @param fillFactor fill in factor for ILUT solver
void setIlutFillFactor(int fillFactor) {
m_fill_factor = fillFactor;
m_solver.setFillfactor(fillFactor);
}

//! Print preconditioner contents
void printPreconditioner() {
std::stringstream ss;
Eigen::IOFormat HeavyFmt(Eigen::FullPrecision, 0, ", ", ";\n", "[", "]", "[", "]");
ss << Eigen::MatrixXd(m_precon_matrix).format(HeavyFmt);
writelog(ss.str());
}

//! Print jacobian contents
void printJacobian() {
std::stringstream ss;
Eigen::SparseMatrix<double> jacobian(m_dim, m_dim);
jacobian.setFromTriplets(m_jac_trips.begin(), m_jac_trips.end());
ss << Eigen::MatrixXd(jacobian);
writelog(ss.str());
}

protected:
//! ilut fill factor
double m_fill_factor = 0;

//! ilut drop tolerance
double m_drop_tol = 0;

//! Vector of triples representing the jacobian used in preconditioning
std::vector<Eigen::Triplet<double>> m_jac_trips;

//! Storage of appropriately sized identity matrix for making the preconditioner
Eigen::SparseMatrix<double> m_identity;

//! Container that is the sparse preconditioner
Eigen::SparseMatrix<double> m_precon_matrix;

//! Solver used in solving the linear system
Eigen::IncompleteLUT<double> m_solver;

//! Minimum value a non-diagonal element must be to be included in
//! the preconditioner
double m_threshold = 1e-8;

//! Bool set whether to prune the matrix or not
double m_prune_precon = true;
};

}

#endif
29 changes: 26 additions & 3 deletions include/cantera/numerics/CVodesIntegrator.h
Expand Up @@ -35,7 +35,6 @@ class CVodesIntegrator : public Integrator
virtual void setTolerances(double reltol, size_t n, double* abstol);
virtual void setTolerances(double reltol, double abstol);
virtual void setSensitivityTolerances(double reltol, double abstol);
virtual void setProblemType(int probtype);
virtual void initialize(double t0, FuncEval& func);
virtual void reinitialize(double t0, FuncEval& func);
virtual void integrate(double tout);
Expand All @@ -57,6 +56,14 @@ class CVodesIntegrator : public Integrator
virtual void setMaxSteps(int nmax);
virtual int maxSteps();
virtual void setMaxErrTestFails(int n);
virtual AnyMap nonlinearSolverStats() const;
virtual AnyMap linearSolverStats() const;
void setLinearSolverType(std::string linSolverType) {
m_type = linSolverType;
}
virtual std::string linearSolverType() {
return m_type;
}
virtual void setBandwidth(int N_Upper, int N_Lower) {
m_mupper = N_Upper;
m_mlower = N_Lower;
Expand All @@ -65,6 +72,23 @@ class CVodesIntegrator : public Integrator
return static_cast<int>(m_np);
}
virtual double sensitivity(size_t k, size_t p);
virtual void setProblemType(int probtype)
{
warn_deprecated("CVodesIntegrator::setProblemType()",
"To be removed. Set linear solver type with setLinearSolverType");
if (probtype == DIAG)
{
setLinearSolverType("DIAG");
} else if (probtype == DENSE + NOJAC) {
setLinearSolverType("DENSE");
} else if (probtype == BAND + NOJAC) {
setLinearSolverType("BAND");
} else if (probtype == GMRES) {
setLinearSolverType("GMRES");
} else {
setLinearSolverType("Invalid Option");
}
}

//! Returns a string listing the weighted error estimates associated
//! with each solution component.
Expand Down Expand Up @@ -93,7 +117,7 @@ class CVodesIntegrator : public Integrator
double m_time; //!< The current integrator time
N_Vector m_y, m_abstol;
N_Vector m_dky;
int m_type;
std::string m_type;
int m_itol;
int m_method;
int m_maxord;
Expand All @@ -107,7 +131,6 @@ class CVodesIntegrator : public Integrator
N_Vector* m_yS;
size_t m_np;
int m_mupper, m_mlower;

//! Indicates whether the sensitivities stored in m_yS have been updated
//! for at the current integrator time.
bool m_sens_ok;
Expand Down
43 changes: 43 additions & 0 deletions include/cantera/numerics/FuncEval.h
Expand Up @@ -14,6 +14,7 @@

namespace Cantera
{

/**
* Virtual base class for ODE right-hand-side function evaluators.
* Classes derived from FuncEval evaluate the right-hand-side function
Expand Down Expand Up @@ -49,6 +50,48 @@ class FuncEval
*/
int eval_nothrow(double t, double* y, double* ydot);

/*! Evaluate the setup processes for the Jacobian preconditioner.
* @param[in] t time.
* @param[in] y solution vector, length neq()
* @param gamma the gamma in M=I-gamma*J
* @warning This function is an experimental part of the %Cantera API and may be
* changed or removed without notice.
*/
virtual void preconditionerSetup(double t, double* y, double gamma) {
throw NotImplementedError("FuncEval::preconditionerSetup");
}

/*! Evaluate the linear system Ax=b where A is the preconditioner.
* @param[in] rhs right hand side vector used in linear system
* @param[out] output output vector for solution
* @warning This function is an experimental part of the %Cantera API and may be
* changed or removed without notice.
*/
virtual void preconditionerSolve(double* rhs, double* output) {
throw NotImplementedError("FuncEval::preconditionerSolve");
}

/*! Preconditioner setup that doesn't throw an error but returns a
* CVODES flag. It also helps as a first level of polymorphism
* which identifies the specific FuncEval, e.g., ReactorNet.
* @param[in] t time.
* @param[in] y solution vector, length neq()
* @param gamma the gamma in M=I-gamma*J
* @warning This function is an experimental part of the %Cantera API and may be
* changed or removed without notice.
*/
int preconditioner_setup_nothrow(double t, double* y, double gamma);

/*! Preconditioner solve that doesn't throw an error but returns a
* CVODES flag. It also helps as a first level of polymorphism
* which identifies the specific FuncEval, e.g., ReactorNet.
* @param[in] rhs right hand side vector used in linear system
* @param[out] output output vector for solution
* @warning This function is an experimental part of the %Cantera API and may be
* changed or removed without notice.
*/
int preconditioner_solve_nothrow(double* rhs, double* output);

//! Fill in the vector *y* with the current state of the system
virtual void getState(double* y) {
throw NotImplementedError("FuncEval::getState");
Expand Down
81 changes: 81 additions & 0 deletions include/cantera/numerics/Integrator.h
Expand Up @@ -12,8 +12,10 @@
#ifndef CT_INTEGRATOR_H
#define CT_INTEGRATOR_H
#include "FuncEval.h"
#include "PreconditionerBase.h"

#include "cantera/base/global.h"
#include "cantera/base/AnyMap.h"

namespace Cantera
{
Expand Down Expand Up @@ -91,11 +93,68 @@ class Integrator
//! Set the problem type.
/*!
* @param probtype Type of the problem
*
* @deprecated This funciton is to be removed along with the integer constants used
* in conditionals to set the problem type currently. This includes DENSE, JAC,
* NOJAC, BAND, and DIAG
*/
virtual void setProblemType(int probtype) {
warn_deprecated("Integrator::setProblemType()",
"To be removed. Set linear solver type with setLinearSolverType");
warn("setProblemType");
}

//! Set the linear solver type.
/*!
* @param linSolverType Type of the linear solver
*/
virtual void setLinearSolverType(std::string linSolverType) {
warn("setLinearSolverType");
}

//! Set the preconditioner type.
/*!
* @param prectype Type of the problem
*/
virtual void setPreconditionerType(PreconditionerType prectype) {
warn("setPreconditionerType");
}

//! Set preconditioner used by the linear solver
/*!
* @param preconditioner preconditioner object used for the linear solver
*/
virtual void setPreconditioner(shared_ptr<PreconditionerBase> preconditioner) {
m_preconditioner = preconditioner;
m_prec_type = m_preconditioner->preconditionerType();
}

//! Solve a linear system Ax=b where A is the preconditioner
/*!
* @param[in] stateSize length of the rhs and output vectors
* @param[in] rhs right hand side vector used in linear system
* @param[out] output output vector for solution
*/
virtual void preconditionerSolve(size_t stateSize, double* rhs, double* output) {
m_preconditioner->solve(stateSize, rhs, output);
}

//! Return the preconditioner type
virtual PreconditionerType preconditionerType() {
return m_prec_type;
}

//! Return preconditioner reference to object
virtual shared_ptr<PreconditionerBase> preconditioner() {
return m_preconditioner;
}

//! Return the integrator problem type
virtual std::string linearSolverType() {
warn("linearSolverType");
return "";
}

/**
* Initialize the integrator for a new problem. Call after all options have
* been set.
Expand Down Expand Up @@ -219,6 +278,28 @@ class Integrator
return 0.0;
}

//! Get nonlinear solver stats from integrator
virtual AnyMap nonlinearSolverStats() const {
AnyMap stats;
warn("nonlinearSolverStats");
return stats;
}

//! Get linear solver stats from integrator
virtual AnyMap linearSolverStats() const {
AnyMap stats;
warn("linearSolverStats");
return stats;
}

protected:
//! Pointer to preconditioner object used in integration which is
//! set by setPreconditioner and initialized inside of
//! ReactorNet::initialize()
shared_ptr<PreconditionerBase> m_preconditioner;
//! Type of preconditioning used in applyOptions
PreconditionerType m_prec_type = PreconditionerType::NO_PRECONDITION;

private:
doublereal m_dummy;
void warn(const std::string& msg) const {
Expand Down

0 comments on commit c361a5e

Please sign in to comment.