https://mooseframework.inl.gov
Loading...
Searching...
No Matches
GeochemistryKineticRateCalculator.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
11
13{
14void
15calculateRate(const std::vector<Real> & promoting_indices,
16 const std::vector<Real> & promoting_monod_indices,
17 const std::vector<Real> & promoting_half_saturation,
18 const KineticRateUserDescription & description,
19 const std::vector<std::string> & basis_species_name,
20 const std::vector<bool> & basis_species_gas,
21 const std::vector<Real> & basis_molality,
22 const std::vector<Real> & basis_activity,
23 const std::vector<bool> & basis_activity_known,
24 const std::vector<std::string> & eqm_species_name,
25 const std::vector<bool> & eqm_species_gas,
26 const std::vector<Real> & eqm_molality,
27 const std::vector<Real> & eqm_activity,
28 const DenseMatrix<Real> & eqm_stoichiometry,
29 Real kin_moles,
30 Real kin_species_molecular_weight,
31 Real log10K,
32 Real log10_activity_product,
33 const DenseMatrix<Real> & kin_stoichiometry,
34 unsigned kin,
35 Real temp_degC,
36 Real & rate,
37 Real & drate_dkin,
38 std::vector<Real> & drate_dmol)
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}
314}
void mooseError(Args &&... args)
Provides a parametric description of a general kinetic rate.
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.
Holds a user-specified description of a kinetic rate.