Loading [MathJax]/extensions/tex2jax.js
https://mooseframework.inl.gov
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends
PertinentGeochemicalSystem.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 
14  const std::vector<std::string> & basis_species,
15  const std::vector<std::string> & minerals,
16  const std::vector<std::string> & gases,
17  const std::vector<std::string> & kinetic_minerals,
18  const std::vector<std::string> & kinetic_redox,
19  const std::vector<std::string> & kinetic_surface_species,
20  const std::string & redox_ox,
21  const std::string & redox_e)
22  : _db(db),
23  _basis_index(),
24  _basis_info(),
25  _mineral_index(),
26  _mineral_info(),
27  _gas_index(),
28  _gas_info(),
29  _kinetic_mineral_index(),
30  _kinetic_mineral_info(),
31  _kinetic_redox_index(),
32  _kinetic_redox_info(),
33  _kinetic_surface_index(),
34  _kinetic_surface_info(),
35  _secondary_index(),
36  _secondary_info(),
37  _redox_ox(redox_ox),
38  _redox_e(redox_e),
39  _model(_db)
40 {
41  // Use the constructor info to build the "index" and "info" structures
42  buildBasis(basis_species);
43  buildMinerals(minerals);
44  buildGases(gases);
45  buildKineticMinerals(kinetic_minerals);
46  buildKineticRedox(kinetic_redox);
47  buildKineticSurface(kinetic_surface_species);
48 
49  // Pull out all secondary equilibrium species: these are all relevant "redox couples" and
50  // "secondary species" and "surface species"
52 
53  // Build minerals in the case that minerals = {"*"}
54  buildAllMinerals(minerals);
55 
56  // Check that everything can be expressed in terms of the basis, possibly via redox and secondary
57  // species
59  checkGases();
63 
64  // Populate _model
65  createModel();
66 }
67 
68 unsigned
70 {
71  try
72  {
73  return _basis_index.at(name);
74  }
75  catch (const std::out_of_range &)
76  {
77  mooseError("species ", name, " is not in the original basis");
78  }
79  catch (...)
80  {
81  throw;
82  }
83 }
84 
85 std::vector<std::string>
87 {
88  std::vector<std::string> names(_basis_info.size());
89  for (const auto & name_ind : _basis_index)
90  names[name_ind.second] = name_ind.first;
91  return names;
92 }
93 
94 void
95 PertinentGeochemicalSystem::buildBasis(const std::vector<std::string> & basis_species)
96 {
97  unsigned ind = 0;
98  for (const auto & name : basis_species)
99  {
100  if (ind == 0 and name != "H2O")
101  mooseError("First member of basis species list must be H2O");
102  if (_basis_index.count(name) == 1)
103  mooseError(name, " exists more than once in the basis species list");
104  _basis_index.emplace(name, ind);
105  ind += 1;
106  if (_db.isBasisSpecies(name))
107  _basis_info.push_back(_db.getBasisSpecies({name})[name]);
108  else if (_db.isRedoxSpecies(name))
109  {
112  bs.name = rs.name;
113  bs.radius = rs.radius;
114  bs.charge = rs.charge;
116  _basis_info.push_back(bs);
117  }
118  else
119  mooseError(name + " does not exist in the basis species or redox species in " +
120  _db.filename());
121  }
122 }
123 
124 void
125 PertinentGeochemicalSystem::buildMinerals(const std::vector<std::string> & minerals)
126 {
127  unsigned ind = 0;
128  if (minerals.size() == 1 && minerals[0] == "*")
129  return; // buildAllMinerals is called later
130  for (const auto & name : minerals)
131  {
132  if (_mineral_index.count(name) == 1)
133  mooseError(name + " exists more than once in the minerals list");
134  _mineral_index.emplace(name, ind);
135  ind += 1;
136  _mineral_info.push_back(_db.getMineralSpecies({name})[name]);
137  }
138 }
139 
140 void
141 PertinentGeochemicalSystem::buildGases(const std::vector<std::string> & gases)
142 {
143  unsigned ind = 0;
144  for (const auto & name : gases)
145  {
146  if (_gas_index.count(name) == 1)
147  mooseError(name + " exists more than once in the gases list");
148  _gas_index.emplace(name, ind);
149  ind += 1;
151  _gas_info.push_back(gas);
152  }
153 }
154 
155 void
156 PertinentGeochemicalSystem::buildKineticMinerals(const std::vector<std::string> & kinetic_minerals)
157 {
158  unsigned ind = 0;
159  for (const auto & name : kinetic_minerals)
160  {
161  if (_kinetic_mineral_index.count(name) == 1)
162  mooseError(name + " exists more than once in the kinetic_minerals list");
163  if (_mineral_index.count(name) == 1)
164  mooseError(name + " exists in both the minerals and kinetic_minerals lists");
165  _kinetic_mineral_index.emplace(name, ind);
166  ind += 1;
167  _kinetic_mineral_info.push_back(_db.getMineralSpecies({name})[name]);
168  }
169 }
170 
171 void
172 PertinentGeochemicalSystem::buildKineticRedox(const std::vector<std::string> & kinetic_redox)
173 {
174  unsigned ind = 0;
175  for (const auto & name : kinetic_redox)
176  {
177  if (_kinetic_redox_index.count(name) == 1)
178  mooseError(name + " exists more than once in the kinetic_redox list");
179  if (_basis_index.count(name) == 1)
180  mooseError(name + " exists in both the basis_species and kinetic_redox lists");
181  _kinetic_redox_index.emplace(name, ind);
182  ind += 1;
183  _kinetic_redox_info.push_back(_db.getRedoxSpecies({name})[name]);
184  }
185 }
186 
187 void
189  const std::vector<std::string> & kinetic_surface_species)
190 {
191  unsigned ind = 0;
192  for (const auto & name : kinetic_surface_species)
193  {
194  if (_kinetic_surface_index.count(name) == 1)
195  mooseError(name + " exists more than once in the kinetic_surface_species list");
196  _kinetic_surface_index.emplace(name, ind);
197  ind += 1;
198  _kinetic_surface_info.push_back(_db.getSurfaceSpecies({name})[name]);
199  }
200 }
201 
202 void
204 {
205  // run through all redox couples, including them if:
206  // - they are not part of the kinetic_redox list
207  // - they are not part of the basis_species list
208  // - their reaction involves only basis_species or secondary species already encountered
209  unsigned ind = 0;
210  for (const auto & name : _db.redoxCoupleNames())
211  {
212  if (_kinetic_redox_index.count(name) == 0 && _basis_index.count(name) == 0)
213  {
215  // check all reaction species are in the basis
216  bool all_species_in_basis_or_sec = true;
217  for (const auto & element : rs.basis_species)
218  if (_basis_index.count(element.first) == 0 && _secondary_index.count(element.first) == 0)
219  {
220  all_species_in_basis_or_sec = false;
221  break;
222  }
223  if (all_species_in_basis_or_sec)
224  {
225  _secondary_index.emplace(name, ind);
226  ind += 1;
228  ss.name = rs.name;
231  ss.radius = rs.radius;
232  ss.charge = rs.charge;
234  _secondary_info.push_back(ss);
235  }
236  }
237  }
238 
239  // run through all secondary species, including them if:
240  // - their reaction involves only basis_species, or secondary species already encountered
241  // - the name is not _redox_e (which is usually "e-" the free electron)
242  for (const auto & name : _db.secondarySpeciesNames())
243  {
244  if (name == _redox_e)
245  continue;
247  // check all reaction species are in the basis
248  bool all_species_in_basis_or_sec = true;
249  for (const auto & element : ss.basis_species)
250  if (_basis_index.count(element.first) == 0 && _secondary_index.count(element.first) == 0)
251  {
252  all_species_in_basis_or_sec = false;
253  break;
254  }
255  if (all_species_in_basis_or_sec)
256  {
257  _secondary_index.emplace(name, ind);
258  ind += 1;
259  _secondary_info.push_back(ss);
260  }
261  }
262 
263  // run through all surface species, including them if:
264  // - their reaction involves only basis_species or secondary species encountered so far
265  // - they are not in the kinetic_surface_species list
266  for (const auto & name : _db.surfaceSpeciesNames())
267  {
268  if (_kinetic_surface_index.count(name) == 0)
269  {
271  // check all reaction species are in the basis
272  bool all_species_in_basis_or_sec = true;
273  for (const auto & element : ss.basis_species)
274  if (_basis_index.count(element.first) == 0 && _secondary_index.count(element.first) == 0)
275  {
276  all_species_in_basis_or_sec = false;
277  break;
278  }
279  if (all_species_in_basis_or_sec)
280  {
282  to_sec.name = ss.name;
283  to_sec.basis_species = ss.basis_species;
284  to_sec.radius = -1.5; // flag to activity calculators that activity coefficient = 1
285  to_sec.charge = ss.charge;
287  const Real T0 = _db.getTemperatures()[0];
288  for (const auto & temp : _db.getTemperatures())
289  to_sec.equilibrium_const.push_back(ss.log10K + ss.dlog10KdT * (temp - T0));
290 
291  _secondary_index.emplace(name, ind);
292  ind += 1;
293  _secondary_info.push_back(to_sec);
294  }
295  }
296  }
297 }
298 
299 void
300 PertinentGeochemicalSystem::buildAllMinerals(const std::vector<std::string> & minerals)
301 {
302  if (!(minerals.size() == 1 && minerals[0] == "*"))
303  return; // buildMinerals has done its job of building _mineral_info and _mineral_index
304  unsigned ind = 0;
305  for (const auto & name_ms : _db.getMineralSpecies(_db.mineralSpeciesNames()))
306  {
307  if (_kinetic_mineral_index.count(name_ms.first) == 1)
308  continue;
309  bool known_basis_only = true;
310  for (const auto & basis_stoi : name_ms.second.basis_species)
311  {
312  if (_basis_index.count(basis_stoi.first) == 0 &&
313  _secondary_index.count(basis_stoi.first) == 0)
314  {
315  known_basis_only = false;
316  break;
317  }
318  }
319  if (known_basis_only)
320  {
321  _mineral_index.emplace(name_ms.first, ind);
322  ind += 1;
323  _mineral_info.push_back(name_ms.second);
324  }
325  }
326 }
327 
328 bool
330 {
331  bool found = false;
332  for (const auto & name : _db.secondarySpeciesNames())
333  if (name == _redox_e)
334  {
335  found = true;
337  for (const auto & element : ss.basis_species)
338  if (_basis_index.count(element.first) == 0 && _secondary_index.count(element.first) == 0)
339  return false;
340  }
341  return found;
342 }
343 
344 void
345 PertinentGeochemicalSystem::buildRedoxeInfo(std::vector<Real> & redox_e_stoichiometry,
346  std::vector<Real> & redox_e_log10K)
347 {
348  const unsigned num_basis = _basis_info.size();
349  const unsigned numT = _db.getTemperatures().size();
350  redox_e_stoichiometry.assign(num_basis, 0.0);
351  redox_e_log10K.assign(numT, 0.0);
352  for (const auto & name : _db.secondarySpeciesNames())
353  if (name == _redox_e)
354  {
356  for (unsigned i = 0; i < numT; ++i)
357  redox_e_log10K[i] = ss.equilibrium_const[i];
358  for (const auto & react : ss.basis_species)
359  {
360  const Real stoi_coeff = react.second;
361  if (_model.basis_species_index.count(react.first) == 1)
362  {
363  const unsigned col = _model.basis_species_index[react.first];
364  redox_e_stoichiometry[col] += react.second;
365  }
366  else if (_secondary_index.count(react.first) == 1)
367  {
368  // reaction species is not a basis component, but a secondary component.
369  // So express stoichiometry in terms of the secondary component's reaction
370  const unsigned sec_row = _model.eqm_species_index[react.first];
371  for (unsigned i = 0; i < numT; ++i)
372  redox_e_log10K[i] += stoi_coeff * _model.eqm_log10K(sec_row, i);
373  for (unsigned col = 0; col < num_basis; ++col)
374  redox_e_stoichiometry[col] += stoi_coeff * _model.eqm_stoichiometry(sec_row, col);
375  }
376  }
377  }
378 }
379 
380 void
382  const std::vector<GeochemistryMineralSpecies> & mineral_info) const
383 {
384  for (const auto & mineral : mineral_info)
385  {
386  for (const auto & element : mineral.basis_species)
387  if (_basis_index.count(element.first) == 0 && _secondary_index.count(element.first) == 0)
388  mooseError("The reaction for " + mineral.name + " depends on " + element.first +
389  " which is not reducable to a set of basis species");
390  for (const auto & element : mineral.sorption_sites)
391  if (_basis_index.count(element.first) == 0)
392  mooseError("The sorbing sites for " + mineral.name + " include " + element.first +
393  " which is not in the basis_species list");
394  }
395 }
396 
397 void
399 {
400  for (const auto & gas : _gas_info)
401  for (const auto & element : gas.basis_species)
402  if (_basis_index.count(element.first) == 0 && _secondary_index.count(element.first) == 0)
403  mooseError("The reaction for " + gas.name + " depends on " + element.first +
404  " which is not reducable to a set of basis species");
405 }
406 
407 void
409 {
410  for (const auto & kr : _kinetic_redox_info)
411  for (const auto & element : kr.basis_species)
412  if (_basis_index.count(element.first) == 0 && _secondary_index.count(element.first) == 0)
413  mooseError("The reaction for " + kr.name + " depends on " + element.first +
414  " which is not reducable to a set of basis species");
415 }
416 
417 void
419 {
420  for (const auto & kr : _kinetic_surface_info)
421  for (const auto & element : kr.basis_species)
422  if (_basis_index.count(element.first) == 0 && _secondary_index.count(element.first) == 0)
423  mooseError("The reaction for " + kr.name + " depends on " + element.first +
424  " which is not reducable to a set of basis species");
425 }
426 
427 void
429 {
430  const unsigned num_rows = _secondary_info.size() + _mineral_info.size() + _gas_info.size();
431  const unsigned num_cols = _basis_info.size();
432  const unsigned num_temperatures = _db.getTemperatures().size();
433  unsigned ind = 0;
434 
435  // create basis_species_index map
437 
438  // extract the names
439  _model.basis_species_name = std::vector<std::string>(num_cols);
440  for (const auto & species : _basis_info)
441  _model.basis_species_name[_model.basis_species_index[species.name]] = species.name;
442 
443  // initially no basis species are minerals
444  _model.basis_species_mineral = std::vector<bool>(num_cols, false);
445 
446  // initially no basis species are gases
447  _model.basis_species_gas = std::vector<bool>(num_cols, false);
448 
449  // initially all basis species are involved in transport (this gets modified for surface species
450  // below)
451  _model.basis_species_transported = std::vector<bool>(num_cols, true);
452 
453  // record the charge
454  _model.basis_species_charge = std::vector<Real>(num_cols, 0.0);
455  for (const auto & species : _basis_info)
456  _model.basis_species_charge[_model.basis_species_index[species.name]] = species.charge;
457 
458  // record the ionic radius
459  _model.basis_species_radius = std::vector<Real>(num_cols, 0.0);
460  for (const auto & species : _basis_info)
461  _model.basis_species_radius[_model.basis_species_index[species.name]] = species.radius;
462 
463  // record the molecular weight
464  _model.basis_species_molecular_weight = std::vector<Real>(num_cols, 0.0);
465  for (const auto & species : _basis_info)
467  species.molecular_weight;
468 
469  // record the molecular weight (zero for all species except minerals)
470  _model.basis_species_molecular_volume = std::vector<Real>(num_cols, 0.0);
471 
472  // the "eqm_species" stuff is rather long-winded because of the different data structures used
473  // to hold secondary, mineral and gas info. There is a bit of an overlap, however, so let's
474  // create a new data structure that contains that overlapping info
475 
476  std::vector<GeochemistryEquilibriumSpecies> overlap(_secondary_info);
477  for (const auto & species : _mineral_info)
478  {
480  es.name = species.name;
481  es.molecular_weight = species.molecular_weight;
482  es.equilibrium_const = species.equilibrium_const;
483  es.basis_species = species.basis_species;
484  overlap.push_back(es);
485  }
486  for (const auto & species : _gas_info)
487  {
489  es.name = species.name;
490  es.molecular_weight = species.molecular_weight;
491  es.equilibrium_const = species.equilibrium_const;
492  es.basis_species = species.basis_species;
493  overlap.push_back(es);
494  }
495 
496  // create the eqm_species_index map
497  ind = 0;
498  for (const auto & species : overlap)
499  _model.eqm_species_index[species.name] = ind++;
500 
501  // extract the names
502  _model.eqm_species_name = std::vector<std::string>(num_rows);
503  for (const auto & species : _model.eqm_species_index)
504  _model.eqm_species_name[species.second] = species.first;
505 
506  // create the eqm_species_mineral vector
507  _model.eqm_species_mineral = std::vector<bool>(num_rows, false);
508  for (const auto & species : _mineral_info)
509  _model.eqm_species_mineral[_model.eqm_species_index[species.name]] = true;
510  for (const auto & species : _gas_info)
511  _model.eqm_species_mineral[_model.eqm_species_index[species.name]] = false;
512 
513  // create the eqm_species_gas vector
514  _model.eqm_species_gas = std::vector<bool>(num_rows, false);
515  for (const auto & species : _mineral_info)
516  _model.eqm_species_gas[_model.eqm_species_index[species.name]] = false;
517  for (const auto & species : _gas_info)
518  _model.eqm_species_gas[_model.eqm_species_index[species.name]] = true;
519 
520  // create the eqm_species_transported vector (true for non-minerals) - gets modified below for
521  // surface species
522  _model.eqm_species_transported = std::vector<bool>(num_rows, true);
523  for (const auto & species : _mineral_info)
524  _model.eqm_species_transported[_model.eqm_species_index.at(species.name)] = false;
525 
526  // record the charge
528  std::vector<Real>(num_rows, 0.0); // charge of gases and minerals is zero
529  for (const auto & species : _secondary_info)
530  _model.eqm_species_charge[_model.eqm_species_index[species.name]] = species.charge;
531 
532  // record the radius
534  std::vector<Real>(num_rows, 0.0); // ionic radius of gases and minerals is zero
535  for (const auto & species : _secondary_info)
536  _model.eqm_species_radius[_model.eqm_species_index[species.name]] = species.radius;
537 
538  // record the molecular weight
539  _model.eqm_species_molecular_weight = std::vector<Real>(num_rows, 0.0);
540  for (const auto & species : overlap)
542  species.molecular_weight;
543 
544  // record the molecular volume (zero for all species except minerals)
545  _model.eqm_species_molecular_volume = std::vector<Real>(num_rows, 0.0);
546  for (const auto & species : _mineral_info)
548  species.molecular_volume;
549 
550  // record surface-complexation info
551  for (const auto & species : _mineral_info)
552  if (species.surface_area != 0.0)
553  {
555  sci.surface_area = species.surface_area;
556  sci.sorption_sites = species.sorption_sites;
557  _model.surface_complexation_info[species.name] = sci;
558  }
559  for (const auto & species : _kinetic_mineral_info)
560  if (species.surface_area != 0.0)
561  {
563  sci.surface_area = species.surface_area;
564  sci.sorption_sites = species.sorption_sites;
565  _model.surface_complexation_info[species.name] = sci;
566  }
567 
568  // record gas fugacity info
569  for (const auto & species : _gas_info)
570  _model.gas_chi[species.name] = species.chi;
571 
572  // create the stoichiometry matrix
573  _model.eqm_stoichiometry.resize(num_rows, num_cols);
574  _model.eqm_log10K.resize(num_rows, num_temperatures);
575 
576  // populate the stoichiometry
577  for (const auto & species : overlap)
578  {
579  const unsigned row = _model.eqm_species_index[species.name];
580  for (unsigned i = 0; i < num_temperatures; ++i)
581  _model.eqm_log10K(row, i) = species.equilibrium_const[i];
582  for (const auto & react : species.basis_species)
583  {
584  const Real stoi_coeff = react.second;
585  if (_model.basis_species_index.count(react.first) == 1)
586  {
587  const unsigned col = _model.basis_species_index[react.first];
588  _model.eqm_stoichiometry(row, col) += react.second;
589  }
590  else if (_secondary_index.count(react.first) == 1)
591  {
592  // reaction species is not a basis component, but a secondary component.
593  // So express stoichiometry in terms of the secondary component's reaction
594  const unsigned sec_row = _model.eqm_species_index[react.first];
595  for (unsigned i = 0; i < num_temperatures; ++i)
596  _model.eqm_log10K(row, i) += stoi_coeff * _model.eqm_log10K(sec_row, i);
597  for (unsigned col = 0; col < num_cols; ++col)
598  _model.eqm_stoichiometry(row, col) += stoi_coeff * _model.eqm_stoichiometry(sec_row, col);
599  }
600  else
601  mooseError("Species " + species.name + " includes " + react.first +
602  ", which cannot be expressed in terms of the basis. Previous checks must be "
603  "erroneous!");
604  }
605  }
606 
607  // Build the redox information, if any. Here we express any O2(aq) in the redox equations in
608  // terms of redox_e (which is usually e-)
610  std::vector<Real> redox_e_stoichiometry(num_cols, 0.0);
611  std::vector<Real> redox_e_log10K(num_temperatures, 0.0);
612  std::vector<Real> redox_stoi;
613  std::vector<Real> redox_log10K;
614  if ((_model.basis_species_index.count(_redox_ox) == 1) && checkRedoxe())
615  {
616  // construct the stoichiometry and log10K for _redox_e and put it
617  buildRedoxeInfo(redox_e_stoichiometry, redox_e_log10K);
618  redox_stoi.insert(redox_stoi.end(), redox_e_stoichiometry.begin(), redox_e_stoichiometry.end());
619  redox_log10K.insert(redox_log10K.end(), redox_e_log10K.begin(), redox_e_log10K.end());
620  // the electron reaction is
621  // e- = nuw_i * basis_i + beta * O2(aq), where we've pulled out the O2(aq) because it's
622  // special
623  const unsigned o2_index = _model.basis_species_index.at(_redox_ox);
624  const Real beta = redox_e_stoichiometry[o2_index];
625  if (beta != 0.0)
626  {
627  for (const auto & bs : _model.basis_species_index)
628  if (_db.isRedoxSpecies(bs.first))
629  {
630  // this basis species is a redox couple in disequilibrium
631  const GeochemistryRedoxSpecies rs = _db.getRedoxSpecies({bs.first})[bs.first];
632  // check that its reaction involves only basis species, and record the stoichiometry in
633  // the current basis
634  std::vector<Real> stoi(num_cols, 0.0);
635  bool only_involves_basis_species = true;
636  for (const auto & name_stoi : rs.basis_species)
637  {
638  if (_model.basis_species_index.count(name_stoi.first) == 1)
639  stoi[_model.basis_species_index.at(name_stoi.first)] = name_stoi.second;
640  else
641  {
642  only_involves_basis_species = false;
643  break;
644  }
645  }
646  if (!only_involves_basis_species)
647  continue;
648  // Reaction is now
649  // redox = nu_i * basis_i + alpha * O2(aq), where we've pulled the O2(aq) out because
650  // it's special. Now pull the redox couple to the RHS of the reaction, so we have 0 =
651  // -redox + nu_i * basis_i + alpha * O2(aq)
652  stoi[bs.second] = -1.0;
653  // check that the stoichiometry involves O2(aq)
654  const Real alpha = stoi[o2_index];
655  if (alpha == 0.0)
656  continue;
657  // multiply equation -beta/alpha so it reads
658  // 0 = -beta/alpha * (-redox + nu_i * basis_i) - beta * O2(aq)
659  for (unsigned basis_i = 0; basis_i < num_cols; ++basis_i)
660  stoi[basis_i] *= -beta / alpha;
661  // add the equation to e- = nuw_i * basis_i + beta * O2(aq)
662  for (unsigned basis_i = 0; basis_i < num_cols; ++basis_i)
663  stoi[basis_i] += redox_e_stoichiometry[basis_i];
664  // now the reation is e- = nuw_i * basis_i - beta/alpha * (-redox + nu_i * basis_i)
665  redox_stoi.insert(redox_stoi.end(), stoi.begin(), stoi.end());
666 
667  // record the equilibrium constants
668  for (unsigned temp = 0; temp < num_temperatures; ++temp)
669  redox_log10K.push_back((-beta / alpha) * rs.equilibrium_const[temp] +
670  redox_e_log10K[temp]);
671  }
672  }
673  }
674  // record the above in the model.redox_stoichiometry and model.redox_log10K DenseMatrices
675  const unsigned num_redox = redox_stoi.size() / num_cols;
676  _model.redox_stoichiometry.resize(num_redox, num_cols);
677  for (unsigned red = 0; red < num_redox; ++red)
678  for (unsigned basis_i = 0; basis_i < num_cols; ++basis_i)
679  _model.redox_stoichiometry(red, basis_i) = redox_stoi[red * num_cols + basis_i];
680  _model.redox_log10K.resize(num_redox, num_temperatures);
681  for (unsigned red = 0; red < num_redox; ++red)
682  for (unsigned temp = 0; temp < num_temperatures; ++temp)
683  _model.redox_log10K(red, temp) = redox_log10K[red * num_temperatures + temp];
684 
685  // To build the kin_species_index, kin_species_name, etc, let's build an "overlap", similar to
686  // above
687  std::vector<GeochemistryMineralSpecies> overlap_kin(_kinetic_mineral_info);
688  for (const auto & species : _kinetic_redox_info)
689  {
691  ms.name = species.name;
692  ms.molecular_volume = 0.0;
693  ms.basis_species = species.basis_species;
694  ms.molecular_weight = species.molecular_weight;
695  ms.equilibrium_const = species.equilibrium_const;
696  overlap_kin.push_back(ms);
697  }
698  for (const auto & species : _kinetic_surface_info)
699  {
701  ms.name = species.name;
702  ms.molecular_volume = 0.0;
703  ms.basis_species = species.basis_species;
704  ms.molecular_weight = species.molecular_weight;
705  const Real T0 = _db.getTemperatures()[0];
706  for (const auto & temp : _db.getTemperatures())
707  ms.equilibrium_const.push_back(species.log10K + species.dlog10KdT * (temp - T0));
708  overlap_kin.push_back(ms);
709  }
710  const unsigned num_kin = overlap_kin.size();
711 
712  // create the kin_species_index map
713  ind = 0;
714  for (const auto & species : overlap_kin)
715  _model.kin_species_index[species.name] = ind++;
716 
717  // extract the names
718  _model.kin_species_name = std::vector<std::string>(num_kin);
719  for (const auto & species : _model.kin_species_index)
720  _model.kin_species_name[species.second] = species.first;
721 
722  // build the kin_species_mineral info
723  _model.kin_species_mineral = std::vector<bool>(num_kin, true);
724  for (const auto & species : _kinetic_redox_info)
725  _model.kin_species_mineral[_model.kin_species_index[species.name]] = false;
726  for (const auto & species : _kinetic_surface_info)
727  _model.kin_species_mineral[_model.kin_species_index[species.name]] = false;
728 
729  // create the kin_species_transported vector (false for minerals and surface species)
730  _model.kin_species_transported = std::vector<bool>(num_kin, true);
731  for (const auto & species : _kinetic_mineral_info)
732  _model.kin_species_transported[_model.kin_species_index.at(species.name)] = false;
733  for (const auto & species : _kinetic_surface_info)
734  _model.kin_species_transported[_model.kin_species_index.at(species.name)] = false;
735 
736  // build the kin_species_charge info
737  _model.kin_species_charge = std::vector<Real>(num_kin, 0.0);
738  for (const auto & species : _kinetic_redox_info)
739  _model.kin_species_charge[_model.kin_species_index[species.name]] = species.charge;
740  for (const auto & species : _kinetic_surface_info)
741  _model.kin_species_charge[_model.kin_species_index[species.name]] = species.charge;
742 
743  // extract the molecular weight
744  _model.kin_species_molecular_weight = std::vector<Real>(num_kin, 0.0);
745  for (const auto & species : overlap_kin)
747  species.molecular_weight;
748 
749  // extract the molecular volume
750  _model.kin_species_molecular_volume = std::vector<Real>(num_kin, 0.0);
751  for (const auto & species : overlap_kin)
753  species.molecular_volume;
754 
755  // extract the stoichiometry
756  _model.kin_stoichiometry.resize(num_kin, num_cols);
757  _model.kin_log10K.resize(num_kin, num_temperatures);
758 
759  // populate the stoichiometry
760  for (const auto & species : overlap_kin)
761  {
762  const unsigned row = _model.kin_species_index[species.name];
763  for (unsigned i = 0; i < num_temperatures; ++i)
764  _model.kin_log10K(row, i) = species.equilibrium_const[i];
765  for (const auto & react : species.basis_species)
766  {
767  const Real stoi_coeff = react.second;
768  if (_model.basis_species_index.count(react.first) == 1)
769  {
770  const unsigned col = _model.basis_species_index[react.first];
771  _model.kin_stoichiometry(row, col) += react.second;
772  }
773  else if (_model.eqm_species_index.count(react.first) == 1)
774  {
775  // reaction species is not a basis component, but a secondary component.
776  // So express stoichiometry in terms of the secondary component's reaction
777  const unsigned sec_row = _model.eqm_species_index[react.first];
778  for (unsigned i = 0; i < num_temperatures; ++i)
779  _model.kin_log10K(row, i) += stoi_coeff * _model.eqm_log10K(sec_row, i);
780  for (unsigned col = 0; col < num_cols; ++col)
781  _model.kin_stoichiometry(row, col) += stoi_coeff * _model.eqm_stoichiometry(sec_row, col);
782  }
783  else
784  mooseError("Kinetic species " + species.name + " includes " + react.first +
785  ", which cannot be expressed in terms of the basis. Previous checks must be "
786  "erroneous!");
787  }
788  }
789 
790  // check that there are no repeated sorbing sites in the SurfaceComplexationInfo
791  std::vector<std::string> all_sorbing_sites;
792  for (const auto & name_info : _model.surface_complexation_info)
793  for (const auto & name_frac : name_info.second.sorption_sites)
794  if (std::find(all_sorbing_sites.begin(), all_sorbing_sites.end(), name_frac.first) !=
795  all_sorbing_sites.end())
796  mooseError(
797  "The sorbing site ", name_frac.first, " appears in more than one sorbing mineral");
798  else
799  all_sorbing_sites.push_back(name_frac.first);
800 
801  // build the information related to surface sorption, and modify the species_transported vectors
802  _model.surface_sorption_related.assign(num_rows, false);
803  _model.surface_sorption_number.assign(num_rows, 99);
804  for (const auto & name_info :
805  _model.surface_complexation_info) // all minerals involved in surface complexation
806  {
807  for (const auto & name_frac :
808  name_info.second.sorption_sites) // all sorption sites on the given mineral
809  {
810  const unsigned basis_index_of_sorption_site = _model.basis_species_index.at(name_frac.first);
811  _model.basis_species_transported[basis_index_of_sorption_site] = false;
812  }
813  bool mineral_involved_in_eqm = false;
814  for (const auto & name_frac :
815  name_info.second.sorption_sites) // all sorption sites on the given mineral
816  {
817  const unsigned basis_index_of_sorption_site = _model.basis_species_index.at(name_frac.first);
818  for (unsigned j = 0; j < num_rows; ++j) // all equilibrium species
819  if (_model.eqm_stoichiometry(j, basis_index_of_sorption_site) != 0.0)
820  {
821  mineral_involved_in_eqm = true;
822  break;
823  }
824  }
825  if (!mineral_involved_in_eqm)
826  continue;
827  const unsigned num_surface_sorption = _model.surface_sorption_name.size();
828  _model.surface_sorption_name.push_back(name_info.first);
829  _model.surface_sorption_area.push_back(name_info.second.surface_area);
830  for (const auto & name_frac :
831  name_info.second.sorption_sites) // all sorption sites on the given mineral
832  {
833  const unsigned basis_index_of_sorption_site = _model.basis_species_index.at(name_frac.first);
834  for (unsigned j = 0; j < num_rows; ++j) // all equilibrium species
835  if (_model.eqm_stoichiometry(j, basis_index_of_sorption_site) != 0.0)
836  {
838  mooseError("It is an error for any equilibrium species (such as ",
840  ") to have a reaction involving more than one sorbing site");
842  _model.surface_sorption_number[j] = num_surface_sorption;
844  }
845  }
846  }
847 }
848 
851 {
852  return _model;
853 }
854 
855 void
857 {
858  const std::string kinetic_species = description.kinetic_species_name;
859  if (_model.kin_species_index.count(kinetic_species) == 0)
860  mooseError("Cannot prescribe a kinetic rate to species ",
861  kinetic_species,
862  " since it is not a kinetic species");
863  const unsigned kinetic_species_index = _model.kin_species_index.at(kinetic_species);
864 
865  // build the promoting index list
866  const unsigned num_pro = description.promoting_species.size();
867  const unsigned num_basis = _model.basis_species_name.size();
868  const unsigned num_eqm = _model.eqm_species_name.size();
869  std::vector<Real> promoting_ind(num_basis + num_eqm, 0.0);
870  std::vector<Real> promoting_m_ind(num_basis + num_eqm, 0.0);
871  std::vector<Real> promoting_k(num_basis + num_eqm, 0.0);
872  for (unsigned i = 0; i < num_pro; ++i)
873  {
874  unsigned index = 0;
875  const std::string promoting_species = description.promoting_species[i];
876  if (_model.basis_species_index.count(promoting_species) == 1)
877  index = _model.basis_species_index.at(promoting_species);
878  else if (_model.eqm_species_index.count(promoting_species) == 1)
879  index = num_basis + _model.eqm_species_index.at(promoting_species);
880  else
881  mooseError(
882  "Promoting species ", promoting_species, " must be a basis or a secondary species");
883  promoting_ind[index] = description.promoting_indices[i];
884  promoting_m_ind[index] = description.promoting_monod_indices[i];
885  promoting_k[index] = description.promoting_half_saturation[i];
886  }
887  unsigned progeny_num = 0;
888  if (_model.basis_species_index.count(description.progeny) == 1)
889  progeny_num = _model.basis_species_index.at(description.progeny);
890  else if (_model.eqm_species_index.count(description.progeny) == 1)
891  progeny_num = num_basis + _model.eqm_species_index.at(description.progeny);
892  else
893  mooseError("Progeny ", description.progeny, " must be a basis or a secondary species");
894 
895  // append the result to kin_rate
896  _model.kin_rate.push_back(KineticRateDefinition(kinetic_species_index,
897  promoting_ind,
898  promoting_m_ind,
899  promoting_k,
900  progeny_num,
901  description));
902 }
std::vector< bool > surface_sorption_related
surface_sorption_related[j] = true iff the j^th equilibrium species is involved in surface sorption ...
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...
DenseMatrix< Real > redox_stoichiometry
redox_stoichiometry(i, j) = stoichiometric coefficients for i^th redox species that is in disequilibr...
std::vector< Real > eqm_species_molecular_weight
all quantities have a molecular weight (g)
std::map< std::string, GeochemistrySurfaceSpecies > getSurfaceSpecies(const std::vector< std::string > &names)
Get the surface sorbing species information.
std::map< std::string, GeochemistryEquilibriumSpecies > getEquilibriumSpecies(const std::vector< std::string > &names)
Get the secondary equilibrium species information.
std::string redox_lhs
the name of the species on the left-hand side of the redox equations.
void buildGases(const std::vector< std::string > &gases)
using the gas list, this method builds _gas_index and _gas_info
std::vector< bool > kin_species_mineral
kin_species_mineral[j] = true iff the j^th kinetic species is a mineral
A single rate expression for the kinetic species with index kinetic_species_index.
std::vector< bool > basis_species_transported
basis_species_transported[j] = true iff the j^th basis species is transported in reactive-transport s...
Data structure for basis (primary) species.
unsigned getIndexOfOriginalBasisSpecies(const std::string &name) const
std::vector< Real > kin_species_charge
all kinetic quantities have a charge (mineral charge = 0)
std::vector< std::string > surface_sorption_name
surface_sorption_name[k] = name of the mineral involved in surface sorption.
std::map< std::string, Real > basis_species
void buildKineticMinerals(const std::vector< std::string > &kinetic_minerals)
using the kinetic_minerals list, this method builds _kinetic_mineral_index and _kinetic_mineral_info ...
void buildKineticRedox(const std::vector< std::string > &kinetic_redox)
using the kinetic_redox list, this method builds _kinetic_redox_index and _kinetic_redox_info ...
void mooseError(Args &&... args)
Data structure for mineral species.
std::vector< std::string > redoxCoupleNames() const
Returns a list of all the names of the "redox couples" in the database.
std::vector< bool > eqm_species_gas
eqm_species_gas[i] = true iff the i^th equilibrium species is a gas
std::map< std::string, GeochemistryMineralSpecies > getMineralSpecies(const std::vector< std::string > &names)
Get the mineral species information.
std::map< std::string, GeochemistryGasSpecies > getGasSpecies(const std::vector< std::string > &names)
Get the gas species information.
std::unordered_map< std::string, unsigned > _basis_index
given a species name, return its index in the corresponding "info" std::vector
std::vector< GeochemistryBasisSpecies > _basis_info
a vector of all relevant species
std::unordered_map< std::string, unsigned > basis_species_index
basis_species_index[name] = index of the basis species, within all ModelGeochemicalDatabase internal ...
std::unordered_map< std::string, unsigned > _mineral_index
given a species name, return its index in the corresponding "info" std::vector
void createModel()
Fully populate the ModelGeochemicalDatabase.
DenseMatrix< Real > kin_stoichiometry
kin_stoichiometry(i, j) = stoichiometric coefficient for kinetic species "i" in terms of the basis sp...
Data structure for redox species.
std::vector< Real > kin_species_molecular_weight
all quantities have a molecular weight (g/mol)
void buildAllMinerals(const std::vector< std::string > &minerals)
If minerals = {"*"} then populate _mineral_index and _mineral_info with all relevant minerals This is...
std::vector< Real > kin_species_molecular_volume
all quantities have a molecular volume (cm^3/mol) (only nonzero for minerals, however) ...
const FileName & filename() const
Filename of database.
std::unordered_map< std::string, unsigned > eqm_species_index
eqm_species_index[name] = index of the equilibrium species (secondary aqueous species, redox couples in equilibrium with the aqueous solution, minerals in equilibrium with the aqueous solution, gases in equilibrium with the aqueous solution) within all ModelGeochemicalDatabase internal datastrcutres, with given name
void checkKineticSurfaceSpecies() const
Check that all kinetic surface species in the _kinetic_surface_species list have reactions that invol...
DenseMatrix< Real > redox_log10K
redox_log10K(i, j) = log10(equilibrium constant) for i^th redox species at the j^th temperature point...
std::vector< GeochemistryMineralSpecies > _mineral_info
a vector of all relevant species
Data structure for sorbing surface species.
std::vector< GeochemistrySurfaceSpecies > _kinetic_surface_info
a vector of all relevant species
std::map< std::string, Real > basis_species
ModelGeochemicalDatabase _model
The important datastructure built by this class.
std::unordered_map< std::string, unsigned > kin_species_index
kin_species_index[name] = index of the kinetic species, within all ModelGeochemicalDatabase internal ...
void checkGases() const
Check that all gases in the "gases" list have reactions that involve only the basis_species or second...
void buildBasis(const std::vector< std::string > &basis_species)
using the basis_species list, this method builds _basis_index and _basis_info
DenseMatrix< Real > eqm_stoichiometry
eqm_stoichiometry(i, j) = stoichiometric coefficient for equilibrium species "i" in terms of the basi...
std::vector< std::string > mineralSpeciesNames() const
Returns a list of all the names of the "mineral species" in the database.
const std::vector< Real > & getTemperatures() const
Get the temperature points that the equilibrium constant is defined at.
std::vector< std::string > surfaceSpeciesNames() const
Returns a list of all the names of the "surface species" in the database.
std::vector< Real > basis_species_radius
all quantities have an ionic radius (Angstrom) for computing activity (mineral radius = 0...
Data structure for mineral species.
const GeochemicalDatabaseReader db("database/moose_testdb.json", true, true, false)
void buildRedoxeInfo(std::vector< Real > &redox_e_stoichiometry, std::vector< Real > &redox_e_log10K)
Extract the stoichiometry and log10K for the _redox_e species.
PertinentGeochemicalSystem(const GeochemicalDatabaseReader &db, const std::vector< std::string > &basis_species, const std::vector< std::string > &minerals, const std::vector< std::string > &gases, const std::vector< std::string > &kinetic_minerals, const std::vector< std::string > &kinetic_redox, const std::vector< std::string > &kinetic_surface_species, const std::string &redox_ox, const std::string &redox_e)
std::vector< unsigned > surface_sorption_number
surface_sorption_number[j] = the index of the surface potential that should be used to modify the equ...
const ModelGeochemicalDatabase & modelGeochemicalDatabase() const
Return a reference to the ModelGeochemicalDatabase structure.
void checkKineticRedox() const
Check that all kinetic redox species in the _kinetic_redox list have reactions that involve only the ...
void addKineticRate(const KineticRateUserDescription &description)
Adds a rate description for kinetic_species.
std::unordered_map< std::string, unsigned > _kinetic_redox_index
given a species name, return its index in the corresponding "info" std::vector
const std::string name
Definition: Setup.h:20
void buildSecondarySpecies()
Extract all relevant "redox couples" and "secondary species" and "surface species" from the database...
std::map< std::string, Real > sorption_sites
std::map< std::string, GeochemistryBasisSpecies > getBasisSpecies(const std::vector< std::string > &names)
Get the basis (primary) species information.
std::unordered_map< std::string, unsigned > _secondary_index
given a species name, return its index in the corresponding "info" std::vector
std::unordered_map< std::string, unsigned > _kinetic_surface_index
given a species name, return its index in the corresponding "info" std::vector
std::vector< GeochemistryMineralSpecies > _kinetic_mineral_info
a vector of all relevant species
std::unordered_map< std::string, std::vector< Real > > gas_chi
Holds info on gas fugacity "chi" parameters.
DenseMatrix< Real > kin_log10K
kin_log10K(i, j) = log10(equilibrium constant for the i^th kinetic species at the j^th temperature po...
std::vector< bool > kin_species_transported
kin_species_transported[j] = true iff the j^th kinetic species is transported in reactive-transport s...
std::vector< Real > eqm_species_radius
all quantities have an ionic radius (Angstrom) for computing activity (mineral radius = 0...
void buildKineticSurface(const std::vector< std::string > &kinetic_surface)
using the kinetic_surface list, this method builds _kinetic_surface_index and _kinetic_surface_info ...
DenseMatrix< Real > eqm_log10K
eqm_log10K(i, j) = log10(equilibrium constant) for i^th equilibrium species at the j^th temperature p...
std::vector< GeochemistryRedoxSpecies > _kinetic_redox_info
a vector of all relevant species
void checkMinerals(const std::vector< GeochemistryMineralSpecies > &mineral_info) const
Check that all minerals in mineral_info have reactions that involve only the basis_species or seconda...
std::vector< Real > basis_species_molecular_weight
all quantities have a molecular weight (g)
std::map< std::string, Real > basis_species
std::vector< GeochemistryGasSpecies > _gas_info
a vector of all relevant species
Data structure designed to hold information related to sorption via surface complexation.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< bool > eqm_species_transported
eqm_species_transported[i] = true iff the i^th eqm species is transported in reactive-transport sims ...
static const std::string alpha
Definition: NS.h:134
Data structure for secondary equilibrium species.
std::vector< std::string > secondarySpeciesNames() const
Returns a list of all the names of the "secondary species" and "free electron" in the database...
std::vector< Real > eqm_species_molecular_volume
all quantities have a molecular volume (cm^3) (only nonzero for minerals, however) ...
std::vector< std::string > originalBasisNames() const
std::vector< Real > basis_species_charge
all quantities have a charge (mineral charge = 0, gas charge = 0, oxide charge = 0) ...
Holds a user-specified description of a kinetic rate.
void resize(const unsigned int new_m, const unsigned int new_n)
GeochemicalDatabaseReader _db
The database.
std::vector< bool > basis_species_gas
basis_species_gas[j] = true iff the j^th basis species is a gas
std::map< std::string, Real > basis_species
std::vector< bool > basis_species_mineral
basis_species_mineral[j] = true iff the j^th basis species is a mineral
std::vector< Real > eqm_species_charge
all quantities have a charge (mineral charge = 0, gas charge = 0, oxide charge = 0) ...
std::vector< std::string > kin_species_name
kin_species_name[j] = name of the j^th kinetic species
Data structure to hold all relevant information from the database file.
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
std::vector< bool > eqm_species_mineral
eqm_species_mineral[i] = true iff the i^th equilibrium species is a mineral
std::vector< Real > basis_species_molecular_volume
all quantities have a molecular volume (cm^3) (only nonzero for minerals, however) ...
std::vector< GeochemistryEquilibriumSpecies > _secondary_info
a vector of all relevant species
std::map< std::string, GeochemistryRedoxSpecies > getRedoxSpecies(const std::vector< std::string > &names)
Get the redox species (couples) information.
const std::string _redox_e
The name of the free electron involved in redox reactions.
std::unordered_map< std::string, unsigned > _gas_index
given a species name, return its index in the corresponding "info" std::vector
std::vector< std::string > promoting_species
const std::string _redox_ox
The name of the oxygen in all disequilibrium-redox equations, eg O2(aq), which must be a basis specie...
std::unordered_map< std::string, SurfaceComplexationInfo > surface_complexation_info
Holds info on surface complexation, if any, in the model.
std::unordered_map< std::string, unsigned > _kinetic_mineral_index
given a species name, return its index in the corresponding "info" std::vector
std::vector< std::string > eqm_species_name
eqm_species_name[i] = name of the i^th eqm species
for(PetscInt i=0;i< nvars;++i)
void buildMinerals(const std::vector< std::string > &minerals)
using the minerals list, this method builds _mineral_index and _mineral_info, unless minerals = {"*"}...
std::vector< std::string > basis_species_name
basis_species_name[j] = name of the j^th basis species
Class for reading geochemical reactions from a MOOSE geochemical database.
bool isRedoxSpecies(const std::string &name) const
std::vector< KineticRateDefinition > kin_rate
rates given to kinetic species.
bool isBasisSpecies(const std::string &name) const
Checks if species is of given type.