https://mooseframework.inl.gov
Loading...
Searching...
No Matches
Functions
GeochemistryKineticRateCalculator Namespace Reference

Provides a parametric description of a general kinetic rate. More...

Functions

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.
 

Detailed Description

Provides a parametric description of a general kinetic rate.

This is a separate class with very little functionality partly so that it can be used by multiple other classes (PertinentGeochemicalSystem, GeochemistryKineticRate) but also so that it can be easily added to if more general kinetic rates are ever required.

The rate is a product of the following terms:

intrinsic_rate_constant area_quantity mass of the kinetic_species, measured in grams, if multiply_by_mass is true (molality of kinetic_species)^kinetic_molal_index 1 / ((molality of kinetic_species)^kinetic_molal_index + kinetic_half_saturation^kinetic_molal_index)^kinetic_monod_index product over the promoting_species of m^(promoting_index) / (m^(promoting_index) + half_saturation^(promoting_index))^(promoting_monod_index) |1 - (Q/K)^theta|^eta, where K = eqm_constant_in_database * exp(- energy_captured / R / TK) exp(activation_energy / R * (1/T0 - 1/T)) D(1 - (Q/K))

Some explanation may be useful:

intrinsic_rate_constant * area_quantity * [mass of kinetic_species] has units mol.s^-1. This allows for the following common possibilities:

The "m" in the promoting species product is:

Q is the activity product, defined by the kinetic_species reaction (defined in the database file). K is the reaction's equilibrium constant (defined in the database file) multiplied by exp(-energy_captured / (RT)). R = 8.314472 m^2.kg.s^-2.K^-1.mol^-1 = 8.314472 J.K^-1.mol^-1 is the gas constant. T is the temperature in Kelvin. T0 is a reference temperature, in Kelvin. It is inputted as 1/T0 so that 1/T0 = 0 is possible.

D(x) depends on direction. If direction == BOTH then D(x) = sgn(x). If direction == DISSOLUTION then D(x) = (x>0)?1:0. If direction == PRECIPITATION then D(x) = (x<0)?-1:0. If direction == RAW then D=1. If direction == DEATH then D=1.

The amount of kinetic species that is generated is kinetic_bio_efficiency * rate. Note that rate

‍0 for dissolution of the kinetic species, so kinetic_bio_efficiency defaults to -1. However,

for biogeochemistry, it is appropriate to set kinetic_bio_efficiency > 0, so that "dissolution" of the biomass (with rate > 0) generates further biomass

Function Documentation

◆ calculateRate()

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

The rate is a product of the following terms:

intrinsic_rate_constant area_quantity mass of the kinetic_species, measured in grams, if multiply_by_mass is true product over the promoting_species of m^(promoting_index) |1 - (Q/K)^theta|^eta exp(activation_energy / R * (1/T0 - 1/T)) D(1 - (Q/K))

Parameters
promoting_indicesof the basis species and equilibrium species. Note that this is different from description.promoting_indices (which is paired with description.promoting_species) because it is the promoting indices for the current basis and equilibrium. For computational efficiency promoting_indices[0:num_basis-1] are the promoting indices for the current basis species while promoting_indices[num_basis:] are the promoting indices for the current eqm species
descriptioncontains definition of intrinsic_rate_constant, area_quantity, etc
basis_species_namevector of basis species names
basis_species_gasi^th element is true if the i^th basis species is a gas
basis_molalityvector of the basis molalities (zeroth element is kg of solvent water, and this is mole number for mineral species)
basis_activityvector of basis activities
basis_activity_knowni^th element is true if the activity for the i^th basis species has been constrained by the user
eqm_species_namevector of eqm species names
eqm_species_gasi^th element is true if the i^th eqm species is a gas
eqm_molalityvector of the eqm molalities (zeroth element is kg of solvent water, and this is mole number for mineral species)
eqm_activityvector of eqm activities
eqm_stoichiometrymatrix of stoichiometric coefficient
kin_molesmoles of the kinetic species
kin_species_molecular_weightmolecular weight (g/mol) of the kinetic species
kin_stoichiometrykinetic stoichiometry matrix (only row=kin is used)
kinthe row of kin_stoichiometry that corresponds to the kinetic species
log10Klog10(equilibrium constant) of the kinetic species
log10_activity_productlog10(activity product) of the kinetic species
temp_degtemperature in degC
[out]ratethe rate computed by this method
[out]drate_dkind(rate)/d(mole number of the kinetic species)
[out]drate_dmold(rate)/d(basis molality[i])

Definition at line 15 of file GeochemistryKineticRateCalculator.C.

