https://mooseframework.inl.gov
Public Member Functions | Private Member Functions | Private Attributes | List of all members
GeochemicalSolver Class Reference

This class contains methods to solve the algebraic system in GeochemicalSystem. More...

#include <GeochemicalSolver.h>

Public Member Functions

 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. More...
 
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. More...
 
void setMaxInitialResidual (Real max_initial_residual)
 Set value for max_initial_residual. More...
 
Real getMaxInitialResidual () const
 Get value for max_initial_residual. More...
 
void setRampMaxIonicStrength (unsigned ramp_max_ionic_strength)
 Sets the value of _ramp_max_ionic_strength. More...
 
unsigned getRampMaxIonicStrength () const
 Gets the value of _ramp_max_ionic_strength. More...
 

Private Member Functions

Real computeResidual (const GeochemicalSystem &egs, DenseVector< Real > &residual, const DenseVector< Real > &mole_additions) const
 Builds the residual of the algebraic system. More...
 
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 get new_mol. More...
 
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. More...
 
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 residual. More...
 

Private Attributes

GeochemistryIonicStrength_is
 The ionic-strength calculator. More...
 
const unsigned _num_basis
 Number of species in the basis. More...
 
const unsigned _num_kin
 Number of kinetic species. More...
 
unsigned _num_basis_in_algebraic_system
 Number of basis molalities (and potentially solvent water mass) in the algebraic system. More...
 
unsigned _num_in_algebraic_system
 Number of unknowns (molalities and surface potentials) in the algebraic system. More...
 
DenseVector< Real_residual
 residual of the algebraic system we wish to solve More...
 
Real _abs_residual
 L1 norm of residual. More...
 
DenseMatrix< Real_jacobian
 jacobian of the algebraic system More...
 
DenseVector< Real_new_mol
 the new molality after finding the solution of _jacobian * neg_change_mol = _residual More...
 
const Real _abs_tol
 If the residual of the algebraic system falls below this value, the Newton process has converged. More...
 
const Real _rel_tol
 If the residual of the algebraic system falls below this value times the initial residual, the Newton process has converged. More...
 
Real _res0_times_rel
 _res0_times_rel = _rel_tol * initial residual More...
 
const unsigned _max_iter
 maximum number of iterations allowed during an inner solve More...
 
Real _max_initial_residual
 maximum desired initial residual More...
 
const Real _swap_threshold
 If a basis molality < swap_threshold, we attempt to swap it out of the basis. More...
 
const unsigned _max_swaps_allowed
 Maximum number of swaps allowed before the solve aborts. More...
 
const std::vector< std::string > _prevent_precipitation
 The minerals named in this list can have positive saturation indices and will not precipitate. More...
 
const Real _max_ionic_strength
 Maximum ionic strength allowed. More...
 
unsigned _ramp_max_ionic_strength
 Number of iterations over which to increase the maximum ionic strength to _max_ionic_strength. More...
 
bool _evaluate_kin_always
 When to compute the kinetic rates: if true then evaluate before every residual calculation, otherwise evaluate only at the start of the solve process (using, eg, the old molality values) More...
 
DenseVector< Real_input_mole_additions
 the mole_additions for the basis and kinetic species as specified in the argument of solveSystem More...
 
DenseMatrix< Real_input_dmole_additions
 d(mole_additions)/d(species) as specified in the argument of solveSystem More...
 

Detailed Description

This class contains methods to solve the algebraic system in GeochemicalSystem.

Definition at line 18 of file GeochemicalSolver.h.

Constructor & Destructor Documentation

◆ GeochemicalSolver()

GeochemicalSolver::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.

