OGS
PhreeqcKernel.cpp
Go to the documentation of this file.
1
11#include "PhreeqcKernel.h"
12
13#include <phreeqcpp/cxxKinetics.h>
14
15#include <cmath>
16#include <cstdlib>
17
18#include "BaseLib/Error.h"
21
22namespace ChemistryLib
23{
24namespace PhreeqcKernelData
25{
27 MeshLib::Mesh const& mesh,
28 GlobalLinearSolver& linear_solver,
29 std::size_t const num_chemical_systems,
30 std::vector<std::pair<int, std::string>> const&
31 process_id_to_component_name_map,
32 std::string const& database,
33 AqueousSolution aqueous_solution,
34 std::unique_ptr<EquilibriumReactants>&& equilibrium_reactants,
35 std::unique_ptr<Kinetics>&& kinetic_reactants,
36 std::vector<ReactionRate>&& reaction_rates)
37 : ChemicalSolverInterface(mesh, linear_solver),
38 _initial_aqueous_solution(aqueous_solution.getInitialAqueousSolution()),
39 _aqueous_solution(aqueous_solution.castToBaseClassNoninitialized()),
40 _reaction_rates(std::move(reaction_rates))
41{
43
44 loadDatabase(database);
45
46 // solution
47 for (std::size_t chemical_system_id = 0;
48 chemical_system_id < num_chemical_systems;
49 ++chemical_system_id)
50 {
51 aqueous_solution.setChemicalSystemID(chemical_system_id);
52 Rxn_solution_map.emplace(chemical_system_id,
53 *aqueous_solution.castToBaseClass());
54 }
55 use.Set_solution_in(true);
56
57 // equilibrium reactants
58 if (equilibrium_reactants)
59 {
60 tidyEquilibriumReactants(*equilibrium_reactants);
61
62 for (std::size_t chemical_system_id = 0;
63 chemical_system_id < num_chemical_systems;
64 ++chemical_system_id)
65 {
66 equilibrium_reactants->setChemicalSystemID(chemical_system_id);
67 Rxn_pp_assemblage_map.emplace(
68 chemical_system_id, *equilibrium_reactants->castToBaseClass());
69 }
70 // explicitly release and delete equilibrium_reactants
71 equilibrium_reactants.reset(nullptr);
72
73 use.Set_pp_assemblage_in(true);
74 }
75
76 // kinetic reactants
77 if (kinetic_reactants)
78 {
79 for (std::size_t chemical_system_id = 0;
80 chemical_system_id < num_chemical_systems;
81 ++chemical_system_id)
82 {
83 kinetic_reactants->setChemicalSystemID(chemical_system_id);
84 Rxn_kinetics_map.emplace(chemical_system_id,
85 *kinetic_reactants->castToBaseClass());
86 }
87 // explicitly release and delete kinetic_reactants
88 kinetic_reactants.reset(nullptr);
89
90 use.Set_kinetics_in(true);
91 }
92
93 // rates
95
97
99
100 for (auto const& map_pair : process_id_to_component_name_map)
101 {
102 auto const transport_process_id = map_pair.first;
103 auto const& transport_process_variable = map_pair.second;
104
105 auto master_species =
106 master_bsearch(transport_process_variable.c_str());
107
108 _process_id_to_master_map[transport_process_id] = master_species;
109 }
110}
111
113 EquilibriumReactants const& equilibrium_reactants)
114{
115 // extract a part of function body from int
116 // Phreeqc::tidy_pp_assemblage(void)
117 count_elts = 0;
118 double coef = 1.0;
119 for (auto const& phase_component :
120 equilibrium_reactants.getPhaseComponents())
121 {
122 int phase_id;
123 struct phase* phase_component_ptr =
124 phase_bsearch(phase_component.first.c_str(), &phase_id, FALSE);
125 add_elt_list(phase_component_ptr->next_elt, coef);
126 }
127
128 cxxNameDouble nd = elt_list_NameDouble();
129 const_cast<cxxPPassemblage*>(equilibrium_reactants.castToBaseClass())
130 ->Set_eltList(nd);
131}
132
133void PhreeqcKernel::loadDatabase(std::string const& database)
134{
135 std::ifstream in(database);
136 if (!in)
137 {
138 OGS_FATAL("Unable to open database file '{:s}'.", database);
139 }
140 assert(phrq_io->get_istream() == nullptr);
141 phrq_io->push_istream(&in, false);
142 read_database();
143}
144
146{
147 std::vector<struct rate> rates(_reaction_rates.size());
148 int rate_id = 0;
149 for (auto const& reaction_rate : _reaction_rates)
150 {
151 rates[rate_id].name = reaction_rate.kinetic_reactant.data();
152
153 // Commands' strings are freed by Phreeqc dtor.
154 rates[rate_id].commands = static_cast<char*>(
155 malloc(sizeof(char) * reaction_rate.commands().size() + 1));
156 if (rates[rate_id].commands == nullptr)
157 {
158 OGS_FATAL("Could not allocate memory for rate[{:d}] commands.",
159 rate_id);
160 }
161 reaction_rate.commands().copy(rates[rate_id].commands,
162 std::string::npos);
163 rates[rate_id].commands[reaction_rate.commands().size()] = '\0';
164
165 rates[rate_id].new_def = 1;
166 rates[rate_id].linebase = nullptr;
167 rates[rate_id].varbase = nullptr;
168 rates[rate_id].loopbase = nullptr;
169
170 ++rate_id;
171 };
172}
173
175{
176 std::vector<GlobalVector*> process_solutions;
177
178 setAqueousSolutions(process_solutions);
179
180 setTimeStepSize(dt);
181
182 callPhreeqc(process_solutions);
183}
184
185static bool isHydrogen(std::string_view const element)
186{
187 return element == "H";
188}
189
191 std::vector<GlobalVector*> const& process_solutions)
192{
193 assert(!process_solutions.empty());
194 // Loop over chemical systems
195 std::size_t const num_chemical_systems = process_solutions[0]->size();
196 for (std::size_t chemical_system_id = 0;
197 chemical_system_id < num_chemical_systems;
198 ++chemical_system_id)
199 {
200 auto& aqueous_solution = Rxn_solution_map[chemical_system_id];
201
202 auto initial_aqueous_solution =
203 getOrCreateInitialAqueousSolution(aqueous_solution);
204
205 auto& components = initial_aqueous_solution->Get_comps();
206 // Loop over transport process id map to retrieve component
207 // concentrations from process solutions
208 for (auto const& map_pair : _process_id_to_master_map)
209 {
210 auto const transport_process_id = map_pair.first;
211 auto const& master_species = map_pair.second;
212
213 auto& transport_process_solution =
214 process_solutions[transport_process_id];
215
216 char const* const element_name = master_species->elt->name;
217 auto const concentration =
218 transport_process_solution->get(chemical_system_id);
219 if (isHydrogen(element_name))
220 {
221 // Set pH value by hydrogen concentration.
222 double const pH = -std::log10(concentration);
223 aqueous_solution.Set_ph(pH);
224
225 {
226 auto hydrogen = components.find("H(1)");
227 if (hydrogen != components.end())
228 {
229 hydrogen->second.Set_input_conc(pH);
230 }
231 }
232 }
233 else
234 {
235 // Set component concentrations.
236 components[element_name].Set_input_conc(concentration);
237 }
238 }
239 }
240}
241
243 cxxSolution& aqueous_solution)
244{
245 if (!aqueous_solution.Get_initial_data())
246 {
247 aqueous_solution.Set_initial_data(_initial_aqueous_solution.get());
248 aqueous_solution.Set_new_def(true);
249 }
250
251 return aqueous_solution.Get_initial_data();
252}
253
255{
256 // Loop over rxn kinetics map
257 for (auto& map_pair : Rxn_kinetics_map)
258 {
259 auto& kinetics = map_pair.second;
260 kinetics.Get_steps().push_back(dt);
261 }
262}
263
264void PhreeqcKernel::callPhreeqc(std::vector<GlobalVector*>& process_solutions)
265{
266 if (process_solutions.empty())
267 {
268 OGS_FATAL("About to access an empty process solutions vector.");
269 }
270 std::size_t const num_chemical_systems = process_solutions[0]->size();
271 for (std::size_t chemical_system_id = 0;
272 chemical_system_id < num_chemical_systems;
273 ++chemical_system_id)
274 {
275 Rxn_new_solution.insert(chemical_system_id);
276 new_solution = 1;
277 use.Set_n_solution_user(chemical_system_id);
278
279 if (!Rxn_pp_assemblage_map.empty())
280 {
281 Rxn_new_pp_assemblage.insert(chemical_system_id);
282 use.Set_pp_assemblage_in(true);
283 use.Set_n_pp_assemblage_user(chemical_system_id);
284 }
285
286 if (!Rxn_kinetics_map.empty())
287 {
288 use.Set_kinetics_in(true);
289 use.Set_n_kinetics_user(chemical_system_id);
290 }
291
292 initial_solutions(false);
293
294 reactions();
295
296 updateNodalProcessSolutions(process_solutions, chemical_system_id);
297
298 reset(chemical_system_id);
299 }
300}
301
302void PhreeqcKernel::reset(std::size_t const chemical_system_id)
303{
304 // Clean up
305 Rxn_new_solution.clear();
306
307 // Solution
308 {
309 Rxn_solution_map[chemical_system_id] = *_aqueous_solution;
310 Rxn_solution_map[chemical_system_id].Set_n_user_both(
311 chemical_system_id);
312 Rxn_solution_map[chemical_system_id].Set_pe(-s_eminus->la);
313 }
314
315 // Equilibrium reactants
316 if (!Rxn_pp_assemblage_map.empty())
317 {
318 Rxn_new_pp_assemblage.clear();
319 for (int j = 0; j < count_unknowns; j++)
320 {
321 if (x == nullptr || x[j]->type != PP)
322 continue;
323
324 auto& phase_components = Rxn_pp_assemblage_map[chemical_system_id]
325 .Get_pp_assemblage_comps();
326 phase_components[x[j]->pp_assemblage_comp_name].Set_moles(
327 x[j]->moles);
328 }
329 }
330
331 // Kinetics
332 if (!Rxn_kinetics_map.empty())
333 {
334 Rxn_kinetics_map[chemical_system_id].Get_steps().clear();
335 }
336}
337
339 std::vector<GlobalVector*> const& process_solutions,
340 std::size_t const node_id)
341{
342 for (auto const& map_pair : _process_id_to_master_map)
343 {
344 auto const transport_process_id = map_pair.first;
345 auto const& master_species = map_pair.second;
346
347 auto& transport_process_solution =
348 process_solutions[transport_process_id];
349
350 char const* const element_name = master_species->elt->name;
351 if (isHydrogen(element_name))
352 {
353 // Update hydrogen concentration by pH value.
354 auto const hydrogen_concentration = std::pow(10, s_hplus->la);
355 transport_process_solution->set(node_id, hydrogen_concentration);
356 }
357 else
358 {
359 // Update solutions of component transport processes.
360 auto const concentration =
361 master_species->primary
362 ? master_species->total_primary / mass_water_aq_x
363 : master_species->total / mass_water_aq_x;
364 transport_process_solution->set(node_id, concentration);
365 }
366 }
367}
368} // namespace PhreeqcKernelData
369} // namespace ChemistryLib
#define OGS_FATAL(...)
Definition Error.h:26
void setChemicalSystemID(std::size_t const chemical_system_id)
std::map< std::string, cxxPPassemblageComp > const & getPhaseComponents() const
std::vector< ReactionRate > const _reaction_rates
std::map< int, struct master * > _process_id_to_master_map
void setAqueousSolutions(std::vector< GlobalVector * > const &process_solutions)
void tidyEquilibriumReactants(EquilibriumReactants const &equilibrium_reactants)
void loadDatabase(std::string const &database)
void updateNodalProcessSolutions(std::vector< GlobalVector * > const &process_solutions, std::size_t const node_id)
PhreeqcKernel(MeshLib::Mesh const &mesh, GlobalLinearSolver &linear_solver, std::size_t const num_chemical_systems, std::vector< std::pair< int, std::string > > const &process_id_to_component_name_map, std::string const &database, AqueousSolution aqueous_solution, std::unique_ptr< EquilibriumReactants > &&equilibrium_reactants, std::unique_ptr< Kinetics > &&kinetic_reactants, std::vector< ReactionRate > &&reaction_rates)
std::unique_ptr< cxxSolution const > _aqueous_solution
cxxISolution * getOrCreateInitialAqueousSolution(cxxSolution &aqueous_solution)
std::unique_ptr< cxxISolution const > _initial_aqueous_solution
void callPhreeqc(std::vector< GlobalVector * > &process_solutions)
void executeSpeciationCalculation(double const dt) override
void reset(std::size_t const chemical_system_id)
static bool isHydrogen(std::string_view const element)