https://mooseframework.inl.gov
GeochemistryConsoleOutput.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 #include "GeochemistryConstants.h"
14 
16 
19 {
21  params.addParam<unsigned int>("precision", 4, "Precision for printing values");
22  params.addParam<Real>(
23  "mol_cutoff",
24  1E-40,
25  "Information regarding species with molalities less than this amount will not be outputted");
26  params.addParam<bool>(
27  "solver_info",
28  false,
29  "Print information (to the console) from the solver including residuals, swaps, etc");
30  return params;
31 }
32 
35 {
38  params.addRequiredParam<UserObjectName>("geochemistry_reactor",
39  "The name of the GeochemistryReactor UserObject");
40  params.addRangeCheckedParam<Real>("stoichiometry_tolerance",
41  1E-6,
42  "stoichiometry_tolerance >= 0.0",
43  "if abs(any stoichiometric coefficient) < stoi_tol then it is "
44  "set to zero, and so will not appear in the output");
45  params.addRequiredParam<UserObjectName>(
46  "nearest_node_number_UO",
47  "The NearestNodeNumber UserObject that defines the physical point at which to query the "
48  "GeochemistryReactor");
49  params.addClassDescription("Outputs results from a GeochemistryReactor at a particular point");
50  return params;
51 }
52 
54  : Output(parameters),
55  UserObjectInterface(this),
56  _reactor(getUserObject<GeochemistryReactorBase>("geochemistry_reactor")),
57  _nnn(getUserObject<NearestNodeNumberUO>("nearest_node_number_UO")),
58  _precision(getParam<unsigned int>("precision")),
59  _stoi_tol(getParam<Real>("stoichiometry_tolerance")),
60  _solver_info(getParam<bool>("solver_info")),
61  _mol_cutoff(getParam<Real>("mol_cutoff"))
62 {
63 }
64 
65 void
67 {
68  const Node * closest_node = _nnn.getClosestNode();
69  if (!closest_node)
70  return;
71  const dof_id_type closest_id = closest_node->id();
72 
73  if (_solver_info)
74  _console << _reactor.getSolverOutput(closest_id).str();
75 
76  // retrieve information
77  const GeochemicalSystem & egs = _reactor.getGeochemicalSystem(closest_id);
78  const unsigned num_basis = egs.getNumInBasis();
79  const unsigned num_eqm = egs.getNumInEquilibrium();
80  const unsigned num_kin = egs.getNumKinetic();
81  const std::vector<Real> & basis_molality = egs.getSolventMassAndFreeMolalityAndMineralMoles();
82  const std::vector<Real> & basis_activity = egs.getBasisActivity();
83  const std::vector<Real> & basis_act_coef = egs.getBasisActivityCoefficient();
84  const std::vector<Real> & bulk_moles = egs.getBulkMolesOld();
85  const std::vector<Real> & eqm_molality = egs.getEquilibriumMolality();
86  const std::vector<Real> & eqm_act_coef = egs.getEquilibriumActivityCoefficient();
87  const std::vector<Real> & eqm_SI = egs.getSaturationIndices();
88  const std::vector<Real> & kin_moles = egs.getKineticMoles();
90 
91  _console << std::setprecision(_precision);
92 
93  _console << "\nSummary:\n";
94 
95  _console << "Total number of iterations required = " << _reactor.getSolverIterations(closest_id)
96  << "\n";
97  _console << "Error in calculation = " << _reactor.getSolverResidual(closest_id) << "mol\n";
98  _console << "Charge of solution = " << egs.getTotalChargeOld() << "mol";
99  _console << " (charge-balance species = "
101 
102  _console << "Mass of solvent water = " << basis_molality[0] << "kg\n";
103 
104  Real mass = bulk_moles[0] / GeochemistryConstants::MOLES_PER_KG_WATER;
105  for (unsigned i = 1; i < num_basis; ++i) // do not loop over water
106  mass += bulk_moles[i] * mgd.basis_species_molecular_weight[i] / 1000.0;
107  _console << "Total mass = " << mass << "kg";
108  if (num_kin == 0)
109  _console << "\n";
110  else
111  {
112  _console << " (including kinetic species and free minerals)\n";
113  for (unsigned k = 0; k < num_kin; ++k)
114  mass -= kin_moles[k] * mgd.kin_species_molecular_weight[k] / 1000.0;
115  _console << "Mass without kinetic species but including free minerals = " << mass << "kg\n";
116  }
117  // remove the free minerals
118  for (unsigned i = 1; i < num_basis; ++i) // do not loop over water
119  if (mgd.basis_species_mineral[i])
120  mass -= basis_molality[i] * mgd.basis_species_molecular_weight[i] / 1000.0;
121  // remove surface complexes
122  for (const auto & name_info :
123  mgd.surface_complexation_info) // all minerals involved in surface complexation
124  for (const auto & name_frac :
125  name_info.second.sorption_sites) // all sorption sites on the given mineral
126  {
127  const unsigned i =
128  mgd.basis_species_index.at(name_frac.first); // i = basis_index_of_sorption_site
129  mass -= basis_molality[i] * mgd.basis_species_molecular_weight[i] / 1000.0;
130  }
131  _console << "Mass of aqueous solution = " << mass << "kg";
132  if (num_kin == 0)
133  _console << " (without free minerals)\n";
134  else
135  _console << " (without kinetic species and without free minerals)\n";
136 
137  // Output the aqueous solution pH, if relevant
138  if (mgd.basis_species_index.count("H+"))
139  _console << "pH = " << -std::log10(basis_activity[mgd.basis_species_index.at("H+")]) << "\n";
140  if (mgd.eqm_species_index.count("H+"))
141  _console << "pH = "
142  << -std::log10(eqm_molality[mgd.eqm_species_index.at("H+")] *
143  eqm_act_coef[mgd.eqm_species_index.at("H+")])
144  << "\n";
145 
146  // Output the aqueous solution pe, if relevant
147  if (mgd.redox_stoichiometry.m() > 0)
148  _console << "pe = " << egs.getRedoxLog10K(0) - egs.log10RedoxActivityProduct(0) << "\n";
149 
150  // Output ionic strengths
151  _console << "Ionic strength = " << egs.getIonicStrength() << "mol/kg(solvent water)\n";
152  _console << "Stoichiometric ionic strength = " << egs.getStoichiometricIonicStrength()
153  << "mol/kg(solvent water)\n";
154 
155  // Output activity of water
156  _console << "Activity of water = " << basis_activity[0] << "\n";
157 
158  // Output temperature
159  _console << "Temperature = " << egs.getTemperature() << "\n";
160 
161  // Output the basis species information, sorted by molality
162  std::vector<unsigned> basis_order =
163  GeochemistrySortedIndices::sortedIndices(basis_molality, false);
164  _console << "\nBasis Species:\n";
165  for (const auto & i : basis_order)
166  if (i == 0 || mgd.basis_species_gas[i])
167  continue;
168  else
169  {
170  _console << mgd.basis_species_name[i] << "; bulk_moles = " << bulk_moles[i]
171  << "mol; bulk_conc = "
172  << bulk_moles[i] * mgd.basis_species_molecular_weight[i] * 1000.0 / mass
173  << "mg/kg(soln);";
174  if (!mgd.basis_species_mineral[i])
175  _console << " molality = " << basis_molality[i] << "mol/kg(solvent water); free_conc = "
176  << basis_molality[i] * basis_molality[0] / mass *
178  << "mg/kg(soln); act_coeff = " << basis_act_coef[i]
179  << "; log10(a) = " << std::log10(basis_activity[i]) << "\n";
180  else if (mgd.basis_species_mineral[i])
181  _console << " free_moles = " << basis_molality[i] << "mol; free_mass = "
182  << basis_molality[i] * mgd.basis_species_molecular_weight[i] * 1000.0 << "mg\n";
183  }
184  for (unsigned i = 0; i < num_basis; ++i)
185  if (mgd.basis_species_gas[i])
186  _console << mgd.basis_species_name[i] << "; fugacity = " << basis_activity[i] << "\n";
187 
188  // Output the equilibrium species info, sorted by molality
189  std::vector<unsigned> eqm_order = GeochemistrySortedIndices::sortedIndices(eqm_molality, false);
190  _console << "\nEquilibrium Species:\n";
191  for (const auto & i : eqm_order)
192  if (eqm_molality[i] <= _mol_cutoff)
193  break;
194  else if (mgd.eqm_species_gas[i])
195  continue;
196  else
197  _console << mgd.eqm_species_name[i] << "; molality = " << eqm_molality[i]
198  << "mol/kg(solvent water); free_conc = "
199  << eqm_molality[i] * basis_molality[0] / mass * mgd.eqm_species_molecular_weight[i] *
200  1000.0
201  << "mg/kg(soln); act_coeff = " << eqm_act_coef[i]
202  << "; log10(a) = " << std::log10(eqm_molality[i] * eqm_act_coef[i]) << "; "
203  << mgd.eqm_species_name[i] << " = "
206  << "; log10K = " << egs.getLog10K(i) << "\n";
207  for (unsigned i = 0; i < num_eqm; ++i)
208  if (mgd.eqm_species_gas[i])
209  {
210  Real log10f = 0;
211  for (unsigned basis_i = 0; basis_i < num_basis; ++basis_i)
212  log10f += mgd.eqm_stoichiometry(i, basis_i) * std::log10(basis_activity[basis_i]);
213  log10f -= egs.getLog10K(i);
215  << "; act_coeff = " << egs.getEquilibriumActivityCoefficient(i)
216  << "; fugacity = " << std::pow(10.0, log10f) << "; " << mgd.eqm_species_name[i]
217  << " = "
220  << "; log10K = " << egs.getLog10K(i) << "\n";
221  }
222 
223  // Output the kinetic species information, sorted by mole number
224  std::vector<unsigned> kin_order = GeochemistrySortedIndices::sortedIndices(kin_moles, false);
225  _console << "\nKinetic Species:\n";
226  for (const auto & k : kin_order)
227  {
228  _console << mgd.kin_species_name[k] << "; moles = " << kin_moles[k]
229  << "; mass = " << kin_moles[k] * mgd.kin_species_molecular_weight[k] * 1000.0
230  << "mg; ";
232  _console << "volume = " << kin_moles[k] * mgd.kin_species_molecular_volume[k] << "cm^3; ";
233  _console << mgd.kin_species_name[k] << " = "
236  << "; log10(Q) = " << egs.log10KineticActivityProduct(k)
237  << "; log10K = " << egs.getKineticLog10K(k)
238  << "; dissolution_rate*dt = " << -_reactor.getMoleAdditions(closest_id)(num_basis + k)
239  << "\n";
240  }
241 
242  // Output the mineral info, sorted by saturation indices
243  std::vector<unsigned> mineral_order = GeochemistrySortedIndices::sortedIndices(eqm_SI, false);
244  _console << "\nMinerals:\n";
245  for (const auto & i : mineral_order)
246  if (mgd.eqm_species_mineral[i])
247  _console << mgd.eqm_species_name[i] << " = "
250  << "; log10K = " << egs.getLog10K(i) << "; SI = " << eqm_SI[i] << "\n";
251 
252  // Output the Nernst potentials, if relevant
253  _console << "\nNernst potentials:\n";
254  if (mgd.redox_stoichiometry.m() > 0)
255  outputNernstInfo(egs);
256 
257  const unsigned num_pot = egs.getNumSurfacePotentials();
258  if (num_pot > 0)
259  {
260  _console << "\nSorbing surfaces:\n";
261  const std::vector<Real> area = egs.getSorbingSurfaceArea();
262  for (unsigned sp = 0; sp < num_pot; ++sp)
263  _console << mgd.surface_sorption_name[sp] << "; area = " << area[sp]
264  << "m^2; specific charge = " << egs.getSurfaceCharge(sp)
265  << "C/m^2; surface potential = " << egs.getSurfacePotential(sp) << "V\n";
266  }
267 
268  DenseVector<Real> bulk_in_original_basis = egs.getBulkOldInOriginalBasis();
269  DenseVector<Real> transported_bulk_in_original_basis = egs.getTransportedBulkInOriginalBasis();
270  std::vector<std::string> original_basis_names =
272  _console << "\nIn original basis:\n";
273  for (unsigned i = 0; i < num_basis; ++i)
274  _console << original_basis_names[i] << "; total_bulk_moles = " << bulk_in_original_basis(i)
275  << "; transported_bulk_moles = " << transported_bulk_in_original_basis(i) << "\n";
276 
277  _console << std::flush;
278 }
279 
280 void
282 {
283  // Copy egs_ref so we can call non-const methods (viz, swaps). This does not copy the data in
284  // egs.getModelGeochemicalDatabase(), only the reference (both references refer to the same
285  // block of memory) and unfortunately the swaps below manipulate that memory
286  GeochemicalSystem egs = egs_ref;
287  // Since we only want to do the swaps to print out some Nernst info, but don't want to impact
288  // the rest of the simulation, copy the mgd, so that we can copy it back into the aforementioned
289  // block of memory at the end of this method
290  ModelGeochemicalDatabase mgd_without_nernst_swaps = egs.getModelGeochemicalDatabase();
291 
293  // attempt to undo the swaps that have been done to mgd_ref.
294  // NOTE FOR FUTURE: In the current code (2020, June 1) swapping the redox stuff in mgd seems
295  // redundant. While the current code does these swaps, here we just undo all the swaps again to
296  // write the redox stuff in the original basis. Since this is the only place that the redox
297  // stuff is used, doing any swaps on the redox stuff is currently just a waste of time! take a
298  // copy of the following because they get modified during the swaps
299  const std::vector<unsigned> have_swapped_out_of_basis = mgd_ref.have_swapped_out_of_basis;
300  const std::vector<unsigned> have_swapped_into_basis = mgd_ref.have_swapped_into_basis;
301  for (int sw = have_swapped_out_of_basis.size() - 1; sw >= 0; --sw)
302  {
303  // Don't check for gases being swapped in or out of the basis (which usually can't happen in
304  // the middle of a Newton process) because we're going to trash all the swaps at the end of
305  // this method, and we don't have to worry about bulk moles, etc: we just want the
306  // stoichiometries and the activities
307  try
308  {
309  egs.performSwapNoCheck(have_swapped_out_of_basis[sw], have_swapped_into_basis[sw]);
310  }
311  catch (const MooseException & e)
312  {
313  const std::string to_swap_in = mgd_ref.eqm_species_name[have_swapped_into_basis[sw]];
314  const std::string to_swap_out = mgd_ref.basis_species_name[have_swapped_out_of_basis[sw]];
315  // Don't crash the entire simulation, just because Nernst-related swapping does not work
316  mooseWarning("Swapping ", to_swap_out, " and ", to_swap_in, ": ", e.what());
317  }
318  }
319 
323 
324  if (mgd_ref.redox_lhs == egs_ref.getOriginalRedoxLHS())
325  {
326  std::vector<Real> eh(mgd_ref.redox_stoichiometry.m());
327  for (unsigned red = 0; red < mgd_ref.redox_stoichiometry.m(); ++red)
328  eh[red] = prefactor * (egs.log10RedoxActivityProduct(red) - egs.getRedoxLog10K(red));
329  const std::vector<unsigned> eh_order = GeochemistrySortedIndices::sortedIndices(eh, false);
330  for (const auto & red : eh_order)
331  _console << mgd_ref.redox_lhs << " = "
333  red,
334  mgd_ref.basis_species_name,
335  _stoi_tol,
336  _precision)
337  << "; Eh = " << eh[red] << "V\n";
338  }
339 
340  _console << std::flush;
341 
342  // restore the original mgd by copying the data in copy_of_mgd into the memory referenced by
343  // egs.getModelGeochemicalDatabase()
344  egs.setModelGeochemicalDatabase(mgd_without_nernst_swaps);
345 }
DenseMatrix< Real > redox_stoichiometry
redox_stoichiometry(i, j) = stoichiometric coefficients for i^th redox species that is in disequilibr...
virtual unsigned getSolverIterations(dof_id_type node_id) const =0
std::vector< Real > eqm_species_molecular_weight
all quantities have a molecular weight (g)
Real getTemperature() const
DenseVector< Real > getBulkOldInOriginalBasis() const
constexpr Real MOLES_PER_KG_WATER
std::string redox_lhs
the name of the species on the left-hand side of the redox equations.
std::vector< bool > kin_species_mineral
kin_species_mineral[j] = true iff the j^th kinetic species is a mineral
Real getIonicStrength() const
Get the ionic strength.
virtual const char * what() const
std::vector< std::string > surface_sorption_name
surface_sorption_name[k] = name of the mineral involved in surface sorption.
std::string reaction(const DenseMatrix< Real > &stoi, unsigned row, const std::vector< std::string > &names, Real stoi_tol=1.0E-6, int precision=4)
Returns a nicely formatted string corresponding to the reaction defined by the given row of the stoic...
const std::vector< Real > & getBulkMolesOld() const
virtual void output() override
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
Real getBasisActivityCoefficient(unsigned i) const
constexpr Real CELSIUS_TO_KELVIN
std::vector< bool > eqm_species_gas
eqm_species_gas[i] = true iff the i^th equilibrium species is a gas
Outputs information (to the console) from a GeochemistryReactorBase at a point.
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
const Real _stoi_tol
Tolerance on stoichiometric coefficients before they are deemed to be zero.
DenseMatrix< Real > kin_stoichiometry
kin_stoichiometry(i, j) = stoichiometric coefficient for kinetic species "i" in terms of the basis sp...
std::vector< Real > kin_species_molecular_weight
all quantities have a molecular weight (g/mol)
if(subdm)
virtual const std::stringstream & getSolverOutput(dof_id_type node_id) const =0
std::vector< Real > kin_species_molecular_volume
all quantities have a molecular volume (cm^3/mol) (only nonzero for minerals, however) ...
const NearestNodeNumberUO & _nnn
UserObject defining the node of interest.
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
Real log10KineticActivityProduct(unsigned kin) const
unsigned getChargeBalanceBasisIndex() const
return the index of the charge-balance species in the basis list
virtual Real getSolverResidual(dof_id_type node_id) const =0
unsigned getNumKinetic() const
returns the number of kinetic species
const Node * getClosestNode() const
void outputNernstInfo(const GeochemicalSystem &egs_ref) const
void mooseWarning(Args &&... args) const
void addRequiredParam(const std::string &name, const std::string &doc_string)
InputParameters emptyInputParameters()
unsigned getNumInBasis() const
returns the number of species in the basis
static InputParameters sharedParams()
contains params that are shared with Actions that use this object
DenseMatrix< Real > eqm_stoichiometry
eqm_stoichiometry(i, j) = stoichiometric coefficient for equilibrium species "i" in terms of the basi...
Real getTotalChargeOld() const
Real getBasisActivity(unsigned i) const
Real getStoichiometricIonicStrength() const
Get the stoichiometric ionic strength.
virtual const GeochemicalSystem & getGeochemicalSystem(dof_id_type node_id) const =0
const bool _solver_info
Whether to print solver info.
const Real _mol_cutoff
Species with molalities less than mol_cutoff will not be outputted.
Real getKineticMoles(unsigned kin) const
Real getKineticLog10K(unsigned kin) const
const std::vector< Real > & getSorbingSurfaceArea() const
const ModelGeochemicalDatabase & getModelGeochemicalDatabase() const
const unsigned _precision
precision of output
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-...
registerMooseObject("GeochemistryApp", GeochemistryConsoleOutput)
virtual const DenseVector< Real > & getMoleAdditions(dof_id_type node_id) const =0
Real getRedoxLog10K(unsigned red) const
std::vector< Real > basis_species_molecular_weight
all quantities have a molecular weight (g)
const GeochemistryReactorBase & _reactor
the Reactor from which to extract info
void setModelGeochemicalDatabase(const ModelGeochemicalDatabase &mgd)
Copies a ModelGeochemicalDatabase into our _mgd structure.
Real getLog10K(unsigned j) const
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Real getSurfacePotential(unsigned sp) const
unsigned getNumSurfacePotentials() const
return the number of surface potentials
std::vector< std::string > originalBasisNames() const
Real getEquilibriumMolality(unsigned j) const
This class holds information about bulk composition, molalities, activities, activity coefficients...
Real log10RedoxActivityProduct(unsigned red) const
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
Real getEquilibriumActivityCoefficient(unsigned j) const
void addClassDescription(const std::string &doc_string)
std::vector< std::string > kin_species_name
kin_species_name[j] = name of the j^th kinetic species
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
void performSwapNoCheck(unsigned swap_out_of_basis, unsigned swap_into_basis)
Perform the basis swap, and ensure that the resulting system is consistent.
const std::vector< Real > & getSolventMassAndFreeMolalityAndMineralMoles() const
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
const ConsoleStream _console
DenseVector< Real > getTransportedBulkInOriginalBasis() const
Base class that controls the spatio-temporal solution of geochemistry reactions.
GeochemistryConsoleOutput(const InputParameters &parameters)
unsigned getNumInEquilibrium() const
returns the number of species in equilibrium with the basis components
std::unordered_map< std::string, SurfaceComplexationInfo > surface_complexation_info
Holds info on surface complexation, if any, in the model.
MooseUnits pow(const MooseUnits &, int)
static InputParameters validParams()
std::vector< std::string > eqm_species_name
eqm_species_name[i] = name of the i^th eqm species
static const std::string k
Definition: NS.h:130
static InputParameters validParams()
Real getSurfaceCharge(unsigned sp) const
void ErrorVector unsigned int
std::vector< std::string > basis_species_name
basis_species_name[j] = name of the j^th basis species
const std::string & getOriginalRedoxLHS() const
const PertinentGeochemicalSystem & getPertinentGeochemicalSystem() const
returns a reference to the PertinentGeochemicalSystem used to creat the ModelGeochemicalDatabase ...
uint8_t dof_id_type
std::vector< unsigned > have_swapped_out_of_basis
Species that have been swapped out of the basis.
std::vector< Real > getSaturationIndices() const