Parameters
num_basisNumber of basis species in the system
mum_kinNumber of kinetic species in the system
isThe object to compute ionic strengths. GeochemicalSolver changes the maximum value of ionic strength as it progresses towards the solution
abs_tolThe Newton solution process is deemed to have converged if the L1 norm of the residual < abs_tol
rel_tolThe Newton solution process is deemed to have converged if the L1 norm of the residual < rel_tol * residual_0
max_iterOnly max_iter Newton iterations are allowed in solving the system
max_initial_residualegs guesses the initial configuration, but sometimes this results in a very large initial residual. GeochemicalSolver will attempt to alter the initial guesses for molalities so that the initial residual is less than max_initial_residual. This parameter may be varied during runtime which might be useful in time-dependent simulations if the first residual is poor but the remainder are deemed likely to be OK
swap_thresholdIf a basis molality < swap_threshold at the end of the Newton process, GeochemicalSolver attempts to swap it out of the basis
max_swaps_allowedMaximum number of swaps allowed before the solve process aborts
prevent_precipitationThe minerals named in this vector will not be allowed to precipitate, even if their saturation indices are positive
max_ionic_strengthMaximum ionic strength ever allowed
ramp_max_ionic_strengthThe maximum ionic strength is ramped from 0 to max_ionic_strength over the course of the first ramp_max_ionic_strength Newton iterations. This can help with convergence.
evaluate_kin_alwaysIf true then evaluate the kinetic rates before each residual calculation, otherwise evaluate them only at the start of the solve process (which will be using the molalities, etc from the previous time-step)

Definition at line 13 of file GeochemicalSolver.C.

26  : _is(is),
27  _num_basis(num_basis),
28  _num_kin(num_kin),
32  _abs_residual(0.0),
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),
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 }
const Real _max_ionic_strength
Maximum ionic strength allowed.
const unsigned _num_kin
Number of kinetic species.
const Real _swap_threshold
If a basis molality < swap_threshold, we attempt to swap it out of the basis.
void mooseError(Args &&... args)
const unsigned _max_swaps_allowed
Maximum number of swaps allowed before the solve aborts.
const Real _rel_tol
If the residual of the algebraic system falls below this value times the initial residual, the Newton process has converged.
Real _res0_times_rel
_res0_times_rel = _rel_tol * initial residual
DenseMatrix< Real > _jacobian
jacobian of the algebraic system
GeochemistryIonicStrength & _is
The ionic-strength calculator.
DenseVector< Real > _residual
residual of the algebraic system we wish to solve
DenseMatrix< Real > _input_dmole_additions
d(mole_additions)/d(species) as specified in the argument of solveSystem
unsigned _num_in_algebraic_system
Number of unknowns (molalities and surface potentials) 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 std::vector< std::string > _prevent_precipitation
The minerals named in this list can have positive saturation indices and will not precipitate...
DenseVector< Real > _input_mole_additions
the mole_additions for the basis and kinetic species as specified in the argument of solveSystem ...
Real _abs_residual
L1 norm of residual.
unsigned _num_basis_in_algebraic_system
Number of basis molalities (and potentially solvent water mass) in the algebraic system.
DenseVector< Real > _new_mol
the new molality after finding the solution of _jacobian * neg_change_mol = _residual ...
Real _max_initial_residual
maximum desired initial residual
const unsigned _max_iter
maximum number of iterations allowed during an inner solve
const unsigned _num_basis
Number of species in the basis.
unsigned _ramp_max_ionic_strength
Number of iterations over which to increase the maximum ionic strength to _max_ionic_strength.
bool _evaluate_kin_always
When to compute the kinetic rates: if true then evaluate before every residual calculation, otherwise evaluate only at the start of the solve process (using, eg, the old molality values)

Member Function Documentation

◆ computeResidual()

Real GeochemicalSolver::computeResidual ( const GeochemicalSystem egs,
DenseVector< Real > &  residual,
const DenseVector< Real > &  mole_additions 
) const
private

Builds the residual of the algebraic system.

Parameters
egsThe geochemical system to use to compute the residual
residualThe residual components will be placed here
mole_additionsthe increment of mole number of each basis species and kinetic species since the last timestep.
Returns
the L1 norm of residual

Definition at line 78 of file GeochemicalSolver.C.

Referenced by reduceInitialResidual(), and solveSystem().

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 }
unsigned _num_in_algebraic_system
Number of unknowns (molalities and surface potentials) in the algebraic system.
Real getResidualComponent(unsigned algebraic_ind, const DenseVector< Real > &mole_additions) const
Return the residual of the algebraic system for the given algebraic index.

◆ getMaxInitialResidual()

Real GeochemicalSolver::getMaxInitialResidual ( ) const

Get value for max_initial_residual.

Definition at line 72 of file GeochemicalSolver.C.

73 {
74  return _max_initial_residual;
75 }
Real _max_initial_residual
maximum desired initial residual

◆ getRampMaxIonicStrength()

unsigned GeochemicalSolver::getRampMaxIonicStrength ( ) const

Gets the value of _ramp_max_ionic_strength.

