Cantera
Loading...
Searching...
No Matches
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
12
13namespace Cantera
14{
15
16class DenseMatrix;
17class ThermoPhase;
18//! map property strings to integers
19int _equilflag(const char* xy);
20
21/**
22 * Chemical equilibrium options. Used internally by class ChemEquil.
23 */
24class EquilOpt
25{
26public:
27 EquilOpt() = default;
28
29 double relTolerance = 1e-8; //!< Relative tolerance
30 double absElemTol = 1e-70; //!< Abs Tol in element number
31 int maxIterations = 1000; //!< Maximum number of iterations
32 int iterations = 0; //!< Iteration counter
33
34 /**
35 * Enforce temperature validity limits during equilibrium solver iterations.
36 *
37 * When false, the solver is allowed to extrapolate beyond the nominal
38 * temperature range of the thermodynamic fits, and a warning is issued if
39 * the converged state is outside that range. Broad numerical bounds are
40 * still applied to avoid nonphysical temperatures during iterations.
41 */
43
44 /**
45 * Warn when the element potentials cannot be resolved to `relTolerance` and a
46 * reduced-accuracy solution is returned.
47 *
48 * Should be disabled by callers that respond to such a return by falling back to
49 * a different solver, since in that case the reduced-accuracy solution is
50 * discarded and there is nothing for the user to act on.
51 *
52 * @since New in %Cantera 4.0.
53 */
55
56};
57
58/**
59 * @defgroup equilGroup Chemical Equilibrium
60 * @details Classes and functions used for calculating 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 @cite smith1982, 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 (for example,
78 * 2 H2 + O2). In general, if speed is important, this solver should be tried first,
79 * and if it fails then use MultiPhaseEquil.
80 * @ingroup equilGroup
81 */
82class ChemEquil
83{
84public:
85 ChemEquil() = default;
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(ThermoPhase& s);
95
96 virtual ~ChemEquil() = default;
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 * @return See the other equilibrate() overload for the meaning of the return
107 * value.
108 */
109 int equilibrate(ThermoPhase& s, const char* XY, int loglevel = 0);
110
111 /**
112 * Compute the equilibrium composition for two specified properties and the
113 * specified element moles.
114 *
115 * The two specified properties are obtained by querying the ThermoPhase
116 * object. The properties must be already contained within the current
117 * thermodynamic state of the system.
118 *
119 * @param s phase object to be equilibrated
120 * @param XY property pair to hold constant
121 * @param elMoles specified vector of element abundances.
122 * @param loglevel Specify amount of debug logging (0 to disable)
123 * @return Successful returns are indicated by a return value of 0. A return
124 * value of 1 indicates that the element potentials could not be resolved to
125 * the specified tolerance because the problem is too poorly conditioned, and
126 * that a solution of reduced accuracy is being returned; see
127 * EquilOpt::warnOnInexactConvergence. Unsuccessful returns are indicated by a
128 * return value of -1 for lack of convergence or -3 for a singular Jacobian.
129 *
130 * @since The possible return value of 1 is new in %Cantera 4.0.
131 */
132 int equilibrate(ThermoPhase& s, const char* XY, span<double> elMoles,
133 int loglevel = 0);
134
135 /**
136 * Options controlling how the calculation is carried out.
137 * @see EquilOpt
138 */
140
141protected:
142 //! Pointer to the ThermoPhase object used to initialize this object.
143 /*!
144 * This ThermoPhase object must be compatible with the ThermoPhase objects
145 * input from the equilibrate function. Currently, this means that the 2
146 * ThermoPhases have to have consist of the same species and elements.
147 */
149
150 //! number of atoms of element m in species k.
151 double nAtoms(size_t k, size_t m) const {
152 return m_comp[k*m_mm + m];
153 }
154
155 /**
156 * Prepare for equilibrium calculations.
157 * @param s object representing the solution phase.
158 */
159 void initialize(ThermoPhase& s);
160
161 /**
162 * Set mixture to an equilibrium state consistent with specified element
163 * potentials and temperature.
164 *
165 * @param s mixture to be updated
166 * @param x vector of non-dimensional element potentials
167 * @f[ \lambda_m/RT @f].
168 * @param t temperature in K.
169 */
170 void setToEquilState(ThermoPhase& s, span<const double> x, double t);
171
172 //! Estimate the initial mole numbers. This version borrows from the
173 //! MultiPhaseEquil solver.
174 int setInitialMoles(ThermoPhase& s, span<double> elMoleGoal, int loglevel = 0);
175
176 //! Generate a starting estimate for the element potentials.
177 int estimateElementPotentials(ThermoPhase& s, span<double> lambda,
178 span<double> elMolesGoal, int loglevel = 0);
179
180 /**
181 * Do a calculation of the element potentials using the Brinkley method,
182 * p. 129 Smith and Missen @cite smith1982.
183 *
184 * We have found that the previous estimate may not be good enough to
185 * avoid drastic numerical issues associated with the use of a numerically
186 * generated Jacobian used in the main algorithm.
187 *
188 * The Brinkley algorithm, here, assumes a constant T, P system and uses a
189 * linearized analytical Jacobian that turns out to be very stable even
190 * given bad initial guesses.
191 *
192 * The pressure and temperature to be used are in the ThermoPhase object
193 * input into the routine.
194 *
195 * The initial guess for the element potentials used by this routine is
196 * taken from the input vector, x.
197 *
198 * elMoles is the input element abundance vector to be matched.
199 *
200 * Nonideal phases are handled in principle. This is done by calculating
201 * the activity coefficients and adding them into the formula in the
202 * correct position. However, these are treated as a RHS contribution
203 * only. Therefore, convergence might be a problem. This has not been
204 * tested. Also molality based unit systems aren't handled.
205 *
206 * On return, int return value contains the success code:
207 * - 0 - successful
208 * - 1 - unsuccessful, max num iterations exceeded
209 * - -3 - unsuccessful, singular Jacobian
210 *
211 * NOTE: update for activity coefficients.
212 */
213 int estimateEP_Brinkley(ThermoPhase& s, span<double> lambda, span<double> elMoles);
214
215 //! Find an acceptable step size and take it.
216 /*!
217 * The original implementation employed a line search technique that
218 * enforced a reduction in the norm of the residual at every successful
219 * step. Unfortunately, this method created false convergence errors near
220 * the end of a significant number of steps, usually special conditions
221 * where there were stoichiometric constraints.
222 *
223 * This new method just does a delta damping approach, based on limiting
224 * the jump in the dimensionless element potentials. Mole fractions are
225 * limited to a factor of 2 jump in the values from this method. Near
226 * convergence, the delta damping gets out of the way.
227 */
228 void dampStep(span<double> oldx, span<double> step, span<double> x);
229
230 /**
231 * Evaluates the residual vector F, of length #m_mm
232 */
233 void equilResidual(ThermoPhase& s, span<const double> x,
234 span<const double> elmtotal, span<double> resid,
235 double xval, double yval, int loglevel = 0);
236
237 void equilJacobian(ThermoPhase& s, span<double> x,
238 span<const double> elmols, DenseMatrix& jac,
239 double xval, double yval, int loglevel = 0);
240
241 void adjustEloc(ThermoPhase& s, span<double> elMolesGoal);
242
243 //! Update internally stored state information.
244 void update(const ThermoPhase& s);
245
246 /**
247 * Given a vector of dimensionless element abundances, this routine
248 * calculates the moles of the elements and the moles of the species.
249 *
250 * @param s ThermoPhase object
251 * @param[in] x current dimensionless element potentials
252 * @param[in] Xmol_i_calc Mole fractions of the species
253 * @param[in] pressureConst Pressure
254 */
255 double calcEmoles(ThermoPhase& s, span<double> x, const double& n_t,
256 span<const double> Xmol_i_calc, span<double> eMolesCalc,
257 span<double> n_i_calc, double pressureConst);
258
259 size_t m_mm; //!< number of elements in the phase
260 size_t m_kk; //!< number of species in the phase
261 size_t m_skip = npos;
262
263 //! This is equal to the rank of the stoichiometric coefficient matrix when
264 //! it is computed. It's initialized to #m_mm.
266
267 function<double(ThermoPhase&)> m_p1, m_p2;
268
269 //! Current value of the mole fractions in the single phase. length = #m_kk.
270 vector<double> m_molefractions;
271
272 //! Current value of the sum of the element abundances given the current
273 //! element potentials.
274 double m_elementTotalSum = 1.0;
275
276 //! Current value of the element mole fractions. Note these aren't the goal
277 //! element mole fractions.
278 vector<double> m_elementmolefracs;
279 vector<double> m_jwork1;
280 vector<double> m_jwork2;
281
282 //! Storage of the element compositions. natom(k,m) = m_comp[k*m_mm+ m];
283 vector<double> m_comp;
284
285 //! Index of the element id corresponding to the electric charge of each
286 //! species. Equal to -1 if there is no such element id.
287 size_t m_eloc = npos;
288
289 vector<double> m_mu_RT;
290
291 //! Dimensionless values of the Gibbs free energy for the standard state of
292 //! each species, at the temperature and pressure of the solution (the star
293 //! standard state).
294 vector<double> m_muSS_RT;
295 vector<size_t> m_component;
296
297 //! element fractional cutoff, below which the element will be zeroed.
298 double m_elemFracCutoff = 1e-100;
299
300 vector<size_t> m_orderVectorElements;
301 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
int setInitialMoles(ThermoPhase &s, span< double > elMoleGoal, int loglevel=0)
Estimate the initial mole numbers.
void equilResidual(ThermoPhase &s, span< const double > x, span< const double > elmtotal, span< double > resid, double xval, double yval, int loglevel=0)
Evaluates the residual vector F, of length m_mm.
int equilibrate(ThermoPhase &s, const char *XY, int loglevel=0)
Equilibrate a phase, holding the elemental composition fixed at the initial value found within the Th...
size_t m_kk
number of species in the phase
Definition ChemEquil.h:260
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:265
ThermoPhase * m_phase
Pointer to the ThermoPhase object used to initialize this object.
Definition ChemEquil.h:148
double m_elementTotalSum
Current value of the sum of the element abundances given the current element potentials.
Definition ChemEquil.h:274
void update(const ThermoPhase &s)
Update internally stored state information.
int estimateElementPotentials(ThermoPhase &s, span< double > lambda, span< double > elMolesGoal, int loglevel=0)
Generate a starting estimate for the element potentials.
size_t m_eloc
Index of the element id corresponding to the electric charge of each species.
Definition ChemEquil.h:287
double calcEmoles(ThermoPhase &s, span< double > x, const double &n_t, span< const double > Xmol_i_calc, span< double > eMolesCalc, span< double > n_i_calc, double pressureConst)
Given a vector of dimensionless element abundances, this routine calculates the moles of the elements...
void initialize(ThermoPhase &s)
Prepare for equilibrium calculations.
Definition ChemEquil.cpp:89
EquilOpt options
Options controlling how the calculation is carried out.
Definition ChemEquil.h:139
vector< double > m_molefractions
Current value of the mole fractions in the single phase. length = m_kk.
Definition ChemEquil.h:270
vector< double > m_comp
Storage of the element compositions. natom(k,m) = m_comp[k*m_mm+ m];.
Definition ChemEquil.h:283
double nAtoms(size_t k, size_t m) const
number of atoms of element m in species k.
Definition ChemEquil.h:151
double m_elemFracCutoff
element fractional cutoff, below which the element will be zeroed.
Definition ChemEquil.h:298
vector< double > m_muSS_RT
Dimensionless values of the Gibbs free energy for the standard state of each species,...
Definition ChemEquil.h:294
void dampStep(span< double > oldx, span< double > step, span< double > x)
Find an acceptable step size and take it.
vector< double > m_elementmolefracs
Current value of the element mole fractions.
Definition ChemEquil.h:278
size_t m_mm
number of elements in the phase
Definition ChemEquil.h:259
void setToEquilState(ThermoPhase &s, span< const double > x, double t)
Set mixture to an equilibrium state consistent with specified element potentials and temperature.
int estimateEP_Brinkley(ThermoPhase &s, span< double > lambda, span< double > elMoles)
Do a calculation of the element potentials using the Brinkley method, p.
A class for full (non-sparse) matrices with Fortran-compatible data storage, which adds matrix operat...
Definition DenseMatrix.h:42
Chemical equilibrium options.
Definition ChemEquil.h:25
double absElemTol
Abs Tol in element number.
Definition ChemEquil.h:30
int iterations
Iteration counter.
Definition ChemEquil.h:32
bool warnOnInexactConvergence
Warn when the element potentials cannot be resolved to relTolerance and a reduced-accuracy solution i...
Definition ChemEquil.h:54
bool enforceTemperatureLimits
Enforce temperature validity limits during equilibrium solver iterations.
Definition ChemEquil.h:42
double relTolerance
Relative tolerance.
Definition ChemEquil.h:29
int maxIterations
Maximum number of iterations.
Definition ChemEquil.h:31
Base class for a phase with thermodynamic properties.
This file contains definitions of constants, types and terms that are used in internal routines and a...
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:183
int _equilflag(const char *xy)
map property strings to integers
Definition ChemEquil.cpp:21