OGS
CreateChemicalSolverInterface.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
5
6#include <phreeqcpp/cxxKinetics.h>
7
9#include "BaseLib/Error.h"
10#include "BaseLib/FileTools.h"
14#include "MeshLib/Mesh.h"
15#include "PhreeqcIO.h"
23#include "PhreeqcIOData/Dump.h"
24#include "PhreeqcIOData/Knobs.h"
28#include "PhreeqcKernel.h"
36
37namespace
38{
39std::string parseDatabasePath(BaseLib::ConfigTree const& config)
40{
41 // database
43 auto const database = config.getConfigParameter<std::string>("database");
44 auto path_to_database =
45 BaseLib::joinPaths(config.projectDirectory().string(), database);
46
47 if (!BaseLib::IsFileExisting(path_to_database))
48 {
49 OGS_FATAL("Not found the specified thermodynamicdatabase: {:s}",
50 path_to_database);
51 }
52
53 INFO("Found the specified thermodynamic database: {:s}", path_to_database);
54
55 return path_to_database;
56}
57} // namespace
58
59namespace ChemistryLib
60{
61template <>
62std::unique_ptr<ChemicalSolverInterface>
64 std::vector<std::unique_ptr<MeshLib::Mesh>> const& meshes,
65 std::map<std::string, std::unique_ptr<GlobalLinearSolver>> const&
66 linear_solvers,
67 BaseLib::ConfigTree const& config, std::string const& output_directory)
68{
69 auto mesh_name =
71 config.getConfigParameter<std::string>("mesh");
72
73 // Find and extract mesh from the list of meshes.
74 auto const& mesh = MeshLib::findMeshByName(meshes, mesh_name);
75
76 assert(mesh.getID() != 0);
77 DBUG("Found mesh '{:s}' with id {:d}.", mesh.getName(), mesh.getID());
78
79 auto const ls_name =
81 config.getConfigParameter<std::string>("linear_solver");
82 auto const& linear_solver = BaseLib::getOrError(
83 linear_solvers, ls_name,
84 "A linear solver with the given name does not exist.");
85
86 auto path_to_database = parseDatabasePath(config);
87
88 // chemical system
89 auto chemical_system =
90 PhreeqcIOData::createChemicalSystem(config, *meshes[0]);
91
92 // rates
95 config.getConfigSubtreeOptional("rates"));
96
97 // surface
98 auto const& surface = chemical_system->surface;
99
100 // exchange
101 auto const& exchangers = chemical_system->exchangers;
102
103 // whether to use stream for data exchange
104 auto use_stream_for_data_exchange =
106 config.getConfigParameter<bool>("use_stream_for_data_exchange", true);
107
108 // number of OpenMP threads for parallel chemistry execution
109 auto const num_chemistry_threads_opt =
111 config.getConfigParameterOptional<int>("chemistry_threads");
112 if (num_chemistry_threads_opt && *num_chemistry_threads_opt <= 0)
113 {
114 OGS_FATAL(
115 "<chemistry_threads> must be a positive integer, but got {:d}.",
116 *num_chemistry_threads_opt);
117 }
118 int const num_chemistry_threads =
119 num_chemistry_threads_opt ? *num_chemistry_threads_opt
121
122 // Negative (or zero) threshold below which clamped concentrations are
123 // reported. All negative concentrations are clamped to zero unconditionally
124 // (PHREEQC rejects negatives); this only sets the value below which an
125 // aggregated warning is emitted. Default -1e-12 mol/kgw.
126 auto const concentration_warning_threshold =
128 config.getConfigParameter<double>("concentration_warning_threshold",
129 -1e-12);
130 if (concentration_warning_threshold > 0.0)
131 {
132 OGS_FATAL(
133 "<concentration_warning_threshold> must not be positive (it is a "
134 "threshold for negative concentrations), but got {:g}.",
135 concentration_warning_threshold);
136 }
137
138 // dump
139 auto const project_file_name =
140 BaseLib::joinPaths(output_directory,
142 config.projectFilePath().string()));
143 // old dump file is deleted if it exists
144 auto dump = surface.empty() && exchangers.empty()
145 ? nullptr
146 : std::make_unique<PhreeqcIOData::Dump>(project_file_name);
147
148 // knobs
149 auto knobs = PhreeqcIOData::createKnobs(
151 config.getConfigSubtree("knobs"));
152
153 // user punch
154 auto user_punch = PhreeqcIOData::createUserPunch(
156 config.getConfigSubtreeOptional("user_punch"), *meshes[0]);
157
158 // output
159 auto const use_high_precision =
161 config.getConfigParameter<bool>("use_high_precision", true);
162 auto output = PhreeqcIOData::createOutput(
163 *chemical_system, user_punch, use_high_precision, project_file_name);
164
165 if (use_stream_for_data_exchange)
166 {
167 INFO("PhreeqcIO will use stringstream for data exchange.");
168 }
169 else
170 {
171 INFO("PhreeqcIO will use file for data exchange.");
172 }
173
174 return std::make_unique<PhreeqcIOData::PhreeqcIO>(
175 mesh, *linear_solver, std::move(project_file_name),
176 std::move(path_to_database), std::move(chemical_system),
177 std::move(reaction_rates), std::move(user_punch), std::move(output),
178 std::move(dump), std::move(knobs), use_stream_for_data_exchange,
179 num_chemistry_threads, concentration_warning_threshold);
180}
181
182template <>
183std::unique_ptr<ChemicalSolverInterface>
185 std::vector<std::unique_ptr<MeshLib::Mesh>> const& meshes,
186 std::map<std::string, std::unique_ptr<GlobalLinearSolver>> const&
187 linear_solvers,
188 BaseLib::ConfigTree const& config, std::string const& /*output_directory*/)
189{
190 auto mesh = *meshes[0];
191
192 auto const ls_name =
194 config.getConfigParameter<std::string>("linear_solver");
195 auto const& linear_solver = BaseLib::getOrError(
196 linear_solvers, ls_name,
197 "A linear solver with the given name does not exist.");
198
199 auto path_to_database = parseDatabasePath(config);
200
201 // TODO (renchao): remove mapping process id to component name.
202 std::vector<std::pair<int, std::string>> process_id_to_component_name_map;
203 // solution
204 auto aqueous_solution = PhreeqcKernelData::createAqueousSolution(
206 config.getConfigSubtree("solution"),
207 process_id_to_component_name_map);
208
209 // kinetic reactants
210 auto kinetic_reactants = PhreeqcKernelData::createKineticReactants(
212 config.getConfigSubtreeOptional("kinetic_reactants"), mesh);
213
214 // rates
217 config.getConfigSubtreeOptional("rates"));
218
219 // equilibrium reactants
220 auto equilibrium_reactants = PhreeqcKernelData::createEquilibriumReactants(
222 config.getConfigSubtreeOptional("equilibrium_reactants"), mesh);
223
224 return std::make_unique<PhreeqcKernelData::PhreeqcKernel>(
225 mesh, *linear_solver, mesh.computeNumberOfBaseNodes(),
226 process_id_to_component_name_map, std::move(path_to_database),
227 std::move(aqueous_solution), std::move(equilibrium_reactants),
228 std::move(kinetic_reactants), std::move(reaction_rates));
229}
230
231template <>
232std::unique_ptr<ChemicalSolverInterface>
234 std::vector<std::unique_ptr<MeshLib::Mesh>> const& meshes,
235 std::map<std::string, std::unique_ptr<GlobalLinearSolver>> const&
236 linear_solvers,
237 BaseLib::ConfigTree const& config, std::string const& /*output_directory*/)
238{
239 auto mesh_name =
241 config.getConfigParameter<std::string>("mesh");
242
243 // Find and extract mesh from the list of meshes.
244 auto const& mesh = MeshLib::findMeshByName(meshes, mesh_name);
245
246 assert(mesh.getID() != 0);
247 DBUG("Found mesh '{:s}' with id {:d}.", mesh.getName(), mesh.getID());
248
249 auto const ls_name =
251 config.getConfigParameter<std::string>("linear_solver");
252 auto const& linear_solver = BaseLib::getOrError(
253 linear_solvers, ls_name,
254 "A linear solver with the given name does not exist.");
255
256 auto chemical_reaction_data =
259 config.getConfigSubtree("chemical_reactions"));
260
261 // create sparse stoichiometric matrix
262 std::vector<double> stoichiometric_matrix_vec;
263 for (auto const& per_chemical_reaction_data : chemical_reaction_data)
264 {
265 stoichiometric_matrix_vec.insert(
266 stoichiometric_matrix_vec.end(),
267 per_chemical_reaction_data->stoichiometric_vector.begin(),
268 per_chemical_reaction_data->stoichiometric_vector.end());
269 }
270
271 auto const num_components =
273 config.getConfigParameter<int>("number_of_components");
274
275 Eigen::MatrixXd const stoichiometric_matrix = MathLib::toMatrix(
276 stoichiometric_matrix_vec, num_components, num_components);
277
278 Eigen::SparseMatrix<double> sparse_stoichiometric_matrix;
279 sparse_stoichiometric_matrix = stoichiometric_matrix.sparseView();
280
281 return std::make_unique<SelfContainedSolverData::SelfContainedSolver>(
282 mesh, *linear_solver, sparse_stoichiometric_matrix,
283 std::move(chemical_reaction_data));
284}
285} // namespace ChemistryLib
Definition of one reactive chemical system for PHREEQC coupling.
Chemical-solver interface used in OGS operator-split reactive transport.
#define OGS_FATAL(...)
Definition Error.h:10
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
PHREEQC-backed ChemicalSolverInterface implementation.
std::optional< ConfigTree > getConfigSubtreeOptional(std::string const &root) const
std::filesystem::path projectDirectory() const
std::optional< T > getConfigParameterOptional(std::string const &param) const
T getConfigParameter(std::string const &param) const
ConfigTree getConfigSubtree(std::string const &root) const
std::filesystem::path const & projectFilePath() const
Used to get the project file name.
Definition ConfigTree.h:297
bool IsFileExisting(const std::string &strFilename)
Returns true if given file exists.
Definition FileTools.cpp:23
std::string extractBaseNameWithoutExtension(std::string const &pathname)
int getNumberOfChemistryThreads()
std::string joinPaths(std::string const &pathA, std::string const &pathB)
OGS_NO_DANGLING Map::mapped_type & getOrError(Map &map, Key const &key, std::string const &error_message)
Definition Algorithm.h:112
std::unique_ptr< UserPunch > createUserPunch(std::optional< BaseLib::ConfigTree > const &config, MeshLib::Mesh const &mesh)
std::unique_ptr< Output > createOutput(ChemicalSystem const &chemical_system, std::unique_ptr< UserPunch > const &user_punch, bool const use_high_precision, std::string const &project_file_name)
std::unique_ptr< ChemicalSystem > createChemicalSystem(BaseLib::ConfigTree const &config, MeshLib::Mesh &mesh)
Knobs createKnobs(BaseLib::ConfigTree const &config)
std::unique_ptr< EquilibriumReactants > createEquilibriumReactants(std::optional< BaseLib::ConfigTree > const &config, MeshLib::Mesh const &mesh)
std::unique_ptr< Kinetics > createKineticReactants(std::optional< BaseLib::ConfigTree > const &config, MeshLib::Mesh const &mesh)
AqueousSolution createAqueousSolution(BaseLib::ConfigTree const &config, std::vector< std::pair< int, std::string > > const &process_id_to_component_name_map)
std::vector< std::unique_ptr< ChemicalReaction > > createChemicalReactionData(BaseLib::ConfigTree const &config)
template std::vector< PhreeqcKernelData::ReactionRate > createReactionRates< PhreeqcKernelData::ReactionRate >(std::optional< BaseLib::ConfigTree > const &config)
std::unique_ptr< ChemicalSolverInterface > createChemicalSolverInterface< ChemicalSolver::SelfContained >(std::vector< std::unique_ptr< MeshLib::Mesh > > const &meshes, std::map< std::string, std::unique_ptr< GlobalLinearSolver > > const &linear_solvers, BaseLib::ConfigTree const &config, std::string const &)
std::unique_ptr< ChemicalSolverInterface > createChemicalSolverInterface< ChemicalSolver::PhreeqcKernel >(std::vector< std::unique_ptr< MeshLib::Mesh > > const &meshes, std::map< std::string, std::unique_ptr< GlobalLinearSolver > > const &linear_solvers, BaseLib::ConfigTree const &config, std::string const &)
std::unique_ptr< ChemicalSolverInterface > createChemicalSolverInterface< ChemicalSolver::Phreeqc >(std::vector< std::unique_ptr< MeshLib::Mesh > > const &meshes, std::map< std::string, std::unique_ptr< GlobalLinearSolver > > const &linear_solvers, BaseLib::ConfigTree const &config, std::string const &output_directory)
template std::vector< PhreeqcIOData::ReactionRate > createReactionRates< PhreeqcIOData::ReactionRate >(std::optional< BaseLib::ConfigTree > const &config)
Eigen::Map< const Matrix > toMatrix(std::vector< double > const &data, Eigen::MatrixXd::Index rows, Eigen::MatrixXd::Index cols)
Mesh & findMeshByName(std::vector< std::unique_ptr< Mesh > > const &meshes, std::string_view const name)
Definition Mesh.cpp:356
std::string parseDatabasePath(BaseLib::ConfigTree const &config)