Definition at line 470 of file GeochemicalSolver.C.

471 {
473 }
unsigned _ramp_max_ionic_strength
Number of iterations over which to increase the maximum ionic strength to _max_ionic_strength.

◆ reduceInitialResidual()

bool GeochemicalSolver::reduceInitialResidual ( GeochemicalSystem egs,
Real  dt,
DenseVector< Real > &  mole_additions,
DenseMatrix< Real > &  dmole_additions 
)
private

Progressively alter the initial-guess molalities for the algebraic system to attempt to reduce the residual.

Parameters
egsThe GeochemicalSystem we're trying to solve
dttime-step size (used for determining kinetic rates)
mole_additionsthe increment of mole number of each basis species and kinetic species since the last timestep. This may change during this function as kinetic rates depend on molalities
dmole_additionsd(mole_additions)/(molality and kinetic moles)

Definition at line 241 of file GeochemicalSolver.C.

Referenced by solveSystem().

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 }
std::vector< Real > getAlgebraicBasisValues() const
DenseVector< Real > _residual
residual of the algebraic system we wish to solve
DenseMatrix< Real > _input_dmole_additions
d(mole_additions)/d(species) as specified in the argument of solveSystem
DenseVector< Real > _input_mole_additions
the mole_additions for the basis and kinetic species as specified in the argument of solveSystem ...
Real _abs_residual
L1 norm of residual.
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, mole number, etc, and then, using dt, calculates the appropriate mole_additions and dmole_additions.
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-...
unsigned _num_basis_in_algebraic_system
Number of basis molalities (and potentially solvent water mass) in the algebraic system.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< Real > getAlgebraicVariableValues() const
Real computeResidual(const GeochemicalSystem &egs, DenseVector< Real > &residual, const DenseVector< Real > &mole_additions) const
Builds the residual of the algebraic system.
Real _max_initial_residual
maximum desired initial residual
void setAlgebraicVariables(const DenseVector< Real > &algebraic_var)
Set the variables in the algebraic system (molalities and potentially the mass of solvent water...
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
bool _evaluate_kin_always
When to compute the kinetic rates: if true then evaluate before every residual calculation, otherwise evaluate only at the start of the solve process (using, eg, the old molality values)

◆ setMaxInitialResidual()

void GeochemicalSolver::setMaxInitialResidual ( Real  max_initial_residual)

Set value for max_initial_residual.

Definition at line 64 of file GeochemicalSolver.C.

Referenced by TEST().

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 }
void mooseError(Args &&... args)
Real _max_initial_residual
maximum desired initial residual

◆ setRampMaxIonicStrength()

void GeochemicalSolver::setRampMaxIonicStrength ( unsigned  ramp_max_ionic_strength)

Sets the value of _ramp_max_ionic_strength.

Definition at line 462 of file GeochemicalSolver.C.

Referenced by GeochemistryTimeDependentReactor::execute(), and GeochemistrySpatialReactor::execute().

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 }
void mooseError(Args &&... args)
const unsigned _max_iter
maximum number of iterations allowed during an inner solve
unsigned _ramp_max_ionic_strength
Number of iterations over which to increase the maximum ionic strength to _max_ionic_strength.

◆ solveAndUnderrelax()

void GeochemicalSolver::solveAndUnderrelax ( const GeochemicalSystem egs,
DenseMatrix< Real > &  jacobian,
DenseVector< Real > &  new_mol 
) const
private

Solves _jacobian * neg_change_mol = _residual for neg_change_mol, then performs an underrelaxation to get new_mol.

Parameters
egsThe geochemical system that we're trying to solve
jacobianthe jacobian of the system
new_molupon exit, this will be the new molality (and surface potential values, if any) values according to the underrelaxed Newton process

Definition at line 88 of file GeochemicalSolver.C.

Referenced by solveSystem().

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 }
DenseVector< Real > _residual
residual of the algebraic system we wish to solve
unsigned _num_in_algebraic_system
Number of unknowns (molalities and surface potentials) in the algebraic system.
void lu_solve(const DenseVector< Real > &b, DenseVector< Real > &x)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< Real > getAlgebraicVariableValues() const

◆ solveSystem()

void GeochemicalSolver::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.

