https://mooseframework.inl.gov
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 
12 GeochemistrySpeciesSwapper::GeochemistrySpeciesSwapper(unsigned basis_size, Real stoi_tol)
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 
23 void
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 
39 void
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])
53  mooseError(
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 
64 void
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 
113 void
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 
126 void
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 
139 void
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 
150 void
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 
161 void
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 
354 void
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 
367 bool
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 }
std::vector< bool > surface_sorption_related
surface_sorption_related[j] = true iff the j^th equilibrium species is involved in surface sorption ...
GeochemistrySpeciesSwapper(unsigned basis_size, Real stoi_tol)
DenseMatrix< Real > redox_stoichiometry
redox_stoichiometry(i, j) = stoichiometric coefficients for i^th redox species that is in disequilibr...
std::vector< Real > eqm_species_molecular_weight
all quantities have a molecular weight (g)
std::string redox_lhs
the name of the species on the left-hand side of the redox equations.
std::vector< bool > basis_species_transported
basis_species_transported[j] = true iff the j^th basis species is transported in reactive-transport s...
DenseVector< Real > _swap_sigma
used in SVD decomposition of swap matrix
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...
DenseMatrix< Real > _swap_VT
used in SVD decomposition of swap matrix
DenseMatrix< Real > _inv_swap_matrix
inverse of swap matrix
void mooseError(Args &&... args)
virtual void zero() override final
std::vector< bool > eqm_species_gas
eqm_species_gas[i] = true iff the i^th equilibrium species is a gas
DenseMatrix< Real > _swap_U
used in SVD decomposition of swap matrix
std::unordered_map< std::string, unsigned > basis_species_index
basis_species_index[name] = index of the basis species, within all ModelGeochemicalDatabase internal ...
const ModelGeochemicalDatabase mgd
DenseMatrix< Real > kin_stoichiometry
kin_stoichiometry(i, j) = stoichiometric coefficient for kinetic species "i" in terms of the basis sp...
unsigned int m() const
std::unordered_map< std::string, unsigned > eqm_species_index
eqm_species_index[name] = index of the equilibrium species (secondary aqueous species, redox couples in equilibrium with the aqueous solution, minerals in equilibrium with the aqueous solution, gases in equilibrium with the aqueous solution) within all ModelGeochemicalDatabase internal datastrcutres, with given name
DenseMatrix< Real > redox_log10K
redox_log10K(i, j) = log10(equilibrium constant) for i^th redox species at the j^th temperature point...
DenseMatrix< Real > swap_to_original_basis
Swap matrix that allows expression in terms of the original basis.
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...
DenseMatrix< Real > eqm_stoichiometry
eqm_stoichiometry(i, j) = stoichiometric coefficient for equilibrium species "i" in terms of the basi...
void vector_mult_transpose(DenseVector< Real > &dest, const DenseVector< Real > &arg) const
void scale_column(const unsigned int col, const Real factor)
std::vector< Real > basis_species_radius
all quantities have an ionic radius (Angstrom) for computing activity (mineral radius = 0...
DenseMatrix< Real > _true_swap_matrix
Before _swap_matrix.svd (a non-const method) is called, we copy _true_swap_matrix = _swap_matrix...
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...
void svd(DenseVector< Real > &sigma)
Real _stoi_tol
tolerance for matrix inversion and stoichiometric coefficients
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< Real > eqm_species_radius
all quantities have an ionic radius (Angstrom) for computing activity (mineral radius = 0...
DenseMatrix< Real > eqm_log10K
eqm_log10K(i, j) = log10(equilibrium constant) for i^th equilibrium species at the j^th temperature p...
void right_multiply_transpose(const DenseMatrix< Real > &A)
std::vector< Real > basis_species_molecular_weight
all quantities have a molecular weight (g)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< bool > eqm_species_transported
eqm_species_transported[i] = true iff the i^th eqm species is transported in reactive-transport sims ...
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...
DenseMatrix< Real > _swap_matrix
swap matrix
std::vector< Real > eqm_species_molecular_volume
all quantities have a molecular volume (cm^3) (only nonzero for minerals, however) ...
virtual void right_multiply(const DenseMatrixBase< Real > &M2) override final
std::vector< Real > basis_species_charge
all quantities have a charge (mineral charge = 0, gas charge = 0, oxide charge = 0) ...
std::vector< unsigned > have_swapped_into_basis
Species that have been swapped into the basis.
std::vector< bool > basis_species_gas
basis_species_gas[j] = true iff the j^th basis species is a gas
std::vector< bool > basis_species_mineral
basis_species_mineral[j] = true iff the j^th basis species is a mineral
std::vector< Real > eqm_species_charge
all quantities have a charge (mineral charge = 0, gas charge = 0, oxide charge = 0) ...
Data structure to hold all relevant information from the database file.
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
std::vector< bool > eqm_species_mineral
eqm_species_mineral[i] = true iff the i^th equilibrium species is a mineral
std::vector< Real > basis_species_molecular_volume
all quantities have a molecular volume (cm^3) (only nonzero for minerals, however) ...
unsigned int n() const
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...
std::vector< std::string > eqm_species_name
eqm_species_name[i] = name of the i^th eqm species
std::vector< std::string > basis_species_name
basis_species_name[j] = name of the j^th basis species
virtual void left_multiply(const DenseMatrixBase< Real > &M2) override final
std::vector< unsigned > have_swapped_out_of_basis
Species that have been swapped out of the basis.
std::vector< KineticRateDefinition > kin_rate
rates given to kinetic species.
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...