LCOV - code coverage report
Current view: top level - src/utils - GeochemicalDatabaseValidator.C (source / functions) Hit Total Coverage
Test: idaholab/moose geochemistry: 8601ad Lines: 172 172 100.0 %
Date: 2025-07-18 13:27:49 Functions: 20 20 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "GeochemicalDatabaseValidator.h"
      11             : 
      12             : #include "Conversion.h"
      13             : #include "MooseUtils.h"
      14             : #include "string"
      15             : 
      16         961 : GeochemicalDatabaseValidator::GeochemicalDatabaseValidator(const FileName filename,
      17         961 :                                                            const nlohmann::json & db)
      18         961 :   : _filename(filename), _root(db)
      19             : {
      20         961 : }
      21             : 
      22             : void
      23         961 : GeochemicalDatabaseValidator::validate()
      24             : {
      25             :   // Check that the database has a Header key (required)
      26         961 :   if (!_root.contains("Header"))
      27           2 :     mooseError("The MOOSE database ", _filename, " does not have a required \"Header\" field");
      28             : 
      29             :   // Check that temperatures are present
      30         959 :   checkHeaderField("temperatures");
      31             : 
      32             :   // The size of the temperature array is used to check all other arrays, which must
      33             :   // have the same number of elements
      34         958 :   _temperature_size = _root["Header"]["temperatures"].size();
      35             : 
      36             :   // Now check that the values are all Real
      37         958 :   checkHeaderArray("temperatures");
      38             : 
      39             :   // Check the pressure values are present and are Reals
      40         958 :   checkHeaderField("pressures");
      41         958 :   checkHeaderArray("pressures");
      42             : 
      43             :   // Check activity model coefficients
      44         958 :   checkHeaderField("activity model");
      45             : 
      46             :   // If the activity model is Debye-Huckel, test the coefficients are also present
      47             :   // and are Reals
      48         957 :   if (_root["Header"]["activity model"] == "debye-huckel")
      49             :   {
      50         956 :     checkHeaderField("adh");
      51         956 :     checkHeaderArray("adh");
      52             : 
      53         956 :     checkHeaderField("bdh");
      54         956 :     checkHeaderArray("bdh");
      55             : 
      56         956 :     checkHeaderField("bdot");
      57        1912 :     checkHeaderArray("bdot");
      58             :   }
      59             : 
      60             :   // Check fugacity model is specified
      61         957 :   checkHeaderField("fugacity model");
      62             : 
      63             :   // If there are neutral species, check their arrays of coefficients
      64         957 :   if (_root["Header"].contains("neutral species"))
      65        3820 :     for (auto & ns : _root["Header"]["neutral species"].items())
      66       11946 :       for (auto & coeffs : ns.value().items())
      67             :       {
      68       16256 :         if (coeffs.key() == "note")
      69         768 :           continue;
      70             : 
      71             :         // Check that there are a correct number of values and that they are real numbers
      72        7360 :         auto values = coeffs.value();
      73       14720 :         checkArraySize(values, "Header:neutral species:" + ns.key() + ":" + coeffs.key());
      74       22078 :         checkArrayValues(values, "Header:neutral species:" + ns.key() + ":" + coeffs.key());
      75             :       }
      76             : 
      77             :   // Check the element data
      78       27961 :   for (auto & el : _root["elements"].items())
      79       52102 :     checkElements(el.key());
      80             : 
      81             :   // Check the basis species data
      82       33011 :   for (auto & species : _root["basis species"].items())
      83       62202 :     checkBasisSpecies(species.key());
      84             : 
      85             :   // Check the secondary species data
      86         955 :   if (_root.contains("secondary species"))
      87      297282 :     for (auto & species : _root["secondary species"].items())
      88      590770 :       checkSecondarySpecies(species.key());
      89             : 
      90             :   // Check the mineral species data
      91         950 :   if (_root.contains("mineral species"))
      92      334487 :     for (auto & species : _root["mineral species"].items())
      93      665224 :       checkMineralSpecies(species.key());
      94             : 
      95             :   // Check the sorbing mineral species data
      96         949 :   if (_root.contains("sorbing minerals"))
      97        1690 :     for (auto & species : _root["sorbing minerals"].items())
      98        1748 :       checkSorbingMineralSpecies(species.key());
      99             : 
     100             :   // Check the gas species data
     101         949 :   if (_root.contains("gas species"))
     102        7990 :     for (auto & species : _root["gas species"].items())
     103       12920 :       checkGasSpecies(species.key());
     104             : 
     105             :   // Check the redox couple data
     106         949 :   if (_root.contains("redox couples"))
     107       29699 :     for (auto & species : _root["redox couples"].items())
     108       55614 :       checkRedoxSpecies(species.key());
     109             : 
     110             :   // Check the oxide species data
     111         949 :   if (_root.contains("oxides"))
     112       23456 :     for (auto & species : _root["oxides"].items())
     113       43848 :       checkOxideSpecies(species.key());
     114             : 
     115             :   // Check the surface species data
     116         949 :   if (_root.contains("surface species"))
     117       11204 :     for (auto & species : _root["surface species"].items())
     118       20776 :       checkSurfaceSpecies(species.key());
     119         949 : }
     120             : 
     121             : bool
     122     9396868 : GeochemicalDatabaseValidator::isValueReal(const nlohmann::json & value) const
     123             : {
     124             :   if (value.is_number())
     125             :     return true;
     126             :   try
     127             :   {
     128      134179 :     MooseUtils::convert<Real>(value, true);
     129             :   }
     130           4 :   catch (const std::invalid_argument & err)
     131             :   {
     132             :     return false;
     133           4 :   }
     134             : 
     135      134175 :   return true;
     136             : }
     137             : 
     138             : void
     139       12143 : GeochemicalDatabaseValidator::checkArrayValues(const nlohmann::json & array,
     140             :                                                const std::string field) const
     141             : {
     142             :   // Check each value in the array can be successfully converted to a Real
     143      121429 :   for (auto & item : array.items())
     144       97144 :     if (!isValueReal(item.value()))
     145           1 :       mooseError("Array value ",
     146           2 :                  nlohmann::to_string(item.value()),
     147             :                  " in the ",
     148             :                  field,
     149             :                  " field of ",
     150           1 :                  _filename,
     151             :                  " cannot be converted to Real");
     152       12142 : }
     153             : 
     154             : void
     155      664843 : GeochemicalDatabaseValidator::checkArrayValues(const std::string type,
     156             :                                                const std::string species,
     157             :                                                const std::string field) const
     158             : {
     159             :   // Check each value in the array can be successfully converted to a Real
     160     6643258 :   for (auto & item : _root[type][species][field].items())
     161     5313573 :     if (!isValueReal(item.value()))
     162           1 :       mooseError("Array value ",
     163           2 :                  nlohmann::to_string(item.value()),
     164             :                  " in the ",
     165             :                  field,
     166             :                  " field of ",
     167             :                  type,
     168             :                  " ",
     169             :                  species,
     170             :                  " in ",
     171           1 :                  _filename,
     172             :                  " cannot be converted to Real");
     173      664842 : }
     174             : 
     175             : void
     176       12144 : GeochemicalDatabaseValidator::checkArraySize(const nlohmann::json & array,
     177             :                                              const std::string field) const
     178             : {
     179       12144 :   if (array.size() != _temperature_size)
     180           1 :     mooseError("The number of values in the ",
     181             :                field,
     182             :                " field of ",
     183           1 :                _filename,
     184             :                " is not equal to the number of temperature values");
     185       12143 : }
     186             : 
     187             : void
     188      662260 : GeochemicalDatabaseValidator::checkArraySize(const std::string type,
     189             :                                              const std::string species,
     190             :                                              const std::string field) const
     191             : {
     192      662260 :   if (_root[type][species][field].size() != _temperature_size)
     193           1 :     mooseError("The number of values in the ",
     194             :                field,
     195             :                " field of ",
     196             :                type,
     197             :                " ",
     198             :                species,
     199             :                " in ",
     200           1 :                _filename,
     201             :                " is not equal to the number of temperature values");
     202      662259 : }
     203             : 
     204             : void
     205        6700 : GeochemicalDatabaseValidator::checkHeaderField(const std::string field) const
     206             : {
     207        6700 :   if (!_root["Header"].contains(field))
     208           2 :     mooseError(
     209           2 :         "The MOOSE database ", _filename, " does not have a required \"Header:", field, "\" field");
     210        6698 : }
     211             : 
     212             : void
     213        4784 : GeochemicalDatabaseValidator::checkHeaderArray(const std::string field) const
     214             : {
     215             :   // Check that all values are real numbers and of size equal to the temperature
     216        4784 :   checkArraySize(_root["Header"][field], "Header:" + field);
     217        4784 :   checkArrayValues(_root["Header"][field], "Header:" + field);
     218        4784 : }
     219             : 
     220             : void
     221     1839582 : GeochemicalDatabaseValidator::checkSpeciesValue(const std::string type,
     222             :                                                 const std::string species,
     223             :                                                 const std::string field) const
     224             : {
     225     1839582 :   if (!_root[type].contains(species) || !_root[type][species].contains(field))
     226           1 :     mooseError("The ", type, " ", species, " in ", _filename, " does not have a ", field);
     227             : 
     228             :   // The field value should be a real number
     229     1839581 :   if (!isValueReal(_root[type][species][field]))
     230           1 :     mooseError(field,
     231             :                " value ",
     232           1 :                nlohmann::to_string(_root[type][species][field]),
     233             :                " of the ",
     234             :                type,
     235             :                " ",
     236             :                species,
     237             :                " in ",
     238           1 :                _filename,
     239             :                " cannot be converted to Real");
     240     1839580 : }
     241             : 
     242             : void
     243      726549 : GeochemicalDatabaseValidator::checkSpeciesWeightValue(const std::string type,
     244             :                                                       const std::string species,
     245             :                                                       const std::string field) const
     246             : {
     247      726549 :   if (!_root[type].contains(species) || !_root[type][species].contains(field))
     248           1 :     mooseError("The ", type, " ", species, " in ", _filename, " does not have a ", field);
     249             : 
     250             :   // Each weight value for each constituent should be a real number
     251     3599665 :   for (auto & item : _root[type][species][field].items())
     252     2146570 :     if (!isValueReal(item.value()))
     253           1 :       mooseError("Weight value ",
     254           2 :                  nlohmann::to_string(item.value()),
     255             :                  " of constituent ",
     256           1 :                  item.key(),
     257             :                  " of the ",
     258             :                  type,
     259             :                  " ",
     260             :                  species,
     261             :                  " in ",
     262           1 :                  _filename,
     263             :                  " cannot be converted to Real");
     264      726547 : }
     265             : 
     266             : void
     267       26051 : GeochemicalDatabaseValidator::checkElements(const std::string element) const
     268             : {
     269             :   // Check molecular weight can be converted to a Real
     270       52102 :   checkSpeciesValue("elements", element, "molecular weight");
     271       26051 : }
     272             : 
     273             : void
     274       31101 : GeochemicalDatabaseValidator::checkBasisSpecies(const std::string species) const
     275             : {
     276             :   // Check molecular weight can be converted to a Real
     277       62202 :   checkSpeciesValue("basis species", species, "molecular weight");
     278             : 
     279             :   // Check charge can be converted to a Real
     280       62202 :   checkSpeciesValue("basis species", species, "charge");
     281             : 
     282             :   // Check ionic radius can be converted to a Real
     283       62202 :   checkSpeciesValue("basis species", species, "radius");
     284             : 
     285             :   // Check element weights can be converted to a Real
     286       62202 :   checkSpeciesWeightValue("basis species", species, "elements");
     287       31101 : }
     288             : 
     289             : void
     290      295385 : GeochemicalDatabaseValidator::checkSecondarySpecies(const std::string species) const
     291             : {
     292             :   // Check molecular weight can be converted to a Real
     293      590770 :   checkSpeciesValue("secondary species", species, "molecular weight");
     294             : 
     295             :   // Check charge can be converted to a Real
     296      590771 :   checkSpeciesValue("secondary species", species, "charge");
     297             : 
     298             :   // Check ionic radius can be converted to a Real
     299      590769 :   checkSpeciesValue("secondary species", species, "radius");
     300             : 
     301             :   // Check basis species weights can be converted to a Real
     302      590768 :   checkSpeciesWeightValue("secondary species", species, "species");
     303             : 
     304             :   // Check the number of logk values and whether they can be converted to a Real
     305      590763 :   checkArraySize("secondary species", species, "logk");
     306      590760 :   checkArrayValues("secondary species", species, "logk");
     307      295380 : }
     308             : 
     309             : void
     310      332612 : GeochemicalDatabaseValidator::checkMineralSpecies(const std::string species) const
     311             : {
     312             :   // Check molecular weight can be converted to a Real
     313      665224 :   checkSpeciesValue("mineral species", species, "molecular weight");
     314             : 
     315             :   // Check molar volume can be converted to a Real
     316      665224 :   checkSpeciesValue("mineral species", species, "molar volume");
     317             : 
     318             :   // Check basis species weights can be converted to a Real
     319      665224 :   checkSpeciesWeightValue("mineral species", species, "species");
     320             : 
     321             :   // Check the number of logk values and whether they can be converted to a Real
     322      665224 :   checkArraySize("mineral species", species, "logk");
     323      665225 :   checkArrayValues("mineral species", species, "logk");
     324      332611 : }
     325             : 
     326             : void
     327         874 : GeochemicalDatabaseValidator::checkSorbingMineralSpecies(const std::string species) const
     328             : {
     329             :   // Check surface area can be converted to a Real
     330        1748 :   checkSpeciesValue("sorbing minerals", species, "surface area");
     331             : 
     332             :   // Check sorbing site weights can be converted to a Real
     333        1748 :   checkSpeciesWeightValue("sorbing minerals", species, "sorbing sites");
     334         874 : }
     335             : 
     336             : void
     337        6460 : GeochemicalDatabaseValidator::checkGasSpecies(const std::string species) const
     338             : {
     339             :   // Check molecular weight can be converted to a Real
     340       12920 :   checkSpeciesValue("gas species", species, "molecular weight");
     341             : 
     342             :   // Check basis species weights can be converted to a Real
     343       12920 :   checkSpeciesWeightValue("gas species", species, "species");
     344             : 
     345             :   // Check the number of logk values and whether they can be converted to a Real
     346       12920 :   checkArraySize("gas species", species, "logk");
     347       12920 :   checkArrayValues("gas species", species, "logk");
     348             : 
     349             :   // Check optional fugacity coefficients
     350        6460 :   if (_root["gas species"][species].contains("chi"))
     351        5168 :     checkArrayValues("gas species", species, "chi");
     352             : 
     353        6460 :   if (_root["gas species"][species].contains("Pcrit"))
     354        9746 :     checkSpeciesValue("gas species", species, "Pcrit");
     355             : 
     356        6460 :   if (_root["gas species"][species].contains("Tcrit"))
     357        9746 :     checkSpeciesValue("gas species", species, "Tcrit");
     358             : 
     359        6460 :   if (_root["gas species"][species].contains("omega"))
     360        9746 :     checkSpeciesValue("gas species", species, "omega");
     361        6460 : }
     362             : 
     363             : void
     364       27807 : GeochemicalDatabaseValidator::checkRedoxSpecies(const std::string species) const
     365             : {
     366             :   // Check molecular weight can be converted to a Real
     367       55614 :   checkSpeciesValue("redox couples", species, "molecular weight");
     368             : 
     369             :   // Check charge can be converted to a Real
     370       55614 :   checkSpeciesValue("redox couples", species, "charge");
     371             : 
     372             :   // Check ionic radius can be converted to a Real
     373       55614 :   checkSpeciesValue("redox couples", species, "radius");
     374             : 
     375             :   // Check basis species weights can be converted to a Real
     376       55614 :   checkSpeciesWeightValue("redox couples", species, "species");
     377             : 
     378             :   // Check the number of logk values and whether they can be converted to a Real
     379       55614 :   checkArraySize("redox couples", species, "logk");
     380       55614 :   checkArrayValues("redox couples", species, "logk");
     381       27807 : }
     382             : 
     383             : void
     384       21924 : GeochemicalDatabaseValidator::checkOxideSpecies(const std::string species) const
     385             : {
     386             :   // Check molecular weight can be converted to a Real
     387       43848 :   checkSpeciesValue("oxides", species, "molecular weight");
     388             : 
     389             :   // Check basis species weights can be converted to a Real
     390       43848 :   checkSpeciesWeightValue("oxides", species, "species");
     391       21924 : }
     392             : 
     393             : void
     394       10388 : GeochemicalDatabaseValidator::checkSurfaceSpecies(const std::string species) const
     395             : {
     396             :   // Check molecular weight can be converted to a Real
     397       20776 :   checkSpeciesValue("surface species", species, "molecular weight");
     398             : 
     399             :   // Check charge can be converted to a Real
     400       20776 :   checkSpeciesValue("surface species", species, "charge");
     401             : 
     402             :   // Check basis species weights can be converted to a Real
     403       20776 :   checkSpeciesWeightValue("surface species", species, "species");
     404             : 
     405             :   // Check equilibrium constants can be converted to a Real
     406       20776 :   checkSpeciesValue("surface species", species, "log K");
     407       20776 :   checkSpeciesValue("surface species", species, "dlogK/dT");
     408       10388 : }

Generated by: LCOV version 1.14