Parameters
egsThe geochemical system that needs to be solved. This gets modified as the solve progresses
ssTextual information such as (iteration, residual) and swap information is written to this stringstream
tot_iterThe total number of iterations used in the solve
abs_residualThe residual of the algebraic system as this method exits
mole_additionsthe increment of mole number of each basis species and kinetic species since the last timestep. This must have size _num_basis + _num_kin. For the basis species, this is the amount of each species being injected into the system over the timestep. For the kinetic species, this is -dt*reaction_rate. Please note: do not decompose the kinetic-species additions into basis components and add them to the first slots of mole_additions! This method does that decomposition automatically. The first _num_basis slots of mole_additions contain kinetic-independent additions, while the last _num_kin slots contain kinetic-rate contributions. This may change during the solution process as kinetic rates depend on molalities.
dmole_additionsdmole_additions(a, b) = d(mole_additions(a))/d(basis_molality(b)) for b < _num_basis and d(mole_additions(a))/d(kinetic_moles(b - _num_basis)) otherwise.

Definition at line 316 of file GeochemicalSolver.C.

Referenced by GeochemistryTimeDependentReactor::execute(), GeochemistrySpatialReactor::execute(), GeochemistryTimeIndependentReactor::initialSetup(), GeochemistryTimeDependentReactor::initialSetup(), GeochemistrySpatialReactor::initialSetup(), GeochemistryTimeDependentReactor::postSolveExchanger(), and TEST().

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);
457  egs.enforceChargeBalance();
458  }
459 }
const Real _max_ionic_strength
Maximum ionic strength allowed.
const unsigned _num_kin
Number of kinetic species.
virtual const char * what() const
const Real _swap_threshold
If a basis molality < swap_threshold, we attempt to swap it out of the basis.
const unsigned _max_swaps_allowed
Maximum number of swaps allowed before the solve aborts.
const ModelGeochemicalDatabase mgd
void setMaxIonicStrength(Real max_ionic_strength)
Set the maximum ionic strength.
const Real _rel_tol
If the residual of the algebraic system falls below this value times the initial residual, the Newton process has converged.
Real _res0_times_rel
_res0_times_rel = _rel_tol * initial residual
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...
DenseMatrix< Real > _jacobian
jacobian of the algebraic system
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.
unsigned getChargeBalanceBasisIndex() const
return the index of the charge-balance species in the basis list
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...
GeochemistryIonicStrength & _is
The ionic-strength calculator.
DenseVector< Real > _residual
residual of the algebraic system we wish to solve
DenseMatrix< Real > _input_dmole_additions
d(mole_additions)/d(species) as specified in the argument of solveSystem
unsigned _num_in_algebraic_system
Number of unknowns (molalities and surface potentials) in the algebraic system.
unsigned getNumInAlgebraicSystem() const
const Real _abs_tol
If the residual of the algebraic system falls below this value, the Newton process has converged...
DenseVector< Real > _input_mole_additions
the mole_additions for the basis and kinetic species as specified in the argument of solveSystem ...
Real _abs_residual
L1 norm of residual.
const ModelGeochemicalDatabase & getModelGeochemicalDatabase() const
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, mole number, etc, and then, using dt, calculates the appropriate mole_additions and dmole_additions.
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...
unsigned _num_basis_in_algebraic_system
Number of basis molalities (and potentially solvent water mass) in the algebraic system.
void enforceChargeBalance()
Enforces charge balance by altering the constraint_value and bulk_moles_old of the charge-balance spe...
bool alterChargeBalanceSpecies(Real threshold_molality)
If the molality of the charge-balance basis species is less than threshold molality, then attempt to change the charge-balance species, preferably to another component with opposite charge and big molality.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
DenseVector< Real > _new_mol
the new molality after finding the solution of _jacobian * neg_change_mol = _residual ...
Real computeResidual(const GeochemicalSystem &egs, DenseVector< Real > &residual, const DenseVector< Real > &mole_additions) const
Builds the residual of the algebraic system.
void setMaxStoichiometricIonicStrength(Real max_stoichiometric_ionic_strength)
Set the maximum stoichiometric ionic strength.
Real _max_initial_residual
maximum desired initial residual
const unsigned _max_iter
maximum number of iterations allowed during an inner solve
void setAlgebraicVariables(const DenseVector< Real > &algebraic_var)
Set the variables in the algebraic system (molalities and potentially the mass of solvent water...
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")
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.
const unsigned _num_basis
Number of species in the basis.
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...
unsigned _ramp_max_ionic_strength
Number of iterations over which to increase the maximum ionic strength to _max_ionic_strength.
void performSwap(unsigned swap_out_of_basis, unsigned swap_into_basis)
Perform the basis swap, and ensure that the resulting system is consistent.
unsigned getNumBasisInAlgebraicSystem() const
return the number of basis species in the algebraic system
bool _evaluate_kin_always
When to compute the kinetic rates: if true then evaluate before every residual calculation, otherwise evaluate only at the start of the solve process (using, eg, the old molality values)
std::vector< std::string > basis_species_name
basis_species_name[j] = name of the j^th basis species

◆ swapNeeded()

bool GeochemicalSolver::swapNeeded ( const GeochemicalSystem egs,
unsigned &  swap_out_of_basis,
unsigned &  swap_into_basis,
std::stringstream &  ss 
) const
private

Check if a basis swap is needed.

It is needed if:

  • the Newton process did not converge and some non-minerals have small molality
  • the free number of moles of a basis mineral is negative
  • the saturation index of an equilibrium mineral is positive (and it is not in the prevent_precipitation list)
    Parameters
    egsGeochemical system that we're trying to solve
    swap_out_of_basisthe index of the species in the basis that will be removed from the basis
    swap_into_basisthe index of the equilibrium mineneral that will be added to the basis
    Returns
    true if a swap is needed, false otherwise

Definition at line 107 of file GeochemicalSolver.C.

Referenced by solveSystem().

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] &&
133  egs.getChargeBalanceBasisIndex() != 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 }
const Real _swap_threshold
If a basis molality < swap_threshold, we attempt to swap it out of the basis.
const ModelGeochemicalDatabase mgd
Real _res0_times_rel
_res0_times_rel = _rel_tol * initial residual
unsigned getChargeBalanceBasisIndex() const
return the index of the charge-balance species in the basis list
DenseMatrix< Real > eqm_stoichiometry
eqm_stoichiometry(i, j) = stoichiometric coefficient for equilibrium species "i" in terms of the basi...
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...
const Real _abs_tol
If the residual of the algebraic system falls below this value, the Newton process has converged...
const std::vector< std::string > _prevent_precipitation
The minerals named in this list can have positive saturation indices and will not precipitate...
Real _abs_residual
L1 norm of residual.
const ModelGeochemicalDatabase & getModelGeochemicalDatabase() 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-...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Real getEquilibriumMolality(unsigned j) const
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
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
const std::vector< Real > & getSolventMassAndFreeMolalityAndMineralMoles() const
const std::vector< bool > & getInAlgebraicSystem() const
const unsigned _num_basis
Number of species in the basis.
const GeochemistrySpeciesSwapper & getSwapper() const
const std::vector< bool > & getBasisActivityKnown() const
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
std::vector< Real > getSaturationIndices() const

