LCOV - code coverage report
Current view: top level - src/utils - GeochemistrySpeciesSwapper.C (source / functions) Hit Total Coverage
Test: idaholab/moose geochemistry: 2bf808 Lines: 179 179 100.0 %
Date: 2025-07-17 01:29:12 Functions: 11 11 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 "GeochemistrySpeciesSwapper.h"
      11             : 
      12        1615 : GeochemistrySpeciesSwapper::GeochemistrySpeciesSwapper(unsigned basis_size, Real stoi_tol)
      13        1615 :   : _stoi_tol(stoi_tol),
      14        1615 :     _swap_matrix(basis_size, basis_size),
      15        1615 :     _true_swap_matrix(basis_size, basis_size),
      16        1615 :     _inv_swap_matrix(basis_size, basis_size),
      17        1615 :     _swap_sigma(basis_size),
      18        1615 :     _swap_U(basis_size, basis_size),
      19        1615 :     _swap_VT(basis_size, basis_size)
      20             : {
      21        1615 : }
      22             : 
      23             : void
      24         845 : GeochemistrySpeciesSwapper::checkSwap(const ModelGeochemicalDatabase & mgd,
      25             :                                       const std::string & replace_this,
      26             :                                       const std::string & with_this)
      27             : {
      28         845 :   if (replace_this == "H2O")
      29           1 :     mooseError("Cannot remove H2O from the basis");
      30             :   if (mgd.basis_species_index.count(replace_this) == 0)
      31           3 :     mooseError(replace_this + " is not in the basis, so cannot be removed from the basis");
      32             :   if (mgd.eqm_species_index.count(with_this) == 0)
      33           1 :     mooseError(with_this + " is not an equilibrium species, so cannot be "
      34             :                            "removed from the equilibrium species list");
      35             : 
      36         840 :   checkSwap(mgd, mgd.basis_species_index.at(replace_this), mgd.eqm_species_index.at(with_this));
      37         835 : }
      38             : 
      39             : void
      40        1249 : GeochemistrySpeciesSwapper::checkSwap(const ModelGeochemicalDatabase & mgd,
      41             :                                       unsigned basis_index_to_replace,
      42             :                                       unsigned eqm_index_to_insert)
      43             : {
      44        1249 :   const unsigned num_cols = mgd.basis_species_index.size();
      45        1249 :   const unsigned num_rows = mgd.eqm_species_index.size();
      46        1249 :   if (basis_index_to_replace == 0)
      47           1 :     mooseError("Cannot remove H2O from the basis");
      48        1248 :   if (basis_index_to_replace >= num_cols)
      49           1 :     mooseError(basis_index_to_replace, " exceeds the number of basis species in the problem");
      50        1247 :   if (eqm_index_to_insert >= num_rows)
      51           1 :     mooseError(eqm_index_to_insert, " exceeds the number of equilibrium species in the problem");
      52        1246 :   if (mgd.surface_sorption_related[eqm_index_to_insert])
      53           1 :     mooseError(
      54             :         "Equilibrium species ",
      55             :         mgd.eqm_species_name[eqm_index_to_insert],
      56             :         " is involved in surface sorption so cannot be swapped into the basis.  If this is truly "
      57             :         "necessary, code enhancements will need to be made including: recording whether basis "
      58             :         "species are involved in surface sorption, including them in the surface-potential "
      59             :         "calculations, and carefully swapping surface-potential-modified equilibrium constants");
      60             : 
      61        1245 :   constructInverseMatrix(mgd, basis_index_to_replace, eqm_index_to_insert);
      62        1241 : }
      63             : 
      64             : void
      65        1245 : GeochemistrySpeciesSwapper::constructInverseMatrix(const ModelGeochemicalDatabase & mgd,
      66             :                                                    unsigned basis_index_to_replace,
      67             :                                                    unsigned eqm_index_to_insert)
      68             : {
      69        1245 :   const unsigned num_cols = mgd.basis_species_index.size();
      70             : 
      71        1245 :   if (_swap_matrix.n() != num_cols)
      72           1 :     mooseError("GeochemistrySpeciesSwapper constructed with incorrect "
      73             :                "basis_species size");
      74             : 
      75             :   // This is a private method, called from checkSwap, so we know that all the inputs have been
      76             :   // sanity-checked.  The only way the swap could be invalid is that the swap matrix is not
      77             :   // invertible.  This could be due to the solve algorithm attempting to perform an invalid swap, so
      78             :   // only mooseExceptions are thrown below, instead of mooseError, to allow the solve algorithm a
      79             :   // chance to attempt another swap.
      80             : 
      81             :   // construct the swap matrix.  new_basis = swap_matrix * old_basis
      82             :   // We shove the equilibrium species into the "slot" of the basis species it is
      83             :   // replacing
      84        1244 :   _swap_matrix.zero();
      85       12744 :   for (unsigned i = 0; i < num_cols; ++i)
      86       11500 :     _swap_matrix(i, i) = 1.0;
      87       12744 :   for (unsigned i = 0; i < num_cols; ++i)
      88       11500 :     _swap_matrix(basis_index_to_replace, i) = mgd.eqm_stoichiometry(eqm_index_to_insert, i);
      89             : 
      90             :   // record the swap matrix since the SVD trashes it
      91        1244 :   _true_swap_matrix = _swap_matrix;
      92             : 
      93             :   // In order to invert _swap_matrix, perform the SVD: A = U * D * VT
      94             :   // Although every matrix has a SVD, this process might fail due to numerical errors.  Therefore
      95             :   // the following could be wrapped in a try-catch block, but I have never been able to trigger a
      96             :   // failure since _swap_matrix is always so well-formed due to all the checks on the database while
      97             :   // parsing it
      98        1244 :   _swap_matrix.svd(_swap_sigma, _swap_U, _swap_VT);
      99        1244 :   const Real l1norm = _swap_sigma.l1_norm();
     100       12741 :   for (unsigned i = 0; i < num_cols; ++i)
     101       11500 :     if (std::abs(_swap_sigma(i) / l1norm) < _stoi_tol)
     102           3 :       mooseException("Matrix is not invertible, which signals an invalid basis swap");
     103             : 
     104             :   // Find the inverse, which is VT^-1 * D^-1 * U^-1 = V * D^-1 * UT
     105       12724 :   for (unsigned i = 0; i < num_cols; ++i)
     106       11483 :     _swap_U.scale_column(i, 1.0 / _swap_sigma(i)); // (scale the columns of U)^T = D^-1 * UT
     107       12724 :   for (unsigned i = 0; i < num_cols; ++i)
     108      140354 :     for (unsigned j = 0; j < num_cols; ++j)
     109      128871 :       _inv_swap_matrix(i, j) = _swap_VT(j, i);        // _inv_swap_matrix = V
     110        1241 :   _inv_swap_matrix.right_multiply_transpose(_swap_U); // V * (scale the columns of U)^T
     111        1241 : }
     112             : 
     113             : void
     114         839 : GeochemistrySpeciesSwapper::performSwap(ModelGeochemicalDatabase & mgd,
     115             :                                         const std::string & replace_this,
     116             :                                         const std::string & with_this)
     117             : {
     118             :   // check the swap is valid (if not then mooseError or mooseException)
     119             :   // and if it's valid, create the inverse swap matrix
     120         839 :   checkSwap(mgd, replace_this, with_this);
     121             : 
     122             :   // perform the swap inside the MGD datastructure
     123         835 :   alterMGD(mgd, mgd.basis_species_index.at(replace_this), mgd.eqm_species_index.at(with_this));
     124         835 : }
     125             : 
     126             : void
     127         406 : GeochemistrySpeciesSwapper::performSwap(ModelGeochemicalDatabase & mgd,
     128             :                                         unsigned basis_index_to_replace,
     129             :                                         unsigned eqm_index_to_insert)
     130             : {
     131             :   // check the swap is valid (if not then mooseError or mooseException)
     132             :   // and if it's valid, create the inverse swap matrix
     133         406 :   checkSwap(mgd, basis_index_to_replace, eqm_index_to_insert);
     134             : 
     135             :   // perform the swap inside the MGD datastructure
     136         406 :   alterMGD(mgd, basis_index_to_replace, eqm_index_to_insert);
     137         406 : }
     138             : 
     139             : void
     140           3 : GeochemistrySpeciesSwapper::performSwap(ModelGeochemicalDatabase & mgd,
     141             :                                         DenseVector<Real> & bulk_composition,
     142             :                                         const std::string & replace_this,
     143             :                                         const std::string & with_this)
     144             : {
     145           3 :   performSwap(mgd, replace_this, with_this);
     146             :   // compute the bulk composition expressed in the new basis
     147           3 :   alterBulkComposition(mgd.basis_species_index.size(), bulk_composition);
     148           2 : }
     149             : 
     150             : void
     151         406 : GeochemistrySpeciesSwapper::performSwap(ModelGeochemicalDatabase & mgd,
     152             :                                         DenseVector<Real> & bulk_composition,
     153             :                                         unsigned basis_index_to_replace,
     154             :                                         unsigned eqm_index_to_insert)
     155             : {
     156         406 :   performSwap(mgd, basis_index_to_replace, eqm_index_to_insert);
     157             :   // compute the bulk composition expressed in the new basis
     158         406 :   alterBulkComposition(mgd.basis_species_index.size(), bulk_composition);
     159         406 : }
     160             : 
     161             : void
     162        1241 : GeochemistrySpeciesSwapper::alterMGD(ModelGeochemicalDatabase & mgd,
     163             :                                      unsigned basis_index_to_replace,
     164             :                                      unsigned eqm_index_to_insert)
     165             : {
     166        1241 :   const unsigned num_cols = mgd.basis_species_index.size();
     167        1241 :   const unsigned num_rows = mgd.eqm_species_index.size();
     168             :   const unsigned num_temperatures = mgd.eqm_log10K.n();
     169             :   const unsigned num_redox = mgd.redox_stoichiometry.m();
     170             :   const unsigned kin_rows = mgd.kin_stoichiometry.m();
     171        1241 :   const unsigned num_rate = mgd.kin_rate.size();
     172        1241 :   const unsigned pro_ind_eqm =
     173        1241 :       num_cols + eqm_index_to_insert; // index of the (eqm species that is being swapped into the
     174             :                                       // basis) in the kinetic-rate promoting_indices vectors
     175             : 
     176             :   // change names
     177        1241 :   const std::string basis_name = mgd.basis_species_name[basis_index_to_replace];
     178        1241 :   const std::string eqm_name = mgd.eqm_species_name[eqm_index_to_insert];
     179             :   mgd.basis_species_name[basis_index_to_replace] = eqm_name;
     180             :   mgd.basis_species_index.erase(basis_name);
     181        1241 :   mgd.basis_species_index[eqm_name] = basis_index_to_replace;
     182             :   mgd.eqm_species_name[eqm_index_to_insert] = basis_name;
     183             :   mgd.eqm_species_index.erase(eqm_name);
     184        1241 :   mgd.eqm_species_index[basis_name] = eqm_index_to_insert;
     185             : 
     186             :   // flag indicating whether the redox_lhs is the equilibrium species that is being put into the
     187             :   // basis
     188        1241 :   const bool redox_lhs_going_to_basis = (eqm_name == mgd.redox_lhs);
     189             :   mooseAssert(
     190             :       !redox_lhs_going_to_basis,
     191             :       "Should not be able to swap the redox LHS into the basis"); // At present, it is impossible to
     192             :                                                                   // swap redox_lhs into the basis,
     193             :                                                                   // since redox_lhs cannot be an
     194             :                                                                   // equilibrium species, because it
     195             :                                                                   // is explicitly excluded in
     196             :                                                                   // PertinentGeochemicalSystem when
     197             :                                                                   // building the secondary species.
     198             :   /*
     199             :   In past versions of the code, it was possible to swap the redox LHS into the basis.
     200             :   If you want to make it possible again then mgd needs to be modified as follows
     201             :   if (redox_lhs_going_to_basis)
     202             :   {
     203             :     // need to make the redox_lhs equal to the species that is being taken from the basis
     204             :     mgd.redox_lhs = basis_name;
     205             :     for (unsigned red = 0; red < num_redox; ++red)
     206             :     {
     207             :       const Real alpha = mgd.eqm_stoichiometry(
     208             :           eqm_index_to_insert, basis_index_to_replace); // must be nonzero due to valid swap
     209             :       const Real alpha_r = mgd.redox_stoichiometry(red, basis_index_to_replace);
     210             :       mooseAssert(alpha != alpha_r,
     211             :                   "Cannot swap equilibrium species "
     212             :                       << eqm_name << " with basis species " << basis_name
     213             :                       << " because a redox reaction would result in 0 <-> 1");
     214             :       const Real coef = 1.0 / (alpha - alpha_r);
     215             :       for (unsigned i = 0; i < num_cols; ++i)
     216             :         mgd.redox_stoichiometry(red, i) = coef * (mgd.redox_stoichiometry(red, i) -
     217             :                                                   mgd.eqm_stoichiometry(eqm_index_to_insert, i));
     218             :       mgd.redox_stoichiometry(red, basis_index_to_replace) = 0.0;
     219             :       for (unsigned t = 0; t < num_temperatures; ++t)
     220             :         mgd.redox_log10K(red, t) =
     221             :             coef * (mgd.redox_log10K(red, t) - mgd.eqm_log10K(eqm_index_to_insert, t));
     222             :     }
     223             :   }
     224             :   */
     225             : 
     226             :   // record that the swap has occurred
     227        1241 :   if (mgd.swap_to_original_basis.n() == 0)
     228         542 :     mgd.swap_to_original_basis = _true_swap_matrix;
     229             :   else
     230         699 :     mgd.swap_to_original_basis.left_multiply(_true_swap_matrix);
     231        1241 :   mgd.have_swapped_out_of_basis.push_back(basis_index_to_replace);
     232        1241 :   mgd.have_swapped_into_basis.push_back(eqm_index_to_insert);
     233             : 
     234             :   // express stoichiometry in new basis
     235       12724 :   for (unsigned i = 0; i < num_cols; ++i)
     236       11483 :     mgd.eqm_stoichiometry(eqm_index_to_insert, i) = 0.0;
     237        1241 :   mgd.eqm_stoichiometry(eqm_index_to_insert, basis_index_to_replace) =
     238             :       1.0; // 1 * replace_this <-> 1 * replace_this
     239        1241 :   mgd.eqm_stoichiometry.right_multiply(_inv_swap_matrix);
     240       64149 :   for (unsigned i = 0; i < num_rows; ++i)
     241      857623 :     for (unsigned j = 0; j < num_cols; ++j)
     242      794715 :       if (std::abs(mgd.eqm_stoichiometry(i, j)) < _stoi_tol)
     243      535232 :         mgd.eqm_stoichiometry(i, j) = 0.0;
     244             : 
     245             :   // if the redox_lhs is not changed by the swap, alter the redox stoichiometry
     246        1241 :   if (!redox_lhs_going_to_basis)
     247        1241 :     mgd.redox_stoichiometry.right_multiply(_inv_swap_matrix);
     248        2036 :   for (unsigned red = 0; red < num_redox; ++red)
     249       11721 :     for (unsigned j = 0; j < num_cols; ++j)
     250       10926 :       if (std::abs(mgd.redox_stoichiometry(red, j)) < _stoi_tol)
     251        7655 :         mgd.redox_stoichiometry(red, j) = 0.0;
     252             : 
     253             :   // express kinetic stoichiometry in new basis
     254        1241 :   mgd.kin_stoichiometry.right_multiply(_inv_swap_matrix);
     255        1392 :   for (unsigned i = 0; i < kin_rows; ++i)
     256        1614 :     for (unsigned j = 0; j < num_cols; ++j)
     257        1463 :       if (std::abs(mgd.kin_stoichiometry(i, j)) < _stoi_tol)
     258         937 :         mgd.kin_stoichiometry(i, j) = 0.0;
     259             : 
     260             :   // alter equilibrium constants for each temperature point
     261       11169 :   for (unsigned t = 0; t < num_temperatures; ++t)
     262             :   {
     263        9928 :     const Real log10k_eqm_species = mgd.eqm_log10K(eqm_index_to_insert, t);
     264        9928 :     mgd.eqm_log10K(eqm_index_to_insert, t) =
     265             :         0.0; // 1 * replace_this <-> 1 * replace_this with log10K = 0
     266      513192 :     for (unsigned row = 0; row < num_rows; ++row)
     267      503264 :       mgd.eqm_log10K(row, t) -=
     268      503264 :           mgd.eqm_stoichiometry(row, basis_index_to_replace) * log10k_eqm_species;
     269             : 
     270             :     // similar for the redox equations
     271        9928 :     if (!redox_lhs_going_to_basis)
     272       16288 :       for (unsigned red = 0; red < num_redox; ++red)
     273        6360 :         mgd.redox_log10K(red, t) -=
     274        6360 :             mgd.redox_stoichiometry(red, basis_index_to_replace) * log10k_eqm_species;
     275             : 
     276             :     // similar for kinetic
     277       11136 :     for (unsigned kin = 0; kin < kin_rows; ++kin)
     278        1208 :       mgd.kin_log10K(kin, t) -=
     279        1208 :           mgd.kin_stoichiometry(kin, basis_index_to_replace) * log10k_eqm_species;
     280             :   }
     281             : 
     282             :   // swap the "is mineral" information
     283             :   const bool basis_was_mineral = mgd.basis_species_mineral[basis_index_to_replace];
     284        1241 :   mgd.basis_species_mineral[basis_index_to_replace] = mgd.eqm_species_mineral[eqm_index_to_insert];
     285        1241 :   mgd.eqm_species_mineral[eqm_index_to_insert] = basis_was_mineral;
     286             : 
     287             :   // swap the "is gas" information
     288             :   const bool basis_was_gas = mgd.basis_species_gas[basis_index_to_replace];
     289        1241 :   mgd.basis_species_gas[basis_index_to_replace] = mgd.eqm_species_gas[eqm_index_to_insert];
     290        1241 :   mgd.eqm_species_gas[eqm_index_to_insert] = basis_was_gas;
     291             : 
     292             :   // swap the "is transported" information
     293             :   const bool basis_was_transported = mgd.basis_species_transported[basis_index_to_replace];
     294        2482 :   mgd.basis_species_transported[basis_index_to_replace] =
     295        1241 :       mgd.eqm_species_transported[eqm_index_to_insert];
     296        1241 :   mgd.eqm_species_transported[eqm_index_to_insert] = basis_was_transported;
     297             : 
     298             :   // swap the "charge" information
     299        1241 :   const Real basis_charge = mgd.basis_species_charge[basis_index_to_replace];
     300        1241 :   mgd.basis_species_charge[basis_index_to_replace] = mgd.eqm_species_charge[eqm_index_to_insert];
     301        1241 :   mgd.eqm_species_charge[eqm_index_to_insert] = basis_charge;
     302             : 
     303             :   // swap the "radius" information
     304        1241 :   const Real basis_radius = mgd.basis_species_radius[basis_index_to_replace];
     305        1241 :   mgd.basis_species_radius[basis_index_to_replace] = mgd.eqm_species_radius[eqm_index_to_insert];
     306        1241 :   mgd.eqm_species_radius[eqm_index_to_insert] = basis_radius;
     307             : 
     308             :   // swap the "molecular weight" information
     309        1241 :   const Real basis_molecular_weight = mgd.basis_species_molecular_weight[basis_index_to_replace];
     310        1241 :   mgd.basis_species_molecular_weight[basis_index_to_replace] =
     311             :       mgd.eqm_species_molecular_weight[eqm_index_to_insert];
     312        1241 :   mgd.eqm_species_molecular_weight[eqm_index_to_insert] = basis_molecular_weight;
     313             : 
     314             :   // swap the "molecular volume" information
     315        1241 :   const Real basis_molecular_volume = mgd.basis_species_molecular_volume[basis_index_to_replace];
     316        1241 :   mgd.basis_species_molecular_volume[basis_index_to_replace] =
     317             :       mgd.eqm_species_molecular_volume[eqm_index_to_insert];
     318        1241 :   mgd.eqm_species_molecular_volume[eqm_index_to_insert] = basis_molecular_volume;
     319             : 
     320             :   // No need to swap surface_complexation_info or gas_chi because they are not
     321             :   // bound to basis or eqm species
     322             : 
     323             :   // swap promoting indices in the rates
     324        1385 :   for (unsigned r = 0; r < num_rate; ++r)
     325             :   {
     326             :     const Real promoting_index_of_original_basis =
     327         144 :         mgd.kin_rate[r].promoting_indices[basis_index_to_replace];
     328         144 :     mgd.kin_rate[r].promoting_indices[basis_index_to_replace] =
     329         144 :         mgd.kin_rate[r].promoting_indices[pro_ind_eqm];
     330         144 :     mgd.kin_rate[r].promoting_indices[pro_ind_eqm] = promoting_index_of_original_basis;
     331             :     const Real promoting_monod_index_of_original_basis =
     332         144 :         mgd.kin_rate[r].promoting_monod_indices[basis_index_to_replace];
     333         144 :     mgd.kin_rate[r].promoting_monod_indices[basis_index_to_replace] =
     334             :         mgd.kin_rate[r].promoting_monod_indices[pro_ind_eqm];
     335         144 :     mgd.kin_rate[r].promoting_monod_indices[pro_ind_eqm] = promoting_monod_index_of_original_basis;
     336             :     const Real promoting_half_saturation_of_original_basis =
     337         144 :         mgd.kin_rate[r].promoting_half_saturation[basis_index_to_replace];
     338         144 :     mgd.kin_rate[r].promoting_half_saturation[basis_index_to_replace] =
     339             :         mgd.kin_rate[r].promoting_half_saturation[pro_ind_eqm];
     340         144 :     mgd.kin_rate[r].promoting_half_saturation[pro_ind_eqm] =
     341             :         promoting_half_saturation_of_original_basis;
     342             :   }
     343             : 
     344             :   // swap progeny, if needed
     345        1385 :   for (unsigned r = 0; r < num_rate; ++r)
     346             :   {
     347         144 :     if (mgd.kin_rate[r].progeny_index == pro_ind_eqm)
     348           1 :       mgd.kin_rate[r].progeny_index = basis_index_to_replace;
     349         143 :     else if (mgd.kin_rate[r].progeny_index == basis_index_to_replace)
     350           1 :       mgd.kin_rate[r].progeny_index = pro_ind_eqm;
     351             :   }
     352        1241 : }
     353             : 
     354             : void
     355         409 : GeochemistrySpeciesSwapper::alterBulkComposition(unsigned basis_size,
     356             :                                                  DenseVector<Real> & bulk_composition) const
     357             : {
     358         409 :   if (bulk_composition.size() != basis_size)
     359           1 :     mooseError("GeochemistrySpeciesSwapper: bulk_composition has size ",
     360           1 :                bulk_composition.size(),
     361             :                " which differs from the basis size");
     362         408 :   DenseVector<Real> result;
     363         408 :   _inv_swap_matrix.vector_mult_transpose(result, bulk_composition);
     364             :   bulk_composition = result;
     365         408 : }
     366             : 
     367             : bool
     368          80 : GeochemistrySpeciesSwapper::findBestEqmSwap(unsigned basis_ind,
     369             :                                             const ModelGeochemicalDatabase & mgd,
     370             :                                             const std::vector<Real> & eqm_molality,
     371             :                                             bool minerals_allowed,
     372             :                                             bool gas_allowed,
     373             :                                             bool sorption_allowed,
     374             :                                             unsigned & best_eqm_species) const
     375             : {
     376          80 :   const unsigned num_eqm = mgd.eqm_species_name.size();
     377          80 :   if (eqm_molality.size() != num_eqm)
     378           1 :     mooseError("Size of eqm_molality is ",
     379           1 :                eqm_molality.size(),
     380             :                " which is not equal to the number of equilibrium species ",
     381             :                num_eqm);
     382          79 :   if (basis_ind >= mgd.basis_species_name.size())
     383           1 :     mooseError("basis index ", basis_ind, " must be less than ", mgd.basis_species_name.size());
     384          78 :   best_eqm_species = 0;
     385             :   bool legitimate_swap_found = false;
     386             :   Real best_stoi = 0.0;
     387        5715 :   for (unsigned j = 0; j < num_eqm; ++j)
     388             :   {
     389        5637 :     if (mgd.eqm_stoichiometry(j, basis_ind) == 0.0)
     390        2810 :       continue;
     391        2827 :     if (!minerals_allowed && mgd.eqm_species_mineral[j])
     392         212 :       continue;
     393        2615 :     if (!gas_allowed && mgd.eqm_species_gas[j])
     394          15 :       continue;
     395        2600 :     if (!sorption_allowed && mgd.surface_sorption_related[j])
     396           2 :       continue;
     397        2598 :     const Real stoi = std::abs(mgd.eqm_stoichiometry(j, basis_ind)) * eqm_molality[j];
     398        2598 :     if (stoi >= best_stoi)
     399             :     {
     400             :       best_stoi = stoi;
     401         485 :       best_eqm_species = j;
     402             :       legitimate_swap_found = true;
     403             :     }
     404             :   }
     405          78 :   return legitimate_swap_found;
     406             : }

Generated by: LCOV version 1.14