8#include <boost/algorithm/string.hpp>
41 using Ts::operator()...;
46template <
typename DataBlock>
48 std::vector<DataBlock>
const& data_blocks)
50 std::copy(data_blocks.begin(), data_blocks.end(),
51 std::ostream_iterator<DataBlock>(os));
63 auto& components = aqueous_solution.
components;
64 for (
unsigned component_id = 0; component_id < components.size();
68 *components[component_id].amount);
69 components[component_id].amount->set(global_index,
70 concentrations[component_id]);
75 aqueous_solution.
pH->set(global_index, concentrations.back());
78template <
typename Reactant>
87 auto const& solid_constituent = solid_phase.
component(reactant.name);
89 if (solid_constituent.hasProperty(
94 .template initialValue<double>(pos, t);
96 (*reactant.molality)[chemical_system_id] = molality;
97 (*reactant.molality_prev)[chemical_system_id] = molality;
101 auto const volume_fraction =
104 .template initialValue<double>(pos, t);
106 (*reactant.volume_fraction)[chemical_system_id] = volume_fraction;
108 (*reactant.volume_fraction_prev)[chemical_system_id] = volume_fraction;
110 auto const fluid_density =
112 .template initialValue<double>(pos, t);
114 auto const porosity =
116 .template initialValue<double>(pos, t);
118 auto const molar_volume =
120 .template initialValue<double>(pos, t);
122 (*reactant.molality)[chemical_system_id] =
123 volume_fraction / fluid_density / porosity / molar_volume;
125 (*reactant.molality_prev)[chemical_system_id] =
126 (*reactant.molality)[chemical_system_id];
130template <
typename Reactant>
137 double const t,
double const dt)
139 auto const& solid_constituent = solid_phase.
component(reactant.name);
141 if (solid_constituent.hasProperty(
144 (*reactant.molality_prev)[chemical_system_id] =
145 (*reactant.molality)[chemical_system_id];
150 auto const volume_fraction =
151 (*reactant.volume_fraction)[chemical_system_id];
153 (*reactant.volume_fraction_prev)[chemical_system_id] =
154 (*reactant.volume_fraction)[chemical_system_id];
156 auto const fluid_density =
158 .template value<double>(vars, pos, t, dt);
160 auto const molar_volume =
162 .template value<double>(vars, pos, t, dt);
164 (*reactant.molality)[chemical_system_id] =
165 volume_fraction / fluid_density / vars.
porosity / molar_volume;
167 (*reactant.molality_prev)[chemical_system_id] =
168 (*reactant.molality)[chemical_system_id];
171template <
typename Site>
178 auto const& solid_constituent = solid_phase.
component(site.name);
180 auto const molality =
182 .template initialValue<double>(pos, t);
184 (*site.molality)[chemical_system_id] = molality;
187template <
typename Reactant>
192 double const porosity,
double const t,
195 auto const& solid_phase = medium.
phase(
"Solid");
196 auto const& liquid_phase = medium.
phase(
"AqueousLiquid");
200 auto const liquid_density =
202 .template value<double>(vars, pos, t, dt);
204 auto const& solid_constituent = solid_phase.component(reactant.name);
206 if (solid_constituent.hasProperty(
212 auto const molar_volume =
214 .template value<double>(vars, pos, t, dt);
216 (*reactant.volume_fraction)[chemical_system_id] +=
217 ((*reactant.molality)[chemical_system_id] -
218 (*reactant.molality_prev)[chemical_system_id]) *
219 liquid_density * porosity * molar_volume;
222template <
typename Reactant>
228 auto const& solid_phase = medium.
phase(
"Solid");
230 auto const& solid_constituent = solid_phase.
component(reactant.name);
232 if (solid_constituent.hasProperty(
238 porosity -= ((*reactant.volume_fraction)[chemical_system_id] -
239 (*reactant.volume_fraction_prev)[chemical_system_id]);
242template <
typename Reactant>
244 Reactant
const& reactant,
245 std::vector<GlobalIndexType>
const& chemical_system_indices)
247 double const sum = std::accumulate(
248 chemical_system_indices.begin(), chemical_system_indices.end(), 0.0,
250 { return s + (*reactant.molality)[id]; });
251 return sum / chemical_system_indices.size();
255extern std::string
specifyFileName(std::string
const& project_file_name,
256 std::string
const& file_extension);
260 std::string
const& project_file_name,
261 std::string&& database,
262 std::unique_ptr<ChemicalSystem>&& chemical_system,
263 std::vector<ReactionRate>&& reaction_rates,
264 std::unique_ptr<UserPunch>&& user_punch,
265 std::unique_ptr<Output>&& output,
266 std::unique_ptr<Dump>&& dump,
275 _dump(std::move(dump)),
282 "Failed to initialize phreeqc instance, due to lack of memory.");
289 "Failed in loading the specified thermodynamic database file: "
297 "Failed to fly the flag for the specified file {:s} where phreeqc "
298 "will write output.",
299 _output->basic_output_setups.output_file);
329 std::vector<double>
const& concentrations,
335 setAqueousSolution(concentrations, chemical_system_id,
338 auto const& solid_phase = medium.
phase(
"Solid");
339 auto const& liquid_phase = medium.
phase(
"AqueousLiquid");
343 initializeReactantMolality(kinetic_reactant, chemical_system_id,
344 solid_phase, liquid_phase, medium, pos, t);
349 initializeReactantMolality(equilibrium_reactant, chemical_system_id,
350 solid_phase, liquid_phase, medium, pos, t);
355 initializeSiteMolality(exchanger, chemical_system_id, solid_phase, pos,
361 if (
auto const surface_site_ptr =
362 std::get_if<MoleBasedSurfaceSite>(&surface_site))
364 initializeSiteMolality(*surface_site_ptr, chemical_system_id,
365 solid_phase, pos, t);
371 std::vector<double>
const& concentrations,
377 setAqueousSolution(concentrations, chemical_system_id,
380 auto const& solid_phase = medium->
phase(
"Solid");
381 auto const& liquid_phase = medium->
phase(
"AqueousLiquid");
385 setReactantMolality(kinetic_reactant, chemical_system_id, solid_phase,
386 liquid_phase, vars, pos, t, dt);
391 setReactantMolality(equilibrium_reactant, chemical_system_id,
392 solid_phase, liquid_phase, vars, pos, t, dt);
406 int const component_id,
GlobalIndexType const chemical_system_id)
const
409 auto& components = aqueous_solution.components;
410 auto const&
pH = *aqueous_solution.pH;
412 if (component_id <
static_cast<int>(components.size()))
415 *components[component_id].amount);
417 return components[component_id].amount->get(chemical_system_id);
423 return pH.get(chemical_system_id);
433 auto const& dump_file =
_dump->dump_file;
434 std::ifstream in(dump_file);
446 OGS_FATAL(
"Error when reading phreeqc dump file '{:s}'", dump_file);
459 OGS_FATAL(
"Could not open file '{:s}' for writing phreeqc inputs.",
463 out << std::scientific
464 << std::setprecision(std::numeric_limits<double>::max_digits10);
465 out << (*
this << dt);
469 OGS_FATAL(
"Failed in generating phreeqc input file '{:s}'.",
478 bool const fixing_pe =
488 os << phreeqc_io.
_knobs <<
"\n";
490 os << *phreeqc_io.
_output <<
"\n";
495 os << *user_punch <<
"\n";
499 if (!reaction_rates.empty())
502 os << reaction_rates <<
"\n";
505 for (std::size_t chemical_system_id = 0;
507 ++chemical_system_id)
509 os <<
"SOLUTION " << chemical_system_id + 1 <<
"\n";
511 os, chemical_system_id);
513 auto const& dump = phreeqc_io.
_dump;
516 auto const& aqueous_solutions_prev = dump->aqueous_solutions_prev;
518 if (!aqueous_solutions_prev.empty())
520 os << aqueous_solutions_prev[chemical_system_id] <<
"\n\n";
524 os <<
"USE solution none\n";
527 os <<
"USE solution " << chemical_system_id + 1 <<
"\n\n";
529 auto const& equilibrium_reactants =
531 if (!equilibrium_reactants.empty() || fixing_pe)
533 os <<
"EQUILIBRIUM_PHASES " << chemical_system_id + 1 <<
"\n";
534 for (
auto const& equilibrium_reactant : equilibrium_reactants)
536 equilibrium_reactant.print(os, chemical_system_id);
545 auto const& kinetic_reactants =
547 if (!kinetic_reactants.empty())
549 os <<
"KINETICS " << chemical_system_id + 1 <<
"\n";
550 for (
auto const& kinetic_reactant : kinetic_reactants)
552 kinetic_reactant.print(os, chemical_system_id);
554 os <<
"-steps " << phreeqc_io.
_dt <<
"\n\n";
558 if (!surface.empty())
568 os <<
"SURFACE " << chemical_system_id + 1 <<
"\n";
569 std::size_t aqueous_solution_id =
570 dump->aqueous_solutions_prev.empty()
571 ? chemical_system_id + 1
573 os <<
"-equilibrate with solution " << aqueous_solution_id <<
"\n";
576 if (std::holds_alternative<DensityBasedSurfaceSite>(
579 os <<
"-sites_units density\n";
583 os <<
"-sites_units absolute\n";
586 for (
auto const& surface_site : surface)
591 os << s.name <<
" " << s.site_density <<
" "
592 << s.specific_surface_area <<
" "
595 [&os, chemical_system_id](
598 << (*s.molality)[chemical_system_id]
605 if (std::holds_alternative<MoleBasedSurfaceSite>(surface.front()))
613 os <<
"SAVE solution " << chemical_system_id + 1 <<
"\n";
617 if (!exchangers.empty())
619 os <<
"EXCHANGE " << chemical_system_id + 1 <<
"\n";
620 std::size_t
const aqueous_solution_id =
621 dump->aqueous_solutions_prev.empty()
622 ? chemical_system_id + 1
624 os <<
"-equilibrate with solution " << aqueous_solution_id <<
"\n";
625 for (
auto const& exchanger : exchangers)
627 exchanger.print(os, chemical_system_id);
629 os <<
"SAVE solution " << chemical_system_id + 1 <<
"\n";
635 auto const& dump = phreeqc_io.
_dump;
646 INFO(
"Phreeqc: Executing chemical calculation.");
651 "Failed in performing speciation calculation with the generated "
652 "phreeqc input file '{:s}'.",
659 auto const& basic_output_setups =
_output->basic_output_setups;
660 auto const& phreeqc_result_file = basic_output_setups.output_file;
661 DBUG(
"Reading phreeqc results from file '{:s}'.", phreeqc_result_file);
662 std::ifstream in(phreeqc_result_file);
666 OGS_FATAL(
"Could not open phreeqc result file '{:s}'.",
667 phreeqc_result_file);
674 OGS_FATAL(
"Error when reading phreeqc result file '{:s}'",
675 phreeqc_result_file);
684 in.ignore(std::numeric_limits<std::streamsize>::max(),
'\n');
687 auto const& output = *phreeqc_io.
_output;
688 auto const& dropped_item_ids = output.dropped_item_ids;
693 int const num_skipped_lines =
694 1 + (!surface.empty() ? 1 : 0) + (!exchangers.empty() ? 1 : 0);
696 auto& equilibrium_reactants =
700 for (std::size_t chemical_system_id = 0;
702 ++chemical_system_id)
705 for (
int i = 0; i < num_skipped_lines; ++i)
707 in.ignore(std::numeric_limits<std::streamsize>::max(),
'\n');
711 if (!std::getline(in, line))
714 "Error when reading calculation result of Solution {:d} "
715 "after the reaction.",
719 std::vector<double> accepted_items;
720 std::vector<std::string> items;
721 boost::trim_if(line, boost::is_any_of(
"\t "));
722 boost::algorithm::split(items, line, boost::is_any_of(
"\t "),
723 boost::token_compress_on);
724 for (
int item_id = 0; item_id < static_cast<int>(items.size());
727 if (std::find(dropped_item_ids.begin(), dropped_item_ids.end(),
728 item_id) == dropped_item_ids.end())
733 value = std::stod(items[item_id]);
735 catch (
const std::invalid_argument& e)
738 "Invalid argument. Could not convert string '{:s}' to "
739 "double for chemical system {:d}, column {:d}. "
740 "Exception '{:s}' was thrown.",
741 items[item_id], chemical_system_id + 1, item_id,
744 catch (
const std::out_of_range& e)
747 "Out of range error. Could not convert string "
748 "'{:s}' to double for chemical system {:d}, column "
749 "{:d}. Exception '{:s}' was thrown.",
750 items[item_id], chemical_system_id + 1, item_id,
753 accepted_items.push_back(value);
756 assert(accepted_items.size() == output.accepted_items.size());
759 auto& components = aqueous_solution->components;
765 for (
int item_id = 0; item_id < static_cast<int>(accepted_items.size());
768 auto const& accepted_item = output.accepted_items[item_id];
769 auto const& item_name = accepted_item.name;
771 auto compare_by_name = [&item_name](
auto const& item)
772 {
return item.name == item_name; };
774 switch (accepted_item.item_type)
780 *aqueous_solution->pH);
781 aqueous_solution->pH->set(
782 global_index, std::pow(10, -accepted_items[item_id]));
788 (*aqueous_solution->pe)[chemical_system_id] =
789 accepted_items[item_id];
796 components, compare_by_name,
798 OGS_FATAL(
"Could not find component '{:s}'.",
803 component.amount->set(global_index,
804 accepted_items[item_id]);
810 auto const& equilibrium_reactant =
812 equilibrium_reactants, compare_by_name,
816 "Could not find equilibrium reactant "
820 (*equilibrium_reactant.molality)[chemical_system_id] =
821 accepted_items[item_id];
828 kinetic_reactants, compare_by_name,
830 OGS_FATAL(
"Could not find kinetic reactant '{:s}'.",
833 (*kinetic_reactant.molality)[chemical_system_id] =
834 accepted_items[item_id];
840 auto const& secondary_variables =
841 user_punch->secondary_variables;
843 auto const& secondary_variable =
845 secondary_variables, compare_by_name,
848 "Could not find secondary variable '{:s}'.",
851 (*secondary_variable.value)[chemical_system_id] =
852 accepted_items[item_id];
864 std::vector<std::string> component_names;
866 std::transform(components.begin(), components.end(),
867 std::back_inserter(component_names),
868 [](
auto const& c) { return c.name; });
870 component_names.push_back(
"H");
872 return component_names;
879 double const t,
double const dt)
883 updateReactantVolumeFraction(kinetic_reactant, chemical_system_id,
884 medium, pos, porosity, t, dt);
889 updateReactantVolumeFraction(equilibrium_reactant, chemical_system_id,
890 medium, pos, porosity, t, dt);
901 setPorosityPostReaction(kinetic_reactant, chemical_system_id, medium,
907 setPorosityPostReaction(equilibrium_reactant, chemical_system_id,
913 std::size_t
const ele_id,
914 std::vector<GlobalIndexType>
const& chemical_system_indices)
918 (*kinetic_reactant.mesh_prop_molality)[ele_id] =
919 averageReactantMolality(kinetic_reactant, chemical_system_indices);
922 for (
auto const& equilibrium_reactant :
925 (*equilibrium_reactant.mesh_prop_molality)[ele_id] =
926 averageReactantMolality(equilibrium_reactant,
927 chemical_system_indices);
Definition of one reactive chemical system for PHREEQC coupling.
MathLib::EigenLisLinearSolver GlobalLinearSolver
GlobalMatrix::IndexType GlobalIndexType
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Per-system aqueous state exchanged with PHREEQC.
PHREEQC-backed ChemicalSolverInterface implementation.
std::vector< GlobalIndexType > chemical_system_index_map
GlobalLinearSolver & linear_solver
ChemicalSolverInterface(MeshLib::Mesh const &mesh, GlobalLinearSolver &linear_solver_)
std::string const _phreeqc_input_file
double getConcentration(int const component_id, GlobalIndexType const chemical_system_id) const override
PhreeqcIO(MeshLib::Mesh const &mesh, GlobalLinearSolver &linear_solver, std::string const &project_file_name, std::string &&database, std::unique_ptr< ChemicalSystem > &&chemical_system, std::vector< ReactionRate > &&reaction_rates, std::unique_ptr< UserPunch > &&user_punch, std::unique_ptr< Output > &&output, std::unique_ptr< Dump > &&dump, Knobs &&knobs)
void setAqueousSolutionsPrevFromDumpFile() override
std::unique_ptr< ChemicalSystem > _chemical_system
std::vector< ReactionRate > const _reaction_rates
std::unique_ptr< UserPunch > _user_punch
const int phreeqc_instance_id
void writeInputsToFile(double const dt)
void setChemicalSystemConcrete(std::vector< double > const &concentrations, GlobalIndexType const &chemical_system_id, MaterialPropertyLib::Medium const *medium, MaterialPropertyLib::VariableArray const &vars, ParameterLib::SpatialPosition const &pos, double const t, double const dt) override
std::unique_ptr< Dump > const _dump
void initialize() override
std::unique_ptr< Output > const _output
void readOutputsFromFile()
std::size_t _num_chemical_systems
void computeSecondaryVariable(std::size_t const ele_id, std::vector< GlobalIndexType > const &chemical_system_indices) override
void initializeChemicalSystemConcrete(std::vector< double > const &concentrations, GlobalIndexType const &chemical_system_id, MaterialPropertyLib::Medium const &medium, ParameterLib::SpatialPosition const &pos, double const t) override
void updateVolumeFractionPostReaction(GlobalIndexType const &chemical_system_id, MaterialPropertyLib::Medium const &medium, ParameterLib::SpatialPosition const &pos, double const porosity, double const t, double const dt) override
void executeSpeciationCalculation(double const dt) override
std::string const _database
std::vector< std::string > const getComponentList() const override
void updatePorosityPostReaction(GlobalIndexType const &chemical_system_id, MaterialPropertyLib::Medium const &medium, double &porosity) override
Phase const & phase(std::size_t index) const
Component const & component(std::size_t const &index) const
ranges::range_reference_t< Range > findElementOrError(Range &range, std::predicate< ranges::range_reference_t< Range > > auto &&predicate, std::invocable auto error_callback)
void initializeSiteMolality(Site &site, GlobalIndexType const &chemical_system_id, MaterialPropertyLib::Phase const &solid_phase, ParameterLib::SpatialPosition const &pos, double const t)
void initializeReactantMolality(Reactant &reactant, GlobalIndexType const &chemical_system_id, MaterialPropertyLib::Phase const &solid_phase, MaterialPropertyLib::Phase const &liquid_phase, MaterialPropertyLib::Medium const &medium, ParameterLib::SpatialPosition const &pos, double const t)
void setPorosityPostReaction(Reactant &reactant, GlobalIndexType const &chemical_system_id, MaterialPropertyLib::Medium const &medium, double &porosity)
void setReactantMolality(Reactant &reactant, GlobalIndexType const &chemical_system_id, MaterialPropertyLib::Phase const &solid_phase, MaterialPropertyLib::Phase const &liquid_phase, MaterialPropertyLib::VariableArray const &vars, ParameterLib::SpatialPosition const &pos, double const t, double const dt)
void setAqueousSolution(std::vector< double > const &concentrations, GlobalIndexType const &chemical_system_id, AqueousSolution &aqueous_solution)
static double averageReactantMolality(Reactant const &reactant, std::vector< GlobalIndexType > const &chemical_system_indices)
void updateReactantVolumeFraction(Reactant &reactant, GlobalIndexType const &chemical_system_id, MaterialPropertyLib::Medium const &medium, ParameterLib::SpatialPosition const &pos, double const porosity, double const t, double const dt)
std::ostream & operator<<(std::ostream &os, PhreeqcIO const &phreeqc_io)
std::string specifyFileName(std::string const &project_file_name, std::string const &file_extension)
std::istream & operator>>(std::istream &in, PhreeqcIO &phreeqc_io)
void setLocalAccessibleVector(PETScVector const &x)
std::vector< Component > components
std::unique_ptr< GlobalVector > pH