Member Data Documentation

◆ _abs_residual

Real GeochemicalSolver::_abs_residual
private

L1 norm of residual.

Definition at line 118 of file GeochemicalSolver.h.

Referenced by reduceInitialResidual(), solveSystem(), and swapNeeded().

◆ _abs_tol

const Real GeochemicalSolver::_abs_tol
private

If the residual of the algebraic system falls below this value, the Newton process has converged.

Definition at line 124 of file GeochemicalSolver.h.

Referenced by solveSystem(), and swapNeeded().

◆ _evaluate_kin_always

bool GeochemicalSolver::_evaluate_kin_always
private

When to compute the kinetic rates: if true then evaluate before every residual calculation, otherwise evaluate only at the start of the solve process (using, eg, the old molality values)

Definition at line 147 of file GeochemicalSolver.h.

Referenced by reduceInitialResidual(), and solveSystem().

◆ _input_dmole_additions

DenseMatrix<Real> GeochemicalSolver::_input_dmole_additions
private

d(mole_additions)/d(species) as specified in the argument of solveSystem

Definition at line 151 of file GeochemicalSolver.h.

Referenced by reduceInitialResidual(), and solveSystem().

◆ _input_mole_additions

DenseVector<Real> GeochemicalSolver::_input_mole_additions
private

the mole_additions for the basis and kinetic species as specified in the argument of solveSystem

Definition at line 149 of file GeochemicalSolver.h.

Referenced by reduceInitialResidual(), and solveSystem().

◆ _is

GeochemistryIonicStrength& GeochemicalSolver::_is
private

The ionic-strength calculator.

