Cantera  2.5.1
ChemEquil.h
Go to the documentation of this file.
1 /**
2  * @file ChemEquil.h Chemical equilibrium.
3  */
4 
5 // This file is part of Cantera. See License.txt in the top-level directory or
6 // at https://cantera.org/license.txt for license and copyright information.
7 
8 #ifndef CT_CHEM_EQUIL_H
9 #define CT_CHEM_EQUIL_H
10 
11 // Cantera includes
12 #include "cantera/base/ct_defs.h"
15 #include <functional>
16 
17 namespace Cantera
18 {
19 
20 class DenseMatrix;
21 /// map property strings to integers
22 int _equilflag(const char* xy);
23 
24 /**
25  * Chemical equilibrium options. Used internally by class ChemEquil.
26  */
27 class EquilOpt
28 {
29 public:
30  EquilOpt() : relTolerance(1.e-8), absElemTol(1.0E-70),maxIterations(1000),
31  iterations(0),
32  maxStepSize(10.0), propertyPair(TP), contin(false) {}
33 
34  doublereal relTolerance; ///< Relative tolerance
35  doublereal absElemTol; ///< Abs Tol in element number
36  int maxIterations; ///< Maximum number of iterations
37  int iterations; ///< Iteration counter
38 
39  /**
40  * Maximum step size. Largest change in any element potential or
41  * in log(T) allowed in one Newton step. Default: 10.0
42  */
43  doublereal maxStepSize;
44 
45  /**
46  * Property pair flag. Determines which two thermodynamic properties
47  * are fixed.
48  */
50 
51  /**
52  * Continuation flag. Set true if the calculation should be initialized from
53  * the last calculation. Otherwise, the calculation will be started from
54  * scratch and the initial composition and element potentials estimated.
55  */
56  bool contin;
57 };
58 
59 /**
60  * @defgroup equil Chemical Equilibrium
61  */
62 
63 /**
64  * Class ChemEquil implements a chemical equilibrium solver for single-phase
65  * solutions. It is a "non-stoichiometric" solver in the terminology of Smith
66  * and Missen, meaning that every intermediate state is a valid chemical
67  * equilibrium state, but does not necessarily satisfy the element constraints.
68  * In contrast, the solver implemented in class MultiPhaseEquil uses a
69  * "stoichiometric" algorithm, in which each intermediate state satisfies the
70  * element constraints but is not a state of chemical equilibrium. Non-
71  * stoichiometric methods are faster when they converge, but stoichiometric ones
72  * tend to be more robust and can be used also for problems with multiple
73  * condensed phases. As expected, the ChemEquil solver is faster than
74  * MultiPhaseEquil for many single-phase equilibrium problems (particularly if
75  * there are only a few elements but very many species), but can be less stable.
76  * Problem situations include low temperatures where only a few species have
77  * non-zero mole fractions, precisely stoichiometric compositions (e.g. 2 H2 +
78  * O2). In general, if speed is important, this solver should be tried first,
79  * and if it fails then use MultiPhaseEquil.
80  * @ingroup equil
81  */
82 class ChemEquil
83 {
84 public:
85  ChemEquil();
86 
87  //! Constructor combined with the initialization function
88  /*!
89  * This constructor initializes the ChemEquil object with everything it
90  * needs to start solving equilibrium problems.
91  *
92  * @param s ThermoPhase object that will be used in the equilibrium calls.
93  */
94  ChemEquil(thermo_t& s);
95 
96  virtual ~ChemEquil();
97 
98  /*!
99  * Equilibrate a phase, holding the elemental composition fixed at the
100  * initial value found within the ThermoPhase object *s*.
101  *
102  * The value of two specified properties are obtained by querying the
103  * ThermoPhase object. The properties must be already contained within the
104  * current thermodynamic state of the system.
105  */
106  int equilibrate(thermo_t& s, const char* XY, int loglevel = 0);
107 
108  /*!
109  * Compute the equilibrium composition for two specified properties and the
110  * specified element moles.
111  *
112  * The two specified properties are obtained by querying the ThermoPhase
113  * object. The properties must be already contained within the current
114  * thermodynamic state of the system.
115  *
116  * @param s phase object to be equilibrated
117  * @param XY property pair to hold constant
118  * @param elMoles specified vector of element abundances.
119  * @param loglevel Specify amount of debug logging (0 to disable)
120  * @return Successful returns are indicated by a return value of 0.
121  * Unsuccessful returns are indicated by a return value of -1 for lack
122  * of convergence or -3 for a singular Jacobian.
123  */
124  int equilibrate(thermo_t& s, const char* XY, vector_fp& elMoles,
125  int loglevel = 0);
126 
127  /**
128  * Options controlling how the calculation is carried out.
129  * @see EquilOptions
130  */
132 
133 protected:
134  //! Pointer to the ThermoPhase object used to initialize this object.
135  /*!
136  * This ThermoPhase object must be compatible with the ThermoPhase objects
137  * input from the equilibrate function. Currently, this means that the 2
138  * ThermoPhases have to have consist of the same species and elements.
139  */
141 
142  //! number of atoms of element m in species k.
143  doublereal nAtoms(size_t k, size_t m) const {
144  return m_comp[k*m_mm + m];
145  }
146 
147  /*!
148  * Prepare for equilibrium calculations.
149  * @param s object representing the solution phase.
150  */
151  void initialize(thermo_t& s);
152 
153  /*!
154  * Set mixture to an equilibrium state consistent with specified element
155  * potentials and temperature.
156  *
157  * @param s mixture to be updated
158  * @param x vector of non-dimensional element potentials
159  * \f[ \lambda_m/RT \f].
160  * @param t temperature in K.
161  */
162  void setToEquilState(thermo_t& s,
163  const vector_fp& x, doublereal t);
164 
165  //! Estimate the initial mole numbers. This version borrows from the
166  //! MultiPhaseEquil solver.
167  int setInitialMoles(thermo_t& s, vector_fp& elMoleGoal, int loglevel = 0);
168 
169  //! Generate a starting estimate for the element potentials.
171  vector_fp& elMolesGoal, int loglevel = 0);
172 
173  /*!
174  * Do a calculation of the element potentials using the Brinkley method,
175  * p. 129 Smith and Missen.
176  *
177  * We have found that the previous estimate may not be good enough to
178  * avoid drastic numerical issues associated with the use of a numerically
179  * generated Jacobian used in the main algorithm.
180  *
181  * The Brinkley algorithm, here, assumes a constant T, P system and uses a
182  * linearized analytical Jacobian that turns out to be very stable even
183  * given bad initial guesses.
184  *
185  * The pressure and temperature to be used are in the ThermoPhase object
186  * input into the routine.
187  *
188  * The initial guess for the element potentials used by this routine is
189  * taken from the input vector, x.
190  *
191  * elMoles is the input element abundance vector to be matched.
192  *
193  * Nonideal phases are handled in principle. This is done by calculating
194  * the activity coefficients and adding them into the formula in the
195  * correct position. However, these are treated as a RHS contribution
196  * only. Therefore, convergence might be a problem. This has not been
197  * tested. Also molality based unit systems aren't handled.
198  *
199  * On return, int return value contains the success code:
200  * - 0 - successful
201  * - 1 - unsuccessful, max num iterations exceeded
202  * - -3 - unsuccessful, singular Jacobian
203  *
204  * NOTE: update for activity coefficients.
205  */
206  int estimateEP_Brinkley(thermo_t& s, vector_fp& lambda, vector_fp& elMoles);
207 
208  //! Find an acceptable step size and take it.
209  /*!
210  * The original implementation employed a line search technique that
211  * enforced a reduction in the norm of the residual at every successful
212  * step. Unfortunately, this method created false convergence errors near
213  * the end of a significant number of steps, usually special conditions
214  * where there were stoichiometric constraints.
215  *
216  * This new method just does a delta damping approach, based on limiting
217  * the jump in the dimensionless element potentials. Mole fractions are
218  * limited to a factor of 2 jump in the values from this method. Near
219  * convergence, the delta damping gets out of the way.
220  */
221  int dampStep(thermo_t& s, vector_fp& oldx,
222  double oldf, vector_fp& grad, vector_fp& step, vector_fp& x,
223  double& f, vector_fp& elmols, double xval, double yval);
224 
225  /**
226  * Evaluates the residual vector F, of length #m_mm
227  */
228  void equilResidual(thermo_t& s, const vector_fp& x,
229  const vector_fp& elmtotal, vector_fp& resid,
230  double xval, double yval, int loglevel = 0);
231 
232  void equilJacobian(thermo_t& s, vector_fp& x,
233  const vector_fp& elmols, DenseMatrix& jac,
234  double xval, double yval, int loglevel = 0);
235 
236  void adjustEloc(thermo_t& s, vector_fp& elMolesGoal);
237 
238  //! Update internally stored state information.
239  void update(const thermo_t& s);
240 
241  /**
242  * Given a vector of dimensionless element abundances, this routine
243  * calculates the moles of the elements and the moles of the species.
244  *
245  * @param[in] x = current dimensionless element potentials..
246  */
247  double calcEmoles(thermo_t& s, vector_fp& x,
248  const double& n_t, const vector_fp& Xmol_i_calc,
249  vector_fp& eMolesCalc, vector_fp& n_i_calc,
250  double pressureConst);
251 
252  size_t m_mm; //!< number of elements in the phase
253  size_t m_kk; //!< number of species in the phase
254  size_t m_skip;
255 
256  //! This is equal to the rank of the stoichiometric coefficient matrix when
257  //! it is computed. It's initialized to #m_mm.
259 
260  std::function<double(ThermoPhase&)> m_p1, m_p2;
261 
262  //! Current value of the mole fractions in the single phase. length = #m_kk.
264 
265  //! Current value of the sum of the element abundances given the current
266  //! element potentials.
267  doublereal m_elementTotalSum;
268 
269  //! Current value of the element mole fractions. Note these aren't the goal
270  //! element mole fractions.
272  vector_fp m_reswork;
273  vector_fp m_jwork1;
274  vector_fp m_jwork2;
275 
276  //! Storage of the element compositions. natom(k,m) = m_comp[k*m_mm+ m];
278  doublereal m_temp, m_dens;
279  doublereal m_p0;
280 
281  //! Index of the element id corresponding to the electric charge of each
282  //! species. Equal to -1 if there is no such element id.
283  size_t m_eloc;
284 
285  vector_fp m_startSoln;
286 
287  vector_fp m_grt;
288  vector_fp m_mu_RT;
289 
290  //! Dimensionless values of the Gibbs free energy for the standard state of
291  //! each species, at the temperature and pressure of the solution (the star
292  //! standard state).
294  std::vector<size_t> m_component;
295 
296  //! element fractional cutoff, below which the element will be zeroed.
298  bool m_doResPerturb;
299 
300  std::vector<size_t> m_orderVectorElements;
301  std::vector<size_t> m_orderVectorSpecies;
302 
303  //! Verbosity of printed output. No messages when m_loglevel == 0. More
304  //! output as level increases.
306 };
307 
308 }
309 
310 #endif
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
Class ChemEquil implements a chemical equilibrium solver for single-phase solutions.
Definition: ChemEquil.h:83
int equilibrate(thermo_t &s, const char *XY, int loglevel=0)
Definition: ChemEquil.cpp:308
vector_fp m_muSS_RT
Dimensionless values of the Gibbs free energy for the standard state of each species,...
Definition: ChemEquil.h:293
void equilResidual(thermo_t &s, const vector_fp &x, const vector_fp &elmtotal, vector_fp &resid, double xval, double yval, int loglevel=0)
Evaluates the residual vector F, of length m_mm.
Definition: ChemEquil.cpp:728
void update(const thermo_t &s)
Update internally stored state information.
Definition: ChemEquil.cpp:153
thermo_t * m_phase
Pointer to the ThermoPhase object used to initialize this object.
Definition: ChemEquil.h:140
size_t m_kk
number of species in the phase
Definition: ChemEquil.h:253
int m_loglevel
Verbosity of printed output.
Definition: ChemEquil.h:305
size_t m_nComponents
This is equal to the rank of the stoichiometric coefficient matrix when it is computed.
Definition: ChemEquil.h:258
size_t m_eloc
Index of the element id corresponding to the electric charge of each species.
Definition: ChemEquil.h:283
int dampStep(thermo_t &s, vector_fp &oldx, double oldf, vector_fp &grad, vector_fp &step, vector_fp &x, double &f, vector_fp &elmols, double xval, double yval)
Find an acceptable step size and take it.
Definition: ChemEquil.cpp:689
EquilOpt options
Options controlling how the calculation is carried out.
Definition: ChemEquil.h:131
vector_fp m_molefractions
Current value of the mole fractions in the single phase. length = m_kk.
Definition: ChemEquil.h:263
int setInitialMoles(thermo_t &s, vector_fp &elMoleGoal, int loglevel=0)
Estimate the initial mole numbers.
Definition: ChemEquil.cpp:182
double calcEmoles(thermo_t &s, vector_fp &x, const double &n_t, const vector_fp &Xmol_i_calc, vector_fp &eMolesCalc, vector_fp &n_i_calc, double pressureConst)
Given a vector of dimensionless element abundances, this routine calculates the moles of the elements...
Definition: ChemEquil.cpp:807
vector_fp m_comp
Storage of the element compositions. natom(k,m) = m_comp[k*m_mm+ m];.
Definition: ChemEquil.h:277
void setToEquilState(thermo_t &s, const vector_fp &x, doublereal t)
Definition: ChemEquil.cpp:132
vector_fp m_elementmolefracs
Current value of the element mole fractions.
Definition: ChemEquil.h:271
int estimateEP_Brinkley(thermo_t &s, vector_fp &lambda, vector_fp &elMoles)
Definition: ChemEquil.cpp:843
doublereal nAtoms(size_t k, size_t m) const
number of atoms of element m in species k.
Definition: ChemEquil.h:143
int estimateElementPotentials(thermo_t &s, vector_fp &lambda, vector_fp &elMolesGoal, int loglevel=0)
Generate a starting estimate for the element potentials.
Definition: ChemEquil.cpp:218
double m_elemFracCutoff
element fractional cutoff, below which the element will be zeroed.
Definition: ChemEquil.h:297
doublereal m_elementTotalSum
Current value of the sum of the element abundances given the current element potentials.
Definition: ChemEquil.h:267
void initialize(thermo_t &s)
Definition: ChemEquil.cpp:64
size_t m_mm
number of elements in the phase
Definition: ChemEquil.h:252
A class for full (non-sparse) matrices with Fortran-compatible data storage, which adds matrix operat...
Definition: DenseMatrix.h:51
Chemical equilibrium options.
Definition: ChemEquil.h:28
int iterations
Iteration counter.
Definition: ChemEquil.h:37
doublereal maxStepSize
Maximum step size.
Definition: ChemEquil.h:43
doublereal relTolerance
Relative tolerance.
Definition: ChemEquil.h:34
doublereal absElemTol
Abs Tol in element number.
Definition: ChemEquil.h:35
int maxIterations
Maximum number of iterations.
Definition: ChemEquil.h:36
int propertyPair
Property pair flag.
Definition: ChemEquil.h:49
bool contin
Continuation flag.
Definition: ChemEquil.h:56
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:102
This file contains definitions of terms that are used in internal routines and are unlikely to need m...
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:180
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:264
int _equilflag(const char *xy)
map property strings to integers
Definition: ChemEquil.cpp:21