https://mooseframework.inl.gov
Loading...
Searching...
No Matches
GeochemistrySpeciesSwapper.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
11
13 : _stoi_tol(stoi_tol),
14 _swap_matrix(basis_size, basis_size),
15 _true_swap_matrix(basis_size, basis_size),
16 _inv_swap_matrix(basis_size, basis_size),
17 _swap_sigma(basis_size),
18 _swap_U(basis_size, basis_size),
19 _swap_VT(basis_size, basis_size)
20{
21}
22
23void
25 const std::string & replace_this,
26 const std::string & with_this)
27{
28 if (replace_this == "H2O")
29 mooseError("Cannot remove H2O from the basis");
30 if (mgd.basis_species_index.count(replace_this) == 0)
31 mooseError(replace_this + " is not in the basis, so cannot be removed from the basis");
32 if (mgd.eqm_species_index.count(with_this) == 0)
33 mooseError(with_this + " is not an equilibrium species, so cannot be "
34 "removed from the equilibrium species list");
35
36 checkSwap(mgd, mgd.basis_species_index.at(replace_this), mgd.eqm_species_index.at(with_this));
37}
38
39void
41 unsigned basis_index_to_replace,
42 unsigned eqm_index_to_insert)
43{
44 const unsigned num_cols = mgd.basis_species_index.size();
45 const unsigned num_rows = mgd.eqm_species_index.size();
46 if (basis_index_to_replace == 0)
47 mooseError("Cannot remove H2O from the basis");
48 if (basis_index_to_replace >= num_cols)
49 mooseError(basis_index_to_replace, " exceeds the number of basis species in the problem");
50 if (eqm_index_to_insert >= num_rows)
51 mooseError(eqm_index_to_insert, " exceeds the number of equilibrium species in the problem");
52 if (mgd.surface_sorption_related[eqm_index_to_insert])
54 "Equilibrium species ",
55 mgd.eqm_species_name[eqm_index_to_insert],
56 " is involved in surface sorption so cannot be swapped into the basis. If this is truly "
57 "necessary, code enhancements will need to be made including: recording whether basis "
58 "species are involved in surface sorption, including them in the surface-potential "
59 "calculations, and carefully swapping surface-potential-modified equilibrium constants");
60
61 constructInverseMatrix(mgd, basis_index_to_replace, eqm_index_to_insert);
62}
63
64void
66 unsigned basis_index_to_replace,
67 unsigned eqm_index_to_insert)
68{
69 const unsigned num_cols = mgd.basis_species_index.size();
70
71 if (_swap_matrix.n() != num_cols)
72 mooseError("GeochemistrySpeciesSwapper constructed with incorrect "
73 "basis_species size");
74
75 // This is a private method, called from checkSwap, so we know that all the inputs have been
76 // sanity-checked. The only way the swap could be invalid is that the swap matrix is not
77 // invertible. This could be due to the solve algorithm attempting to perform an invalid swap, so
78 // only mooseExceptions are thrown below, instead of mooseError, to allow the solve algorithm a
79 // chance to attempt another swap.
80
81 // construct the swap matrix. new_basis = swap_matrix * old_basis
82 // We shove the equilibrium species into the "slot" of the basis species it is
83 // replacing
85 for (unsigned i = 0; i < num_cols; ++i)
86 _swap_matrix(i, i) = 1.0;
87 for (unsigned i = 0; i < num_cols; ++i)
88 _swap_matrix(basis_index_to_replace, i) = mgd.eqm_stoichiometry(eqm_index_to_insert, i);
89
90 // record the swap matrix since the SVD trashes it
92
93 // In order to invert _swap_matrix, perform the SVD: A = U * D * VT
94 // Although every matrix has a SVD, this process might fail due to numerical errors. Therefore
95 // the following could be wrapped in a try-catch block, but I have never been able to trigger a
96 // failure since _swap_matrix is always so well-formed due to all the checks on the database while
97 // parsing it
99 const Real l1norm = _swap_sigma.l1_norm();
100 for (unsigned i = 0; i < num_cols; ++i)
101 if (std::abs(_swap_sigma(i) / l1norm) < _stoi_tol)
102 mooseException("Matrix is not invertible, which signals an invalid basis swap");
103
104 // Find the inverse, which is VT^-1 * D^-1 * U^-1 = V * D^-1 * UT
105 for (unsigned i = 0; i < num_cols; ++i)
106 _swap_U.scale_column(i, 1.0 / _swap_sigma(i)); // (scale the columns of U)^T = D^-1 * UT
107 for (unsigned i = 0; i < num_cols; ++i)
108 for (unsigned j = 0; j < num_cols; ++j)
109 _inv_swap_matrix(i, j) = _swap_VT(j, i); // _inv_swap_matrix = V
110 _inv_swap_matrix.right_multiply_transpose(_swap_U); // V * (scale the columns of U)^T
111}
112
113void
115 const std::string & replace_this,
116 const std::string & with_this)
117{
118 // check the swap is valid (if not then mooseError or mooseException)
119 // and if it's valid, create the inverse swap matrix
120 checkSwap(mgd, replace_this, with_this);
121
122 // perform the swap inside the MGD datastructure
123 alterMGD(mgd, mgd.basis_species_index.at(replace_this), mgd.eqm_species_index.at(with_this));
124}
125
126void
128 unsigned basis_index_to_replace,
129 unsigned eqm_index_to_insert)
130{
131 // check the swap is valid (if not then mooseError or mooseException)
132 // and if it's valid, create the inverse swap matrix
133 checkSwap(mgd, basis_index_to_replace, eqm_index_to_insert);
134
135 // perform the swap inside the MGD datastructure
136 alterMGD(mgd, basis_index_to_replace, eqm_index_to_insert);
137}
138
139void
141 DenseVector<Real> & bulk_composition,
142 const std::string & replace_this,
143 const std::string & with_this)
144{
145 performSwap(mgd, replace_this, with_this);
146 // compute the bulk composition expressed in the new basis
147 alterBulkComposition(mgd.basis_species_index.size(), bulk_composition);
148}
149
150void
152 DenseVector<Real> & bulk_composition,
153 unsigned basis_index_to_replace,
154 unsigned eqm_index_to_insert)
155{
156 performSwap(mgd, basis_index_to_replace, eqm_index_to_insert);
157 // compute the bulk composition expressed in the new basis
158 alterBulkComposition(mgd.basis_species_index.size(), bulk_composition);
159}
160
161void
163 unsigned basis_index_to_replace,
164 unsigned eqm_index_to_insert)
165{
166 const unsigned num_cols = mgd.basis_species_index.size();
167 const unsigned num_rows = mgd.eqm_species_index.size();
168 const unsigned num_temperatures = mgd.eqm_log10K.n();
169 const unsigned num_redox = mgd.redox_stoichiometry.m();
170 const unsigned kin_rows = mgd.kin_stoichiometry.m();
171 const unsigned num_rate = mgd.kin_rate.size();
172 const unsigned pro_ind_eqm =
173 num_cols + eqm_index_to_insert; // index of the (eqm species that is being swapped into the
174 // basis) in the kinetic-rate promoting_indices vectors
175
176 // change names
177 const std::string basis_name = mgd.basis_species_name[basis_index_to_replace];
178 const std::string eqm_name = mgd.eqm_species_name[eqm_index_to_insert];
179 mgd.basis_species_name[basis_index_to_replace] = eqm_name;
180 mgd.basis_species_index.erase(basis_name);
181 mgd.basis_species_index[eqm_name] = basis_index_to_replace;
182 mgd.eqm_species_name[eqm_index_to_insert] = basis_name;
183 mgd.eqm_species_index.erase(eqm_name);
184 mgd.eqm_species_index[basis_name] = eqm_index_to_insert;
185
186 // flag indicating whether the redox_lhs is the equilibrium species that is being put into the
187 // basis
188 const bool redox_lhs_going_to_basis = (eqm_name == mgd.redox_lhs);
189 mooseAssert(
190 !redox_lhs_going_to_basis,
191 "Should not be able to swap the redox LHS into the basis"); // At present, it is impossible to
192 // swap redox_lhs into the basis,
193 // since redox_lhs cannot be an
194 // equilibrium species, because it
195 // is explicitly excluded in
196 // PertinentGeochemicalSystem when
197 // building the secondary species.
198 /*
199 In past versions of the code, it was possible to swap the redox LHS into the basis.
200 If you want to make it possible again then mgd needs to be modified as follows
201 if (redox_lhs_going_to_basis)
202 {
203 // need to make the redox_lhs equal to the species that is being taken from the basis
204 mgd.redox_lhs = basis_name;
205 for (unsigned red = 0; red < num_redox; ++red)
206 {
207 const Real alpha = mgd.eqm_stoichiometry(
208 eqm_index_to_insert, basis_index_to_replace); // must be nonzero due to valid swap
209 const Real alpha_r = mgd.redox_stoichiometry(red, basis_index_to_replace);
210 mooseAssert(alpha != alpha_r,
211 "Cannot swap equilibrium species "
212 << eqm_name << " with basis species " << basis_name
213 << " because a redox reaction would result in 0 <-> 1");
214 const Real coef = 1.0 / (alpha - alpha_r);
215 for (unsigned i = 0; i < num_cols; ++i)
216 mgd.redox_stoichiometry(red, i) = coef * (mgd.redox_stoichiometry(red, i) -
217 mgd.eqm_stoichiometry(eqm_index_to_insert, i));
218 mgd.redox_stoichiometry(red, basis_index_to_replace) = 0.0;
219 for (unsigned t = 0; t < num_temperatures; ++t)
220 mgd.redox_log10K(red, t) =
221 coef * (mgd.redox_log10K(red, t) - mgd.eqm_log10K(eqm_index_to_insert, t));
222 }
223 }
224 */
225
226 // record that the swap has occurred
227 if (mgd.swap_to_original_basis.n() == 0)
229 else
231 mgd.have_swapped_out_of_basis.push_back(basis_index_to_replace);
232 mgd.have_swapped_into_basis.push_back(eqm_index_to_insert);
233
234 // express stoichiometry in new basis
235 for (unsigned i = 0; i < num_cols; ++i)
236 mgd.eqm_stoichiometry(eqm_index_to_insert, i) = 0.0;
237 mgd.eqm_stoichiometry(eqm_index_to_insert, basis_index_to_replace) =
238 1.0; // 1 * replace_this <-> 1 * replace_this
240 for (unsigned i = 0; i < num_rows; ++i)
241 for (unsigned j = 0; j < num_cols; ++j)
242 if (std::abs(mgd.eqm_stoichiometry(i, j)) < _stoi_tol)
243 mgd.eqm_stoichiometry(i, j) = 0.0;
244
245 // if the redox_lhs is not changed by the swap, alter the redox stoichiometry
246 if (!redox_lhs_going_to_basis)
248 for (unsigned red = 0; red < num_redox; ++red)
249 for (unsigned j = 0; j < num_cols; ++j)
250 if (std::abs(mgd.redox_stoichiometry(red, j)) < _stoi_tol)
251 mgd.redox_stoichiometry(red, j) = 0.0;
252
253 // express kinetic stoichiometry in new basis
255 for (unsigned i = 0; i < kin_rows; ++i)
256 for (unsigned j = 0; j < num_cols; ++j)
257 if (std::abs(mgd.kin_stoichiometry(i, j)) < _stoi_tol)
258 mgd.kin_stoichiometry(i, j) = 0.0;
259
260 // alter equilibrium constants for each temperature point
261 for (unsigned t = 0; t < num_temperatures; ++t)
262 {
263 const Real log10k_eqm_species = mgd.eqm_log10K(eqm_index_to_insert, t);
264 mgd.eqm_log10K(eqm_index_to_insert, t) =
265 0.0; // 1 * replace_this <-> 1 * replace_this with log10K = 0
266 for (unsigned row = 0; row < num_rows; ++row)
267 mgd.eqm_log10K(row, t) -=
268 mgd.eqm_stoichiometry(row, basis_index_to_replace) * log10k_eqm_species;
269
270 // similar for the redox equations
271 if (!redox_lhs_going_to_basis)
272 for (unsigned red = 0; red < num_redox; ++red)
273 mgd.redox_log10K(red, t) -=
274 mgd.redox_stoichiometry(red, basis_index_to_replace) * log10k_eqm_species;
275
276 // similar for kinetic
277 for (unsigned kin = 0; kin < kin_rows; ++kin)
278 mgd.kin_log10K(kin, t) -=
279 mgd.kin_stoichiometry(kin, basis_index_to_replace) * log10k_eqm_species;
280 }
281
282 // swap the "is mineral" information
283 const bool basis_was_mineral = mgd.basis_species_mineral[basis_index_to_replace];
284 mgd.basis_species_mineral[basis_index_to_replace] = mgd.eqm_species_mineral[eqm_index_to_insert];
285 mgd.eqm_species_mineral[eqm_index_to_insert] = basis_was_mineral;
286
287 // swap the "is gas" information
288 const bool basis_was_gas = mgd.basis_species_gas[basis_index_to_replace];
289 mgd.basis_species_gas[basis_index_to_replace] = mgd.eqm_species_gas[eqm_index_to_insert];
290 mgd.eqm_species_gas[eqm_index_to_insert] = basis_was_gas;
291
292 // swap the "is transported" information
293 const bool basis_was_transported = mgd.basis_species_transported[basis_index_to_replace];
294 mgd.basis_species_transported[basis_index_to_replace] =
295 mgd.eqm_species_transported[eqm_index_to_insert];
296 mgd.eqm_species_transported[eqm_index_to_insert] = basis_was_transported;
297
298 // swap the "charge" information
299 const Real basis_charge = mgd.basis_species_charge[basis_index_to_replace];
300 mgd.basis_species_charge[basis_index_to_replace] = mgd.eqm_species_charge[eqm_index_to_insert];
301 mgd.eqm_species_charge[eqm_index_to_insert] = basis_charge;
302
303 // swap the "radius" information
304 const Real basis_radius = mgd.basis_species_radius[basis_index_to_replace];
305 mgd.basis_species_radius[basis_index_to_replace] = mgd.eqm_species_radius[eqm_index_to_insert];
306 mgd.eqm_species_radius[eqm_index_to_insert] = basis_radius;
307
308 // swap the "molecular weight" information
309 const Real basis_molecular_weight = mgd.basis_species_molecular_weight[basis_index_to_replace];
310 mgd.basis_species_molecular_weight[basis_index_to_replace] =
311 mgd.eqm_species_molecular_weight[eqm_index_to_insert];
312 mgd.eqm_species_molecular_weight[eqm_index_to_insert] = basis_molecular_weight;
313
314 // swap the "molecular volume" information
315 const Real basis_molecular_volume = mgd.basis_species_molecular_volume[basis_index_to_replace];
316 mgd.basis_species_molecular_volume[basis_index_to_replace] =
317 mgd.eqm_species_molecular_volume[eqm_index_to_insert];
318 mgd.eqm_species_molecular_volume[eqm_index_to_insert] = basis_molecular_volume;
319
320 // No need to swap surface_complexation_info or gas_chi because they are not
321 // bound to basis or eqm species
322
323 // swap promoting indices in the rates
324 for (unsigned r = 0; r < num_rate; ++r)
325 {
326 const Real promoting_index_of_original_basis =
327 mgd.kin_rate[r].promoting_indices[basis_index_to_replace];
328 mgd.kin_rate[r].promoting_indices[basis_index_to_replace] =
329 mgd.kin_rate[r].promoting_indices[pro_ind_eqm];
330 mgd.kin_rate[r].promoting_indices[pro_ind_eqm] = promoting_index_of_original_basis;
331 const Real promoting_monod_index_of_original_basis =
332 mgd.kin_rate[r].promoting_monod_indices[basis_index_to_replace];
333 mgd.kin_rate[r].promoting_monod_indices[basis_index_to_replace] =
334 mgd.kin_rate[r].promoting_monod_indices[pro_ind_eqm];
335 mgd.kin_rate[r].promoting_monod_indices[pro_ind_eqm] = promoting_monod_index_of_original_basis;
336 const Real promoting_half_saturation_of_original_basis =
337 mgd.kin_rate[r].promoting_half_saturation[basis_index_to_replace];
338 mgd.kin_rate[r].promoting_half_saturation[basis_index_to_replace] =
339 mgd.kin_rate[r].promoting_half_saturation[pro_ind_eqm];
340 mgd.kin_rate[r].promoting_half_saturation[pro_ind_eqm] =
341 promoting_half_saturation_of_original_basis;
342 }
343
344 // swap progeny, if needed
345 for (unsigned r = 0; r < num_rate; ++r)
346 {
347 if (mgd.kin_rate[r].progeny_index == pro_ind_eqm)
348 mgd.kin_rate[r].progeny_index = basis_index_to_replace;
349 else if (mgd.kin_rate[r].progeny_index == basis_index_to_replace)
350 mgd.kin_rate[r].progeny_index = pro_ind_eqm;
351 }
352}
353
354void
356 DenseVector<Real> & bulk_composition) const
357{
358 if (bulk_composition.size() != basis_size)
359 mooseError("GeochemistrySpeciesSwapper: bulk_composition has size ",
360 bulk_composition.size(),
361 " which differs from the basis size");
362 DenseVector<Real> result;
363 _inv_swap_matrix.vector_mult_transpose(result, bulk_composition);
364 bulk_composition = result;
365}
366
367bool
370 const std::vector<Real> & eqm_molality,
371 bool minerals_allowed,
372 bool gas_allowed,
373 bool sorption_allowed,
374 unsigned & best_eqm_species) const
375{
376 const unsigned num_eqm = mgd.eqm_species_name.size();
377 if (eqm_molality.size() != num_eqm)
378 mooseError("Size of eqm_molality is ",
379 eqm_molality.size(),
380 " which is not equal to the number of equilibrium species ",
381 num_eqm);
382 if (basis_ind >= mgd.basis_species_name.size())
383 mooseError("basis index ", basis_ind, " must be less than ", mgd.basis_species_name.size());
384 best_eqm_species = 0;
385 bool legitimate_swap_found = false;
386 Real best_stoi = 0.0;
387 for (unsigned j = 0; j < num_eqm; ++j)
388 {
389 if (mgd.eqm_stoichiometry(j, basis_ind) == 0.0)
390 continue;
391 if (!minerals_allowed && mgd.eqm_species_mineral[j])
392 continue;
393 if (!gas_allowed && mgd.eqm_species_gas[j])
394 continue;
395 if (!sorption_allowed && mgd.surface_sorption_related[j])
396 continue;
397 const Real stoi = std::abs(mgd.eqm_stoichiometry(j, basis_ind)) * eqm_molality[j];
398 if (stoi >= best_stoi)
399 {
400 best_stoi = stoi;
401 best_eqm_species = j;
402 legitimate_swap_found = true;
403 }
404 }
405 return legitimate_swap_found;
406}
const ModelGeochemicalDatabase mgd
void mooseError(Args &&... args)
void alterMGD(ModelGeochemicalDatabase &mgd, unsigned basis_index_to_replace, unsigned eqm_index_to_insert)
Modify the ModelGeochemicalDatabase mgd to swap the indexed basis species and the indexed equilibrium...
void alterBulkComposition(unsigned basis_size, DenseVector< Real > &bulk_composition) const
Calculate the bulk composition in the new basis based on the bulk composition in the original basis.
void checkSwap(const ModelGeochemicalDatabase &mgd, const std::string &replace_this, const std::string &with_this)
Check that replacing the named basis species with the named equilibrium species is valid.
DenseMatrix< Real > _swap_U
used in SVD decomposition of swap matrix
DenseMatrix< Real > _swap_matrix
swap matrix
DenseMatrix< Real > _swap_VT
used in SVD decomposition of swap matrix
void performSwap(ModelGeochemicalDatabase &mgd, const std::string &replace_this, const std::string &with_this)
Check that replacing the named basis species with the named equilibrium species is valid,...
Real _stoi_tol
tolerance for matrix inversion and stoichiometric coefficients
GeochemistrySpeciesSwapper(unsigned basis_size, Real stoi_tol)
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.
DenseMatrix< Real > _true_swap_matrix
Before _swap_matrix.svd (a non-const method) is called, we copy _true_swap_matrix = _swap_matrix.
DenseVector< Real > _swap_sigma
used in SVD decomposition of swap matrix
DenseMatrix< Real > _inv_swap_matrix
inverse of swap matrix
void constructInverseMatrix(const ModelGeochemicalDatabase &mgd, unsigned basis_index_to_replace, unsigned eqm_index_to_insert)
Construct the swap matrix, and its inverse, that describes the swap between the indexed basis species...
unsigned int n() const
unsigned int m() const
void scale_column(const unsigned int col, const T factor)
void right_multiply_transpose(const DenseMatrix< T > &A)
void svd(DenseVector< Real > &sigma)
virtual void right_multiply(const DenseMatrixBase< T > &M2) override final
void vector_mult_transpose(DenseVector< T > &dest, const DenseVector< T > &arg) const
virtual void left_multiply(const DenseMatrixBase< T > &M2) override final
virtual void zero() override final
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
std::vector< Real > eqm_species_radius
all quantities have an ionic radius (Angstrom) for computing activity (mineral radius = 0,...
DenseMatrix< Real > eqm_stoichiometry
eqm_stoichiometry(i, j) = stoichiometric coefficient for equilibrium species "i" in terms of the basi...
std::vector< Real > basis_species_molecular_weight
all quantities have a molecular weight (g)
DenseMatrix< Real > kin_stoichiometry
kin_stoichiometry(i, j) = stoichiometric coefficient for kinetic species "i" in terms of the basis sp...
std::vector< Real > basis_species_radius
all quantities have an ionic radius (Angstrom) for computing activity (mineral radius = 0,...
std::vector< Real > basis_species_charge
all quantities have a charge (mineral charge = 0, gas charge = 0, oxide charge = 0)
std::vector< Real > eqm_species_molecular_volume
all quantities have a molecular volume (cm^3) (only nonzero for minerals, however)
std::string redox_lhs
the name of the species on the left-hand side of the redox equations.
std::vector< Real > eqm_species_molecular_weight
all quantities have a molecular weight (g)
std::vector< unsigned > have_swapped_into_basis
Species that have been swapped into the basis.
std::vector< bool > basis_species_transported
basis_species_transported[j] = true iff the j^th basis species is transported in reactive-transport s...
DenseMatrix< Real > redox_stoichiometry
redox_stoichiometry(i, j) = stoichiometric coefficients for i^th redox species that is in disequilibr...
DenseMatrix< Real > swap_to_original_basis
Swap matrix that allows expression in terms of the original basis.
std::vector< bool > basis_species_mineral
basis_species_mineral[j] = true iff the j^th basis species is a mineral
std::vector< KineticRateDefinition > kin_rate
rates given to kinetic species.
DenseMatrix< Real > redox_log10K
redox_log10K(i, j) = log10(equilibrium constant) for i^th redox species at the j^th temperature point
DenseMatrix< Real > kin_log10K
kin_log10K(i, j) = log10(equilibrium constant for the i^th kinetic species at the j^th temperature po...
std::vector< bool > surface_sorption_related
surface_sorption_related[j] = true iff the j^th equilibrium species is involved in surface sorption
std::vector< unsigned > have_swapped_out_of_basis
Species that have been swapped out of the basis.
std::vector< Real > basis_species_molecular_volume
all quantities have a molecular volume (cm^3) (only nonzero for minerals, however)
std::unordered_map< std::string, unsigned > eqm_species_index
eqm_species_index[name] = index of the equilibrium species (secondary aqueous species,...
std::vector< std::string > basis_species_name
basis_species_name[j] = name of the j^th basis species
std::unordered_map< std::string, unsigned > basis_species_index
basis_species_index[name] = index of the basis species, within all ModelGeochemicalDatabase internal ...
DenseMatrix< Real > eqm_log10K
eqm_log10K(i, j) = log10(equilibrium constant) for i^th equilibrium species at the j^th temperature p...
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
std::vector< Real > eqm_species_charge
all quantities have a charge (mineral charge = 0, gas charge = 0, oxide charge = 0)
std::vector< bool > eqm_species_gas
eqm_species_gas[i] = true iff the i^th equilibrium species is a gas
std::vector< bool > eqm_species_transported
eqm_species_transported[i] = true iff the i^th eqm species is transported in reactive-transport sims