39{
40 const unsigned num_basis = basis_species_name.size();
41 const unsigned num_eqm = eqm_species_name.size();
42
43 if (num_basis + num_eqm != promoting_indices.size())
44 mooseError("kinetic_rate: promoting_indices incorrectly sized ", promoting_indices.size());
45 if (num_basis + num_eqm != promoting_monod_indices.size())
46 mooseError("kinetic_rate: promoting_monod_indices incorrectly sized ",
47 promoting_monod_indices.size());
48 if (num_basis + num_eqm != promoting_half_saturation.size())
49 mooseError("kinetic_rate: promoting_half_saturation incorrectly sized ",
50 promoting_half_saturation.size());
51 if (!(num_basis == basis_species_gas.size() && num_basis == basis_molality.size() &&
52 num_basis == basis_activity.size() && num_basis == basis_activity_known.size() &&
53 num_basis == drate_dmol.size()))
54 mooseError("kinetic_rate: incorrectly sized basis-species vectors ",
55 num_basis,
56 " ",
57 basis_species_gas.size(),
58 " ",
59 basis_molality.size(),
60 " ",
61 basis_activity.size(),
62 " ",
63 basis_activity_known.size(),
64 " ",
65 drate_dmol.size());
66 if (!(num_eqm == eqm_species_gas.size() && num_eqm == eqm_molality.size() &&
67 num_eqm == eqm_activity.size()))
68 mooseError("kinetic_rate: incorrectly sized equilibrium-species vectors ",
69 num_eqm,
70 " ",
71 eqm_species_gas.size(),
72 " ",
73 eqm_molality.size(),
74 " ",
75 eqm_activity.size());
76 if (!(eqm_stoichiometry.m() == num_eqm && eqm_stoichiometry.n() == num_basis))
77 mooseError("kinetic_rate: incorrectly sized eqm stoichiometry matrix ",
78 eqm_stoichiometry.m(),
79 " ",
80 eqm_stoichiometry.n());
81 if (!(kin_stoichiometry.m() > kin && kin_stoichiometry.n() == num_basis))
82 mooseError("kinetic_rate: incorrectly sized kinetic stoichiometry matrix ",
83 kin_stoichiometry.m(),
84 " ",
85 kin_stoichiometry.n());
86
87 rate = description.intrinsic_rate_constant;
88 rate *= description.area_quantity;
89 if (description.multiply_by_mass)
90 rate *= kin_moles * kin_species_molecular_weight;
91
92 const Real kin_molality = kin_moles / basis_molality[0];
93 const Real dkin_molality_dkin_moles = 1.0 / basis_molality[0];
94 const Real dkin_molality_dnw = -kin_molality / basis_molality[0];
95 if (description.kinetic_molal_index != 0.0)
96 rate *= std::pow(kin_molality, description.kinetic_molal_index);
97 if (description.kinetic_monod_index != 0.0)
98 rate /=
99 std::pow(std::pow(kin_molality, description.kinetic_molal_index) +
100 std::pow(description.kinetic_half_saturation, description.kinetic_molal_index),
101 description.kinetic_monod_index);
102
103 // promoting species numerators
104 for (unsigned i = 0; i < num_basis; ++i)
105 {
106 if (promoting_indices[i] == 0.0)
107 continue;
108 if (basis_species_gas[i] || basis_species_name[i] == "H+" || basis_species_name[i] == "OH-")
109 rate *= std::pow(basis_activity[i], promoting_indices[i]);
110 else
111 rate *= std::pow(basis_molality[i], promoting_indices[i]);
112 }
113 for (unsigned j = 0; j < num_eqm; ++j)
114 {
115 const unsigned index = num_basis + j;
116 if (promoting_indices[index] == 0.0)
117 continue;
118 if (eqm_species_gas[j] || eqm_species_name[j] == "H+" || eqm_species_name[j] == "OH-")
119 rate *= std::pow(eqm_activity[j], promoting_indices[index]);
120 else
121 rate *= std::pow(eqm_molality[j], promoting_indices[index]);
122 }
123
124 // promoting species denominators: the monod terms
125 for (unsigned i = 0; i < num_basis; ++i)
126 {
127 if (promoting_monod_indices[i] == 0.0)
128 continue;
129 if (basis_species_gas[i] || basis_species_name[i] == "H+" || basis_species_name[i] == "OH-")
130 rate /= std::pow(std::pow(basis_activity[i], promoting_indices[i]) +
131 std::pow(promoting_half_saturation[i], promoting_indices[i]),
132 promoting_monod_indices[i]);
133 else
134 rate /= std::pow(std::pow(basis_molality[i], promoting_indices[i]) +
135 std::pow(promoting_half_saturation[i], promoting_indices[i]),
136 promoting_monod_indices[i]);
137 }
138 for (unsigned j = 0; j < num_eqm; ++j)
139 {
140 const unsigned index = num_basis + j;
141 if (promoting_monod_indices[index] == 0.0)
142 continue;
143 if (eqm_species_gas[j] || eqm_species_name[j] == "H+" || eqm_species_name[j] == "OH-")
144 rate /= std::pow(std::pow(eqm_activity[j], promoting_indices[index]) +
145 std::pow(promoting_half_saturation[index], promoting_indices[index]),
146 promoting_monod_indices[index]);
147 else
148 rate /= std::pow(std::pow(eqm_molality[j], promoting_indices[index]) +
149 std::pow(promoting_half_saturation[index], promoting_indices[index]),
150 promoting_monod_indices[index]);
151 }
152
153 // temperature dependence
154 rate *= std::exp(
156 (description.one_over_T0 - 1.0 / (temp_degC + GeochemistryConstants::CELSIUS_TO_KELVIN)));
157
158 // dependence on activity and equilibrium constant
159 const Real log10K_bio = log10K - description.energy_captured /
163 const Real ap_over_k = std::pow(10.0, log10_activity_product - log10K_bio);
164 const Real theta_term = std::pow(ap_over_k, description.theta);
165 switch (description.direction)
166 {
168 if (ap_over_k > 1.0) // precipitation
169 rate = -rate;
170 // leave the sign of rate unchanged for dissolution
171 break;
173 if (ap_over_k > 1.0) // precipitation
174 rate = 0.0;
175 // leave the sign of rate unchanged for dissolution
176 break;
178 if (ap_over_k > 1.0) // precipitation
179 rate = -rate;
180 else // dissolution
181 rate = 0.0;
182 break;
184 break; // no dependence on 1 - ap_over_k
186 break; // no dependence on 1 - ap_over_k
187 }
188 // const Real rate_no_theta_term = rate; // needed for derivative calcs when theta_term == 1
189 rate *= (description.eta == 0.0)
190 ? 1.0
191 : ((theta_term == 1.0) ? 0.0 : std::pow(std::abs(1.0 - theta_term), description.eta));
192
193 for (unsigned i = 0; i < num_basis; ++i)
194 drate_dmol[i] = 0.0;
195 drate_dkin = 0.0;
196
197 // derivatives with respect to the kinetic species moles
198 if (description.multiply_by_mass)
199 drate_dkin += rate / kin_moles;
200
201 if (description.kinetic_molal_index != 0.0)
202 {
203 const Real d_by_dkin_molality = description.kinetic_molal_index * rate / kin_molality;
204 drate_dkin += d_by_dkin_molality * dkin_molality_dkin_moles;
205 drate_dmol[0] += d_by_dkin_molality * dkin_molality_dnw;
206 }
207 if (!(description.kinetic_molal_index == 0 || description.kinetic_monod_index == 0.0))
208 {
209 const Real d_by_dkin_molality =
210 -description.kinetic_monod_index * description.kinetic_molal_index *
211 std::pow(kin_molality, description.kinetic_molal_index - 1) * rate /
212 (std::pow(kin_molality, description.kinetic_molal_index) +
213 std::pow(description.kinetic_half_saturation, description.kinetic_molal_index));
214 drate_dkin += d_by_dkin_molality * dkin_molality_dkin_moles;
215 drate_dmol[0] += d_by_dkin_molality * dkin_molality_dnw;
216 }
217
218 // Derivatives of the promoting-species numerators (ignore all derivatives of activity
219 // coefficients), so
220 // d(molality^P) / dmolality = P * molality^P / molality , and
221 // d(activity^P)/d(molality) = P activity^(P-1) d(activity)/d(molality) = P activity^(P-1)
222 // activity_product = P * activity^P / molality
223 for (unsigned i = 0; i < num_basis; ++i)
224 {
225 if (promoting_indices[i] == 0.0)
226 continue;
227 else if (basis_species_gas[i]) // molality is undefined
228 continue;
229 else
230 drate_dmol[i] += promoting_indices[i] * rate / basis_molality[i];
231 }
232 for (unsigned j = 0; j < num_eqm; ++j)
233 {
234 // d(eqm^P)/d(basis_i) = P eqm^P / eqm * d(eqm)/d(basis_i) = P eqm^P * stoi / basis_i
235 const unsigned index = num_basis + j;
236 if (promoting_indices[index] == 0.0)
237 continue;
238 for (unsigned i = 1; i < num_basis; ++i) // deriv of water activity is ignored
239 if (!(basis_species_gas[i] || eqm_stoichiometry(j, i) == 0.0))
240 drate_dmol[i] +=
241 promoting_indices[index] * rate * eqm_stoichiometry(j, i) / basis_molality[i];
242 }
243 // Derivatives of the promoting-species denominators (ignore all derivatives of activity
244 // coefficients) so
245 // d((molality^P + K)^(-u))/d(molality) = -u (molality^P + K)^(-u) / (molality^P + K) * P *
246 // molality^P / molality
247 // d((activity^P + K)^(-u))/d(molality) = -u (activity^P + K)^(-u) /
248 // (activity^P + K) * P * activity^P / molality
249 for (unsigned i = 0; i < num_basis; ++i)
250 {
251 if (promoting_indices[i] == 0.0 || promoting_monod_indices[i] == 0.0)
252 continue;
253 else if (basis_species_gas[i]) // molality is undefined
254 continue;
255 else if (basis_species_name[i] == "H+" || basis_species_name[i] == "OH-")
256 drate_dmol[i] -= promoting_monod_indices[i] * promoting_indices[i] *
257 std::pow(basis_activity[i], promoting_indices[i]) * rate /
258 basis_molality[i] /
259 (std::pow(basis_activity[i], promoting_indices[i]) +
260 std::pow(promoting_half_saturation[i], promoting_indices[i]));
261 else
262 drate_dmol[i] -= promoting_monod_indices[i] * promoting_indices[i] *
263 std::pow(basis_molality[i], promoting_indices[i] - 1) * rate /
264 (std::pow(basis_molality[i], promoting_indices[i]) +
265 std::pow(promoting_half_saturation[i], promoting_indices[i]));
266 }
267 for (unsigned j = 0; j < num_eqm; ++j)
268 {
269 const unsigned index = num_basis + j;
270 if (promoting_indices[index] == 0.0 || promoting_monod_indices[index] == 0.0)
271 continue;
272 for (unsigned i = 1; i < num_basis; ++i) // deriv of water activity is ignored
273 {
274 if (basis_species_gas[i] || eqm_stoichiometry(j, i) == 0.0)
275 continue;
276 else if (eqm_species_gas[j] || eqm_species_name[j] == "H+" || eqm_species_name[j] == "OH-")
277 drate_dmol[i] -= promoting_monod_indices[index] * promoting_indices[index] *
278 std::pow(eqm_activity[j], promoting_indices[index]) * rate *
279 eqm_stoichiometry(j, i) /
280 (std::pow(eqm_activity[j], promoting_indices[index]) +
281 std::pow(promoting_half_saturation[index], promoting_indices[index])) /
282 basis_molality[i];
283 else
284 drate_dmol[i] -= promoting_monod_indices[index] * promoting_indices[index] *
285 std::pow(eqm_molality[j], promoting_indices[index]) * rate *
286 eqm_stoichiometry(j, i) /
287 (std::pow(eqm_molality[j], promoting_indices[index]) +
288 std::pow(promoting_half_saturation[index], promoting_indices[index])) /
289 basis_molality[i];
290 }
291 }
292 // derivative of the activity-product term
293 Real deriv_ap_term = 0.0;
294 if (theta_term != 1.0)
295 deriv_ap_term =
296 description.eta * rate / std::abs(1 - theta_term) * (-description.theta) * theta_term;
297 else // theta_term = 1, so rate = 0 (unless eta = 0 too)
298 {
299 if (description.eta > 1)
300 deriv_ap_term = 0.0;
301 else if (description.eta == 1.0)
302 deriv_ap_term = 0.0; // rate_no_theta_term * description.theta * theta_term;
303 else if (description.eta == 0.0)
304 deriv_ap_term = 0.0;
305 else
306 deriv_ap_term = std::numeric_limits<Real>::max();
307 }
308 if (theta_term > 1.0)
309 deriv_ap_term = -deriv_ap_term;
310 for (unsigned i = 1; i < num_basis; ++i)
311 if (!(basis_species_gas[i] || kin_stoichiometry(kin, i) == 0.0 || deriv_ap_term == 0.0))
312 drate_dmol[i] += deriv_ap_term * kin_stoichiometry(kin, i) / basis_molality[i];
313}
ExpressionBuilder::EBTerm pow(const ExpressionBuilder::EBTerm &left, T exponent)
void mooseError(Args &&... args)
Point eta
unsigned int n() const
unsigned int m() const
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

Referenced by GeochemicalSystem::addKineticRates(), TEST(), TEST(), TEST(), TEST(), and TEST().