Definition at line 106 of file GeochemicalSolver.h.

Referenced by solveSystem().

◆ _jacobian

DenseMatrix<Real> GeochemicalSolver::_jacobian
private

jacobian of the algebraic system

Definition at line 120 of file GeochemicalSolver.h.

Referenced by solveSystem().

◆ _max_initial_residual

Real GeochemicalSolver::_max_initial_residual
private

maximum desired initial residual

Definition at line 132 of file GeochemicalSolver.h.

Referenced by getMaxInitialResidual(), reduceInitialResidual(), setMaxInitialResidual(), and solveSystem().

◆ _max_ionic_strength

const Real GeochemicalSolver::_max_ionic_strength
private

Maximum ionic strength allowed.

Definition at line 140 of file GeochemicalSolver.h.

Referenced by solveSystem().

◆ _max_iter

const unsigned GeochemicalSolver::_max_iter
private

maximum number of iterations allowed during an inner solve

Definition at line 130 of file GeochemicalSolver.h.

Referenced by GeochemicalSolver(), setRampMaxIonicStrength(), and solveSystem().

◆ _max_swaps_allowed

const unsigned GeochemicalSolver::_max_swaps_allowed
private

Maximum number of swaps allowed before the solve aborts.

Definition at line 136 of file GeochemicalSolver.h.

Referenced by solveSystem().

◆ _new_mol

DenseVector<Real> GeochemicalSolver::_new_mol
private

the new molality after finding the solution of _jacobian * neg_change_mol = _residual

Definition at line 122 of file GeochemicalSolver.h.

Referenced by solveSystem().

◆ _num_basis

const unsigned GeochemicalSolver::_num_basis
private

Number of species in the basis.

Definition at line 108 of file GeochemicalSolver.h.

Referenced by solveSystem(), and swapNeeded().

◆ _num_basis_in_algebraic_system

unsigned GeochemicalSolver::_num_basis_in_algebraic_system
private

Number of basis molalities (and potentially solvent water mass) in the algebraic system.

Definition at line 112 of file GeochemicalSolver.h.

Referenced by reduceInitialResidual(), and solveSystem().

◆ _num_in_algebraic_system

unsigned GeochemicalSolver::_num_in_algebraic_system
private

Number of unknowns (molalities and surface potentials) in the algebraic system.

Definition at line 114 of file GeochemicalSolver.h.

Referenced by computeResidual(), solveAndUnderrelax(), and solveSystem().

◆ _num_kin

const unsigned GeochemicalSolver::_num_kin
private

Number of kinetic species.

Definition at line 110 of file GeochemicalSolver.h.

Referenced by solveSystem().

◆ _prevent_precipitation

const std::vector<std::string> GeochemicalSolver::_prevent_precipitation
private

The minerals named in this list can have positive saturation indices and will not precipitate.

Definition at line 138 of file GeochemicalSolver.h.

Referenced by swapNeeded().

◆ _ramp_max_ionic_strength

unsigned GeochemicalSolver::_ramp_max_ionic_strength
private

Number of iterations over which to increase the maximum ionic strength to _max_ionic_strength.

Definition at line 142 of file GeochemicalSolver.h.

Referenced by GeochemicalSolver(), getRampMaxIonicStrength(), setRampMaxIonicStrength(), and solveSystem().

◆ _rel_tol

const Real GeochemicalSolver::_rel_tol
private

If the residual of the algebraic system falls below this value times the initial residual, the Newton process has converged.

Definition at line 126 of file GeochemicalSolver.h.

Referenced by solveSystem().

◆ _res0_times_rel

Real GeochemicalSolver::_res0_times_rel
private

_res0_times_rel = _rel_tol * initial residual

Definition at line 128 of file GeochemicalSolver.h.

Referenced by solveSystem(), and swapNeeded().

◆ _residual

DenseVector<Real> GeochemicalSolver::_residual
private

residual of the algebraic system we wish to solve

Definition at line 116 of file GeochemicalSolver.h.

Referenced by reduceInitialResidual(), solveAndUnderrelax(), and solveSystem().

◆ _swap_threshold

const Real GeochemicalSolver::_swap_threshold
private

If a basis molality < swap_threshold, we attempt to swap it out of the basis.

Definition at line 134 of file GeochemicalSolver.h.

Referenced by solveSystem(), and swapNeeded().


The documentation for this class was generated from the following files: