https://mooseframework.inl.gov
Loading...
Searching...
No Matches
GeochemicalSystem.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "GeochemicalSystem.h"
14
19 const std::vector<std::string> & swap_out_of_basis,
20 const std::vector<std::string> & swap_into_basis,
21 const std::string & charge_balance_species,
22 const std::vector<std::string> & constrained_species,
23 const std::vector<Real> & constraint_value,
24 const MultiMooseEnum & constraint_unit,
25 const MultiMooseEnum & constraint_user_meaning,
26 Real initial_temperature,
27 unsigned iters_to_make_consistent,
28 Real min_initial_molality,
29 const std::vector<std::string> & kin_name,
30 const std::vector<Real> & kin_initial,
31 const MultiMooseEnum & kin_unit)
32 : _mgd(mgd),
33 _num_basis(mgd.basis_species_index.size()),
34 _num_eqm(mgd.eqm_species_index.size()),
35 _num_redox(mgd.redox_stoichiometry.m()),
36 _num_surface_pot(mgd.surface_sorption_name.size()),
37 _num_kin(mgd.kin_species_index.size()),
38 _swapper(swapper),
39 _swap_out(swap_out_of_basis),
40 _swap_in(swap_into_basis),
41 _gac(gac),
42 _is(is),
43 _charge_balance_species(charge_balance_species),
44 _original_charge_balance_species(charge_balance_species),
45 _charge_balance_basis_index(0),
46 _constrained_species(constrained_species),
47 _constraint_value(constraint_value),
48 _original_constraint_value(constraint_value),
49 _constraint_unit(constraint_unit.size()),
50 _constraint_user_meaning(constraint_user_meaning.size()),
51 _constraint_meaning(constraint_user_meaning.size()),
52 _eqm_log10K(_num_eqm),
53 _redox_log10K(_num_redox),
54 _kin_log10K(_num_kin),
55 _num_basis_in_algebraic_system(0),
56 _num_in_algebraic_system(0),
57 _in_algebraic_system(_num_basis),
58 _algebraic_index(_num_basis),
59 _basis_index(_num_basis),
60 _bulk_moles_old(_num_basis),
61 _basis_molality(_num_basis),
62 _basis_activity_known(_num_basis),
63 _basis_activity(_num_basis),
64 _eqm_molality(_num_eqm),
65 _basis_activity_coef(_num_basis),
66 _eqm_activity_coef(_num_eqm),
67 _eqm_activity(_num_eqm),
68 _surface_pot_expr(_num_surface_pot),
69 _sorbing_surface_area(_num_surface_pot),
70 _kin_moles(_num_kin),
71 _kin_moles_old(_num_kin),
72 _iters_to_make_consistent(iters_to_make_consistent),
73 _temperature(initial_temperature),
74 _min_initial_molality(min_initial_molality),
75 _original_redox_lhs()
76{
77 for (unsigned i = 0; i < constraint_user_meaning.size(); ++i)
79 static_cast<ConstraintUserMeaningEnum>(constraint_user_meaning.get(i));
80 for (unsigned i = 0; i < constraint_unit.size(); ++i)
82 static_cast<GeochemistryUnitConverter::GeochemistryUnit>(constraint_unit.get(i));
83 const unsigned ku_size = kin_unit.size();
84 std::vector<GeochemistryUnitConverter::GeochemistryUnit> k_unit(ku_size);
85 for (unsigned i = 0; i < ku_size; ++i)
86 k_unit[i] = static_cast<GeochemistryUnitConverter::GeochemistryUnit>(kin_unit.get(i));
87 checkAndInitialize(kin_name, kin_initial, k_unit);
88}
89
95 const std::vector<std::string> & swap_out_of_basis,
96 const std::vector<std::string> & swap_into_basis,
97 const std::string & charge_balance_species,
98 const std::vector<std::string> & constrained_species,
99 const std::vector<Real> & constraint_value,
100 const std::vector<GeochemistryUnitConverter::GeochemistryUnit> & constraint_unit,
101 const std::vector<ConstraintUserMeaningEnum> & constraint_user_meaning,
102 Real initial_temperature,
103 unsigned iters_to_make_consistent,
104 Real min_initial_molality,
105 const std::vector<std::string> & kin_name,
106 const std::vector<Real> & kin_initial,
107 const std::vector<GeochemistryUnitConverter::GeochemistryUnit> & kin_unit)
108 : _mgd(mgd),
109 _num_basis(mgd.basis_species_index.size()),
110 _num_eqm(mgd.eqm_species_index.size()),
111 _num_redox(mgd.redox_stoichiometry.m()),
112 _num_surface_pot(mgd.surface_sorption_name.size()),
113 _num_kin(mgd.kin_species_index.size()),
114 _swapper(swapper),
115 _swap_out(swap_out_of_basis),
116 _swap_in(swap_into_basis),
117 _gac(gac),
118 _is(is),
119 _charge_balance_species(charge_balance_species),
120 _original_charge_balance_species(charge_balance_species),
121 _charge_balance_basis_index(0),
122 _constrained_species(constrained_species),
123 _constraint_value(constraint_value),
124 _original_constraint_value(constraint_value),
125 _constraint_unit(constraint_unit),
126 _constraint_user_meaning(constraint_user_meaning),
127 _constraint_meaning(constraint_user_meaning.size()),
128 _eqm_log10K(_num_eqm),
129 _redox_log10K(_num_redox),
130 _kin_log10K(_num_kin),
131 _num_basis_in_algebraic_system(0),
132 _num_in_algebraic_system(0),
133 _in_algebraic_system(_num_basis),
134 _algebraic_index(_num_basis),
135 _basis_index(_num_basis),
136 _bulk_moles_old(_num_basis),
137 _basis_molality(_num_basis),
138 _basis_activity_known(_num_basis),
139 _basis_activity(_num_basis),
140 _eqm_molality(_num_eqm),
141 _basis_activity_coef(_num_basis),
142 _eqm_activity_coef(_num_eqm),
143 _eqm_activity(_num_eqm),
144 _surface_pot_expr(_num_surface_pot),
145 _sorbing_surface_area(_num_surface_pot),
146 _kin_moles(_num_kin),
147 _kin_moles_old(_num_kin),
148 _iters_to_make_consistent(iters_to_make_consistent),
149 _temperature(initial_temperature),
150 _min_initial_molality(min_initial_molality),
151 _original_redox_lhs()
152{
153 checkAndInitialize(kin_name, kin_initial, kin_unit);
154}
155
156void
158 const std::vector<std::string> & kin_name,
159 const std::vector<Real> & kin_initial,
160 const std::vector<GeochemistryUnitConverter::GeochemistryUnit> & kin_unit)
161{
162 // initialize every the kinetic species
163 const unsigned num_kin_name = kin_name.size();
164 if (!(_num_kin == num_kin_name && _num_kin == kin_initial.size() && _num_kin == kin_unit.size()))
165 mooseError("Initial mole number (or mass or volume) and a unit must be provided for each "
166 "kinetic species ",
167 _num_kin,
168 " ",
169 num_kin_name,
170 " ",
171 kin_initial.size(),
172 " ",
173 kin_unit.size());
174 for (const auto & name_index : _mgd.kin_species_index)
175 {
176 const unsigned ind = std::distance(
177 kin_name.begin(), std::find(kin_name.begin(), kin_name.end(), name_index.first));
178 if (ind < num_kin_name)
179 {
186 mooseError("Kinetic species ",
187 name_index.first,
188 ": units must be moles or mass, or volume in the case of minerals");
189 const Real moles = GeochemistryUnitConverter::toMoles(
190 kin_initial[ind], kin_unit[ind], name_index.first, _mgd);
191 setKineticMoles(name_index.second, moles);
192 }
193 else
194 mooseError("Initial mole number or mass or volume for kinetic species ",
195 name_index.first,
196 " must be provided");
197 }
198
199 // check sanity of swaps
200 if (_swap_out.size() != _swap_in.size())
201 mooseError("swap_out_of_basis must have same length as swap_into_basis");
202 for (unsigned i = 0; i < _swap_out.size(); ++i)
204 mooseError("Cannot swap out ",
206 " because it is the charge-balance species\n");
207
208 // do swaps desired by user. any exception here is an error
209 for (unsigned i = 0; i < _swap_out.size(); ++i)
210 try
211 {
213 }
214 catch (const MooseException & e)
215 {
216 mooseError(e.what());
217 }
218
219 // check charge-balance species is in the basis and has a charge
221 mooseError("Cannot enforce charge balance using ",
223 " because it is not in the basis");
226 mooseError("Cannot enforce charge balance using ",
228 " because it has zero charge");
229
230 // check that constraint vectors have appropriate sizes
231 if (_constrained_species.size() != _constraint_value.size())
232 mooseError("Constrained species names must have same length as constraint values");
233 if (_constrained_species.size() != _constraint_unit.size())
234 mooseError("Constrained species names must have same length as constraint units");
236 mooseError("Constrained species names must have same length as constraint meanings");
237 if (_constrained_species.size() != _num_basis)
238 mooseError("Constrained species names must have same length as the number of species in the "
239 "basis (each component must be provided with a single constraint");
240
241 // check that each _constrained_species name appears in the basis
242 for (const auto & name : _mgd.basis_species_name)
243 if (std::find(_constrained_species.begin(), _constrained_species.end(), name) ==
245 mooseError("The basis species ", name, " must appear in the constrained species list");
246
247 // order the constraints in the same way as the basis species. This makes the remainder of the
248 // code much cleaner.
249 std::vector<std::string> c_s(_num_basis);
250 std::vector<Real> c_v(_num_basis);
251 std::vector<GeochemistryUnitConverter::GeochemistryUnit> c_u(_num_basis);
252 std::vector<ConstraintUserMeaningEnum> c_m(_num_basis);
253 for (unsigned i = 0; i < _num_basis; ++i)
254 {
255 const unsigned basis_ind = _mgd.basis_species_index.at(_constrained_species[i]);
256 c_s[basis_ind] = _constrained_species[i];
257 c_v[basis_ind] = _constraint_value[i];
258 c_u[basis_ind] = _constraint_unit[i];
259 c_m[basis_ind] = _constraint_user_meaning[i];
260 }
262 _constraint_value = c_v;
263 _constraint_unit = c_u;
266
267 // run through the constraints, checking physical and chemical consistency, converting to mole
268 // units, and building constraint_meaning
269 for (unsigned i = 0; i < _constrained_species.size(); ++i)
270 {
271 const std::string name = _constrained_species[i];
272
273 switch (_constraint_user_meaning[i])
274 {
276 {
277 // if the mass of solvent water is provided, check it is positive
278 if (_constraint_value[i] <= 0.0)
279 mooseError("Specified mass of solvent water must be positive: you entered ",
282 mooseError("Units for kg_solvent_water must be kg");
284 break;
285 }
288 {
289 // convert to mole units and specify correct constraint_meaning
292 // add contributions from kinetic moles, if necessary
294 for (unsigned k = 0; k < _mgd.kin_species_name.size(); ++k)
301 mooseError("Species ", name, ": units for bulk composition must be moles or mass");
302 if (name == "H2O")
304 else
306 break;
307 }
309 {
310 // if free concentration, check it is positive and perform the translation to mole units
311 if (_constraint_value[i] <= 0.0)
312 mooseError("Specified free concentration values must be positive: you entered ",
315 _constraint_unit[i] ==
317 _constraint_unit[i] ==
319 _constraint_unit[i] ==
321 _constraint_unit[i] ==
324 "Species ",
325 name,
326 ": units for free concentration quantities must be molal or mass_per_kg_solvent");
330 break;
331 }
333 {
334 // if free mineral, check it is positive and perform the translation to mole units
335 if (_constraint_value[i] <= 0.0)
336 mooseError("Specified free mineral values must be positive: you entered ",
344 mooseError("Species ",
345 name,
346 ": units for free mineral quantities must be moles, mass or volume");
350 break;
351 }
353 {
354 if (_constraint_value[i] <= 0.0)
355 mooseError("Specified activity values must be positive: you entered ",
359 "Species ", name, ": dimensionless units must be used when specifying activity");
361 break;
362 }
364 {
365 // if log10activity is provided, convert it to activity
367 mooseError("Species ",
368 name,
369 ": dimensionless units must be used when specifying log10activity\n");
370 _constraint_value[i] = std::pow(10.0, _constraint_value[i]);
372 break;
373 }
375 {
376 // if fugacity is provided, check it is positive
377 if (_constraint_value[i] <= 0.0)
378 mooseError("Specified fugacity values must be positive: you entered ",
382 "Species ", name, ": dimensionless units must be used when specifying fugacity\n");
384 break;
385 }
387 {
388 // if log10fugacity is provided, convert it to fugacity
390 mooseError("Species ",
391 name,
392 ": dimensionless units must be used when specifying log10fugacity\n");
393 _constraint_value[i] = std::pow(10.0, _constraint_value[i]);
395 break;
396 }
397 }
398
399 // check that water is provided with correct meaning
400 if (name == "H2O")
404 mooseError("H2O must be provided with either a mass of solvent water, a bulk composition "
405 "in moles or mass, or an activity");
406
407 // check that gases are provided with the correct meaning
408 if (_mgd.basis_species_gas[i])
410 mooseError("The gas ", name, " must be provided with a fugacity");
411
412 // check that minerals are provided with the correct meaning
416 mooseError("The mineral ",
417 name,
418 " must be provided with either: a free number of moles, a free mass or a free "
419 "volume; or a bulk composition of moles or mass");
420
421 // check that non-water, non-minerals, non-gases are provided with the correct meaning
422 if (name != "H2O" && !_mgd.basis_species_gas[i] && !_mgd.basis_species_mineral[i])
426 mooseError("The basis species ",
427 name,
428 " must be provided with a free concentration, bulk composition or an activity");
429
430 // check that the charge-balance species has been provided MOLES_BULK_SPECIES
433 mooseError("For code consistency, the species ",
434 name,
435 " must be provided with a bulk composition because it is the charge-balance "
436 "species. The value provided should be a reasonable estimate of the mole "
437 "number, but will be overridden as the solve progresses");
438 }
440
441 initialize();
442}
443
444void
462
463void
465{
466 // the steps 1 and 2 below could be iterated for a long time (or a Newton process could even be
467 // followed) to provide better estimates of activities and molalities, but this is not done in the
468 // conventional geochemistry approach: there are just too many unknowns and approximations
469 // employed during the algebraic-system solve to justify iterating towards the perfectly
470 // consistent initial condition
471 for (unsigned picard = 0; picard < _iters_to_make_consistent + 1; ++picard)
472 {
473 // Step 1: compute ionic strengths and activities using the eqm molalities
478
479 // Step 2: compute equilibrium molality based on the activities just computed
481 }
482
486}
487
488unsigned
493
494Real
496{
497 if (j >= _num_eqm)
498 mooseError("Cannot retrieve log10K for equilibrium species ",
499 j,
500 " since there are only ",
501 _num_eqm,
502 " equilibrium species");
503 return _eqm_log10K[j];
504}
505
506unsigned
508{
509 return _num_redox;
510}
511
512Real
514{
515 if (red >= _num_redox)
516 mooseError("Cannot retrieve log10K for redox species ",
517 red,
518 " since there are only ",
520 " redox species");
521 return _redox_log10K[red];
522}
523
524Real
526{
527 if (red >= _num_redox)
528 mooseError("Cannot retrieve activity product for redox species ",
529 red,
530 " since there are only ",
532 " redox species");
533 Real log10ap = 0.0;
534 for (unsigned basis_i = 0; basis_i < _num_basis; ++basis_i)
535 log10ap += _mgd.redox_stoichiometry(red, basis_i) * std::log10(_basis_activity[basis_i]);
536 return log10ap;
537}
538
539unsigned
541{
542 return _num_kin;
543}
544
545Real
547{
548 if (kin >= _num_kin)
549 mooseError("Cannot retrieve log10K for kinetic species ",
550 kin,
551 " since there are only ",
552 _num_kin,
553 " kinetic species");
554 return _kin_log10K[kin];
555}
556
557const std::vector<Real> &
562
563Real
565{
566 if (kin >= _num_kin)
567 mooseError("Cannot retrieve activity product for kinetic species ",
568 kin,
569 " since there are only ",
570 _num_kin,
571 " kinetic species");
572 Real log10ap = 0.0;
573 for (unsigned basis_i = 0; basis_i < _num_basis; ++basis_i)
574 log10ap += _mgd.kin_stoichiometry(kin, basis_i) * std::log10(_basis_activity[basis_i]);
575 return log10ap;
576}
577
578void
580{
581 const std::vector<Real> temps = _mgd.original_database->getTemperatures();
582 const unsigned numT = temps.size();
583 const std::string model_type = _mgd.original_database->getLogKModel();
584
585 for (unsigned eqm_j = 0; eqm_j < _num_eqm; ++eqm_j)
586 {
588 temps, _mgd.eqm_log10K.sub_matrix(eqm_j, 1, 0, numT).get_values(), model_type);
589 interp.generate();
590 _eqm_log10K[eqm_j] = interp.sample(temperature);
591 }
592 for (unsigned red = 0; red < _num_redox; ++red)
593 {
595 temps, _mgd.redox_log10K.sub_matrix(red, 1, 0, numT).get_values(), model_type);
596 interp.generate();
597 _redox_log10K[red] = interp.sample(temperature);
598 }
599 for (unsigned kin = 0; kin < _num_kin; ++kin)
600 {
602 temps, _mgd.kin_log10K.sub_matrix(kin, 1, 0, numT).get_values(), model_type);
603 interp.generate();
604 _kin_log10K[kin] = interp.sample(temperature);
605 }
606}
607
608void
609GeochemicalSystem::buildAlgebraicInfo(std::vector<bool> & in_algebraic_system,
610 unsigned & num_basis_in_algebraic_system,
611 unsigned & num_in_algebraic_system,
612 std::vector<unsigned> & algebraic_index,
613 std::vector<unsigned> & basis_index) const
614{
615 // build in_algebraic_system
616 for (const auto & name_index : _mgd.basis_species_index)
617 {
618 const std::string name = name_index.first;
619 const unsigned basis_ind = name_index.second;
620 const ConstraintMeaningEnum meaning = _constraint_meaning[basis_ind];
621 if (name == "H2O")
622 in_algebraic_system[basis_ind] = (meaning == ConstraintMeaningEnum::MOLES_BULK_WATER);
623 else if (_mgd.basis_species_gas[basis_ind])
624 in_algebraic_system[basis_ind] = false;
625 else if (_mgd.basis_species_mineral[basis_ind])
626 in_algebraic_system[basis_ind] = false;
627 else
628 in_algebraic_system[basis_ind] = (meaning == ConstraintMeaningEnum::MOLES_BULK_SPECIES);
629 }
630
631 // build algebraic_index and basis_index
632 num_basis_in_algebraic_system = 0;
633 algebraic_index.resize(_num_basis, 0);
634 basis_index.resize(_num_basis, 0);
635 for (unsigned basis_ind = 0; basis_ind < _num_basis; ++basis_ind)
636 if (in_algebraic_system[basis_ind])
637 {
638 algebraic_index[basis_ind] = _num_basis_in_algebraic_system;
639 basis_index[_num_basis_in_algebraic_system] = basis_ind;
640 num_basis_in_algebraic_system += 1;
641 }
642
643 num_in_algebraic_system = num_basis_in_algebraic_system + _num_surface_pot + _num_kin;
644}
645
646unsigned
651
652unsigned
657
658unsigned
663
664const std::vector<bool> &
669
670const std::vector<unsigned> &
675
676const std::vector<unsigned> &
681
682std::vector<Real>
684{
686 for (unsigned a = 0; a < _num_basis_in_algebraic_system; ++a)
688 for (unsigned s = 0; s < _num_surface_pot; ++s)
690 for (unsigned k = 0; k < _num_kin; ++k)
692 return var;
693}
694
695std::vector<Real>
697{
698 std::vector<Real> var(_num_basis_in_algebraic_system);
699 for (unsigned a = 0; a < _num_basis_in_algebraic_system; ++a)
701 return var;
702}
703
704DenseVector<Real>
706{
707 DenseVector<Real> var(_num_in_algebraic_system);
708 for (unsigned a = 0; a < _num_basis_in_algebraic_system; ++a)
710 for (unsigned s = 0; s < _num_surface_pot; ++s)
712 for (unsigned k = 0; k < _num_kin; ++k)
714 return var;
715}
716
717void
718GeochemicalSystem::initBulkAndFree(std::vector<Real> & bulk_moles_old,
719 std::vector<Real> & basis_molality) const
720{
721 for (unsigned i = 0; i < _num_basis; ++i)
722 {
723 // water is done first, so dividing by basis_molality[0] is OK
724 const Real value = _constraint_value[i];
726 switch (meaning)
727 {
729 {
730 bulk_moles_old[i] = value;
731 basis_molality[i] = std::max(
733 0.999 * value /
734 GeochemistryConstants::MOLES_PER_KG_WATER); // mass of solvent water (water is an
735 // algebraic variable). Guess used to
736 // initialize the Newton process
737 break;
738 }
740 {
741 bulk_moles_old[i] = value * GeochemistryConstants::MOLES_PER_KG_WATER /
742 0.999; // initial guess (water is not an algebraic variable). Will be
743 // determined exactly during the solve
744 basis_molality[i] = value; // mass of solvent water
745 break;
746 }
748 {
749 bulk_moles_old[i] = value;
750 basis_molality[i] = std::max(
752 0.9 * value / basis_molality[0]); // initial guess (i is an algebraic variable). This
753 // is what we solve for in the Newton process
754 break;
755 }
757 {
758 bulk_moles_old[i] =
759 value * basis_molality[0] / 0.9; // initial guess (i is not an algebraic variable).
760 // Will be determined exactly during the solve
761 basis_molality[i] = value;
762 break;
763 }
765 {
766 bulk_moles_old[i] = value / 0.9; // initial guess (i is not an algebraic variable). Will
767 // be determined exactly during the solve
768 basis_molality[i] = value; // note, this is *moles*, not molality
769 break;
770 }
772 {
773 bulk_moles_old[i] = 0.0; // initial guess (i is not an algebraic variable). will be
774 // determined exactly after the solve
775 basis_molality[i] =
776 0.0; // never used in any solve process, but since this is a gas should be zero, and
777 // setting this explicitly elimiates if(species=gas) checks in various loops
778 break;
779 }
781 {
782 bulk_moles_old[i] = value / 0.9; // initial guess (i is not an algebraic variable). Will
783 // be determined exactly during the solve
784 if (i == 0)
785 basis_molality[i] = 1.0; // assumption
786 else
787 basis_molality[i] =
788 value /
789 0.9; // assume activity_coefficient = 0.9. this will be updated during the solve
790 break;
791 }
792 }
793 }
794}
795
796Real
801
802const std::vector<Real> &
807
808DenseVector<Real>
810{
811 DenseVector<Real> result(_bulk_moles_old);
812 if (_mgd.swap_to_original_basis.n() == 0)
813 return result; // no swaps have been performed
815 return result;
816}
817
818DenseVector<Real>
820{
821 std::vector<Real> trans_bulk;
823 DenseVector<Real> result(trans_bulk);
824 if (_mgd.swap_to_original_basis.n() == 0)
825 return result; // no swaps have been performed
827 return result;
828}
829
830const std::vector<Real> &
835
836void
837GeochemicalSystem::buildKnownBasisActivities(std::vector<bool> & basis_activity_known,
838 std::vector<Real> & basis_activity) const
839{
840 basis_activity_known = std::vector<bool>(_num_basis, false);
841 basis_activity.resize(_num_basis);
842
843 // all aqueous species with provided activity, and all gases with fugacity have known activity
844 for (unsigned i = 0; i < _num_basis; ++i)
845 {
848 {
849 basis_activity_known[i] = true;
850 basis_activity[i] = _constraint_value[i];
851 }
852 }
853
854 // all minerals have activity = 1.0
855 for (unsigned basis_ind = 0; basis_ind < _num_basis; ++basis_ind)
856 if (_mgd.basis_species_mineral[basis_ind])
857 {
858 basis_activity_known[basis_ind] = true;
859 basis_activity[basis_ind] = 1.0;
860 }
861}
862
863const std::vector<bool> &
868
869Real
871{
872 if (i >= _num_basis)
873 mooseError("Cannot retrieve basis activity for species ",
874 i,
875 " since there are only ",
877 " basis species");
878 return _basis_activity[i];
879}
880
881const std::vector<Real> &
886
887Real
889{
890 if (j >= _num_eqm)
891 mooseError("Cannot retrieve molality for equilibrium species ",
892 j,
893 " since there are only ",
894 _num_eqm,
895 " equilibrium species");
896 return _eqm_molality[j];
897}
898
899const std::vector<Real> &
904
905Real
907{
908 if (k >= _num_kin)
909 mooseError("Cannot retrieve moles for kinetic species ",
910 k,
911 " since there are only ",
912 _num_kin,
913 " kinetic species");
914 return _kin_moles[k];
915}
916
917void
919{
920 if (k >= _num_kin)
921 mooseError("Cannot set moles for kinetic species ",
922 k,
923 " since there are only ",
924 _num_kin,
925 " kinetic species");
926 if (moles <= 0.0)
927 mooseError("Mole number for kinetic species must be positive, not ", moles);
928 _kin_moles[k] = moles;
929 _kin_moles_old[k] = moles;
930}
931
932const std::vector<Real> &
934{
935 return _kin_moles;
936}
937
938void
939GeochemicalSystem::computeRemainingBasisActivities(std::vector<Real> & basis_activity) const
940{
941 if (!_basis_activity_known[0])
942 basis_activity[0] = _gac.waterActivity();
943 for (unsigned basis_ind = 1; basis_ind < _num_basis; ++basis_ind) // don't loop over water
944 if (!_basis_activity_known[basis_ind]) // basis_activity_known = true for minerals, gases and
945 // species with activities provided by the user
946 basis_activity[basis_ind] = _basis_activity_coef[basis_ind] * _basis_molality[basis_ind];
947}
948
949Real
951{
952 if (j >= _num_eqm)
953 mooseError("Cannot retrieve activity coefficient for equilibrium species ",
954 j,
955 " since there are only ",
956 _num_eqm,
957 " equilibrium species");
958 return _eqm_activity_coef[j];
959}
960
961const std::vector<Real> &
966
967Real
969{
970 if (i >= _num_basis)
971 mooseError("Cannot retrieve basis activity coefficient for species ",
972 i,
973 " since there are only ",
975 " basis species");
976 return _basis_activity_coef[i];
977}
978
979const std::vector<Real> &
984
985void
986GeochemicalSystem::updateBasisMolalityForKnownActivity(std::vector<Real> & basis_molality) const
987{
988 for (unsigned i = 1; i < _num_basis; ++i) // don't loop over water
989 {
991 basis_molality[i] =
992 _basis_activity[i] / _basis_activity_coef[i]; // just those for
993 // which activity is provided by the user
994 }
995}
996
997Real
999{
1000 Real log10ap = 0.0;
1001 for (unsigned basis_i = 0; basis_i < _num_basis; ++basis_i)
1002 log10ap += _mgd.eqm_stoichiometry(eqm_j, basis_i) * std::log10(_basis_activity[basis_i]);
1003 return log10ap;
1004}
1005
1006void
1007GeochemicalSystem::computeEqmMolalities(std::vector<Real> & eqm_molality) const
1008{
1009 for (unsigned eqm_j = 0; eqm_j < _num_eqm; ++eqm_j)
1010 {
1011 if (_mgd.eqm_species_mineral[eqm_j] || _mgd.eqm_species_gas[eqm_j])
1012 eqm_molality[eqm_j] = 0.0;
1013 else
1014 {
1015 // compute log10 version first, in an attempt to eliminate overflow and underflow problems
1016 // such as 10^(1000)
1017 const Real log10m = log10ActivityProduct(eqm_j) - _eqm_log10K[eqm_j];
1018 eqm_molality[eqm_j] =
1019 std::pow(10.0, log10m) / _eqm_activity_coef[eqm_j] * surfaceSorptionModifier(eqm_j);
1020 }
1021 }
1022}
1023
1024Real
1026{
1027 if (eqm_j >= _num_eqm)
1028 return 1.0;
1029 if (!_mgd.surface_sorption_related[eqm_j])
1030 return 1.0;
1031 return std::pow(_surface_pot_expr[_mgd.surface_sorption_number[eqm_j]],
1032 2.0 * _mgd.eqm_species_charge[eqm_j]);
1033}
1034
1035void
1036GeochemicalSystem::enforceChargeBalanceIfSimple(std::vector<Real> & constraint_value,
1037 std::vector<Real> & bulk_moles_old) const
1038{
1039 Real tot_charge = 0.0;
1040 for (unsigned basis_i = 0; basis_i < _num_basis; ++basis_i)
1041 if (_mgd.basis_species_charge[basis_i] != 0.0)
1042 {
1044 tot_charge += _mgd.basis_species_charge[basis_i] * constraint_value[basis_i];
1045 else
1046 return;
1047 }
1048 // kinetic species are counted in the bulk
1049 // all charged basis species must have been provided with a MOLES_BULK_SPECIES value, so we can
1050 // easily enforce charge neutrality
1052 constraint_value[_charge_balance_basis_index];
1053 constraint_value[_charge_balance_basis_index] =
1055 bulk_moles_old[_charge_balance_basis_index] = constraint_value[_charge_balance_basis_index];
1056}
1057
1058Real
1060{
1061 Real tot_charge = 0.0;
1062 for (unsigned basis_i = 0; basis_i < _num_basis; ++basis_i)
1063 tot_charge += _mgd.basis_species_charge[basis_i] * _bulk_moles_old[basis_i];
1064 // kinetic species already counted in bulk_moles
1065 return tot_charge;
1066}
1067
1068void
1073
1074void
1075GeochemicalSystem::enforceChargeBalance(std::vector<Real> & constraint_value,
1076 std::vector<Real> & bulk_moles_old) const
1077{
1078 const Real tot_charge = getTotalChargeOld();
1079 constraint_value[_charge_balance_basis_index] -=
1081 bulk_moles_old[_charge_balance_basis_index] = constraint_value[_charge_balance_basis_index];
1082}
1083
1084void
1085GeochemicalSystem::setAlgebraicVariables(const DenseVector<Real> & algebraic_var)
1086{
1087 if (algebraic_var.size() != _num_in_algebraic_system)
1088 mooseError("Incorrect size in setAlgebraicVariables");
1089 for (unsigned a = 0; a < _num_in_algebraic_system; ++a)
1090 if (algebraic_var(a) <= 0.0)
1091 mooseError("Cannot set algebraic variables to non-positive values such as ",
1092 algebraic_var(a));
1093
1094 for (unsigned a = 0; a < _num_basis_in_algebraic_system; ++a)
1095 _basis_molality[_basis_index[a]] = algebraic_var(a);
1096 for (unsigned s = 0; s < _num_surface_pot; ++s)
1097 _surface_pot_expr[s] = algebraic_var(s + _num_basis_in_algebraic_system);
1098 for (unsigned k = 0; k < _num_kin; ++k)
1100
1102}
1103
1104void
1105GeochemicalSystem::computeBulk(std::vector<Real> & bulk_moles_old) const
1106{
1107 for (unsigned i = 0; i < _num_basis; ++i)
1108 {
1109 const Real value = _constraint_value[i];
1110 const ConstraintMeaningEnum meaning = _constraint_meaning[i];
1111 switch (meaning)
1112 {
1115 {
1116 bulk_moles_old[i] = value;
1117 break;
1118 }
1124 {
1125 bulk_moles_old[i] = computeBulkFromMolalities(i);
1126 break;
1127 }
1128 }
1129 }
1130}
1131
1132Real
1134{
1135 const Real nw = _basis_molality[0];
1136 Real bulk = 0.0;
1137 if (basis_ind == 0)
1139 else if (_mgd.basis_species_mineral[basis_ind])
1140 bulk = _basis_molality[basis_ind] / nw; // because of multiplication by nw, below
1141 else
1142 bulk = _basis_molality[basis_ind];
1143 for (unsigned j = 0; j < _num_eqm; ++j)
1144 bulk += _mgd.eqm_stoichiometry(j, basis_ind) * _eqm_molality[j];
1145 bulk *= nw;
1146 for (unsigned kin = 0; kin < _num_kin; ++kin)
1147 bulk += _mgd.kin_stoichiometry(kin, basis_ind) * _kin_moles[kin];
1148 return bulk;
1149}
1150
1151void
1152GeochemicalSystem::computeTransportedBulkFromMolalities(std::vector<Real> & transported_bulk) const
1153{
1154 transported_bulk.resize(_num_basis);
1155 const Real nw = _basis_molality[0];
1156 for (unsigned i = 0; i < _num_basis; ++i)
1157 {
1158 transported_bulk[i] = 0.0;
1159 if (i == 0)
1160 transported_bulk[i] = GeochemistryConstants::MOLES_PER_KG_WATER;
1161 else if (_mgd.basis_species_transported[i])
1162 {
1164 transported_bulk[i] = _basis_molality[i] / nw; // because of multiplication by nw, below
1165 else
1166 transported_bulk[i] = _basis_molality[i];
1167 }
1168 else
1169 transported_bulk[i] = 0.0;
1170 for (unsigned j = 0; j < _num_eqm; ++j)
1172 transported_bulk[i] += _mgd.eqm_stoichiometry(j, i) * _eqm_molality[j];
1173 transported_bulk[i] *= nw;
1174 }
1175}
1176
1177Real
1179 const DenseVector<Real> & mole_additions) const
1180{
1181 if (algebraic_ind >= _num_in_algebraic_system)
1182 mooseError("Cannot retrieve residual for algebraic index ",
1183 algebraic_ind,
1184 " because there are only ",
1186 " molalities in the algebraic system and ",
1188 " surface potentials and ",
1189 _num_kin,
1190 " kinetic species");
1191 if (mole_additions.size() != _num_basis + _num_kin)
1192 mooseError("The increment in mole numbers (mole_additions) needs to be of size ",
1193 _num_basis,
1194 " + ",
1195 _num_kin,
1196 " but it is of size ",
1197 mole_additions.size());
1198
1199 if (algebraic_ind < _num_basis_in_algebraic_system) // residual for basis molality
1200 {
1201 /*
1202 * Without the special things for water or the charge-balance species, we're trying to solve
1203 * bulk_new = bulk_old + mole_addition
1204 * where bulk_old is known (from previous time-step or, for a steady-state problem, the
1205 * constraint)
1206 * and mole_addition is given to this function
1207 * and bulk_new = nw * (m + sum_eqm[stoi * mol_eqm]) + sum_kin[stoi * mole_kin]
1208 * Hence, the residual is
1209 * R = -(bulk_old + mole_addition) + nw*(m + sum_eqm[stoi*mol_eqm]) + sum_kin[stoi*mole_kin]
1210 * This is seemingly different from Bethke Eqns(16.7)-(16.9), because Bethke bulk ("M") do not
1211 contain the sum_kin[stoi * mol_kin] terms. Converting the above residual to Bethke's
1212 convention:
1213 * R = -((nw*(m + sum_eqm[stoi*mol_eqm]) + sum_kin[stoi*mole_kin])_old + mole_addition) + nw*(m
1214 + sum_eqm[stoi*mol_eqm]) + sum_kin[stoi*mole_kin]
1215 * = -(M_old + sum_kin[stoi*mole_kin_old] + mole_addition) + M + sum_kin[stoi*mole_kin]
1216 * = -(M_old + mole_addition) + M + sum_kin[stoi*kin_mole_addition]
1217 * which is exactly Eqns(16.7)-(16.9).
1218 * One problem with Bethke's approach is that if kin_mole_addition depends on the mole number
1219 of the kinetic species, there is a "hidden" variable (moles of kinetic species) which is kept
1220 constant during the solve. Here, including the moles of kinetic species as additional
1221 variables allows greater accuracy.
1222 */
1223 const unsigned basis_i = _basis_index[algebraic_ind];
1224 Real res = 0.0;
1225 if (basis_i == 0)
1226 res += -(_bulk_moles_old[basis_i] + mole_additions(basis_i)) +
1228 else if (basis_i == _charge_balance_basis_index)
1229 {
1230 res += _basis_molality[0] * _basis_molality[basis_i];
1231 for (unsigned i = 0; i < _num_basis; ++i)
1232 {
1234 continue;
1235 else if (_mgd.basis_species_charge[i] ==
1236 0.0) // certainly includes water, minerals and gases
1237 continue;
1239 {
1240 // the molalities might not yet have converged so that the bulk moles of this species
1241 // currently equals _bulk_moles_old + mole_additions, but we know
1242 // that when the solve has converged it'll have to, so:
1243 res += _mgd.basis_species_charge[i] * (_bulk_moles_old[i] + mole_additions(i)) /
1244 _mgd.basis_species_charge[basis_i];
1245 }
1246 else
1247 {
1248 // do not know the bulk moles of this species: use the current value for molality and
1249 // kin_moles. Note, there is no mole_additions here since physically any mole_additions
1250 // get instantly get soaked up by the fixed activity (or fixed molality, etc), and
1251 // mathematically when we eventually come to add mole_additions to the bulk_moles_old
1252 // (via addtoBulkMoles, for instance) we immediately return without adding stuff. End
1253 // result: charge-neutrality should not depend on mole_additions for fixed-activity
1254 // (molality, etc) species.
1256 _mgd.basis_species_charge[basis_i];
1257 }
1258 }
1259 }
1260 else
1261 res += -(_bulk_moles_old[basis_i] + mole_additions(basis_i)) +
1262 _basis_molality[0] * _basis_molality[basis_i];
1263 for (unsigned eqm_j = 0; eqm_j < _num_eqm; ++eqm_j)
1264 res += _basis_molality[0] * _mgd.eqm_stoichiometry(eqm_j, basis_i) * _eqm_molality[eqm_j];
1265 for (unsigned kin = 0; kin < _num_kin; ++kin)
1266 res += _mgd.kin_stoichiometry(kin, basis_i) * _kin_moles[kin];
1267 return res;
1268 }
1269 else if (algebraic_ind <
1270 _num_basis_in_algebraic_system + _num_surface_pot) // residual for surface potential
1271 {
1272 const unsigned sp = algebraic_ind - _num_basis_in_algebraic_system;
1273 Real res = surfacePotPrefactor(sp) * (_surface_pot_expr[sp] - 1.0 / _surface_pot_expr[sp]);
1274 for (unsigned j = 0; j < _num_eqm; ++j)
1277 return res;
1278 }
1279
1280 // else: residual for kinetic
1281 const unsigned kin = algebraic_ind - _num_basis_in_algebraic_system - _num_surface_pot;
1282 Real res = _kin_moles[kin] - (_kin_moles_old[kin] + mole_additions(_num_basis + kin));
1283 return res;
1284}
1285
1286void
1287GeochemicalSystem::computeJacobian(const DenseVector<Real> & res,
1288 DenseMatrix<Real> & jac,
1289 const DenseVector<Real> & mole_additions,
1290 const DenseMatrix<Real> & dmole_additions) const
1291{
1292 /* To the reader: yes, this is awfully complicated. The best way of understanding it is to very
1293 * slowly go through the residual calculation and compute the derivatives by hand. It is quite
1294 * well tested, but it'd be great if you could add more tests!
1295 */
1296 if (res.size() != _num_in_algebraic_system)
1297 mooseError(
1298 "Jacobian: residual size must be ", _num_in_algebraic_system, " but it is ", res.size());
1299 if (mole_additions.size() != _num_basis + _num_kin)
1300 mooseError("Jacobian: the increment in mole numbers (mole_additions) needs to be of size ",
1301 _num_basis,
1302 " + ",
1303 _num_kin,
1304 " but it is of size ",
1305 mole_additions.size());
1306 if (!(dmole_additions.n() == _num_basis + _num_kin &&
1307 dmole_additions.m() == _num_basis + _num_kin))
1308 mooseError("Jacobian: the derivative of mole additions (dmole_additions) needs to be of size ",
1310 "x",
1312 " but it is of size ",
1313 dmole_additions.m(),
1314 "x",
1315 dmole_additions.n());
1316 /*
1317Note that in this function we make use of the fact that species can only be in the algebraic
1318system if their molality is unknown (or in the case of water, the mass of solvent mass is
1319unknown). This means that the molality of the equilibrium species depends on
1320(activity_coefficient * basis molality)^stoi, so that the derivative is quite simple. Eg, we
1321don't have to worry about derivatives with respect to fixed-activity things, or fixed fugacity.
1322Also, note that the constructor and the various "set" methods of this class enforce molality > 0,
1323so there are no division-by-zero problems. Also, note that we never compute derivatives of the
1324activity coefficients, or the activity of water with respect to the molalities, as they are
1325assumed to be quite small. Finally, the surface_pot_expr will also always be positive.
1326 */
1327 // correctly size and zero
1329 const Real nw = _basis_molality[0];
1330
1331 // jac(a, b) = d(R_a) / d(m_b), where a corresponds to a molality
1332 for (unsigned a = 0; a < _num_basis_in_algebraic_system; ++a)
1333 {
1334 const unsigned basis_of_a = _basis_index[a];
1335 for (unsigned b = 0; b < _num_basis_in_algebraic_system; ++b)
1336 {
1337 const unsigned basis_of_b = _basis_index[b];
1338
1339 // contribution from mole_additions
1340 if (basis_of_a != _charge_balance_basis_index)
1341 jac(a, b) -= dmole_additions(basis_of_a, basis_of_b);
1342 else
1343 {
1344 for (unsigned i = 0; i < _num_basis; ++i)
1345 {
1347 continue;
1348 else if (_mgd.basis_species_charge[i] ==
1349 0.0) // certainly includes water, minerals and gases
1350 continue;
1352 jac(a, b) += _mgd.basis_species_charge[i] * dmole_additions(i, basis_of_b) /
1353 _mgd.basis_species_charge[basis_of_a];
1354 }
1355 }
1356
1357 // contribution from explicit nw, in case where mass of solvent water is an unknown
1358 if (basis_of_b == 0)
1359 {
1360 if (basis_of_a != _charge_balance_basis_index)
1361 {
1362 // use a short-cut here: dR/dnw = m + sum_eqm[stoi*mol] = (R + bulk + addition - kin)/nw
1363 Real numerator = res(a) + _bulk_moles_old[basis_of_a] + mole_additions(basis_of_a);
1364 for (unsigned kin = 0; kin < _num_kin; ++kin)
1365 numerator -= _mgd.kin_stoichiometry(kin, basis_of_a) * _kin_moles[kin];
1366 jac(a, 0) += numerator / nw;
1367 }
1368 else
1369 {
1370 for (unsigned i = 0; i < _num_basis; ++i)
1371 {
1373 continue;
1374 else if (_mgd.basis_species_charge[i] ==
1375 0.0) // certainly includes water, minerals and gases
1376 continue;
1378 continue;
1379 else
1380 {
1381 Real molal = _basis_molality[i];
1382 for (unsigned j = 0; j < _num_eqm; ++j)
1383 molal += _mgd.eqm_stoichiometry(j, i) * _eqm_molality[j];
1384 jac(a, 0) +=
1385 _mgd.basis_species_charge[i] * molal / _mgd.basis_species_charge[basis_of_a];
1386 }
1387 }
1388 jac(a, 0) += _basis_molality[basis_of_a];
1389 for (unsigned j = 0; j < _num_eqm; ++j)
1390 jac(a, 0) += _mgd.eqm_stoichiometry(j, basis_of_a) * _eqm_molality[j];
1391 }
1392 }
1393
1394 // contribution from molality of basis_of_b
1395 if (basis_of_b != 0)
1396 {
1397 if (a == b)
1398 jac(a, b) += nw; // remember b != 0
1399 for (unsigned eqm_j = 0; eqm_j < _num_eqm; ++eqm_j)
1400 {
1401 jac(a, b) += nw * _mgd.eqm_stoichiometry(eqm_j, basis_of_a) * _eqm_molality[eqm_j] *
1402 _mgd.eqm_stoichiometry(eqm_j, basis_of_b) / _basis_molality[basis_of_b];
1403 }
1404 if (basis_of_a == _charge_balance_basis_index)
1405 {
1406 // additional terms from the charge-balance additions
1407 for (unsigned i = 0; i < _num_basis; ++i)
1408 {
1410 continue;
1411 else if (_mgd.basis_species_charge[i] ==
1412 0.0) // certainly includes water, minerals and gases
1413 continue;
1415 continue;
1416 else
1417 {
1418 const Real prefactor =
1419 _mgd.basis_species_charge[i] * nw / _mgd.basis_species_charge[basis_of_a];
1420 if (i == basis_of_b)
1421 jac(a, b) += prefactor;
1422 for (unsigned eqm_j = 0; eqm_j < _num_eqm; ++eqm_j)
1423 {
1424 jac(a, b) += prefactor * _mgd.eqm_stoichiometry(eqm_j, i) * _eqm_molality[eqm_j] *
1425 _mgd.eqm_stoichiometry(eqm_j, basis_of_b) /
1426 _basis_molality[basis_of_b];
1427 }
1428 }
1429 }
1430 }
1431 }
1432 }
1433 }
1434
1435 // jac(a, b) = d(R_a) / d(surface_pot), where a corresponds to a molality
1436 for (unsigned a = 0; a < _num_basis_in_algebraic_system; ++a)
1437 {
1438 const unsigned basis_of_a = _basis_index[a];
1439 for (unsigned s = 0; s < _num_surface_pot; ++s)
1440 {
1441 const unsigned b = s + _num_basis_in_algebraic_system;
1442 // derivative of nw * _mgd.eqm_stoichiometry(eqm_j, basis_i) * _eqm_molality[eqm_j],
1443 // where _eqm_molality = (_surface_pot_expr)^(2 * charge) * stuff
1444 for (unsigned eqm_j = 0; eqm_j < _num_eqm; ++eqm_j)
1446 jac(a, b) += nw * _mgd.eqm_stoichiometry(eqm_j, basis_of_a) * 2.0 *
1447 _mgd.eqm_species_charge[eqm_j] * _eqm_molality[eqm_j] /
1449 }
1450 }
1451
1452 // jac(a, b) = d(R_a) / d(_kin_moles) where a corresponds to a molality
1453 for (unsigned a = 0; a < _num_basis_in_algebraic_system; ++a)
1454 {
1455 const unsigned basis_of_a = _basis_index[a];
1456
1457 // contribution from mole_additions
1458 for (unsigned kin = 0; kin < _num_kin; ++kin)
1459 {
1460 const unsigned ind = _num_basis + kin;
1461 const unsigned b = _num_basis_in_algebraic_system + _num_surface_pot + kin;
1462 if (basis_of_a != _charge_balance_basis_index)
1463 jac(a, b) -= dmole_additions(basis_of_a, ind);
1464 else
1465 {
1466 for (unsigned i = 0; i < _num_basis; ++i)
1467 {
1469 continue;
1470 else if (_mgd.basis_species_charge[i] ==
1471 0.0) // certainly includes water, minerals and gases
1472 continue;
1474 jac(a, b) += _mgd.basis_species_charge[i] * dmole_additions(i, ind) /
1475 _mgd.basis_species_charge[basis_of_a];
1476 }
1477 }
1478 }
1479
1480 // contribution from sum_kin[stoi*kin_moles]
1481 for (unsigned kin = 0; kin < _num_kin; ++kin)
1482 {
1483 const unsigned b = _num_basis_in_algebraic_system + _num_surface_pot + kin;
1484 jac(a, b) += _mgd.kin_stoichiometry(kin, basis_of_a);
1485 }
1486
1487 // special additional contribution for charge-balance from kinetic stuff
1488 if (basis_of_a == _charge_balance_basis_index)
1489 {
1490 for (unsigned i = 0; i < _num_basis; ++i)
1491 {
1493 continue;
1494 else if (_mgd.basis_species_charge[i] ==
1495 0.0) // certainly includes water, minerals and gases
1496 continue;
1498 continue;
1499 else
1500 {
1501 const Real prefactor =
1503 for (unsigned kin = 0; kin < _num_kin; ++kin)
1504 {
1505 const unsigned b = _num_basis_in_algebraic_system + _num_surface_pot + kin;
1506 jac(a, b) += prefactor * _mgd.kin_stoichiometry(kin, i);
1507 }
1508 }
1509 }
1510 }
1511 }
1512
1513 // jac(a, b) = d(R_a) / d(variable_b) where a corresponds to a surface potential
1514 for (unsigned s = 0; s < _num_surface_pot; ++s)
1515 {
1516 const unsigned a = s + _num_basis_in_algebraic_system;
1517
1518 for (unsigned b = 0; b < _num_basis_in_algebraic_system; ++b)
1519 {
1520 // derivative of nw * _mgd.eqm_species_charge[j] * _eqm_molality[j];
1521 const unsigned basis_of_b = _basis_index[b];
1522 if (basis_of_b == 0) // special case: mass of solvent water is an unknown
1523 {
1524 for (unsigned j = 0; j < _num_eqm; ++j)
1526 jac(a, b) += _mgd.eqm_species_charge[j] * _eqm_molality[j];
1527 }
1528 else
1529 {
1530 for (unsigned j = 0; j < _num_eqm; ++j)
1532 jac(a, b) += nw * _mgd.eqm_species_charge[j] * _eqm_molality[j] *
1533 _mgd.eqm_stoichiometry(j, basis_of_b) / _basis_molality[basis_of_b];
1534 }
1535 }
1536
1537 const Real coef = surfacePotPrefactor(s);
1538 // derivative of coef * (x - 1/x) wrt x, where x = _surface_pot_expr
1539 jac(a, a) += coef * (1.0 + 1.0 / std::pow(_surface_pot_expr[s], 2.0));
1540 // derivative of nw * _mgd.eqm_species_charge[j] * _eqm_molality[j];
1541 // where _eqm_molality = (_surface_pot_expr)^(2 * charge) * stuff
1542 for (unsigned j = 0; j < _num_eqm; ++j)
1544 jac(a, a) += nw * std::pow(_mgd.eqm_species_charge[j], 2.0) * 2.0 * _eqm_molality[j] /
1546 }
1547
1548 // jac(a, b) = d(R_a) / d(variable_b) where a corresponds to a kinetic species
1549 for (unsigned kin = 0; kin < _num_kin; ++kin)
1550 {
1551 const unsigned a = _num_basis_in_algebraic_system + _num_surface_pot + kin;
1552 jac(a, a) += 1.0; // deriv wrt _kin_moles[kin]
1553
1554 // contribution from mole_additions
1555 const unsigned ind_of_addition = _num_basis + kin;
1556 for (unsigned b = 0; b < _num_basis_in_algebraic_system; ++b)
1557 {
1558 const unsigned basis_of_b = _basis_index[b];
1559 jac(a, b) -= dmole_additions(ind_of_addition, basis_of_b);
1560 }
1561 for (unsigned kinp = 0; kinp < _num_kin; ++kinp)
1562 {
1563 const unsigned b = _num_basis_in_algebraic_system + _num_surface_pot + kinp;
1564 const unsigned indp = _num_basis + kinp;
1565 jac(a, b) -= dmole_additions(ind_of_addition, indp);
1566 }
1567 }
1568}
1569
1575
1576void
1577GeochemicalSystem::computeFreeMineralMoles(std::vector<Real> & basis_molality) const
1578{
1579
1580 const Real nw = _basis_molality[0];
1581 for (unsigned i = 0; i < _num_basis; ++i)
1583 {
1585 basis_molality[i] = _constraint_value[i];
1586 else
1587 {
1588 basis_molality[i] = _bulk_moles_old[i];
1589 for (unsigned j = 0; j < _num_eqm; ++j)
1590 basis_molality[i] -= nw * _mgd.eqm_stoichiometry(j, i) * _eqm_molality[j];
1591 for (unsigned kin = 0; kin < _num_kin; ++kin)
1592 basis_molality[i] -= _mgd.kin_stoichiometry(kin, i) * _kin_moles[kin];
1593 }
1594 }
1595}
1596
1597std::vector<Real>
1599{
1600 std::vector<Real> si(_num_eqm);
1601 for (unsigned j = 0; j < _num_eqm; ++j)
1603 si[j] = log10ActivityProduct(j) - _eqm_log10K[j];
1604 else
1605 si[j] = 0.0;
1606 return si;
1607}
1608
1609void
1610GeochemicalSystem::performSwap(unsigned swap_out_of_basis, unsigned swap_into_basis)
1611{
1612 if (swap_out_of_basis == 0)
1613 mooseException("GeochemicalSystem: attempting to swap out water and replace it by ",
1614 _mgd.eqm_species_name[swap_into_basis],
1615 ". This could be because the algorithm would like to "
1616 "swap out the charge-balance species, in which case you should choose a "
1617 "different charge-balance species");
1618 if (swap_out_of_basis == _charge_balance_basis_index)
1619 mooseException("GeochemicalSystem: attempting to swap the charge-balance species out of "
1620 "the basis");
1621 if (_mgd.basis_species_gas[swap_out_of_basis])
1622 mooseException("GeochemicalSystem: attempting to swap a gas out of the basis");
1623 if (_mgd.eqm_species_gas[swap_into_basis])
1624 mooseException("GeochemicalSystem: attempting to swap a gas into the basis");
1625 // perform the swap
1626 performSwapNoCheck(swap_out_of_basis, swap_into_basis);
1627}
1628
1629void
1630GeochemicalSystem::performSwapNoCheck(unsigned swap_out_of_basis, unsigned swap_into_basis)
1631{
1632 DenseVector<Real> bm = _bulk_moles_old;
1633 _swapper.performSwap(_mgd, bm, swap_out_of_basis, swap_into_basis);
1634
1635 // the swap_into_basis species now has fixed bulk moles irrespective of what the
1636 // swap_out_of_basis species had fixed
1638
1639 // the bulk moles will have changed for many components. The _bulk_moles_old values will be
1640 // set in computeConsistentConfiguration, below
1641 for (unsigned basis_i = 0; basis_i < _num_basis; ++basis_i)
1644 {
1645 _constraint_value[basis_i] = bm(basis_i);
1646 _original_constraint_value[basis_i] = bm(basis_i);
1647 }
1648
1649 // In the following, the molalities are carefully swapped so that the configuration stays
1650 // reasonably close to the configuration prior to the swap. This may be advantageous as it
1651 // initializes the Newton process with a better starting guess than what might have been used
1652 Real molality_of_species_just_swapped_in =
1653 _eqm_molality[swap_into_basis]; // is positive, or zero iff (mineral or gas)
1654 if (_mgd.basis_species_mineral[swap_out_of_basis] || _mgd.basis_species_gas[swap_out_of_basis] ||
1655 _eqm_molality[swap_into_basis] == 0.0)
1656 {
1657 // the species just swapped in is a mineral or a gas, so its equilibium molality is
1658 // undefined: make a guess for its molality
1659 molality_of_species_just_swapped_in =
1660 std::max(_min_initial_molality, 0.9 * bm(swap_out_of_basis));
1661 }
1662 Real molality_of_species_just_swapped_out =
1663 _basis_molality[swap_out_of_basis]; // can be negative if a consumed mineral
1664 _basis_molality[swap_out_of_basis] = molality_of_species_just_swapped_in;
1665 _eqm_molality[swap_into_basis] =
1666 std::max(0.0, molality_of_species_just_swapped_out); // this gets recalculated in
1667 // computeConsistentConfiguration, below
1668
1669 // depending if minerals were swapped in or out of the basis, the known activity may have
1670 // changed
1672
1673 // the equilibrium constants will have changed due to the swap
1675
1676 // due to re-orderings in mgd, the charge-balance basis index might have changed
1678 // charge balance might be able to be performed easily
1680
1681 // the algebraic system has probably changed
1686 _basis_index);
1687
1688 // finally compute a consistent configuration, based on the basis molalities, etc, above
1690}
1691
1692unsigned
1694{
1695 return _num_basis;
1696}
1697
1698unsigned
1700{
1701 return _num_eqm;
1702}
1703
1704void
1705GeochemicalSystem::setChargeBalanceSpecies(unsigned new_charge_balance_index)
1706{
1707 // because the original mole number of the charge balance species may have been changed due to
1708 // enforcing charge balance:
1712 // now change the charge balance info
1713 _charge_balance_basis_index = new_charge_balance_index;
1715 // enforce charge-balance if easily possible
1717}
1718
1719bool
1721{
1722 if (_basis_molality[_charge_balance_basis_index] > threshold_molality)
1723 return false;
1724 unsigned best_species_opp_sign = 0;
1725 unsigned best_species_same_sign = 0;
1726 Real best_molality_opp_sign = 0.0;
1727 Real best_molality_same_sign = 0.0;
1728 for (unsigned basis_i = 0; basis_i < _num_basis; ++basis_i)
1729 {
1730 if (basis_i == _charge_balance_basis_index)
1731 continue;
1733 continue;
1734 else if (_mgd.basis_species_charge[basis_i] == 0.0)
1735 continue;
1736 else if (_basis_molality[basis_i] <= threshold_molality)
1737 continue;
1738 // we know basis_i is a charged species with set bulk moles and high molality
1739 if (_mgd.basis_species_charge[basis_i] *
1741 0.0)
1742 {
1743 // charge of opposite sign
1744 if (_basis_molality[basis_i] > best_molality_opp_sign)
1745 {
1746 best_species_opp_sign = basis_i;
1747 best_molality_opp_sign = _basis_molality[basis_i];
1748 }
1749 }
1750 else
1751 {
1752 // charge of same sign
1753 if (_basis_molality[basis_i] > best_molality_same_sign)
1754 {
1755 best_species_same_sign = basis_i;
1756 best_molality_same_sign = _basis_molality[basis_i];
1757 }
1758 }
1759 }
1760 if (best_species_opp_sign != 0)
1761 {
1762 // this is preferred over the same-sign version
1763 setChargeBalanceSpecies(best_species_opp_sign);
1764 return true;
1765 }
1766 else if (best_species_same_sign != 0)
1767 {
1768 // this is not preferred, but is better than no charge-balance species change
1769 setChargeBalanceSpecies(best_species_same_sign);
1770 return true;
1771 }
1772 return false;
1773}
1774
1775bool
1777{
1780 return false; // original charge-balance species no longer in basis
1781 const unsigned original_index = _mgd.basis_species_index.at(_original_charge_balance_species);
1782 if (original_index == _charge_balance_basis_index)
1783 return false; // current charge-balance species is the original
1784 setChargeBalanceSpecies(original_index);
1785 return true;
1786}
1787
1788Real
1793
1794Real
1799
1800Real
1811
1812Real
1814{
1815 if (sp >= _num_surface_pot)
1816 mooseError("Cannot retrieve the surface potential for surface ",
1817 sp,
1818 " since there are only ",
1820 " surfaces involved in surface complexation");
1824}
1825
1826Real
1828{
1829 if (sp >= _num_surface_pot)
1830 mooseError("Cannot retrieve the surface charge for surface ",
1831 sp,
1832 " since there are only ",
1834 " surfaces involved in surface complexation");
1835 // pre = mol of charge per square metre. To convert to Coulombs/m^2:
1836 const Real pre = surfacePotPrefactor(sp) / _sorbing_surface_area[sp] *
1837 (-_surface_pot_expr[sp] + 1.0 / _surface_pot_expr[sp]);
1838 return pre * GeochemistryConstants::FARADAY;
1839}
1840
1841void
1842GeochemicalSystem::computeSorbingSurfaceArea(std::vector<Real> & sorbing_surface_area) const
1843{
1844 for (unsigned sp = 0; sp < _num_surface_pot; ++sp)
1845 {
1846 sorbing_surface_area[sp] = _mgd.surface_sorption_area[sp];
1848 {
1849 const unsigned basis_ind = _mgd.basis_species_index.at(_mgd.surface_sorption_name[sp]);
1850 const Real grams =
1851 _mgd.basis_species_molecular_weight[basis_ind] * _basis_molality[basis_ind];
1852 sorbing_surface_area[sp] *= grams;
1853 }
1854 }
1855}
1856
1857const std::vector<Real> &
1862
1863Real
1865{
1866 return _temperature;
1867}
1868
1869void
1876
1877void
1879 const std::vector<std::string> & names,
1880 const std::vector<Real> & values,
1881 const std::vector<bool> & constraints_from_molalities)
1882{
1883 // assume temperature-dependent quantities have been built during instantiation
1884 // assume algebraic info has been built during instantiation
1885
1886 /*
1887 * STEP 0: Check sizes are correct
1888 */
1889 const unsigned num_names = names.size();
1890 if (num_names != values.size())
1891 mooseError("When setting all molalities, names and values must have same size");
1892 if (num_names != _num_basis + _num_eqm + _num_surface_pot + _num_kin)
1893 mooseError("When setting all molalities, values must be provided for every species and surface "
1894 "potentials");
1895 if (constraints_from_molalities.size() != _num_basis)
1896 mooseError("constraints_from_molalities has size ",
1897 constraints_from_molalities.size(),
1898 " while number of basis species is ",
1899 _num_basis);
1900
1901 /*
1902 * STEP 1: Read values into _basis_molality, _eqm_molality and _surface_pot_expr and
1903 * _kin_moles
1904 */
1905 // The is no guarantee is made that the user-supplied molalities are "good", except they must
1906 // be non-negative. There are lots of "finds" in the loops below, which is slow, but it
1907 // ensures all species are given molalities. This method is designed to be called only every
1908 // once-in-a-while (eg, at the start of a simulation)
1909 for (const auto & name_index : _mgd.basis_species_index)
1910 {
1911 const unsigned ind =
1912 std::distance(names.begin(), std::find(names.begin(), names.end(), name_index.first));
1913 if (ind < num_names)
1914 {
1915 if (_mgd.basis_species_gas[name_index.second])
1916 {
1917 if (values[ind] != 0.0)
1918 mooseError("Molality for gas ",
1919 name_index.first,
1920 " cannot be ",
1921 values[ind],
1922 ": it must be zero");
1923 else
1924 _basis_molality[name_index.second] = values[ind];
1925 }
1926 else if (_mgd.basis_species_mineral[name_index.second])
1927 {
1928 if (values[ind] < 0.0)
1929 mooseError("Molality for mineral ",
1930 name_index.first,
1931 " cannot be ",
1932 values[ind],
1933 ": it must be non-negative");
1934 else
1935 _basis_molality[name_index.second] = values[ind];
1936 }
1937 else if (values[ind] <= 0.0)
1938 mooseError("Molality for species ",
1939 name_index.first,
1940 " cannot be ",
1941 values[ind],
1942 ": it must be positive");
1943 else
1944 _basis_molality[name_index.second] = values[ind];
1945 }
1946 else
1947 mooseError("Molality (or free mineral moles, etc - whatever is appropriate) for species ",
1948 name_index.first,
1949 " needs to be provided when setting all molalities");
1950 }
1951 for (const auto & name_index : _mgd.eqm_species_index)
1952 {
1953 const unsigned ind =
1954 std::distance(names.begin(), std::find(names.begin(), names.end(), name_index.first));
1955 if (ind < num_names)
1956 {
1957 if (_mgd.eqm_species_gas[name_index.second] || _mgd.eqm_species_mineral[name_index.second])
1958 _eqm_molality[name_index.second] =
1959 0.0; // Note: explicitly setting to zero, irrespective of user input. The reason
1960 // for doing this is that during a restore, a basis species with positive
1961 // molality could become a secondary species, which should have zero molality
1962 else if (values[ind] < 0.0)
1963 mooseError("Molality for species ",
1964 name_index.first,
1965 " cannot be ",
1966 values[ind],
1967 ": it must be non-negative");
1968 else
1969 _eqm_molality[name_index.second] = values[ind];
1970 }
1971 else
1972 mooseError("Molality for species ",
1973 name_index.first,
1974 " needs to be provided when setting all molalities");
1975 }
1976 for (unsigned sp = 0; sp < _num_surface_pot; ++sp)
1977 {
1978 const unsigned ind =
1979 std::distance(names.begin(),
1980 std::find(names.begin(),
1981 names.end(),
1982 _mgd.surface_sorption_name[sp] + "_surface_potential_expr"));
1983 if (ind < num_names)
1984 {
1985 if (values[ind] <= 0.0)
1986 mooseError("Surface-potential expression for mineral ",
1988 " cannot be ",
1989 values[ind],
1990 ": it must be positive");
1991 _surface_pot_expr[sp] = values[ind];
1992 }
1993 else
1994 mooseError("Surface potential for mineral ",
1996 " needs to be provided when setting all molalities");
1997 }
1998 for (const auto & name_index : _mgd.kin_species_index)
1999 {
2000 const unsigned ind =
2001 std::distance(names.begin(), std::find(names.begin(), names.end(), name_index.first));
2002 if (ind < num_names)
2003 setKineticMoles(name_index.second, values[ind]);
2004 else
2005 mooseError("Moles for species ",
2006 name_index.first,
2007 " needs to be provided when setting all molalities");
2008 }
2009
2010 /*
2011 * STEP 2: Alter _constraint_values if necessary
2012 */
2013 // If some of the constraints_from_molalities are false, then the molalities provided to this
2014 // method may have to be modified to satisfy the contraints: this alters _basis_molality so
2015 // must occur first
2016 for (unsigned i = 0; i < _num_basis; ++i)
2017 {
2018 const ConstraintMeaningEnum meaning = _constraint_meaning[i];
2022 {
2023 if (constraints_from_molalities[i])
2024 {
2025 // molalities provided to this method dictate the contraints:
2028 }
2029 else
2030 // contraints take precedence over the molalities provided to this method:
2032 }
2033 }
2034
2035 // Potentially alter _constraint_value for the BULK contraints:
2036 for (unsigned i = 0; i < _num_basis; ++i)
2037 {
2038 const ConstraintMeaningEnum meaning = _constraint_meaning[i];
2041 if (constraints_from_molalities[i])
2042 {
2043 // the constraint value should be overridden by the molality-computed bulk mole number
2046 }
2047 }
2048
2049 // Potentially alter _constraint_value for ACTIVITY contraints:
2050 for (unsigned i = 0; i < _num_basis; ++i)
2051 if (constraints_from_molalities[i])
2052 {
2053 const ConstraintMeaningEnum meaning = _constraint_meaning[i];
2054 if (meaning == ConstraintMeaningEnum::FUGACITY)
2055 mooseError("Gas fugacity cannot be determined from molality: constraints_from_molalities "
2056 "must be set false for all gases");
2057 else if (meaning == ConstraintMeaningEnum::ACTIVITY)
2058 {
2059 if (i == 0)
2060 mooseError("Water activity cannot be determined from molalities: "
2061 "constraints_from_molalities "
2062 "must be set to false for water if activity of water is fixed");
2063 // the constraint value should be overidden by the molality provided to this method
2066 }
2067 }
2068
2069 /*
2070 * STEP 3: Follow the initialize() and computeConsistentConfiguration() methods
2071 */
2072 // assume done already: buildTemperatureDependentQuantities
2074 // assume done already: buildAlgebraicInfo
2075 // should not be done, as basis_molality is set by this method instead: initBulkAndFree
2077 // should not be done, as these are set by this method: _eqm_molality.assign
2078 // should not be done, as these are set by this method: _surface_pot_expr.assign
2081 // for constraints_from_molalities = false then the following: (1) overrides the basis
2082 // molality provided to this method; (2) produces a slightly inconsistent equilibrium
2083 // geochemical system because basis_activity_coef was computed on the basis of the basis
2084 // molalities provided to this method
2087 // should not be done, as these are set by this method: computeEqmMolalities
2089 // should not be done, as these are set by this method: computeFreeMineralMoles
2091}
2092
2093const std::vector<GeochemicalSystem::ConstraintMeaningEnum> &
2098
2099void
2101{
2102 for (unsigned basis_ind = 0; basis_ind < _num_basis; ++basis_ind)
2106 changeConstraintToBulk(basis_ind);
2107}
2108
2109void
2111{
2112 if (basis_ind >= _num_basis)
2113 mooseError("Cannot changeConstraintToBulk for species ",
2114 basis_ind,
2115 " because there are only ",
2116 _num_basis,
2117 " basis species");
2118 if (_mgd.basis_species_gas[basis_ind])
2119 {
2120 // this is a special case where we have to swap out the gas in favour of an equilibrium
2121 // species
2122 unsigned swap_into_basis = 0;
2123 bool legitimate_swap_found = false;
2124 Real best_stoi = 0.0;
2125 for (unsigned j = 0; j < _num_eqm; ++j)
2126 {
2127 if (_mgd.eqm_species_gas[j] || _mgd.eqm_stoichiometry(j, basis_ind) == 0.0 ||
2129 continue;
2130 const Real stoi = std::abs(_mgd.eqm_stoichiometry(j, basis_ind));
2131 if (stoi > best_stoi)
2132 {
2133 best_stoi = stoi;
2134 swap_into_basis = j;
2135 legitimate_swap_found = true;
2136 }
2137 }
2138 if (legitimate_swap_found)
2139 performSwapNoCheck(basis_ind, swap_into_basis);
2140 else
2141 mooseError("Attempting to change constraint of gas ",
2142 _mgd.basis_species_name[basis_ind],
2143 " to MOLES_BULK_SPECIES, which involves a search for a suitable non-gas species "
2144 "to swap with. No such species was found");
2145 }
2146 else
2148}
2149
2150void
2151GeochemicalSystem::changeConstraintToBulk(unsigned basis_ind, Real value)
2152{
2153 if (basis_ind >= _num_basis)
2154 mooseError("Cannot changeConstraintToBulk for species ",
2155 basis_ind,
2156 " because there are only ",
2157 _num_basis,
2158 " basis species");
2159 if (_mgd.basis_species_gas[basis_ind])
2160 mooseError("Attempting to changeConstraintToBulk for a gas species. Since a swap is involved, "
2161 "you cannot specify a value for the bulk number of moles. You must use "
2162 "changeConstraintToBulk(basis_ind) method instead of "
2163 "changeConstraintToBulk(basis_ind, value)");
2164 if (basis_ind == 0)
2166 else
2168 setConstraintValue(basis_ind, value);
2169
2170 // it is possible that FIXED_ACTIVITY just became MOLES_BULK_SPECIES
2172
2173 // it is likely the algebraic system has changed
2178 _basis_index);
2179}
2180
2181void
2182GeochemicalSystem::addToBulkMoles(unsigned basis_ind, Real value)
2183{
2184 if (basis_ind >= _num_basis)
2185 mooseError("Cannot addToBulkMoles for species ",
2186 basis_ind,
2187 " because there are only ",
2188 _num_basis,
2189 " basis species");
2190 if (!(_constraint_meaning[basis_ind] ==
2192 _constraint_meaning[basis_ind] ==
2194 return;
2195 setConstraintValue(basis_ind, _constraint_value[basis_ind] + value);
2196}
2197
2198void
2199GeochemicalSystem::setConstraintValue(unsigned basis_ind, Real value)
2200{
2201 if (basis_ind >= _num_basis)
2202 mooseError("Cannot setConstraintValue for species ",
2203 basis_ind,
2204 " because there are only ",
2205 _num_basis,
2206 " basis species");
2207 _constraint_value[basis_ind] = value;
2208 _original_constraint_value[basis_ind] = value;
2209 switch (_constraint_meaning[basis_ind])
2210 {
2213 {
2215 break;
2216 }
2220 {
2221 // the changes resulting from this change are very similar to setAlgebraicVariables
2222 _basis_molality[basis_ind] = value;
2224 break;
2225 }
2227 {
2228 // the changes resulting from this change are very similar to setAlgebraicVariables
2229 _basis_activity[basis_ind] = value;
2230 _basis_molality[basis_ind] = 0.0;
2232 break;
2233 }
2235 {
2236 // the changes resulting from this change are very similar to setAlgebraicVariables
2237 _basis_activity[basis_ind] = value;
2238 _basis_molality[basis_ind] = _basis_activity[basis_ind] / _basis_activity_coef[basis_ind];
2240 break;
2241 }
2242 }
2243}
2244
2245void
2247{
2248 // Altering the constraints on bulk number of moles impacts various other things.
2249 // Here, we follow the initialize() and computeConsistentConfiguration() methods, picking out
2250 // what might have changed.
2251 // Because the constraint meanings have changed, enforcing charge-neutrality might be easy
2253 // Because the constraint values have changed, either through charge-neutrality or directly
2254 // through changing the constraint values, the bulk moles must be updated:
2255 for (unsigned i = 0; i < _num_basis; ++i)
2259 // Because the bulk mineral moles might have changed, the free mineral moles might have
2260 // changed
2261 computeFreeMineralMoles(_basis_molality); // free mineral moles might be changed
2262}
2263
2264const std::string &
2269
2270void
2275
2278{
2279 return _swapper;
2280}
2281
2282void
2284{
2285 // mole numbers of basis minerals
2286 for (unsigned i = 0; i < _num_basis; ++i)
2288 _basis_molality[i] = value;
2289
2290 // mole numbers of sorption sites
2291 for (const auto & name_info :
2292 _mgd.surface_complexation_info) // all minerals involved in surface complexation
2293 for (const auto & name_frac :
2294 name_info.second.sorption_sites) // all sorption sites on the given mineral
2295 {
2296 if (_mgd.basis_species_index.count(name_frac.first))
2297 _basis_molality[_mgd.basis_species_index.at(name_frac.first)] = value;
2298 else
2299 _eqm_molality[_mgd.eqm_species_index.at(name_frac.first)] = value;
2300 }
2301
2302 // mole numbers of sorbed equilibrium species
2303 for (unsigned j = 0; j < _num_eqm; ++j)
2304 {
2306 _eqm_molality[j] = 0.0; // Note: explicitly setting to zero, irrespective of user input,
2307 // to ensure consistency with the rest of the code
2309 _eqm_molality[j] = value;
2310 }
2311
2312 // mole numbers of kinetic species
2313 for (unsigned k = 0; k < _num_kin; ++k)
2315 _kin_moles[k] = value;
2316}
2317
2318Real
2320{
2321 if (eqm_ind >= _num_eqm)
2322 mooseError("Cannot retrieve activity for equilibrium species ",
2323 eqm_ind,
2324 " since there are only ",
2325 _num_eqm,
2326 " equilibrium species");
2327 if (_mgd.eqm_species_mineral[eqm_ind])
2328 return 1.0;
2329 else if (_mgd.eqm_species_gas[eqm_ind])
2330 {
2331 Real log10f = 0.0;
2332 for (unsigned basis_i = 0; basis_i < _num_basis; ++basis_i)
2333 log10f += _mgd.eqm_stoichiometry(eqm_ind, basis_i) * std::log10(_basis_activity[basis_i]);
2334 log10f -= getLog10K(eqm_ind);
2335 return std::pow(10.0, log10f);
2336 }
2337 else
2338 return _eqm_activity_coef[eqm_ind] * _eqm_molality[eqm_ind];
2339}
2340
2341const std::vector<Real> &
2343{
2344 for (unsigned j = 0; j < _num_eqm; ++j)
2346 return _eqm_activity;
2347}
2348
2349void
2350GeochemicalSystem::updateOldWithCurrent(const DenseVector<Real> & mole_additions)
2351{
2352 if (mole_additions.size() != _num_basis + _num_kin)
2353 mooseError("The increment in mole numbers (mole_additions) needs to be of size ",
2354 _num_basis,
2355 " + ",
2356 _num_kin,
2357 " but it is of size ",
2358 mole_additions.size());
2359
2360 // Copy the current kin_moles to the old values
2361 for (unsigned k = 0; k < _num_kin; ++k)
2362 _kin_moles_old[k] = _kin_moles[k];
2363
2364 // The following:
2365 // - just returns if constraint is not BULK type.
2366 // - ensures charge balance if simple
2367 // - sets the constraint value and bulk_moles_old (for BULK-type species)
2368 // - computes basis_molality for the BULK-type mineral species
2369 for (unsigned i = 0; i < _num_basis; ++i)
2370 addToBulkMoles(i, mole_additions(i));
2371
2372 // The following:
2373 // - sets bulk_moles_old to the Constraint value for BULK-type species (duplicating the above
2374 // function)
2375 // - for the other species, computes bulk_moles_old from the molalities
2377
2378 // It is possible that the user would also like to enforceChargeBalance() now, and that is fine
2379 // - there will be no negative consequences
2380}
2381
2382void
2384 DenseVector<Real> & mole_additions,
2385 DenseMatrix<Real> & dmole_additions)
2386{
2387 if (_num_kin == 0)
2388 return;
2389
2390 // check sizes
2391 const unsigned tot = mole_additions.size();
2392 if (!(tot == _num_kin + _num_basis && dmole_additions.m() == tot && dmole_additions.n() == tot))
2393 mooseError("addKineticRates: incorrectly sized additions: ",
2394 tot,
2395 " ",
2396 dmole_additions.m(),
2397 " ",
2398 dmole_additions.n());
2399
2400 // construct eqm_activity for species that we need
2401 for (unsigned j = 0; j < _num_eqm; ++j)
2402 if (_mgd.eqm_species_gas[j] || _mgd.eqm_species_name[j] == "H+" ||
2403 _mgd.eqm_species_name[j] == "OH-")
2405
2406 // calculate the rates and put into appropriate slots
2407 Real rate;
2408 Real drate_dkin;
2409 std::vector<Real> drate_dmol(_num_basis);
2410 for (const auto & krd : _mgd.kin_rate)
2411 {
2412 const unsigned kin = krd.kinetic_species_index;
2414 krd.promoting_monod_indices,
2415 krd.promoting_half_saturation,
2416 krd.description,
2427 _kin_moles[kin],
2429 _kin_log10K[kin],
2432 kin,
2434 rate,
2435 drate_dkin,
2436 drate_dmol);
2437
2438 /* the following block of code may be confusing at first sight.
2439 * (1) The usual case is that the kinetic reaction is written
2440 * kinetic -> basis_species, and direction = BOTH, and kinetic_bio_efficiency = -1
2441 * In this case, the following does mole_additions(kinetic_species) += -1 * rate * dt
2442 * If rate > 0 then the kinetic species decreases in mass.
2443 * The solver will then detect that the kinetic_species has changed, and adjust basis_species
2444 * accordingly, viz it will add rate * dt of basis species to the aqueous solution.
2445 * (2) The common biologically-catalysed case is the reaction is written
2446 * reactants -> products, catalysed by microbe. In the database file this is written as
2447 * microbe -> -reactants + products
2448 * The input file will set direction = BOTH, and kinetic_bio_efficiency != -1
2449 * Suppose that rate > 0, corresponding to reactants being converted to products.
2450 * With kinetic_bio_efficiency = -1) this would correspond to microbe decreasing in mass,
2451 * however with kinetic_bio_efficiency > 0, this is not true.
2452 * The following block of code sets
2453 * mole_additions(microbe) = kinetic_bio_efficiency * rate * dt > 0
2454 * that is, the microbe mass is increasing.
2455 * However, this means the solver will decrease products and increase reactants,
2456 * by kinetic_bio_efficiency * rate * dt because it sees the reaction microbe -> -reactants +
2457 * products in the database file.
2458 * This is clearly wrong, because we actually want the end result to be that the products have
2459 * increased by rate * dt, and the reactants to have decreased by rate * dt.
2460 * So, to counter the solver, we add (-1 - kinetic_bio_efficiency) * rate * dt of reactants and
2461 * add (1 + kinetic_bio_efficienty) * rate * dt of products.
2462 * (3) The other situation is the biological death, where direction = DEATH.
2463 * In this case the database file contains microbe -> -reactants + products
2464 * However, independent of rate, we don't want to change the molality of reactants or products.
2465 * Following the logic of (2), to counter the solver, we add -kinetic_bio_efficiency * rate * dt
2466 * of reactants and kinetic_bio_efficiency * rate * dt of products
2467 */
2468 const unsigned ind = kin + _num_basis;
2469 mole_additions(ind) += krd.description.kinetic_bio_efficiency * rate * dt;
2470 dmole_additions(ind, ind) += krd.description.kinetic_bio_efficiency * drate_dkin * dt;
2471 const Real extra_additions = (krd.description.direction == DirectionChoiceEnum::DEATH)
2472 ? krd.description.kinetic_bio_efficiency
2473 : krd.description.kinetic_bio_efficiency + 1.0;
2474 for (unsigned i = 0; i < _num_basis; ++i)
2475 {
2476 dmole_additions(ind, i) += krd.description.kinetic_bio_efficiency * drate_dmol[i] * dt;
2477 const Real stoi_fac = _mgd.kin_stoichiometry(kin, i) * extra_additions * dt;
2478 mole_additions(i) += stoi_fac * rate;
2479 dmole_additions(i, ind) += stoi_fac * drate_dkin;
2480 for (unsigned j = 0; j < _num_basis; ++j)
2481 dmole_additions(i, j) += stoi_fac * drate_dmol[j];
2482 }
2483
2484 const Real eff = krd.description.progeny_efficiency;
2485 if (eff != 0.0)
2486 {
2487 const unsigned bio_i = krd.progeny_index;
2488 if (bio_i < _num_basis)
2489 {
2490 mole_additions(bio_i) += eff * rate * dt;
2491 dmole_additions(bio_i, ind) += eff * drate_dkin * dt;
2492 for (unsigned i = 0; i < _num_basis; ++i)
2493 dmole_additions(bio_i, i) += eff * drate_dmol[i] * dt;
2494 }
2495 else
2496 {
2497 for (unsigned i = 0; i < _num_basis; ++i)
2498 {
2499 mole_additions(i) += _mgd.eqm_stoichiometry(bio_i - _num_basis, i) * eff * rate * dt;
2500 dmole_additions(i, ind) +=
2501 _mgd.eqm_stoichiometry(bio_i - _num_basis, i) * eff * drate_dkin * dt;
2502 for (unsigned j = 0; j < _num_basis; ++j)
2503 dmole_additions(i, j) +=
2504 _mgd.eqm_stoichiometry(bio_i - _num_basis, i) * eff * drate_dmol[j] * dt;
2505 }
2506 }
2507 }
2508 }
2509}
const ModelGeochemicalDatabase mgd
void mooseError(Args &&... args)
std::array< Real, 2 > values
const std::string name
Definition Setup.h:21
Fit the equilibrium constant values read from the thermodynamic databse at specified temperature valu...
const std::vector< Real > & getTemperatures() const
Get the temperature points that the equilibrium constant is defined at.
std::string getLogKModel() const
Get the equilibrium constant model type.
Real log10KineticActivityProduct(unsigned kin) const
Real computeBulkFromMolalities(unsigned basis_ind) const
std::vector< Real > getSaturationIndices() const
Real getResidualComponent(unsigned algebraic_ind, const DenseVector< Real > &mole_additions) const
Return the residual of the algebraic system for the given algebraic index.
std::string _original_redox_lhs
The left-hand-side of the redox equations in _mgd after initial swaps.
std::vector< Real > getAlgebraicBasisValues() const
Real getEquilibriumActivity(unsigned eqm_ind) const
Returns the value of activity for the equilibrium species with index eqm_index.
std::vector< Real > _eqm_activity_coef
equilibrium activity coefficients
const std::vector< unsigned > & getAlgebraicIndexOfBasisSystem() const
const std::vector< GeochemicalSystem::ConstraintMeaningEnum > & getConstraintMeaning() const
std::vector< Real > _bulk_moles_old
Number of bulk moles of basis species (this includes contributions from kinetic species,...
void enforceChargeBalance()
Enforces charge balance by altering the constraint_value and bulk_moles_old of the charge-balance spe...
std::vector< Real > _sorbing_surface_area
surface areas of the sorbing minerals
Real _temperature
The temperature in degC.
void setAlgebraicVariables(const DenseVector< Real > &algebraic_var)
Set the variables in the algebraic system (molalities and potentially the mass of solvent water,...
Real surfacePotPrefactor(unsigned sp) const
const std::vector< std::string > _swap_out
Species to immediately remove from the basis in favor of _swap_in.
unsigned getNumBasisInAlgebraicSystem() const
return the number of basis species in the algebraic system
std::vector< unsigned > _algebraic_index
_algebraic_index[i] = index in the algebraic system of the basis species i. _basis_index[_algebraic_i...
unsigned getNumKinetic() const
returns the number of kinetic species
std::vector< Real > _constraint_value
Numerical values of the constraints on _constraint_species. In the constructor, this is ordered to ha...
DenseVector< Real > getAlgebraicVariableDenseValues() const
Real getLog10K(unsigned j) 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,...
const GeochemistrySpeciesSwapper & getSwapper() const
const unsigned _num_kin
number of kinetic species
const unsigned _num_surface_pot
number of surface potentials
void computeEqmMolalities(std::vector< Real > &eqm_molality) const
compute the equilibrium molalities (not for minerals or gases)
void setChargeBalanceSpecies(unsigned new_charge_balance_index)
Set the charge-balance species to the basis index provided.
std::vector< bool > _in_algebraic_system
if _in_algebraic_system[i] == true then we need to solve for the i^th basis-species molality
unsigned _iters_to_make_consistent
Iterations to make the initial configuration consistent.
unsigned _num_basis_in_algebraic_system
number of unknown molalities in the algebraic system. Note: surface potentials (if any) are extra qua...
bool revertToOriginalChargeBalanceSpecies()
Changes the charge-balance species to the original that is specified in the constructor (this might n...
GeochemistryActivityCoefficients & _gac
Object to compute the activity coefficients and activity of water.
Real log10ActivityProduct(unsigned eqm_j) const
const std::vector< Real > & getSorbingSurfaceArea() const
const std::string & getOriginalRedoxLHS() const
void setMineralRelatedFreeMoles(Real value)
Set the free mole number of mineral-related species to the value provided.
void initBulkAndFree(std::vector< Real > &bulk_moles_old, std::vector< Real > &basis_molality) const
based on _constrained_value and _constraint_meaning, populate nw, bulk_moles_old and basis_molality w...
std::vector< ConstraintMeaningEnum > _constraint_meaning
The meaning of the values in _constraint_value. In the constructor, this is ordered to have the same ...
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,...
Real log10RedoxActivityProduct(unsigned red) const
Real getRedoxLog10K(unsigned red) const
const unsigned _num_redox
number of redox species
void setKineticMoles(unsigned kin, Real moles)
Sets the current AND old mole number for a kinetic species.
void computeSorbingSurfaceArea(std::vector< Real > &sorbing_surface_area) const
Compute sorbing_surface_area depending on the current molality of the sorbing minerals.
void alterSystemBecauseBulkChanged()
Alter the GeochemicalSystem to reflect changes in bulk composition constraints that occur through,...
void computeConsistentConfiguration()
Compute a reasonably consistent configuration that can be used as an initial condition in a Newton pr...
std::vector< std::string > _constrained_species
Names of the species in that have their values fixed to _constraint_value with meanings _constraint_m...
const std::vector< Real > & getEquilibriumMolality() const
const std::vector< Real > & getBasisActivityCoefficient() const
const std::vector< Real > & computeAndGetEquilibriumActivity()
Computes activity for all equilibrium species (_eqm_activity) and returns a reference to the vector.
std::vector< Real > _eqm_log10K
equilibrium constant of the equilibrium species
Real _min_initial_molality
Minimum molality ever used in an initial guess.
const unsigned _num_eqm
number of equilibrium species
unsigned _num_in_algebraic_system
number of unknowns in the algebraic system (includes molalities and surface potentials)
void changeConstraintToBulk(unsigned basis_ind)
Changes the constraint to MOLES_BULK_SPECIES (or MOLES_BULK_WATER if basis_ind = 0) for the basis ind...
std::vector< Real > _original_constraint_value
Numerical values of the constraints on _constraint_species. In the constructor, this is ordered to ha...
Real getSurfacePotential(unsigned sp) const
std::vector< Real > _surface_pot_expr
surface potential expressions.
void performSwap(unsigned swap_out_of_basis, unsigned swap_into_basis)
Perform the basis swap, and ensure that the resulting system is consistent.
unsigned getNumSurfacePotentials() const
return the number of surface potentials
GeochemicalSystem(ModelGeochemicalDatabase &mgd, GeochemistryActivityCoefficients &gac, GeochemistryIonicStrength &is, GeochemistrySpeciesSwapper &swapper, const std::vector< std::string > &swap_out_of_basis, const std::vector< std::string > &swap_into_basis, const std::string &charge_balance_species, const std::vector< std::string > &constrained_species, const std::vector< Real > &constraint_value, const MultiMooseEnum &constraint_unit, const MultiMooseEnum &constraint_user_meaning, Real initial_temperature, unsigned iters_to_make_consistent, Real min_initial_molality, const std::vector< std::string > &kin_name, const std::vector< Real > &kin_initial_moles, const MultiMooseEnum &kin_unit)
Construct the geochemical system, check consistency of the constructor's arguments,...
unsigned getNumInBasis() const
returns the number of species in the basis
void computeTransportedBulkFromMolalities(std::vector< Real > &transported_bulk) const
Computes the value of transported bulk moles for all basis species using the existing molalities.
void buildTemperatureDependentQuantities(Real temperature)
Using the provided value of temperature, build _eqm_log10K for each eqm species and redox species.
std::vector< ConstraintUserMeaningEnum > _constraint_user_meaning
The user-defined meaning of the values in _constraint_value. In the constructor, this is ordered to h...
void checkAndInitialize(const std::vector< std::string > &kin_name, const std::vector< Real > &kin_initial, const std::vector< GeochemistryUnitConverter::GeochemistryUnit > &kin_unit)
Used during construction: checks for sane inputs and initializes molalities, etc, using the initializ...
unsigned getNumInEquilibrium() const
returns the number of species in equilibrium with the basis components
const std::vector< bool > & getBasisActivityKnown() const
unsigned getChargeBalanceBasisIndex() const
return the index of the charge-balance species in the basis list
Real getTotalChargeOld() const
std::vector< GeochemistryUnitConverter::GeochemistryUnit > _constraint_unit
Units of the _constraint_value when the GeochemicalSystem is constructed. This is used during the con...
void computeRemainingBasisActivities(std::vector< Real > &basis_activity) const
Compute the activity of water and put in basis_activity[0], and use the _basis_activity_coef and _bas...
void setSolventMassAndFreeMolalityAndMineralMolesAndSurfacePotsAndKineticMoles(const std::vector< std::string > &names, const std::vector< Real > &values, const std::vector< bool > &constraints_from_molalities)
Sets:
const std::string _original_charge_balance_species
The species used to enforce charge balance, as provided in the constructor.
GeochemistryIonicStrength & _is
Object that provides the ionic strengths.
const std::vector< bool > & getInAlgebraicSystem() const
DenseVector< Real > getTransportedBulkInOriginalBasis() const
const std::vector< Real > & getEquilibriumActivityCoefficient() const
Real getSolventWaterMass() const
Returns the mass of solvent water.
void enforceChargeBalanceIfSimple(std::vector< Real > &constraint_value, std::vector< Real > &bulk_moles_old) const
If all non-charged basis species are provided with a bulk number of moles, alter the bulk number of m...
GeochemistrySpeciesSwapper & _swapper
swapper that can swap species into and out of the basis
const std::vector< Real > & getKineticLog10K() const
unsigned getNumRedox() const
const std::vector< unsigned > & getBasisIndexOfAlgebraicSystem() const
std::vector< Real > _eqm_molality
molality of equilibrium species
Real getStoichiometricIonicStrength() const
Get the stoichiometric ionic strength.
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.
void setConstraintValue(unsigned basis_ind, Real value)
Set the constraint value for the basis species.
ConstraintUserMeaningEnum
Each basis species must be provided with a constraint, that is chosen by the user from the following ...
const std::vector< Real > & getBulkMolesOld() const
bool alterChargeBalanceSpecies(Real threshold_molality)
If the molality of the charge-balance basis species is less than threshold molality,...
std::vector< Real > _basis_molality
IMPORTANT: this is.
const ModelGeochemicalDatabase & getModelGeochemicalDatabase() const
const std::vector< std::string > _swap_in
Species to immediately add to the basis in favor of _swap_out.
std::vector< bool > _basis_activity_known
whether basis_activity[i] is fixed by the user
Real getIonicStrength() const
Get the ionic strength.
std::string _charge_balance_species
The species used to enforce charge balance.
const std::vector< Real > & getSolventMassAndFreeMolalityAndMineralMoles() const
void updateBasisMolalityForKnownActivity(std::vector< Real > &basis_molality) const
For basis aqueous species (not water, minerals or gases) with activity set by the user,...
unsigned getNumInAlgebraicSystem() const
std::vector< unsigned > _basis_index
_basis_index[i] = index in the basis of the algebraic system species i, for i<num_basis_in_algebraic_...
void performSwapNoCheck(unsigned swap_out_of_basis, unsigned swap_into_basis)
Perform the basis swap, and ensure that the resulting system is consistent.
void initialize()
initialize all variables, ready for a Newton solve of the algebraic system
std::vector< Real > _redox_log10K
equilibrium constant of the redox species
std::vector< Real > getAlgebraicVariableValues() const
std::vector< Real > _kin_log10K
equilibrium constant of the kinetic species
DenseVector< Real > getBulkOldInOriginalBasis() const
Real getSurfaceCharge(unsigned sp) const
void computeJacobian(const DenseVector< Real > &res, DenseMatrix< Real > &jac, const DenseVector< Real > &mole_additions, const DenseMatrix< Real > &dmole_additions) const
Compute the Jacobian for the algebraic system and put it in jac.
std::vector< Real > _eqm_activity
equilibrium activities.
const unsigned _num_basis
number of basis species
unsigned _charge_balance_basis_index
The index in the list of basis species corresponding to the charge-balance species....
const std::vector< Real > & getKineticMoles() const
const std::vector< Real > & getBasisActivity() const
std::vector< Real > _basis_activity_coef
basis activity coefficients
void computeFreeMineralMoles(std::vector< Real > &basis_molality) const
Compute the free mineral moles (ie, basis_molality for basis species that are minerals),...
std::vector< Real > _kin_moles
mole number of kinetic species
void computeBulk(std::vector< Real > &bulk_moles_old) const
Computes the value of bulk_moles_old for all basis species.
ModelGeochemicalDatabase & _mgd
The minimal geochemical database.
ConstraintMeaningEnum
Each basis species has one of the following constraints.
void buildAlgebraicInfo(std::vector< bool > &in_algebraic_system, unsigned &num_basis_in_algebraic_system, unsigned &num_in_algebraic_system, std::vector< unsigned > &algebraic_index, std::vector< unsigned > &basis_index) const
Builds in_algebraic_system, algebraic_index and basis_index, and sets num_basis_in_algebraic_system a...
void closeSystem()
Changes a KG_SOLVENT_WATER constraint to MOLES_BULK_WATER (if there is such a constraint) and all FRE...
Real surfaceSorptionModifier(unsigned eqm_j) const
void buildKnownBasisActivities(std::vector< bool > &basis_activity_known, std::vector< Real > &basis_activity) const
Fully populates basis_activity_known, which is true if activity has been set by the user,...
std::vector< Real > _kin_moles_old
old mole number of kinetic species
void setTemperature(Real temperature)
Sets the temperature to the given quantity.
void setModelGeochemicalDatabase(const ModelGeochemicalDatabase &mgd)
Copies a ModelGeochemicalDatabase into our _mgd structure.
Real getTemperature() const
std::vector< Real > _basis_activity
values of activity (for water, minerals and aqueous basis species) or fugacity (for gases)
Base class to compute activity coefficients for non-minerals and non-gases (since these species do no...
virtual void setInternalParameters(Real temperature, const ModelGeochemicalDatabase &mgd, const std::vector< Real > &basis_species_molality, const std::vector< Real > &eqm_species_molality, const std::vector< Real > &kin_species_molality)=0
Sets internal parameters, such as the ionic strength and Debye-Huckel parameters, prior to computing ...
virtual Real waterActivity() const =0
Computes and returns the activity of water.
virtual void buildActivityCoefficients(const ModelGeochemicalDatabase &mgd, std::vector< Real > &basis_activity_coef, std::vector< Real > &eqm_activity_coef) const =0
Compute the activity coefficients and store them in basis_activity_coef and eqm_activity_coef Note:
Calculators to compute ionic strength and stoichiometric ionic strength.
Real stoichiometricIonicStrength(const ModelGeochemicalDatabase &mgd, const std::vector< Real > &basis_species_molality, const std::vector< Real > &eqm_species_molality, const std::vector< Real > &kin_species_molality) const
Compute stoichiometric ionic strength.
Real ionicStrength(const ModelGeochemicalDatabase &mgd, const std::vector< Real > &basis_species_molality, const std::vector< Real > &eqm_species_molality, const std::vector< Real > &kin_species_molality) const
Compute ionic strength.
Class to swap basis species with equilibrium species.
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,...
virtual void generate()
virtual const char * what() const
unsigned int get(unsigned int i) const
unsigned int size() const
unsigned int n() const
std::vector< T > & get_values()
DenseMatrix sub_matrix(unsigned int row_id, unsigned int row_size, unsigned int col_id, unsigned int col_size) const
void resize(const unsigned int new_m, const unsigned int new_n)
void vector_mult_transpose(DenseVector< T > &dest, const DenseVector< T > &arg) const
constexpr Real DIELECTRIC_CONSTANT_WATER
constexpr Real PERMITTIVITY_FREE_SPACE
void calculateRate(const std::vector< Real > &promoting_indices, const std::vector< Real > &promoting_monod_indices, const std::vector< Real > &promoting_half_saturation, const KineticRateUserDescription &description, const std::vector< std::string > &basis_species_name, const std::vector< bool > &basis_species_gas, const std::vector< Real > &basis_molality, const std::vector< Real > &basis_activity, const std::vector< bool > &basis_activity_known, const std::vector< std::string > &eqm_species_name, const std::vector< bool > &eqm_species_gas, const std::vector< Real > &eqm_molality, const std::vector< Real > &eqm_activity, const DenseMatrix< Real > &eqm_stoichiometry, Real kin_moles, Real kin_species_molecular_weight, Real log10K, Real log10_activity_product, const DenseMatrix< Real > &kin_stoichiometry, unsigned kin, Real temp_degC, Real &rate, Real &drate_dkin, std::vector< Real > &drate_dmol)
Calclates a kinetic rate and its derivative.
Real toMoles(Real quantity, const GeochemistryUnit unit, const std::string &species_name, const ModelGeochemicalDatabase &mgd)
Calculates the amount of moles corresponding to "quantity" "unit"s of species name,...
Data structure to hold all relevant information from the database file.
std::vector< bool > eqm_species_mineral
eqm_species_mineral[i] = true iff the i^th equilibrium species is a mineral
DenseMatrix< Real > eqm_stoichiometry
eqm_stoichiometry(i, j) = stoichiometric coefficient for equilibrium species "i" in terms of the basi...
std::vector< Real > basis_species_molecular_weight
all quantities have a molecular weight (g)
DenseMatrix< Real > kin_stoichiometry
kin_stoichiometry(i, j) = stoichiometric coefficient for kinetic species "i" in terms of the basis sp...
std::vector< Real > basis_species_charge
all quantities have a charge (mineral charge = 0, gas charge = 0, oxide charge = 0)
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
std::vector< unsigned > surface_sorption_number
surface_sorption_number[j] = the index of the surface potential that should be used to modify the equ...
std::vector< bool > basis_species_transported
basis_species_transported[j] = true iff the j^th basis species is transported in reactive-transport s...
std::vector< std::string > kin_species_name
kin_species_name[j] = name of the j^th kinetic species
DenseMatrix< Real > redox_stoichiometry
redox_stoichiometry(i, j) = stoichiometric coefficients for i^th redox species that is in disequilibr...
DenseMatrix< Real > swap_to_original_basis
Swap matrix that allows expression in terms of the original basis.
std::vector< Real > surface_sorption_area
surface_sorption_area[k] = specific surface area [m^2/g] for the k^th mineral involved in surface sor...
std::vector< bool > basis_species_mineral
basis_species_mineral[j] = true iff the j^th basis species is a mineral
std::vector< KineticRateDefinition > kin_rate
rates given to kinetic species.
DenseMatrix< Real > redox_log10K
redox_log10K(i, j) = log10(equilibrium constant) for i^th redox species at the j^th temperature point
DenseMatrix< Real > kin_log10K
kin_log10K(i, j) = log10(equilibrium constant for the i^th kinetic species at the j^th temperature po...
std::unordered_map< std::string, SurfaceComplexationInfo > surface_complexation_info
Holds info on surface complexation, if any, in the model.
std::vector< bool > surface_sorption_related
surface_sorption_related[j] = true iff the j^th equilibrium species is involved in surface sorption
std::unordered_map< std::string, unsigned > eqm_species_index
eqm_species_index[name] = index of the equilibrium species (secondary aqueous species,...
const GeochemicalDatabaseReader * original_database
a pointer to the original database used to build this ModelGeochemicalDatabase
std::vector< std::string > basis_species_name
basis_species_name[j] = name of the j^th basis species
std::unordered_map< std::string, unsigned > basis_species_index
basis_species_index[name] = index of the basis species, within all ModelGeochemicalDatabase internal ...
std::vector< std::string > surface_sorption_name
surface_sorption_name[k] = name of the mineral involved in surface sorption.
DenseMatrix< Real > eqm_log10K
eqm_log10K(i, j) = log10(equilibrium constant) for i^th equilibrium species at the j^th temperature p...
std::vector< bool > basis_species_gas
basis_species_gas[j] = true iff the j^th basis species is a gas
std::unordered_map< std::string, unsigned > kin_species_index
kin_species_index[name] = index of the kinetic species, within all ModelGeochemicalDatabase internal ...
std::vector< std::string > eqm_species_name
eqm_species_name[i] = name of the i^th eqm species
std::vector< Real > eqm_species_charge
all quantities have a charge (mineral charge = 0, gas charge = 0, oxide charge = 0)
std::vector< bool > eqm_species_gas
eqm_species_gas[i] = true iff the i^th equilibrium species is a gas
std::vector< Real > kin_species_molecular_weight
all quantities have a molecular weight (g/mol)
std::vector< bool > eqm_species_transported
eqm_species_transported[i] = true iff the i^th eqm species is transported in reactive-transport sims