https://mooseframework.inl.gov
Loading...
Searching...
No Matches
GeochemicalSolver.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "GeochemicalSolver.h"
12
14 unsigned num_kin,
16 Real abs_tol,
17 Real rel_tol,
18 unsigned max_iter,
19 Real max_initial_residual,
20 Real swap_threshold,
21 unsigned max_swaps_allowed,
22 const std::vector<std::string> & prevent_precipitation,
23 Real max_ionic_strength,
24 unsigned ramp_max_ionic_strength,
25 bool evaluate_kin_always)
26 : _is(is),
27 _num_basis(num_basis),
28 _num_kin(num_kin),
29 _num_basis_in_algebraic_system(_num_basis),
30 _num_in_algebraic_system(_num_basis + _num_kin),
31 _residual(_num_in_algebraic_system),
32 _abs_residual(0.0),
33 _jacobian(_num_in_algebraic_system, _num_in_algebraic_system),
34 _new_mol(_num_in_algebraic_system),
35 _abs_tol(abs_tol),
36 _rel_tol(rel_tol),
37 _res0_times_rel(0.0),
38 _max_iter(max_iter),
39 _max_initial_residual(max_initial_residual),
40 _swap_threshold(swap_threshold),
41 _max_swaps_allowed(max_swaps_allowed),
42 _prevent_precipitation(prevent_precipitation),
43 _max_ionic_strength(max_ionic_strength),
44 _ramp_max_ionic_strength(ramp_max_ionic_strength),
45 _evaluate_kin_always(evaluate_kin_always),
46 _input_mole_additions(_num_basis + _num_kin),
47 _input_dmole_additions(_num_basis + _num_kin, _num_basis + _num_kin)
48{
50 mooseError("GeochemicalSolver: ramp_max_ionic_strength must be less than max_iter");
51 if (max_initial_residual <= 0.0)
52 mooseError("GeochemicalSolver: max_initial_residual must be positive");
53 if (max_ionic_strength < 0.0)
54 mooseError("GeochemicalSolver: max_ionic_strength must not be negative");
55 if (abs_tol < 0.0)
56 mooseError("GeochemicalSolver: abs_tol must not be negative");
57 if (rel_tol < 0.0)
58 mooseError("GeochemicalSolver: rel_tol must not be negative");
59 if (rel_tol == 0.0 && abs_tol == 0.0)
60 mooseError("GeochemicalSolver: either rel_tol or abs_tol must be positive");
61}
62
63void
65{
66 if (max_initial_residual <= 0.0)
67 mooseError("GeochemicalSolver: max_initial_residual must be positive");
68 _max_initial_residual = max_initial_residual;
69}
70
71Real
76
77Real
79 DenseVector<Real> & residual,
80 const DenseVector<Real> & mole_additions) const
81{
82 for (unsigned a_ind = 0; a_ind < _num_in_algebraic_system; ++a_ind)
83 residual(a_ind) = egs.getResidualComponent(a_ind, mole_additions);
84 return residual.l1_norm();
85}
86
87void
89 DenseMatrix<Real> & jacobian,
90 DenseVector<Real> & new_mol) const
91{
92 jacobian.lu_solve(_residual, new_mol);
93
94 // at this point we want to do molality = molality - new_mol, but
95 // Bethke recommends underrelaxation - probably want to do PETSc variational bounds in the
96 // future
97 Real one_over_delta = 1.0;
98 const std::vector<Real> current_molality_and_pot = egs.getAlgebraicVariableValues();
99 for (unsigned a_ind = 0; a_ind < _num_in_algebraic_system; ++a_ind)
100 one_over_delta =
101 std::max(one_over_delta, new_mol(a_ind) * 2.0 / current_molality_and_pot[a_ind]);
102 for (unsigned a_ind = 0; a_ind < _num_in_algebraic_system; ++a_ind)
103 new_mol(a_ind) = current_molality_and_pot[a_ind] - new_mol(a_ind) / one_over_delta;
104}
105
106bool
108 unsigned & swap_out_of_basis,
109 unsigned & swap_into_basis,
110 std::stringstream & ss) const
111{
112 bool swap_needed = false;
113
115
116 // check if any basis minerals have negative free number of moles
117 const std::vector<Real> & basis_molality = egs.getSolventMassAndFreeMolalityAndMineralMoles();
118 const std::vector<unsigned> molality_order =
119 GeochemistrySortedIndices::sortedIndices(basis_molality, true);
120
121 // if the Newton did not converge then check for small molalities in the non-minerals
123 {
124 for (const auto & i : molality_order)
125 {
126 if (basis_molality[i] >= _swap_threshold)
127 {
128 // since we're going through basis_molality in ascending order, as soon as we get >=
129 // _swap_threshold, we're safe
130 break;
131 }
132 else if (!mgd.basis_species_mineral[i] && egs.getInAlgebraicSystem()[i] &&
134 {
135 // a non-mineral in the algebraic system has super low molality: try to find a legitimate
136 // swap
137 swap_out_of_basis = i;
138 bool legitimate_swap_found = egs.getSwapper().findBestEqmSwap(swap_out_of_basis,
139 mgd,
141 true,
142 false,
143 false,
144 swap_into_basis);
145 if (legitimate_swap_found)
146 {
147 ss << "Basis species " << mgd.basis_species_name[swap_out_of_basis]
148 << " has very low molality of " << basis_molality[i] << " compared to "
149 << _swap_threshold << ". Swapping it with equilibrium species "
150 << mgd.eqm_species_name[swap_into_basis] << std::endl;
151 swap_needed = true;
152 break;
153 }
154 // if no legitimate swap is found, then loop around to the next basis species
155 }
156 }
157 }
158 if (swap_needed)
159 return swap_needed;
160
161 // now look through the molalities for minerals that are consumed
162 for (const auto & i : molality_order)
163 {
164 if (basis_molality[i] > 0.0)
165 {
166 // since we're going through basis_molality in ascending order, as soon as we get >0, we're
167 // safe
168 break;
169 }
170 else if (mgd.basis_species_mineral[i])
171 {
172 swap_needed = true;
173 swap_out_of_basis = i;
174 bool legitimate_swap_found = egs.getSwapper().findBestEqmSwap(swap_out_of_basis,
175 mgd,
177 false,
178 false,
179 false,
180 swap_into_basis);
181 if (!legitimate_swap_found)
182 mooseException("Cannot find a legitimate swap for mineral ",
183 mgd.basis_species_name[swap_out_of_basis]);
184 ss << "Mineral " << mgd.basis_species_name[swap_out_of_basis]
185 << " consumed. Swapping it with equilibrium species "
186 << mgd.eqm_species_name[swap_into_basis] << std::endl;
187 break;
188 }
189 }
190 if (swap_needed)
191 return swap_needed;
192
193 // check maximum saturation index is not positive
194 const std::vector<Real> & eqm_SI = egs.getSaturationIndices();
195 const std::vector<unsigned> mineral_SI_order =
197
198 for (const auto & j : mineral_SI_order)
199 if (eqm_SI[j] > 0.0)
201 if (std::find(_prevent_precipitation.begin(),
204 {
205 // mineral has positive saturation index and user is not preventing its precipitation.
206 // determine the basis species to swap out
207 swap_needed = true;
208 swap_into_basis = j;
209 bool legitimate_swap_found = false;
210 swap_out_of_basis = 0;
211 Real best_stoi = 0.0;
212 for (unsigned i = 1; i < _num_basis; ++i) // never swap water (i=0)
213 {
214 if (basis_molality[i] > 0.0 && i != egs.getChargeBalanceBasisIndex() &&
216 {
217 // don't want to swap out the charge-balance species or any gases of fixed fugacity
218 // or any species with fixed activity
219 const Real stoi = std::abs(mgd.eqm_stoichiometry(j, i)) / basis_molality[i];
220 if (stoi > best_stoi)
221 {
222 legitimate_swap_found = true;
223 best_stoi = stoi;
224 swap_out_of_basis = i;
225 }
226 }
227 }
228 if (!legitimate_swap_found)
229 mooseException(
230 "Cannot find a legitimate swap for the supersaturated equilibrium species ",
232 ss << "Mineral " << mgd.eqm_species_name[j]
233 << " supersaturated. Swapping it with basis species "
234 << mgd.basis_species_name[swap_out_of_basis] << std::endl;
235 break;
236 }
237 return swap_needed;
238}
239
240bool
242 Real dt,
243 DenseVector<Real> & mole_additions,
244 DenseMatrix<Real> & dmole_additions)
245{
246 const Real initial_r = _abs_residual;
247
248 // to get an indication of whether we should increase or decrease molalities in the algorithm
249 // below, find the median of the original molalities
250 const std::vector<Real> & original_molality = egs.getAlgebraicBasisValues();
251 const std::vector<unsigned> mol_order =
252 GeochemistrySortedIndices::sortedIndices(original_molality, false);
253 const Real median_molality = original_molality[mol_order[_num_basis_in_algebraic_system / 2]];
254
255 // get the index order of the residual vector (largest first): ignore residuals for surface
256 // potentials (we're only using _num_basis_in_algebraic_system, not _num_in_algebraic_system)
257 unsigned ind = 0;
258 std::vector<unsigned> res_order(_num_basis_in_algebraic_system);
259 std::iota(res_order.begin(), res_order.end(), ind++);
260 std::sort(res_order.begin(),
261 res_order.end(),
262 [&](int i, int j) { return std::abs(_residual(i)) > std::abs(_residual(j)); });
263
264 const std::vector<Real> & original_molality_and_pot = egs.getAlgebraicVariableValues();
265 DenseVector<Real> new_molality_and_pot(original_molality_and_pot);
266 for (const auto & a : res_order)
267 {
268 if (std::abs(_residual(a)) < _max_initial_residual)
269 return false; // haven't managed to find a suitable new molality, and all remaining residual
270 // components are less than the cutoff, so cannot appreciably reduce from now
271 // on
272
273 const Real multiplier = (original_molality_and_pot[a] > median_molality) ? 0.5 : 2.0;
274 // try using the multiplier
275 new_molality_and_pot(a) = multiplier * original_molality_and_pot[a];
276 egs.setAlgebraicVariables(new_molality_and_pot);
278 {
279 mole_additions = _input_mole_additions;
280 dmole_additions = _input_dmole_additions;
281 egs.addKineticRates(dt, mole_additions, dmole_additions);
282 }
283 _abs_residual = computeResidual(egs, _residual, mole_additions);
284 if (_abs_residual < initial_r)
285 return true; // success: found a new molality that decreases the initial |R|
286
287 // the above approach did not decrease |R|, so try using 1/multiplier
288 new_molality_and_pot(a) = original_molality_and_pot[a] / multiplier;
289 egs.setAlgebraicVariables(new_molality_and_pot);
291 {
292 mole_additions = _input_mole_additions;
293 dmole_additions = _input_dmole_additions;
294 egs.addKineticRates(dt, mole_additions, dmole_additions);
295 }
296 _abs_residual = computeResidual(egs, _residual, mole_additions);
297 if (_abs_residual < initial_r)
298 return true; // success: found a new molality that decreases the initial |R|
299
300 // the new molalities did not decrease |R|, so revert to the original molality, and move to
301 // next-biggest residual component
302 new_molality_and_pot(a) = original_molality_and_pot[a];
303 egs.setAlgebraicVariables(new_molality_and_pot);
305 {
306 mole_additions = _input_mole_additions;
307 dmole_additions = _input_dmole_additions;
308 egs.addKineticRates(dt, mole_additions, dmole_additions);
309 }
310 _abs_residual = computeResidual(egs, _residual, mole_additions);
311 }
312 return false;
313}
314
315void
317 std::stringstream & ss,
318 unsigned & tot_iter,
319 Real & abs_residual,
320 Real dt,
321 DenseVector<Real> & mole_additions,
322 DenseMatrix<Real> & dmole_additions)
323{
324 ss.str("");
325 tot_iter = 0;
326 unsigned num_swaps = 0;
327
328 // capture the inputs expressed in the current basis
329 _input_mole_additions = mole_additions;
330 _input_dmole_additions = dmole_additions;
331
333
334 bool still_swapping = true;
335 while (still_swapping && num_swaps <= _max_swaps_allowed)
336 {
339 _residual = DenseVector<Real>(_num_in_algebraic_system);
341 _new_mol = DenseVector<Real>(_num_in_algebraic_system);
342
343 unsigned iter = 0;
344 const Real max_is0 =
345 std::min(1.0, (iter + 1.0) / (_ramp_max_ionic_strength + 1.0)) * _max_ionic_strength;
346 _is.setMaxIonicStrength(max_is0);
348
349 mole_additions = _input_mole_additions;
350 dmole_additions = _input_dmole_additions;
351 egs.addKineticRates(dt, mole_additions, dmole_additions);
352
353 _abs_residual = computeResidual(egs, _residual, mole_additions);
354 bool reducing_initial_molalities = (_abs_residual > _max_initial_residual);
355 const unsigned max_tries =
356 _num_basis *
357 unsigned(
359 std::log(1.1)); // assume each successful call to reduceInitialResidual reduces residual
360 // by about 1.1, but that the correct basis species has to be chosen
361 unsigned tries = 0;
362 while (reducing_initial_molalities && ++tries <= max_tries)
363 reducing_initial_molalities = reduceInitialResidual(egs, dt, mole_additions, dmole_additions);
364
365 ss << "iter = " << iter << " |R| = " << _abs_residual << std::endl;
367 while ((_abs_residual >= _res0_times_rel && _abs_residual >= _abs_tol && iter < _max_iter) ||
369 {
370 iter += 1;
371 tot_iter += 1;
372 egs.computeJacobian(_residual, _jacobian, mole_additions, dmole_additions);
374 const Real max_is =
375 std::min(1.0, (iter + 1.0) / (_ramp_max_ionic_strength + 1.0)) * _max_ionic_strength;
376 _is.setMaxIonicStrength(max_is);
380 ss << "Changed change balance species to "
381 << mgd.basis_species_name.at(egs.getChargeBalanceBasisIndex()) << std::endl;
383 {
384 mole_additions = _input_mole_additions;
385 dmole_additions = _input_dmole_additions;
386 egs.addKineticRates(dt, mole_additions, dmole_additions);
387 }
388 _abs_residual = computeResidual(egs, _residual, mole_additions);
389 ss << "iter = " << iter << " |R| = " << _abs_residual << std::endl;
390 }
391
392 abs_residual = _abs_residual; // record the final residual
393
394 if (iter >= _max_iter)
395 ss << std::endl << "Warning: Number of iterations exceeds " << _max_iter << std::endl;
396
397 unsigned swap_out_of_basis = 0;
398 unsigned swap_into_basis = 0;
399 try
400 {
401 // to ensure basis molality is correct:
402 for (unsigned i = 0; i < _num_basis; ++i)
403 egs.addToBulkMoles(i, mole_additions(i));
404 // now check for small basis molalities, minerals consumed and minerals precipitated
405 still_swapping = swapNeeded(egs, swap_out_of_basis, swap_into_basis, ss);
406 }
407 catch (const MooseException & e)
408 {
409 mooseException(e.what());
410 }
411 if (still_swapping)
412 {
413 // need to do a swap and re-solve
414 try
415 {
416 // before swapping, remove any basis mole_additions that came from kinetics. The
417 // following loop over i, combined with the above egs.addToBulkMoles(mole_additions), where
418 // mole_additions = _input_mole_additions + kinetic_additions, means that the bulk
419 // moles in egs will have only been incremented by _input_mole_additions. Hence,
420 // _input_mole_additions can be set to zero in preparation for the next solve in the new
421 // basis.
422 // NOTE: if _input_mole_additions depend on molalities, this approach introduces error,
423 // because the following call to addToBulkMoles and subsequent _input_mole_additions = 0
424 // means the mole additions are FIXED
425 for (unsigned i = 0; i < _num_basis; ++i)
426 {
427 egs.addToBulkMoles(i, _input_mole_additions(i) - mole_additions(i));
428 _input_mole_additions(i) = 0.0;
429 for (unsigned j = 0; j < _num_basis + _num_kin; ++j)
430 _input_dmole_additions(i, j) = 0.0;
431 }
432 egs.performSwap(swap_out_of_basis, swap_into_basis);
433 num_swaps += 1;
434 }
435 catch (const MooseException & e)
436 {
437 mooseException(e.what());
438 }
439 }
440 }
441
442 if (num_swaps > _max_swaps_allowed)
443 {
444 mooseException("Maximum number of swaps performed during solve");
445 }
447 {
448 mooseException("Failed to converge");
449 }
450 else
451 {
452 // the basis species additions have been added above with addtoBulkMoles: now add the kinetic
453 // additions:
454 for (unsigned i = 0; i < _num_basis; ++i)
455 mole_additions(i) = 0.0;
456 egs.updateOldWithCurrent(mole_additions);
458 }
459}
460
461void
462GeochemicalSolver::setRampMaxIonicStrength(unsigned ramp_max_ionic_strength)
463{
464 if (ramp_max_ionic_strength > _max_iter)
465 mooseError("GeochemicalSolver: ramp_max_ionic_strength must be less than max_iter");
466 _ramp_max_ionic_strength = ramp_max_ionic_strength;
467}
468
469unsigned
const ModelGeochemicalDatabase mgd
void mooseError(Args &&... args)
unsigned _ramp_max_ionic_strength
Number of iterations over which to increase the maximum ionic strength to _max_ionic_strength.
DenseVector< Real > _new_mol
the new molality after finding the solution of _jacobian * neg_change_mol = _residual
Real getMaxInitialResidual() const
Get value for max_initial_residual.
DenseMatrix< Real > _input_dmole_additions
d(mole_additions)/d(species) as specified in the argument of solveSystem
Real _max_initial_residual
maximum desired initial residual
const unsigned _max_iter
maximum number of iterations allowed during an inner solve
bool _evaluate_kin_always
When to compute the kinetic rates: if true then evaluate before every residual calculation,...
const unsigned _num_basis
Number of species in the basis.
void solveSystem(GeochemicalSystem &egs, std::stringstream &ss, unsigned &tot_iter, Real &abs_residual, Real dt, DenseVector< Real > &mole_additions, DenseMatrix< Real > &dmole_additions)
Solve the system.
GeochemicalSolver(unsigned num_basis, unsigned num_kin, GeochemistryIonicStrength &is, Real abs_tol, Real rel_tol, unsigned max_iter, Real max_initial_residual, Real swap_threshold, unsigned max_swaps_allowed, const std::vector< std::string > &prevent_precipitation, Real max_ionic_strength, unsigned ramp_max_ionic_strength, bool evaluate_kin_always)
Construct and check for sensible arguments.
bool swapNeeded(const GeochemicalSystem &egs, unsigned &swap_out_of_basis, unsigned &swap_into_basis, std::stringstream &ss) const
Check if a basis swap is needed.
Real computeResidual(const GeochemicalSystem &egs, DenseVector< Real > &residual, const DenseVector< Real > &mole_additions) const
Builds the residual of the algebraic system.
const std::vector< std::string > _prevent_precipitation
The minerals named in this list can have positive saturation indices and will not precipitate.
void setRampMaxIonicStrength(unsigned ramp_max_ionic_strength)
Sets the value of _ramp_max_ionic_strength.
unsigned getRampMaxIonicStrength() const
Gets the value of _ramp_max_ionic_strength.
DenseVector< Real > _input_mole_additions
the mole_additions for the basis and kinetic species as specified in the argument of solveSystem
DenseVector< Real > _residual
residual of the algebraic system we wish to solve
DenseMatrix< Real > _jacobian
jacobian of the algebraic system
const Real _rel_tol
If the residual of the algebraic system falls below this value times the initial residual,...
const Real _max_ionic_strength
Maximum ionic strength allowed.
const Real _swap_threshold
If a basis molality < swap_threshold, we attempt to swap it out of the basis.
Real _abs_residual
L1 norm of residual.
Real _res0_times_rel
_res0_times_rel = _rel_tol * initial residual
bool reduceInitialResidual(GeochemicalSystem &egs, Real dt, DenseVector< Real > &mole_additions, DenseMatrix< Real > &dmole_additions)
Progressively alter the initial-guess molalities for the algebraic system to attempt to reduce the re...
void solveAndUnderrelax(const GeochemicalSystem &egs, DenseMatrix< Real > &jacobian, DenseVector< Real > &new_mol) const
Solves _jacobian * neg_change_mol = _residual for neg_change_mol, then performs an underrelaxation to...
void setMaxInitialResidual(Real max_initial_residual)
Set value for max_initial_residual.
unsigned _num_basis_in_algebraic_system
Number of basis molalities (and potentially solvent water mass) in the algebraic system.
const Real _abs_tol
If the residual of the algebraic system falls below this value, the Newton process has converged.
const unsigned _num_kin
Number of kinetic species.
GeochemistryIonicStrength & _is
The ionic-strength calculator.
const unsigned _max_swaps_allowed
Maximum number of swaps allowed before the solve aborts.
unsigned _num_in_algebraic_system
Number of unknowns (molalities and surface potentials) in the algebraic system.
This class holds information about bulk composition, molalities, activities, activity coefficients,...
std::vector< Real > getSaturationIndices() const
Real getResidualComponent(unsigned algebraic_ind, const DenseVector< Real > &mole_additions) const
Return the residual of the algebraic system for the given algebraic index.
std::vector< Real > getAlgebraicBasisValues() const
void enforceChargeBalance()
Enforces charge balance by altering the constraint_value and bulk_moles_old of the charge-balance spe...
void setAlgebraicVariables(const DenseVector< Real > &algebraic_var)
Set the variables in the algebraic system (molalities and potentially the mass of solvent water,...
unsigned getNumBasisInAlgebraicSystem() const
return the number of basis species in the algebraic system
void addKineticRates(Real dt, DenseVector< Real > &mole_additions, DenseMatrix< Real > &dmole_additions)
Computes the kinetic rates and their derivatives based on the current values of molality,...
const GeochemistrySpeciesSwapper & getSwapper() const
void updateOldWithCurrent(const DenseVector< Real > &mole_additions)
Usually used at the end of a solve, to provide correct initial conditions to the next time-step,...
void performSwap(unsigned swap_out_of_basis, unsigned swap_into_basis)
Perform the basis swap, and ensure that the resulting system is consistent.
const std::vector< bool > & getBasisActivityKnown() const
unsigned getChargeBalanceBasisIndex() const
return the index of the charge-balance species in the basis list
const std::vector< bool > & getInAlgebraicSystem() const
void addToBulkMoles(unsigned basis_ind, Real value)
Add to the MOLES_BULK_SPECIES (or MOLES_BULK_WATER if basis_ind = 0) for the basis species.
bool alterChargeBalanceSpecies(Real threshold_molality)
If the molality of the charge-balance basis species is less than threshold molality,...
const ModelGeochemicalDatabase & getModelGeochemicalDatabase() const
const std::vector< Real > & getSolventMassAndFreeMolalityAndMineralMoles() const
unsigned getNumInAlgebraicSystem() const
std::vector< Real > getAlgebraicVariableValues() const
void computeJacobian(const DenseVector< Real > &res, DenseMatrix< Real > &jac, const DenseVector< Real > &mole_additions, const DenseMatrix< Real > &dmole_additions) const
Compute the Jacobian for the algebraic system and put it in jac.
Real getEquilibriumMolality(unsigned j) const
Calculators to compute ionic strength and stoichiometric ionic strength.
void setMaxStoichiometricIonicStrength(Real max_stoichiometric_ionic_strength)
Set the maximum stoichiometric ionic strength.
void setMaxIonicStrength(Real max_ionic_strength)
Set the maximum ionic strength.
bool findBestEqmSwap(unsigned basis_ind, const ModelGeochemicalDatabase &mgd, const std::vector< Real > &eqm_molality, bool minerals_allowed, bool gas_allowed, bool sorption_allowed, unsigned &best_eqm_species) const
For the the given basis index, find the equilibrium index that it should be swapped with.
virtual const char * what() const
std::vector< unsigned > sortedIndices(const std::vector< Real > &to_sort, bool ascending)
Produces a vector of indices corresponding to the smallest-to-biggest entries in to_sort (or biggest-...
Data structure to hold all relevant information from the database file.
std::vector< bool > eqm_species_mineral
eqm_species_mineral[i] = true iff the i^th equilibrium species is a mineral
DenseMatrix< Real > eqm_stoichiometry
eqm_stoichiometry(i, j) = stoichiometric coefficient for equilibrium species "i" in terms of the basi...
std::vector< bool > basis_species_mineral
basis_species_mineral[j] = true iff the j^th basis species is a mineral
std::vector< std::string > basis_species_name
basis_species_name[j] = name of the j^th basis species
std::vector< bool > basis_species_gas
basis_species_gas[j] = true iff the j^th basis species is a gas
std::vector< std::string > eqm_species_name
eqm_species_name[i] = name of the i^th eqm species