From fc79d1d0a8b34120d8560e3305ebda7ad8c78aa6 Mon Sep 17 00:00:00 2001 From: Max Luebke Date: Thu, 16 Feb 2023 14:00:31 +0100 Subject: [PATCH 1/9] refactor: DHT_Wrapper class feat: add setters to DHT_Wrapper to set prop type vector and signif vector refactor: remove dependency from SimParams structure feat: introduce new header defining DHT types --- include/poet/DHT_Types.hpp | 8 +++ include/poet/DHT_Wrapper.hpp | 130 +++++------------------------------ src/DHT_Wrapper.cpp | 70 +++++++++++-------- 3 files changed, 67 insertions(+), 141 deletions(-) create mode 100644 include/poet/DHT_Types.hpp diff --git a/include/poet/DHT_Types.hpp b/include/poet/DHT_Types.hpp new file mode 100644 index 000000000..14b55b6fd --- /dev/null +++ b/include/poet/DHT_Types.hpp @@ -0,0 +1,8 @@ +#ifndef DHT_TYPES_H_ +#define DHT_TYPES_H_ + +namespace poet { +enum { DHT_TYPE_DEFAULT, DHT_TYPE_ACT, DHT_TYPE_IGNORE }; +} + +#endif // DHT_TYPES_H_ diff --git a/include/poet/DHT_Wrapper.hpp b/include/poet/DHT_Wrapper.hpp index 469a6e60b..b44c8afae 100644 --- a/include/poet/DHT_Wrapper.hpp +++ b/include/poet/DHT_Wrapper.hpp @@ -21,8 +21,6 @@ #ifndef DHT_WRAPPER_H #define DHT_WRAPPER_H -#include "SimParams.hpp" - #include #include #include @@ -46,7 +44,7 @@ using DHT_ResultObject = struct DHTResobj { std::vector> results; std::vector needPhreeqc; - void ResultsToMapping(std::vector &curr_mapping); + void ResultsToMapping(std::vector &curr_mapping); void ResultsToWP(std::vector &curr_wp); }; @@ -79,15 +77,14 @@ public: * calling DHT_create with all given parameters. Also the fuzzing buffer will * be allocated and all needed parameters extracted from simparams struct. * - * @param params Simulation parameter object * @param dht_comm Communicator which addresses all participating DHT * processes * @param buckets_per_process Count of buckets to allocate for each process * @param key_count Count of key entries * @param data_count Count of data entries */ - DHT_Wrapper(const poet::SimParams ¶ms, MPI_Comm dht_comm, - uint32_t dht_size, uint32_t key_count, uint32_t data_count); + DHT_Wrapper(MPI_Comm dht_comm, uint32_t dht_size, uint32_t key_count, + uint32_t data_count); /** * @brief Destroy the dht wrapper object * @@ -173,138 +170,43 @@ public: * * @return uint64_t Count of hits */ - uint64_t getHits(); + auto getHits() { return this->dht_hits; }; /** * @brief Get the Misses object * * @return uint64_t Count of read misses */ - uint64_t getMisses(); + auto getMisses() { return this->dht_miss; }; /** * @brief Get the Evictions object * * @return uint64_t Count of evictions */ - uint64_t getEvictions(); + auto getEvictions() { return this->dht_evictions; }; + + void SetSignifVector(std::vector signif_vec); + void SetPropTypeVector(std::vector prop_type_vec); private: uint32_t key_count; uint32_t data_count; - /** - * @brief Transform given workpackage into DHT key - * - * A given workpackage will be transformed into a DHT key by rounding each - * value of a workpackage to a given significant digit. Three different types - * of variables 'act', 'logact' and 'ignore' are used. Those types are given - * via the dht_signif_vector. - * - * If a variable is defined as 'act', dht_log is true and non-negative, the - * logarithm with base 10 will be applied. After that the value is negated. In - * case the value is 0 the fuzzing_buffer is also set to 0 at this position. - * If the value is negative a correspondending warning will be printed to - * stderr and the fuzzing buffer will be set to 0 at this index. - * - * If a variable is defined as 'logact' the value will be cut after the - * significant digit. - * - * If a variable ist defined as 'ignore' the fuzzing_buffer will be set to 0 - * at the index of the variable. - * - * If dt_differ is true the current time step of the simulation will be set at - * the end of the fuzzing_buffer. - * - * @param var_count Count of variables for the current work package - * @param key Pointer to work package handled as the key - * @param dt Current time step of the simulation - */ - std::vector fuzzForDHT(int var_count, void *key, double dt); - - /** - * @brief DHT handle - * - * Stores information about the DHT. Will be used as a handle for each DHT - * library call. - * - */ DHT *dht_object; - /** - * @brief Count of hits - * - * The counter will be incremented if a previously simulated workpackage can - * be retrieved with a given key. - * - */ + std::vector fuzzForDHT(int var_count, void *key, double dt); + uint64_t dht_hits = 0; - - /** - * @brief Count of read misses - * - * The counter will be incremented if a given key doesn't retrieve a value - * from the DHT. - * - */ uint64_t dht_miss = 0; - - /** - * @brief Count of evictions - * - * If a value in the DHT must be evicted because of lack of space/reaching the - * last index etc., this counter will be incremented. - * - */ uint64_t dht_evictions = 0; - /** - * @brief Rounded work package values - * - * Stores rounded work package values and serves as the DHT key pointer. - * - */ - double *fuzzing_buffer; + std::vector dht_signif_vector; + std::vector dht_prop_type_vector; - /** - * @brief Indicates change in time step during simulation - * - * If set to true, the time step of simulation will differ between iterations, - * so the current time step must be stored inside the DHT key. Otherwise wrong - * values would be obtained. - * - * If set to false the time step doesn't need to be stored in the DHT key. - * - */ - bool dt_differ; - - /** - * @brief Logarithm before rounding - * - * Indicates if the logarithm with base 10 will be applied to a variable - * before rounding. - * - * Defaults to true. - * - */ - bool dht_log; - - /** - * @brief Significant digits for each variable - * - * Stores the rounding/significant digits for each variable of the work - * package. - * - */ - std::vector dht_signif_vector; - - /** - * @brief Type of each variable - * - * Defines the type of each variable of the work package. - * - */ - std::vector dht_prop_type_vector; + static constexpr int DHT_KEY_SIGNIF_DEFAULT = 5; + static constexpr int DHT_KEY_SIGNIF_TOTALS = 12; + static constexpr int DHT_KEY_SIGNIF_CHARGE = 3; }; } // namespace poet diff --git a/src/DHT_Wrapper.cpp b/src/DHT_Wrapper.cpp index 68367341b..9615b78f5 100644 --- a/src/DHT_Wrapper.cpp +++ b/src/DHT_Wrapper.cpp @@ -18,18 +18,19 @@ ** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "poet/DHT_Wrapper.hpp" +#include "poet/DHT_Types.hpp" #include "poet/HashFunctions.hpp" + #include #include #include #include #include -#include -#include - -#include - #include +#include +#include +#include #include using namespace poet; @@ -67,9 +68,8 @@ void poet::DHT_ResultObject::ResultsToWP(std::vector &curr_wp) { } } -DHT_Wrapper::DHT_Wrapper(const poet::SimParams ¶ms, MPI_Comm dht_comm, - uint32_t dht_size, uint32_t key_count, - uint32_t data_count) +DHT_Wrapper::DHT_Wrapper(MPI_Comm dht_comm, uint32_t dht_size, + uint32_t key_count, uint32_t data_count) : key_count(key_count), data_count(data_count) { poet::initHashCtx(EVP_md5()); // initialize DHT object @@ -80,20 +80,26 @@ DHT_Wrapper::DHT_Wrapper(const poet::SimParams ¶ms, MPI_Comm dht_comm, &poet::hashDHT); // extract needed values from sim_param struct - t_simparams tmp = params.getNumParams(); + // t_simparams tmp = params.getNumParams(); - this->dt_differ = tmp.dt_differ; - this->dht_log = tmp.dht_log; + // this->dt_differ = tmp.dt_differ; + // this->dht_log = tmp.dht_log; - this->dht_signif_vector = params.getDHTSignifVector(); - this->dht_prop_type_vector = params.getDHTPropTypeVector(); + // this->dht_signif_vector = params.getDHTSignifVector(); + // this->dht_prop_type_vector = params.getDHTPropTypeVector(); + + this->dht_signif_vector.resize(key_size, DHT_KEY_SIGNIF_DEFAULT); + this->dht_signif_vector[0] = DHT_KEY_SIGNIF_TOTALS; + this->dht_signif_vector[1] = DHT_KEY_SIGNIF_TOTALS; + this->dht_signif_vector[2] = DHT_KEY_SIGNIF_CHARGE; + + this->dht_prop_type_vector.resize(key_size, DHT_TYPE_DEFAULT); } DHT_Wrapper::~DHT_Wrapper() { // free DHT DHT_free(dht_object, NULL, NULL); - // free fuzzing buffer - free(fuzzing_buffer); + poet::freeHashCtx(); } auto DHT_Wrapper::checkDHT(int length, double dt, @@ -184,17 +190,11 @@ void DHT_Wrapper::printStatistics() { } } -uint64_t DHT_Wrapper::getHits() { return this->dht_hits; } - -uint64_t DHT_Wrapper::getMisses() { return this->dht_miss; } - -uint64_t DHT_Wrapper::getEvictions() { return this->dht_evictions; } - std::vector DHT_Wrapper::fuzzForDHT(int var_count, void *key, double dt) { constexpr double zero_val = 10E-14; - std::vector vecFuzz(var_count); + std::vector vecFuzz(var_count + 1); std::memset(&vecFuzz[0], 0, sizeof(DHT_Keyelement) * var_count); unsigned int i = 0; @@ -203,10 +203,11 @@ std::vector DHT_Wrapper::fuzzForDHT(int var_count, void *key, for (i = 0; i < (unsigned int)var_count; i++) { double &curr_key = ((double *)key)[i]; if (curr_key != 0) { - if (curr_key < zero_val && this->dht_prop_type_vector[i] == "act") { + if (curr_key < zero_val && + this->dht_prop_type_vector[i] == DHT_TYPE_ACT) { continue; } - if (this->dht_prop_type_vector[i] == "ignore") { + if (this->dht_prop_type_vector[i] == DHT_TYPE_IGNORE) { continue; } vecFuzz[i] = round_key_element(curr_key, dht_signif_vector[i]); @@ -214,9 +215,24 @@ std::vector DHT_Wrapper::fuzzForDHT(int var_count, void *key, } // if timestep differs over iterations set current current time step at the // end of fuzzing buffer - if (dt_differ) { - vecFuzz[var_count] = round_key_element(dt, 55); - } + vecFuzz[var_count] = round_key_element(dt, 55); return vecFuzz; } + +void poet::DHT_Wrapper::SetSignifVector(std::vector signif_vec) { + if (signif_vec.size() != this->key_count) { + throw std::runtime_error( + "Significant vector size mismatches count of key elements."); + } + + this->dht_signif_vector = signif_vec; +} +void poet::DHT_Wrapper::SetPropTypeVector(std::vector prop_type_vec) { + if (prop_type_vec.size() != this->key_count) { + throw std::runtime_error( + "Prop type vector size mismatches count of key elements."); + } + + this->dht_prop_type_vector = prop_type_vec; +} From a493054f9dcac96490626dcf77abae9ca2e91bcd Mon Sep 17 00:00:00 2001 From: Max Luebke Date: Thu, 16 Feb 2023 14:30:51 +0100 Subject: [PATCH 2/9] refactor: In SimParams, relocate trivial getter/setters to header file refactor: change vector data types of dht_signif_vector and dht_prop_type_vector to both uint32 --- include/poet/SimParams.hpp | 86 +++++++------------------------------- src/SimParams.cpp | 43 ++++++++++--------- 2 files changed, 35 insertions(+), 94 deletions(-) diff --git a/include/poet/SimParams.hpp b/include/poet/SimParams.hpp index 3a10e612d..b68dbce5d 100644 --- a/include/poet/SimParams.hpp +++ b/include/poet/SimParams.hpp @@ -31,8 +31,8 @@ #include // BSD-licenced -/** Standard DHT Size (Defaults to 1 GiB) */ -#define DHT_SIZE_PER_PROCESS 1073741824 +/** Standard DHT Size. Defaults to 1 GB (1000 MB) */ +constexpr uint32_t DHT_SIZE_PER_PROCESS_MB = 1E3; /** Standard work package size */ #define WORK_PACKAGE_SIZE_DEFAULT 5 @@ -167,16 +167,6 @@ public: */ void initVectorParams(RInside &R, int col_count); - /** - * @brief Set if dt differs - * - * Set a boolean variable if the timestep differs between iterations of - * simulation. - * - * @param dt_differ Boolean value, if dt differs - */ - void setDtDiffer(bool dt_differ); - /** * @brief Get the numerical params struct * @@ -185,7 +175,7 @@ public: * * @return t_simparams Parameter struct */ - t_simparams getNumParams() const; + auto getNumParams() const { return this->simparams; }; /** * @brief Get the DHT_Signif_Vector @@ -196,7 +186,7 @@ public: * @return std::vector Vector of integers containing information about * significant digits */ - std::vector getDHTSignifVector() const; + auto getDHTSignifVector() const { return this->dht_signif_vector; }; /** * @brief Get the DHT_Prop_Type_Vector @@ -206,7 +196,7 @@ public: * @return std::vector Vector if strings defining a type of a * variable */ - std::vector getDHTPropTypeVector() const; + auto getDHTPropTypeVector() const { return this->dht_prop_type_vector; }; /** * @brief Return name of DHT snapshot. @@ -216,7 +206,7 @@ public: * * @return std::string Absolute paht to the DHT snapshot */ - std::string_view getDHTFile() const; + auto getDHTFile() const { return this->dht_file; }; /** * @brief Get the filesim name @@ -226,7 +216,7 @@ public: * * @return std::string Absolute path to R file */ - std::string_view getFilesim() const; + auto getFilesim() const { return this->filesim; }; /** * @brief Get the output directory @@ -236,71 +226,23 @@ public: * * @return std::string Absolute path to output path */ - std::string_view getOutDir() const; + auto getOutDir() const { return this->out_dir; }; private: - /** - * @brief Validate program parameters and flags - * - * Therefore this function iterates over the list of flags and parameters and - * compare them to the class member flagList and paramList. If a program - * argument is not included it is put to a list. This list will be returned. - * - * @return std::list List with all unknown parameters. Might be - * empty. - */ std::list validateOptions(argh::parser cmdl); - /** - * @brief Contains all valid program flags. - * - */ - std::set flaglist{"ignore-result", "dht", "dht-nolog"}; + const std::set flaglist{"ignore-result", "dht", "dht-nolog"}; + const std::set paramlist{"work-package-size", "dht-signif", + "dht-strategy", "dht-size", + "dht-snaps", "dht-file"}; - /** - * @brief Contains all valid program parameters. - * - */ - std::set paramlist{"work-package-size", "dht-signif", - "dht-strategy", "dht-size", - "dht-snaps", "dht-file"}; - - /** - * @brief Struct containing all simulation parameters - * - * Contains only those values which are standard arithmetic C types. - * - */ t_simparams simparams; - /** - * @brief Defines significant digits for each variable of a grid cell - * - */ - std::vector dht_signif_vector; + std::vector dht_signif_vector; + std::vector dht_prop_type_vector; - /** - * @brief Defines the type of a variable - * - */ - std::vector dht_prop_type_vector; - - /** - * @brief Absolute path to a DHT snapshot - * - */ std::string dht_file; - - /** - * @brief Absolute path to R file containing simulation definitions - * - */ std::string filesim; - - /** - * @brief Absolute path to output dir - * - */ std::string out_dir; }; } // namespace poet diff --git a/src/SimParams.cpp b/src/SimParams.cpp index b8b63c823..c7bb3b30a 100644 --- a/src/SimParams.cpp +++ b/src/SimParams.cpp @@ -18,6 +18,7 @@ ** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "poet/DHT_Types.hpp" #include #include @@ -130,7 +131,7 @@ int SimParams::parseFromCmdl(char *argv[], RInside &R) { // cout << "CPP: DHT logarithm before rounding: " << ( dht_logarithm ? "ON" // : "OFF" ) << endl; - cmdl("dht-size", DHT_SIZE_PER_PROCESS) >> simparams.dht_size_per_process; + cmdl("dht-size", DHT_SIZE_PER_PROCESS_MB) >> simparams.dht_size_per_process; // cout << "CPP: DHT size per process (Byte) = " << dht_size_per_process << // endl; @@ -196,17 +197,30 @@ void SimParams::initVectorParams(RInside &R, int col_count) { /*Load significance vector from R setup file (or set default)*/ bool signif_vector_exists = R.parseEval("exists('signif_vector')"); if (signif_vector_exists) { - dht_signif_vector = as>(R["signif_vector"]); - } else { - dht_signif_vector.assign(col_count, simparams.dht_significant_digits); + dht_signif_vector = as>(R["signif_vector"]); } /*Load property type vector from R setup file (or set default)*/ bool prop_type_vector_exists = R.parseEval("exists('prop_type')"); if (prop_type_vector_exists) { - dht_prop_type_vector = as>(R["prop_type"]); - } else { - dht_prop_type_vector.assign(col_count, "act"); + std::vector prop_type_R = + as>(R["prop_type"]); + this->dht_prop_type_vector.clear(); + this->dht_prop_type_vector.reserve(prop_type_R.size()); + + for (const auto &type : prop_type_R) { + if (type == "act") { + this->dht_prop_type_vector.push_back(DHT_TYPE_ACT); + continue; + } + + if (type == "ignore") { + this->dht_prop_type_vector.push_back(DHT_TYPE_IGNORE); + continue; + } + + this->dht_prop_type_vector.push_back(DHT_TYPE_DEFAULT); + } } if (simparams.world_rank == 0) { @@ -228,21 +242,6 @@ void SimParams::initVectorParams(RInside &R, int col_count) { } } -void SimParams::setDtDiffer(bool dt_differ) { simparams.dt_differ = dt_differ; } - -t_simparams SimParams::getNumParams() const { return this->simparams; } - -std::vector SimParams::getDHTSignifVector() const { - return this->dht_signif_vector; -} -std::vector SimParams::getDHTPropTypeVector() const { - return this->dht_prop_type_vector; -} -std::string_view SimParams::getDHTFile() const { return this->dht_file; } - -std::string_view SimParams::getFilesim() const { return this->filesim; } -std::string_view SimParams::getOutDir() const { return this->out_dir; } - std::list SimParams::validateOptions(argh::parser cmdl) { /* store all unknown parameters here */ std::list retList; From e6819b59bcec72033405e05b66a5f1d41585ea63 Mon Sep 17 00:00:00 2001 From: Max Luebke Date: Tue, 14 Feb 2023 16:12:14 +0100 Subject: [PATCH 3/9] BREAKING CHANGE: Introduce ChemistryModule as extension of PhreeqcRM --- app/CMakeLists.txt | 10 +- app/poet.cpp | 261 ++++++++---- app/poet.h.in | 4 + app/poet_prm.cpp | 287 +++++++++++++ bench/dolo_diffu_inner/dolo_inner.pqi | 7 - ext/phreeqcrm | 2 +- include/poet/ChemSimPar.hpp | 273 ------------ include/poet/ChemSimSeq.hpp | 131 ------ include/poet/ChemistryModule.hpp | 413 ++++++++++++++++++ include/poet/DHT_Types.hpp | 2 +- include/poet/DHT_Wrapper.hpp | 6 +- include/poet/PhreeqcWrapper.hpp | 50 --- src/BaseChemModule.cpp | 12 - src/CMakeLists.txt | 8 +- src/ChemMaster.cpp | 445 -------------------- src/ChemSeq.cpp | 112 ----- src/ChemWorker.cpp | 305 -------------- src/ChemistryModule/CMakeLists.txt | 32 ++ src/ChemistryModule/ChemistryModule.cpp | 362 ++++++++++++++++ src/{ => ChemistryModule}/DHT.c | 0 src/{ => ChemistryModule}/DHT_Wrapper.cpp | 0 src/{ => ChemistryModule}/HashFunctions.cpp | 0 src/ChemistryModule/MasterFunctions.cpp | 360 ++++++++++++++++ src/ChemistryModule/WorkerFunctions.cpp | 422 +++++++++++++++++++ src/DiffusionModule.cpp | 1 - src/PhreeqcWrapper.cpp | 336 --------------- 26 files changed, 2082 insertions(+), 1759 deletions(-) create mode 100644 app/poet_prm.cpp delete mode 100644 include/poet/ChemSimPar.hpp delete mode 100644 include/poet/ChemSimSeq.hpp create mode 100644 include/poet/ChemistryModule.hpp delete mode 100644 include/poet/PhreeqcWrapper.hpp delete mode 100644 src/BaseChemModule.cpp delete mode 100644 src/ChemMaster.cpp delete mode 100644 src/ChemSeq.cpp delete mode 100644 src/ChemWorker.cpp create mode 100644 src/ChemistryModule/CMakeLists.txt create mode 100644 src/ChemistryModule/ChemistryModule.cpp rename src/{ => ChemistryModule}/DHT.c (100%) rename src/{ => ChemistryModule}/DHT_Wrapper.cpp (100%) rename src/{ => ChemistryModule}/HashFunctions.cpp (100%) create mode 100644 src/ChemistryModule/MasterFunctions.cpp create mode 100644 src/ChemistryModule/WorkerFunctions.cpp delete mode 100644 src/PhreeqcWrapper.cpp diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 0bf64e04f..9f8c21c49 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -2,9 +2,15 @@ get_poet_version() configure_file(poet.h.in poet.h) -add_executable(poet poet.cpp) +if (POET_USE_PRM_BACKEND) + set(poet_SRC poet_prm.cpp) +else() + set(poet_SRC poet.cpp) +endif() + +add_executable(poet ${poet_SRC}) target_include_directories(poet PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") -target_link_libraries(poet PUBLIC poet_lib MPI::MPI_C) +target_link_libraries(poet PUBLIC poet_lib MPI::MPI_CXX) #target_compile_definitions(poet PRIVATE OMPI_SKIP_MPICXX) install(TARGETS poet DESTINATION bin) diff --git a/app/poet.cpp b/app/poet.cpp index 46e20944a..69999a82d 100644 --- a/app/poet.cpp +++ b/app/poet.cpp @@ -18,11 +18,12 @@ ** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "poet/ChemistryModule.hpp" #include #include +#include #include -#include -#include +#include #include #include #include @@ -39,9 +40,65 @@ using namespace std; using namespace poet; using namespace Rcpp; -template +poet::ChemistryModule::SingleCMap DFToHashMap(const Rcpp::DataFrame &df) { + std::unordered_map out_map; + vector col_names = Rcpp::as>(df.names()); + + for (const auto &name : col_names) { + double val = df[name.c_str()]; + out_map.insert({name, val}); + } + + return out_map; +} + +void set_chem_parameters(poet::ChemistryModule &chem, uint32_t wp_size, + const std::string &database_path) { + chem.SetErrorHandlerMode(1); + chem.SetComponentH2O(false); + chem.SetRebalanceFraction(0.5); + chem.SetRebalanceByCell(true); + chem.UseSolutionDensityVolume(false); + chem.SetPartitionUZSolids(false); + + // Set concentration units + // 1, mg/L; 2, mol/L; 3, kg/kgs + chem.SetUnitsSolution(2); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsPPassemblage(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsExchange(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsSurface(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsGasPhase(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsSSassemblage(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsKinetics(1); + + // Set representative volume + std::vector rv; + rv.resize(wp_size, 1.0); + chem.SetRepresentativeVolume(rv); + + // Set initial porosity + std::vector por; + por.resize(wp_size, 1); + chem.SetPorosity(por); + + // Set initial saturation + std::vector sat; + sat.resize(wp_size, 1.0); + chem.SetSaturation(sat); + + // Load database + chem.LoadDatabase(database_path); +} + inline double RunMasterLoop(SimParams ¶ms, RInside &R, Grid &grid, - ChemistryParams &chem_params) { + ChemistryParams &chem_params, + const GridParams &g_params, uint32_t nxyz_master) { DiffusionModule diffusion(poet::DiffusionParams(R), grid); /* Iteration Count is dynamic, retrieving value from R (is only needed by @@ -50,8 +107,31 @@ inline double RunMasterLoop(SimParams ¶ms, RInside &R, Grid &grid, double sim_time = .0; - ChemistryInstance C(params, R, grid); - C.InitModule(chem_params); + ChemistryModule chem(nxyz_master, params.getNumParams().wp_size, + MPI_COMM_WORLD); + set_chem_parameters(chem, nxyz_master, chem_params.database_path); + chem.RunInitFile(chem_params.input_script); + + chem.InitializeField(grid.GetTotalCellCount(), DFToHashMap(g_params.init_df)); + + if (params.getNumParams().dht_enabled) { + chem.SetDHTEnabled(true, params.getNumParams().dht_size_per_process); + if (!params.getDHTSignifVector().empty()) { + chem.SetDHTSignifVector(params.getDHTSignifVector()); + } + if (!params.getDHTPropTypeVector().empty()) { + chem.SetDHTPropTypeVector(params.getDHTPropTypeVector()); + } + if (!params.getDHTFile().empty()) { + chem.ReadDHTFile(params.getDHTFile()); + } + if (params.getNumParams().dht_snaps > 0) { + chem.SetDHTSnaps(params.getNumParams().dht_snaps, params.getOutDir()); + } + } + + StateMemory *chem_state = grid.RegisterState("state_C", chem.GetPropNames()); + chem_state->mem = chem.GetField(); /* SIMULATION LOOP */ double dStartTime = MPI_Wtime(); @@ -77,7 +157,13 @@ inline double RunMasterLoop(SimParams ¶ms, RInside &R, Grid &grid, cout << "CPP: Chemistry" << endl; - C.Simulate(dt); + chem.SetTimeStep(dt); + + chem.SetField(chem_state->mem); + chem.SetTimeStep(dt); + chem.RunCells(); + chem_state->mem = chem.GetField(); + grid.PreModuleFieldCopy(tick++); R["req_dt"] = dt; @@ -97,12 +183,50 @@ inline double RunMasterLoop(SimParams ¶ms, RInside &R, Grid &grid, << endl << endl; - MPI_Barrier(MPI_COMM_WORLD); + // MPI_Barrier(MPI_COMM_WORLD); } // END SIMULATION LOOP R.parseEvalQ("profiling <- list()"); - C.End(); + + R["simtime_chemistry"] = chem.GetChemistryTime(); + R.parseEvalQ("profiling$simtime_chemistry <- simtime_chemistry"); + + R["chemistry_loop"] = chem.GetMasterLoopTime(); + R.parseEvalQ("profiling$chemistry_loop <- chemistry_loop"); + + R["chemistry_sequential"] = chem.GetMasterSequentialTime(); + R.parseEvalQ("profiling$simtime_sequential <- chemistry_sequential"); + + R["idle_master"] = chem.GetMasterIdleTime(); + R.parseEvalQ("profiling$idle_master <- idle_master"); + + R["idle_worker"] = Rcpp::wrap(chem.GetWorkerIdleTimings()); + R.parseEvalQ("profiling$idle_worker <- idle_worker"); + + R["phreeqc_time"] = Rcpp::wrap(chem.GetWorkerPhreeqcTimings()); + R.parseEvalQ("profiling$phreeqc <- phreeqc_time"); + + R["simtime_transport"] = diffusion.getTransportTime(); + R.parseEvalQ("profiling$simtime_transport <- simtime_transport"); + + // R["phreeqc_count"] = phreeqc_counts; + // R.parseEvalQ("profiling$phreeqc_count <- phreeqc_count"); + + if (params.getNumParams().dht_enabled) { + R["dht_hits"] = Rcpp::wrap(chem.GetWorkerDHTHits()); + R.parseEvalQ("profiling$dht_hits <- dht_hits"); + R["dht_miss"] = Rcpp::wrap(chem.GetWorkerDHTMiss()); + R.parseEvalQ("profiling$dht_miss <- dht_miss"); + R["dht_evictions"] = Rcpp::wrap(chem.GetWorkerDHTEvictions()); + R.parseEvalQ("profiling$dht_evictions <- dht_evictions"); + R["dht_get_time"] = Rcpp::wrap(chem.GetWorkerDHTGetTimings()); + R.parseEvalQ("profiling$dht_get_time <- dht_get_time"); + R["dht_fill_time"] = Rcpp::wrap(chem.GetWorkerDHTFillTimings()); + R.parseEvalQ("profiling$dht_fill_time <- dht_fill_time"); + } + + chem.MasterLoopBreak(); diffusion.end(); return MPI_Wtime() - dStartTime; @@ -119,20 +243,36 @@ int main(int argc, char *argv[]) { MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); - /*Create custom Communicator with all processes except 0 (the master) for DHT - * storage */ - MPI_Comm dht_comm; - - if (world_rank == 0) { - MPI_Comm_split(MPI_COMM_WORLD, MPI_UNDEFINED, world_rank, &dht_comm); - } else { - MPI_Comm_split(MPI_COMM_WORLD, 1, world_rank, &dht_comm); - } - if (world_rank == 0) { cout << "Running POET in version " << poet_version << endl << endl; } + if (world_rank > 0) { + { + uint32_t c_size; + MPI_Bcast(&c_size, 1, MPI_UINT32_T, 0, MPI_COMM_WORLD); + + char *buffer = new char[c_size + 1]; + MPI_Bcast(buffer, c_size, MPI_CHAR, 0, MPI_COMM_WORLD); + buffer[c_size] = '\0'; + + // ChemistryModule worker(nxyz, nxyz, MPI_COMM_WORLD); + ChemistryModule worker = + poet::ChemistryModule::createWorker(MPI_COMM_WORLD); + set_chem_parameters(worker, worker.GetWPSize(), std::string(buffer)); + + delete[] buffer; + + worker.WorkerLoop(); + } + + MPI_Barrier(MPI_COMM_WORLD); + cout << "CPP: finished, cleanup of process " << world_rank << endl; + MPI_Finalize(); + + return EXIT_SUCCESS; + } + /* initialize R runtime */ RInside R(argc, argv); @@ -154,42 +294,18 @@ int main(int argc, char *argv[]) { cout << "CPP: R Init (RInside) on process " << world_rank << endl; - // HACK: we disable master_init and dt_differ propagation here for testing - // purposes - // - bool dt_differ = false; R.parseEvalQ("mysetup <- setup"); - if (world_rank == 0) { // get timestep vector from - // grid_init function ... // - std::string master_init_code = "mysetup <- master_init(setup=setup)"; - R.parseEval(master_init_code); - - // dt_differ = R.parseEval("mysetup$dt_differ"); - // // ... and broadcast it to every other rank unequal to 0 - MPI_Bcast(&dt_differ, 1, MPI_C_BOOL, 0, MPI_COMM_WORLD); - } - /* workers will only read the setup DataFrame defined by input file */ - else { - // R.parseEval("mysetup <- setup"); - MPI_Bcast(&dt_differ, 1, MPI_C_BOOL, 0, MPI_COMM_WORLD); - } - - params.setDtDiffer(dt_differ); - - // initialize chemistry on all processes - // TODO: einlesen einer initial matrix (DataFrame) - // std::string init_chemistry_code = "mysetup <- - // init_chemistry(setup=mysetup)"; R.parseEval(init_chemistry_code); - - // TODO: Grid anpassen + // if (world_rank == 0) { // get timestep vector from + // grid_init function ... // + std::string master_init_code = "mysetup <- master_init(setup=setup)"; + R.parseEval(master_init_code); Grid grid; + GridParams g_params(R); - grid.InitModuleFromParams(GridParams(R)); - grid.PushbackModuleFlow(poet::DIFFUSION_MODULE_NAME, - poet::BaseChemModule::CHEMISTRY_MODULE_NAME); - grid.PushbackModuleFlow(poet::BaseChemModule::CHEMISTRY_MODULE_NAME, - poet::DIFFUSION_MODULE_NAME); + grid.InitModuleFromParams(g_params); + grid.PushbackModuleFlow(poet::DIFFUSION_MODULE_NAME, CHEMISTRY_MODULE_NAME); + grid.PushbackModuleFlow(CHEMISTRY_MODULE_NAME, poet::DIFFUSION_MODULE_NAME); params.initVectorParams(R, grid.GetSpeciesCount()); @@ -203,38 +319,35 @@ int main(int argc, char *argv[]) { cout << "CPP: Init done on process with rank " << world_rank << endl; } - MPI_Barrier(MPI_COMM_WORLD); + // MPI_Barrier(MPI_COMM_WORLD); poet::ChemistryParams chem_params(R); /* THIS IS EXECUTED BY THE MASTER */ - if (world_rank == 0) { - if (world_size == 1) { - dSimTime = RunMasterLoop(params, R, grid, chem_params); - } else { - dSimTime = RunMasterLoop(params, R, grid, chem_params); - } + std::string db_path = chem_params.database_path; + uint32_t c_size = db_path.size(); + MPI_Bcast(&c_size, 1, MPI_UINT32_T, 0, MPI_COMM_WORLD); - cout << "CPP: finished simulation loop" << endl; + MPI_Bcast(db_path.data(), c_size, MPI_CHAR, 0, MPI_COMM_WORLD); + uint32_t nxyz_master = (world_size == 1 ? grid.GetTotalCellCount() : 1); - cout << "CPP: start timing profiling" << endl; + dSimTime = RunMasterLoop(params, R, grid, chem_params, g_params, nxyz_master); - R["simtime"] = dSimTime; - R.parseEvalQ("profiling$simtime <- simtime"); + cout << "CPP: finished simulation loop" << endl; - string r_vis_code; - r_vis_code = "saveRDS(profiling, file=paste0(fileout,'/timings.rds'));"; - R.parseEval(r_vis_code); + cout << "CPP: start timing profiling" << endl; - cout << "CPP: Done! Results are stored as R objects into <" - << params.getOutDir() << "/timings.rds>" << endl; - } - /* THIS IS EXECUTED BY THE WORKERS */ - else { - ChemWorker worker(params, R, grid, dht_comm); - worker.InitModule(chem_params); - worker.loop(); - } + R["simtime"] = dSimTime; + R.parseEvalQ("profiling$simtime <- simtime"); + + string r_vis_code; + r_vis_code = "saveRDS(profiling, file=paste0(fileout,'/timings.rds'));"; + R.parseEval(r_vis_code); + + cout << "CPP: Done! Results are stored as R objects into <" + << params.getOutDir() << "/timings.rds>" << endl; + + MPI_Barrier(MPI_COMM_WORLD); cout << "CPP: finished, cleanup of process " << world_rank << endl; MPI_Finalize(); diff --git a/app/poet.h.in b/app/poet.h.in index 5f5279a2e..d98fe41e9 100644 --- a/app/poet.h.in +++ b/app/poet.h.in @@ -1,6 +1,10 @@ #ifndef POET_H #define POET_H +#include "poet/ChemistryModule.hpp" +#include const char *poet_version = "@POET_VERSION@"; +const char *CHEMISTRY_MODULE_NAME = "state_C"; + #endif // POET_H diff --git a/app/poet_prm.cpp b/app/poet_prm.cpp new file mode 100644 index 000000000..a5eb690b9 --- /dev/null +++ b/app/poet_prm.cpp @@ -0,0 +1,287 @@ +/* +** Copyright (C) 2018-2021 Alexander Lindemann, Max Luebke (University of +** Potsdam) +** +** Copyright (C) 2018-2022 Marco De Lucia, Max Luebke (GFZ Potsdam) +** +** POET is free software; you can redistribute it and/or modify it under the +** terms of the GNU General Public License as published by the Free Software +** Foundation; either version 2 of the License, or (at your option) any later +** version. +** +** POET is distributed in the hope that it will be useful, but WITHOUT ANY +** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +** A PARTICULAR PURPOSE. See the GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License along with +** this program; if not, write to the Free Software Foundation, Inc., 51 +** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "poet/ChemistryModule.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace poet; +using namespace Rcpp; + +void set_chem_parameters(poet::ChemistryModule &chem, uint32_t wp_size, + const std::string &database_path) { + chem.SetErrorHandlerMode(1); + chem.SetComponentH2O(false); + chem.SetRebalanceFraction(0.5); + chem.SetRebalanceByCell(true); + chem.UseSolutionDensityVolume(false); + chem.SetPartitionUZSolids(false); + + // Set concentration units + // 1, mg/L; 2, mol/L; 3, kg/kgs + chem.SetUnitsSolution(2); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsPPassemblage(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsExchange(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsSurface(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsGasPhase(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsSSassemblage(1); + // 0, mol/L cell; 1, mol/L water; 2 mol/L rock + chem.SetUnitsKinetics(1); + + // Set representative volume + std::vector rv; + rv.resize(wp_size, 1.0); + chem.SetRepresentativeVolume(rv); + + // Set initial porosity + std::vector por; + por.resize(wp_size, 1); + chem.SetPorosity(por); + + // Set initial saturation + std::vector sat; + sat.resize(wp_size, 1.0); + chem.SetSaturation(sat); + + // Load database + chem.LoadDatabase(database_path); +} + +inline double RunMasterLoop(SimParams ¶ms, RInside &R, Grid &grid, + ChemistryParams &chem_params, + const GridParams &g_params, uint32_t nxyz_master) { + + DiffusionModule diffusion(poet::DiffusionParams(R), grid); + /* Iteration Count is dynamic, retrieving value from R (is only needed by + * master for the following loop) */ + uint32_t maxiter = R.parseEval("mysetup$iterations"); + + double sim_time = .0; + + ChemistryModule chem(grid.GetTotalCellCount(), MPI_COMM_WORLD); + set_chem_parameters(chem, nxyz_master, chem_params.database_path); + chem.RunInitFile(chem_params.input_script); + + chem.SetSelectedOutputOn(true); + chem.SetTimeStep(0); + chem.RunCells(); + + StateMemory *chem_state = grid.RegisterState("state_C", chem.GetPropNames()); + auto &chem_field = chem_state->mem; + chem_field = chem.GetField(); + /* SIMULATION LOOP */ + + double dStartTime = MPI_Wtime(); + for (uint32_t iter = 1; iter < maxiter + 1; iter++) { + uint32_t tick = 0; + // cout << "CPP: Evaluating next time step" << endl; + // R.parseEvalQ("mysetup <- master_iteration_setup(mysetup)"); + + double dt = Rcpp::as( + R.parseEval("mysetup$timesteps[" + std::to_string(iter) + "]")); + cout << "CPP: Next time step is " << dt << "[s]" << endl; + + /* displaying iteration number, with C++ and R iterator */ + cout << "CPP: Going through iteration " << iter << endl; + cout << "CPP: R's $iter: " << ((uint32_t)(R.parseEval("mysetup$iter"))) + << ". Iteration" << endl; + + /* run transport */ + // TODO: transport to diffusion + diffusion.simulate(dt); + + grid.PreModuleFieldCopy(tick++); + + cout << "CPP: Chemistry" << endl; + + chem.SetTimeStep(dt); + + chem.SetConcentrations(chem_field); + chem.SetTimeStep(dt); + chem.RunCells(); + chem_field = chem.GetField(); + + grid.WriteFieldsToR(R); + grid.PreModuleFieldCopy(tick++); + + R["req_dt"] = dt; + R["simtime"] = (sim_time += dt); + + R.parseEval("mysetup$req_dt <- req_dt"); + R.parseEval("mysetup$simtime <- simtime"); + + // MDL master_iteration_end just writes on disk state_T and + // state_C after every iteration if the cmdline option + // --ignore-results is not given (and thus the R variable + // store_result is TRUE) + R.parseEvalQ("mysetup <- master_iteration_end(setup=mysetup)"); + + cout << endl + << "CPP: End of *coupling* iteration " << iter << "/" << maxiter + << endl + << endl; + + // MPI_Barrier(MPI_COMM_WORLD); + + } // END SIMULATION LOOP + + R.parseEvalQ("profiling <- list()"); + + R["simtime_chemistry"] = chem.GetChemistryTime(); + R.parseEvalQ("profiling$simtime_chemistry <- simtime_chemistry"); + + chem.MpiWorkerBreak(); + diffusion.end(); + + return MPI_Wtime() - dStartTime; +} + +int main(int argc, char *argv[]) { + double dSimTime, sim_end; + + int world_size, world_rank; + + MPI_Init(&argc, &argv); + + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + + MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); + + if (world_rank == 0) { + cout << "Running POET in version " << poet_version << endl << endl; + } + + if (world_rank > 0) { + { + uint32_t nxyz; + MPI_Bcast(&nxyz, 1, MPI_UINT32_T, 0, MPI_COMM_WORLD); + ChemistryModule worker(nxyz, MPI_COMM_WORLD); + worker.MpiWorker(); + } + + MPI_Barrier(MPI_COMM_WORLD); + cout << "CPP: finished, cleanup of process " << world_rank << endl; + MPI_Finalize(); + + return EXIT_SUCCESS; + } + + /* initialize R runtime */ + RInside R(argc, argv); + + /*Loading Dependencies*/ + // TODO: kann raus + std::string r_load_dependencies = "source('../R_lib/kin_r_library.R');"; + R.parseEvalQ(r_load_dependencies); + + SimParams params(world_rank, world_size); + int pret = params.parseFromCmdl(argv, R); + + if (pret == poet::PARSER_ERROR) { + MPI_Finalize(); + return EXIT_FAILURE; + } else if (pret == poet::PARSER_HELP) { + MPI_Finalize(); + return EXIT_SUCCESS; + } + + cout << "CPP: R Init (RInside) on process " << world_rank << endl; + + R.parseEvalQ("mysetup <- setup"); + // if (world_rank == 0) { // get timestep vector from + // grid_init function ... // + std::string master_init_code = "mysetup <- master_init(setup=setup)"; + R.parseEval(master_init_code); + + Grid grid; + GridParams g_params(R); + + grid.InitModuleFromParams(g_params); + grid.PushbackModuleFlow(poet::DIFFUSION_MODULE_NAME, CHEMISTRY_MODULE_NAME); + grid.PushbackModuleFlow(CHEMISTRY_MODULE_NAME, poet::DIFFUSION_MODULE_NAME); + + params.initVectorParams(R, grid.GetSpeciesCount()); + + // MDL: store all parameters + if (world_rank == 0) { + cout << "CPP: Calling R Function to store calling parameters" << endl; + R.parseEvalQ("StoreSetup(setup=mysetup)"); + } + + if (world_rank == 0) { + cout << "CPP: Init done on process with rank " << world_rank << endl; + } + + // MPI_Barrier(MPI_COMM_WORLD); + + poet::ChemistryParams chem_params(R); + + /* THIS IS EXECUTED BY THE MASTER */ + uint32_t nxyz = grid.GetTotalCellCount(); + MPI_Bcast(&nxyz, 1, MPI_UINT32_T, 0, MPI_COMM_WORLD); + uint32_t nxyz_master = grid.GetTotalCellCount(); + + dSimTime = RunMasterLoop(params, R, grid, chem_params, g_params, nxyz_master); + + cout << "CPP: finished simulation loop" << endl; + + cout << "CPP: start timing profiling" << endl; + + R["simtime"] = dSimTime; + R.parseEvalQ("profiling$simtime <- simtime"); + + string r_vis_code; + r_vis_code = "saveRDS(profiling, file=paste0(fileout,'/timings.rds'));"; + R.parseEval(r_vis_code); + + cout << "CPP: Done! Results are stored as R objects into <" + << params.getOutDir() << "/timings.rds>" << endl; + + MPI_Barrier(MPI_COMM_WORLD); + + cout << "CPP: finished, cleanup of process " << world_rank << endl; + MPI_Finalize(); + + if (world_rank == 0) { + cout << "CPP: done, bye!" << endl; + } + + exit(0); +} diff --git a/bench/dolo_diffu_inner/dolo_inner.pqi b/bench/dolo_diffu_inner/dolo_inner.pqi index a0d247542..99e3a2f89 100644 --- a/bench/dolo_diffu_inner/dolo_inner.pqi +++ b/bench/dolo_diffu_inner/dolo_inner.pqi @@ -1,13 +1,6 @@ SELECTED_OUTPUT -high_precision true -reset false - -time - -soln - -temperature true - -water true - -pH - -pe - -totals C Ca Cl Mg -kinetic_reactants Calcite Dolomite -equilibrium O2g diff --git a/ext/phreeqcrm b/ext/phreeqcrm index cc5ef0ff3..f862889b6 160000 --- a/ext/phreeqcrm +++ b/ext/phreeqcrm @@ -1 +1 @@ -Subproject commit cc5ef0ff37233d24108a793a74abe649dcae54b7 +Subproject commit f862889b693507458d450c6319abe5e1dce030f7 diff --git a/include/poet/ChemSimPar.hpp b/include/poet/ChemSimPar.hpp deleted file mode 100644 index 7b4d450a9..000000000 --- a/include/poet/ChemSimPar.hpp +++ /dev/null @@ -1,273 +0,0 @@ -/* -** Copyright (C) 2018-2021 Alexander Lindemann, Max Luebke (University of -** Potsdam) -** -** Copyright (C) 2018-2022 Marco De Lucia, Max Luebke (GFZ Potsdam) -** -** POET is free software; you can redistribute it and/or modify it under the -** terms of the GNU General Public License as published by the Free Software -** Foundation; either version 2 of the License, or (at your option) any later -** version. -** -** POET is distributed in the hope that it will be useful, but WITHOUT ANY -** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -** A PARTICULAR PURPOSE. See the GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License along with -** this program; if not, write to the Free Software Foundation, Inc., 51 -** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef CHEMSIMPAR_H -#define CHEMSIMPAR_H - -#include "ChemSimSeq.hpp" -#include "DHT_Wrapper.hpp" -#include "Grid.hpp" -#include "RInside.h" -#include "SimParams.hpp" -#include "poet/PhreeqcWrapper.hpp" - -#include -#include -#include -#include -#include - -/** Number of data elements that are kept free at each work package */ -#define BUFFER_OFFSET 5 - -/** Message tag indicating work */ -#define TAG_WORK 42 -/** Message tag indicating to finish loop */ -#define TAG_FINISH 43 -/** Message tag indicating timing profiling */ -#define TAG_TIMING 44 -/** Message tag indicating collecting DHT performance */ -#define TAG_DHT_PERF 45 -/** Message tag indicating simulation reached the end of an itertation */ -#define TAG_DHT_ITER 47 - -namespace poet { - -/** - * @brief Class providing execution of master chemistry - * - * Providing member functions to run an iteration and to end a simulation. Also - * a loop to send and recv pkgs from workers is implemented. - * - */ -class ChemMaster : public BaseChemModule { -public: - /** - * @brief Construct a new ChemMaster object - * - * The following steps are executed to create a new object of ChemMaster: - * -# all needed simulation parameters are extracted - * -# memory is allocated - * -# distribution of work packages is calculated - * - * @param params Simulation parameters as SimParams object - * @param R_ R runtime - * @param grid_ Grid object - */ - ChemMaster(SimParams ¶ms, RInside &R_, Grid &grid_); - - /** - * @brief Destroy the ChemMaster object - * - * By freeing ChemMaster all buffers allocated in the Constructor are freed. - * - */ - ~ChemMaster(); - - /** - * @brief Run iteration of simulation in parallel mode - * - * To run the chemistry simulation parallel following steps are done: - * - * -# 'Shuffle' the grid by previously calculated distribution of work - * packages. Convert R grid to C memory area. - * -# Start the send/recv loop. - * Detailed description in sendPkgs respectively in recvPkgs. - * -# 'Unshuffle' - * the grid and convert C memory area to R grid. - * -# Run 'master_chemistry' - * - * The main tasks are instrumented with time measurements. - * - */ - void Simulate(double dt); - - /** - * @brief End chemistry simulation. - * - * Notify the worker to finish their 'work'-loop. This is done by sending - * every worker an empty message with the tag TAG_FINISH. Now the master will - * receive measured times and DHT metrics from all worker one after another. - * Finally he will write all data to the R runtime and return this function. - * - */ - void End(); - - /** - * @brief Get the send time - * - * Time spent in send loop. - * - * @return double sent time in seconds - */ - double getSendTime(); - - /** - * @brief Get the recv time - * - * Time spent in recv loop. - * - * @return double recv time in seconds - */ - double getRecvTime(); - - /** - * @brief Get the idle time - * - * Time master was idling in MPI_Probe of recv loop. - * - * @return double idle time in seconds - */ - double getIdleTime(); - - /** - * @brief Get the Worker time - * - * Time spent in whole send/recv loop. - * - * @return double worker time in seconds - */ - double getWorkerTime(); - - /** - * @brief Get the ChemMaster time - * - * Time spent in 'master_chemistry' R function. - * - * @return double ChemMaster time in seconds - */ - double getChemMasterTime(); - - /** - * @brief Get the sequential time - * - * Time master executed code which must be run sequential. - * - * @return double seqntial time in seconds. - */ - double getSeqTime(); - -private: - void printProgressbar(int count_pkgs, int n_wp, int barWidth = 70); - inline void sendPkgs(int &pkg_to_send, int &count_pkgs, int &free_workers, - double *&work_pointer, const double &dt, - const uint32_t iteration); - inline void recvPkgs(int &pkg_to_recv, bool to_send, int &free_workers); - void shuffleField(const std::vector &in_field, uint32_t size_per_prop, - uint32_t prop_count, double *out_buffer); - void unshuffleField(const double *in_buffer, uint32_t size_per_prop, - uint32_t prop_count, std::vector &out_field); - - uint32_t wp_size; - bool dht_enabled; - - double send_t = 0.f; - double recv_t = 0.f; - double master_idle = 0.f; - double worker_t = 0.f; - double chem_master = 0.f; - double seq_t = 0.f; - - typedef struct { - char has_work; - double *send_addr; - } worker_struct; - - worker_struct *workerlist; - double *send_buffer; - double *mpi_buffer; - std::vector wp_sizes_vector; - poet::StateMemory *state; -}; - -/** - * @brief Class providing execution of worker chemistry - * - * Providing mainly a function to loop and wait for messages from the master. - * - */ -class ChemWorker : public BaseChemModule { -public: - /** - * @brief Construct a new ChemWorker object - * - * The following steps are executed to create a new object of ChemWorker: - * -# all needed simulation parameters are extracted - * -# memory is allocated - * -# Preparetion to create a DHT - * -# and finally create a new DHT_Wrapper - * - * @param params Simulation parameters as SimParams object - * @param R_ R runtime - * @param grid_ Grid object - * @param dht_comm Communicator addressing all processes marked as worker - */ - ChemWorker(SimParams ¶ms, RInside &R_, Grid &grid_, MPI_Comm dht_comm); - - void InitModule(poet::ChemistryParams &chem_params); - /** - * @brief Destroy the ChemWorker object - * - * Therefore all buffers are freed and the DHT_Wrapper object is destroyed. - * - */ - ~ChemWorker(); - - /** - * @brief Start the 'work' loop - * - * Loop in an endless loop. At the beginning probe for a message from the - * master process. If there is a receivable message evaluate the message tag. - * - */ - void loop(); - -private: - void doWork(MPI_Status &probe_status); - void postIter(); - void finishWork(); - void writeFile(); - void readFile(); - - uint32_t wp_size; - uint32_t dht_size_per_process; - uint32_t iteration = 0; - - bool dht_enabled; - bool dt_differ; - int dht_snaps; - std::string dht_file; - std::vector dht_flags; - double *mpi_buffer_results; - DHT_Wrapper *dht; - - std::array timing; - double idle_t = 0.f; - uint32_t phreeqc_count = 0; - - double *mpi_buffer; - - uint32_t ncomps; - std::string out_dir; - - PhreeqcWrapper *phreeqc_rm = std::nullptr_t(); -}; -} // namespace poet -#endif // CHEMSIMPAR_H diff --git a/include/poet/ChemSimSeq.hpp b/include/poet/ChemSimSeq.hpp deleted file mode 100644 index a6f14e35f..000000000 --- a/include/poet/ChemSimSeq.hpp +++ /dev/null @@ -1,131 +0,0 @@ -/* -** Copyright (C) 2018-2021 Alexander Lindemann, Max Luebke (University of -** Potsdam) -** -** Copyright (C) 2018-2022 Marco De Lucia, Max Luebke (GFZ Potsdam) -** -** POET is free software; you can redistribute it and/or modify it under the -** terms of the GNU General Public License as published by the Free Software -** Foundation; either version 2 of the License, or (at your option) any later -** version. -** -** POET is distributed in the hope that it will be useful, but WITHOUT ANY -** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -** A PARTICULAR PURPOSE. See the GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License along with -** this program; if not, write to the Free Software Foundation, Inc., 51 -** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef CHEMSIMSEQ_H -#define CHEMSIMSEQ_H - -#include "DHT_Wrapper.hpp" -#include "Grid.hpp" -#include "PhreeqcWrapper.hpp" -#include "RInside.h" -#include "SimParams.hpp" -#include -#include -#include -#include - -#include -#include - -/** Number of data elements that are kept free at each work package */ -#define BUFFER_OFFSET 5 - -/** Message tag indicating work */ -#define TAG_WORK 42 -/** Message tag indicating to finish loop */ -#define TAG_FINISH 43 -/** Message tag indicating timing profiling */ -#define TAG_TIMING 44 -/** Message tag indicating collecting DHT performance */ -#define TAG_DHT_PERF 45 -/** Message tag indicating simulation reached the end of an itertation */ -#define TAG_DHT_ITER 47 - -namespace poet { -/** - * @brief Base class of the chemical simulation - * - * Providing member functions to run an iteration and to end a simulation. Also - * containing basic parameters for simulation. - * - */ -class BaseChemModule { -public: - BaseChemModule(SimParams ¶ms, RInside &R_, Grid &grid_); - - virtual void InitModule(poet::ChemistryParams &chem_params){}; - - static constexpr const char *CHEMISTRY_MODULE_NAME = "state_c"; - -protected: - int world_rank; - int world_size; - RInside &R; - - Grid &grid; - double chem_t = 0.f; - double current_sim_time = 0; - - uint32_t n_cells_per_prop; - std::vector prop_names; -}; - -class ChemSeq : public BaseChemModule { - -public: - /** - * @brief Construct a new ChemSim object - * - * Creating a new instance of class ChemSim will just extract simulation - * parameters from SimParams object. - * - * @param params SimParams object - * @param R_ R runtime - * @param grid_ Initialized grid - */ - ChemSeq(SimParams ¶ms, RInside &R_, Grid &grid_); - - ~ChemSeq(); - - void InitModule(poet::ChemistryParams &chem_params); - /** - * @brief Run iteration of simulation in sequential mode - * - * This will call the correspondending R function slave_chemistry, followed by - * the execution of master_chemistry. - * - * @todo change function name. Maybe 'slave' to 'seq'. - * - */ - void Simulate(double dt); - - /** - * @brief End simulation - * - * End the simulation by distribute the measured runtime of simulation to the - * R runtime. - * - */ - void End(); - - /** - * @brief Get the Chemistry Time - * - * @return double Runtime of sequential chemistry simulation in seconds - */ - double getChemistryTime(); - -private: - poet::StateMemory *state; - PhreeqcWrapper *phreeqc_rm = std::nullptr_t(); -}; - -} // namespace poet -#endif // CHEMSIMSEQ_H diff --git a/include/poet/ChemistryModule.hpp b/include/poet/ChemistryModule.hpp new file mode 100644 index 000000000..bab677850 --- /dev/null +++ b/include/poet/ChemistryModule.hpp @@ -0,0 +1,413 @@ +#ifndef CHEMISTRYMODULE_H_ +#define CHEMISTRYMODULE_H_ + +#include "IrmResult.h" +#include "PhreeqcRM.h" +#include "poet/DHT_Wrapper.hpp" + +#include +#include +#include +#include +#include +#include + +namespace poet { +/** + * \brief Wrapper around PhreeqcRM to provide POET specific parallelization with + * easy access. + */ +class ChemistryModule : public PhreeqcRM { +public: +#ifdef POET_USE_PRM + /** + * Creates a new instance of Chemistry module with given grid cell count and + * using MPI communicator. + * + * Constructor is just a wrapper around PhreeqcRM's constructor, so just calls + * the base class constructor. + * + * \param nxyz Count of grid cells. + * \param communicator MPI communicator where work will be distributed. + */ + ChemistryModule(uint32_t nxyz, MPI_Comm communicator) + : PhreeqcRM(nxyz, communicator), n_cells(nxyz){}; +#else + /** + * Creates a new instance of Chemistry module with given grid cell count, work + * package size and communicator. + * + * This constructor shall only be called by the master. To create workers, see + * ChemistryModule::createWorker . + * + * When the use of parallelization is intended, the nxyz value shall be set to + * 1 to save memory and only one node is needed for initialization. + * + * \param nxyz Count of grid cells to allocate and initialize for each + * process. For parellel use set to 1 at the master. + * \param wp_size Count of grid cells to fill each work package at maximum. + * \param communicator MPI communicator to distribute work in. + */ + ChemistryModule(uint32_t nxyz, uint32_t wp_size, MPI_Comm communicator); + + /** + * Deconstructor, which frees DHT data structure if used. + */ + ~ChemistryModule(); +#endif + + /** + * Parses the input script and extract information needed during runtime. + * + * **Only run by master**. + * + * Database must be loaded beforehand. + * + * \param input_script_path Path to input script to parse. + */ + void RunInitFile(const std::string &input_script_path); + + /** + * Run the chemical simulation with parameters set. + */ + void RunCells(); + + /** + * Returns the chemical field. + */ + auto GetField() const { return this->field; } + /** + * Returns all known species names, including not only aqueous species, but + * also equilibrium, exchange, surface and kinetic reactants. + */ + auto GetPropNames() const { return this->prop_names; } + + /** + * Return the accumulated runtime in seconds for chemical simulation. + */ + auto GetChemistryTime() const { return this->chem_t; } + +#ifndef POET_USE_PRM + + /** + * Create a new worker instance inside given MPI communicator. + * + * Wraps communication needed before instanciation can take place. + * + * \param communicator MPI communicator to distribute work in. + * + * \returns A worker instance with fixed work package size. + */ + static ChemistryModule createWorker(MPI_Comm communicator); + + /** + * Default work package size. + */ + static constexpr uint32_t CHEM_DEFAULT_WP_SIZE = 5; + + /** + * Intended to alias input parameters for grid initialization with a single + * value per species. + */ + using SingleCMap = std::unordered_map; + + /** + * Intended to alias input parameters for grid initialization with mutlitple + * values per species. + */ + using VectorCMap = std::unordered_map>; + + /** + * Enumerating DHT file options + */ + enum { + DHT_FILES_DISABLED = 0, //!< disabled file output + DHT_FILES_SIMEND, //!< only output of snapshot after simulation + DHT_FILES_ITEREND //!< output snapshots after each iteration + }; + + /** + * Initializes field with a "DataFrame" containing a single value for each + * species. + * + * Each species must have been previously found by + * ChemistryModule::RunInitFile. + * + * \exception std::domain_error Species name of map is not defined. + * + * \param n_cells Count of cells for each species. + * \param mapped_values Unordered map containing one double value for each + * specified species. + */ + void InitializeField(uint32_t n_cells, const SingleCMap &mapped_values); + + /** + * Initializes field with a "DataFrame" containing a vector of values for each + * species. + * + * Each species must have been previously found by + * ChemistryModule::RunInitFile. + * + * There is no check if vector length matches count of grid cells defined. + * + * \exception std::domain_error Species name of map is not defined. + * + * \param mapped_values Unordered map containing a vector of multiple values. + * Size of the vectors shall be the count of grid cells defined previously. + */ + void InitializeField(const VectorCMap &mapped_values); + + /** + * **Only called by workers!** Start the worker listening loop. + */ + void WorkerLoop(); + + /** + * **Called by master** Advise the workers to break the loop. + */ + void MasterLoopBreak(); + + /** + * **Master only** Enables DHT usage for the workers. + * + * If called multiple times enabling the DHT, the current state of DHT will be + * overwritten with a new instance of the DHT. + * + * \param enable Enables or disables the usage of the DHT. + * \param size_mb Size in megabyte to allocate for the DHT if enabled. + */ + void SetDHTEnabled(bool enable, uint32_t size_mb); + /** + * **Master only** Set DHT snapshots to specific mode. + * + * \param type DHT snapshot mode. + * \param out_dir Path to output DHT snapshots. + */ + void SetDHTSnaps(int type, const std::string &out_dir); + /** + * **Master only** Set the vector with significant digits to round before + * inserting into DHT. + * + * \param signif_vec Vector defining significant digit for each species. Order + * is defined by prop_type vector (ChemistryModule::GetPropNames). + */ + void SetDHTSignifVector(std::vector signif_vec); + /** + * **Master only** Set the DHT rounding type of each species. See + * DHT_PROP_TYPES enumemartion for explanation. + * + * \param proptype_vec Vector defining DHT prop type for each species. + */ + void SetDHTPropTypeVector(std::vector proptype_vec); + /** + * **Master only** Load the state of the DHT from given file. + * + * \param input_file File to load data from. + */ + void ReadDHTFile(const std::string &input_file); + + /** + * Overwrite the current field state by another field. + * + * There are no checks if new vector dimensions matches expected sizes etc. + * + * \param field New input field. Current field state of the instance will be + * overwritten. + */ + void SetField(const std::vector &field) { this->field = field; } + + /** + * **Master only** Return count of grid cells. + */ + auto GetNCells() const { return this->n_cells; } + /** + * **Master only** Return work package size. + */ + auto GetWPSize() const { return this->wp_size; } + /** + * **Master only** Return the time in seconds the master spent waiting for any + * free worker. + */ + auto GetMasterIdleTime() const { return this->idle_t; } + /** + * **Master only** Return the time in seconds the master spent in sequential + * part of the simulation, including times for shuffling/unshuffling field + * etc. + */ + auto GetMasterSequentialTime() const { return this->seq_t; } + /** + * **Master only** Return the time in seconds the master spent in the + * send/receive loop. + */ + auto GetMasterLoopTime() const { return this->send_recv_t; } + + /** + * **Master only** Collect and return all accumulated timings recorded by + * workers to run Phreeqc simulation. + * + * \return Vector of all accumulated Phreeqc timings. + */ + std::vector GetWorkerPhreeqcTimings() const; + /** + * **Master only** Collect and return all accumulated timings recorded by + * workers to get values from the DHT. + * + * \return Vector of all accumulated DHT get times. + */ + std::vector GetWorkerDHTGetTimings() const; + /** + * **Master only** Collect and return all accumulated timings recorded by + * workers to write values to the DHT. + * + * \return Vector of all accumulated DHT fill times. + */ + std::vector GetWorkerDHTFillTimings() const; + /** + * **Master only** Collect and return all accumulated timings recorded by + * workers waiting for work packages from the master. + * + * \return Vector of all accumulated waiting times. + */ + std::vector GetWorkerIdleTimings() const; + + /** + * **Master only** Collect and return DHT hits of all workers. + * + * \return Vector of all count of DHT hits. + */ + std::vector GetWorkerDHTHits() const; + + /** + * **Master only** Collect and return DHT misses of all workers. + * + * \return Vector of all count of DHT misses. + */ + std::vector GetWorkerDHTMiss() const; + + /** + * **Master only** Collect and return DHT evictions of all workers. + * + * \return Vector of all count of DHT evictions. + */ + std::vector GetWorkerDHTEvictions() const; +#endif +protected: +#ifdef POET_USE_PRM + void PrmToPoetField(std::vector &field); +#else + enum { + CHEM_INIT, + CHEM_WP_SIZE, + CHEM_DHT_ENABLE, + CHEM_DHT_SIGNIF_VEC, + CHEM_DHT_PROP_TYPE_VEC, + CHEM_DHT_SNAPS, + CHEM_DHT_READ_FILE, + CHEM_WORK_LOOP, + CHEM_PERF, + CHEM_BREAK_MAIN_LOOP + }; + + enum { LOOP_WORK, LOOP_END }; + + enum { + WORKER_PHREEQC, + WORKER_DHT_GET, + WORKER_DHT_FILL, + WORKER_IDLE, + WORKER_DHT_HITS, + WORKER_DHT_MISS, + WORKER_DHT_EVICTIONS + }; + + struct worker_s { + double phreeqc_t = 0.; + double dht_get = 0.; + double dht_fill = 0.; + double idle_t = 0.; + }; + + struct worker_info_s { + char has_work = 0; + double *send_addr; + }; + + using worker_list_t = std::vector; + using workpointer_t = std::vector::iterator; + + void MasterRunParallel(); + void MasterRunSequential(); + + void MasterSendPkgs(worker_list_t &w_list, workpointer_t &work_pointer, + int &pkg_to_send, int &count_pkgs, int &free_workers, + double dt, uint32_t iteration, + const std::vector &wp_sizes_vector); + void MasterRecvPkgs(worker_list_t &w_list, int &pkg_to_recv, bool to_send, + int &free_workers); + + std::vector MasterGatherWorkerTimings(int type) const; + std::vector MasterGatherWorkerMetrics(int type) const; + + void WorkerProcessPkgs(struct worker_s &timings, uint32_t &iteration); + + void WorkerDoWork(MPI_Status &probe_status, int double_count, + struct worker_s &timings); + void WorkerPostIter(MPI_Status &prope_status, uint32_t iteration); + void WorkerPostSim(uint32_t iteration); + + void WorkerWriteDHTDump(uint32_t iteration); + void WorkerReadDHTDump(const std::string &dht_input_file); + + void WorkerPerfToMaster(int type, const struct worker_s &timings); + void WorkerMetricsToMaster(int type); + + IRM_RESULT WorkerRunWorkPackage(std::vector &vecWP, + std::vector &vecMapping, + double dSimTime, double dTimestep); + + void GetWPFromInternals(std::vector &vecWP, uint32_t wp_size); + void SetInternalsFromWP(const std::vector &vecWP, uint32_t wp_size); + + std::vector CalculateWPSizesVector(uint32_t n_cells, + uint32_t wp_size) const; + + int comm_size, comm_rank; + MPI_Comm group_comm; + + bool is_sequential; + bool is_master; + + uint32_t wp_size; + bool dht_enabled = false; + int dht_snaps_type = DHT_FILES_DISABLED; + std::string dht_file_out_dir; + + poet::DHT_Wrapper *dht = std::nullptr_t(); + + static constexpr uint32_t BUFFER_OFFSET = 5; + + inline void ChemBCast(void *buf, int count, MPI_Datatype datatype) const { + MPI_Bcast(buf, count, datatype, 0, this->group_comm); + } + + inline void PropagateFunctionType(int &type) const { + ChemBCast(&type, 1, MPI_INT); + } + double simtime = 0.; + double idle_t = 0.; + double seq_t = 0.; + double send_recv_t = 0.; +#endif + + double chem_t = 0.; + + uint32_t n_cells = 0; + uint32_t prop_count = 0; + std::vector prop_names; + std::vector field; + std::vector speciesPerModule; + static constexpr uint32_t MODULE_COUNT = 5; +}; +} // namespace poet + +#endif // CHEMISTRYMODULE_H_ diff --git a/include/poet/DHT_Types.hpp b/include/poet/DHT_Types.hpp index 14b55b6fd..3a57a4dc1 100644 --- a/include/poet/DHT_Types.hpp +++ b/include/poet/DHT_Types.hpp @@ -2,7 +2,7 @@ #define DHT_TYPES_H_ namespace poet { -enum { DHT_TYPE_DEFAULT, DHT_TYPE_ACT, DHT_TYPE_IGNORE }; +enum DHT_PROP_TYPES { DHT_TYPE_DEFAULT, DHT_TYPE_ACT, DHT_TYPE_IGNORE }; } #endif // DHT_TYPES_H_ diff --git a/include/poet/DHT_Wrapper.hpp b/include/poet/DHT_Wrapper.hpp index b44c8afae..081a23cb8 100644 --- a/include/poet/DHT_Wrapper.hpp +++ b/include/poet/DHT_Wrapper.hpp @@ -197,9 +197,9 @@ private: std::vector fuzzForDHT(int var_count, void *key, double dt); - uint64_t dht_hits = 0; - uint64_t dht_miss = 0; - uint64_t dht_evictions = 0; + uint32_t dht_hits = 0; + uint32_t dht_miss = 0; + uint32_t dht_evictions = 0; std::vector dht_signif_vector; std::vector dht_prop_type_vector; diff --git a/include/poet/PhreeqcWrapper.hpp b/include/poet/PhreeqcWrapper.hpp deleted file mode 100644 index 4a2d7aa27..000000000 --- a/include/poet/PhreeqcWrapper.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef PHREEQCWRAPPER_H_ -#define PHREEQCWRAPPER_H_ - -#include "IrmResult.h" -#include "poet/SimParams.hpp" -#include -#include -#include -#include - -namespace poet { -class PhreeqcWrapper : public PhreeqcRM { -public: - PhreeqcWrapper(uint32_t inxyz); - - void SetupAndLoadDB(const poet::ChemistryParams &chemPar); - void InitFromFile(const std::string &strInputFile); - - auto GetSpeciesCount() -> uint32_t; - auto GetSpeciesCountByModule() -> const std::vector &; - auto GetSpeciesNamesFull() -> const std::vector &; - - void SetInternalsFromWP(const std::vector &vecWP, - uint32_t iCurrWPSize); - void GetWPFromInternals(std::vector &vecWP, uint32_t iCurrWPSize); - - auto ReplaceTotalsByPotentials(const std::vector &vecWP, - uint32_t iCurrWPSize) -> std::vector; - - IRM_RESULT RunWorkPackage(std::vector &vecWP, - std::vector &vecMapping, double dSimTime, - double dTimestep); - -private: - void InitInternals(); - inline void SetMappingForWP(uint32_t iCurrWPSize); - - static constexpr int MODULE_COUNT = 5; - static constexpr uint32_t DHT_SELOUT = 100; - - uint32_t iWPSize; - bool bInactiveCells = false; - uint32_t iSpeciesCount; - std::vector vecSpeciesPerModule; - std::vector vecSpeciesNames; - std::vector vecDefMapping; -}; -} // namespace poet - -#endif // PHREEQCWRAPPER_H_ diff --git a/src/BaseChemModule.cpp b/src/BaseChemModule.cpp deleted file mode 100644 index a7030b986..000000000 --- a/src/BaseChemModule.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -poet::BaseChemModule::BaseChemModule(SimParams ¶ms, RInside &R_, - Grid &grid_) - : R(R_), grid(grid_) { - t_simparams tmp = params.getNumParams(); - this->world_rank = tmp.world_rank; - this->world_size = tmp.world_size; - this->prop_names = this->grid.GetPropNames(); - - this->n_cells_per_prop = this->grid.GetTotalCellCount(); -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b411d8f55..69efdf196 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,14 +7,10 @@ file(GLOB poet_lib_SRC find_library(MATH_LIBRARY m) find_library(CRYPTO_LIBRARY crypto) -option(POET_DHT_DEBUG "Build with DHT debug info" OFF) - add_library(poet_lib ${poet_lib_SRC}) target_include_directories(poet_lib PUBLIC ${PROJECT_SOURCE_DIR}/include) target_link_libraries(poet_lib PUBLIC - MPI::MPI_C ${MATH_LIBRARY} ${CRYPTO_LIBRARY} RRuntime tug PhreeqcRM DataStructures) + MPI::MPI_CXX ${MATH_LIBRARY} ${CRYPTO_LIBRARY} RRuntime tug PhreeqcRM DataStructures ChemistryModule) target_compile_definitions(poet_lib PUBLIC STRICT_R_HEADERS OMPI_SKIP_MPICXX) -if(POET_DHT_DEBUG) - target_compile_definitions(poet_lib PRIVATE DHT_STATISTICS) -endif() +add_subdirectory(ChemistryModule) diff --git a/src/ChemMaster.cpp b/src/ChemMaster.cpp deleted file mode 100644 index 0cc3d85b4..000000000 --- a/src/ChemMaster.cpp +++ /dev/null @@ -1,445 +0,0 @@ -/* -** Copyright (C) 2018-2021 Alexander Lindemann, Max Luebke (University of -** Potsdam) -** -** Copyright (C) 2018-2022 Marco De Lucia, Max Luebke (GFZ Potsdam) -** -** POET is free software; you can redistribute it and/or modify it under the -** terms of the GNU General Public License as published by the Free Software -** Foundation; either version 2 of the License, or (at your option) any later -** version. -** -** POET is distributed in the hope that it will be useful, but WITHOUT ANY -** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -** A PARTICULAR PURPOSE. See the GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License along with -** this program; if not, write to the Free Software Foundation, Inc., 51 -** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "poet/ChemSimPar.hpp" -#include "poet/ChemSimSeq.hpp" -#include "poet/DiffusionModule.hpp" -#include "poet/Grid.hpp" -#include "poet/SimParams.hpp" - -#include -#include -#include -#include -#include -#include -#include - -using namespace poet; -using namespace std; -using namespace Rcpp; - -ChemMaster::ChemMaster(SimParams ¶ms, RInside &R_, Grid &grid_) - : BaseChemModule(params, R_, grid_) { - t_simparams tmp = params.getNumParams(); - - this->wp_size = tmp.wp_size; - this->dht_enabled = tmp.dht_enabled; - - uint32_t grid_size = grid.GetTotalCellCount() * this->prop_names.size(); - - /* allocate memory */ - this->workerlist = new worker_struct[this->world_size - 1]; - std::memset(this->workerlist, '\0', sizeof(worker_struct) * (world_size - 1)); - this->send_buffer = - new double[this->wp_size * this->prop_names.size() + BUFFER_OFFSET]; - this->mpi_buffer = new double[grid_size]; - - /* calculate distribution of work packages */ - uint32_t mod_pkgs = grid.GetTotalCellCount() % this->wp_size; - uint32_t n_packages = (uint32_t)(grid.GetTotalCellCount() / this->wp_size) + - (mod_pkgs != 0 ? 1 : 0); - - Rcpp::Function wp_f("GetWorkPackageSizesVector"); - this->wp_sizes_vector = Rcpp::as>( - wp_f(n_packages, this->wp_size, grid.GetTotalCellCount())); - - this->state = this->grid.RegisterState( - poet::BaseChemModule::CHEMISTRY_MODULE_NAME, this->prop_names); - std::vector &field = this->state->mem; - - field.resize(this->n_cells_per_prop * this->prop_names.size()); - for (uint32_t i = 0; i < this->prop_names.size(); i++) { - std::vector prop_vec = - this->grid.GetSpeciesByName(this->prop_names[i]); - - std::copy(prop_vec.begin(), prop_vec.end(), - field.begin() + (i * this->n_cells_per_prop)); - } -} - -ChemMaster::~ChemMaster() { - delete this->workerlist; - delete this->send_buffer; - delete this->mpi_buffer; -} - -void ChemMaster::Simulate(double dt) { - /* declare most of the needed variables here */ - double chem_a, chem_b; - double seq_a, seq_b, seq_c, seq_d; - double worker_chemistry_a, worker_chemistry_b; - double sim_e_chemistry, sim_f_chemistry; - int pkg_to_send, pkg_to_recv; - int free_workers; - int i_pkgs; - uint32_t iteration; - - /* start time measurement of whole chemistry simulation */ - chem_a = MPI_Wtime(); - - /* start time measurement of sequential part */ - seq_a = MPI_Wtime(); - - std::vector &field = this->state->mem; - - // HACK: transfer the field into R data structure serving as input for phreeqc - R["TMP_T"] = field; - - R.parseEvalQ("mysetup$state_T <- setNames(data.frame(matrix(TMP_T, " - "ncol=length(mysetup$grid$props), nrow=" + - std::to_string(this->n_cells_per_prop) + - ")), mysetup$grid$props)"); - - /* shuffle grid */ - // grid.shuffleAndExport(mpi_buffer); - this->shuffleField(field, this->n_cells_per_prop, this->prop_names.size(), - mpi_buffer); - - /* retrieve needed data from R runtime */ - iteration = (int)R.parseEval("mysetup$iter"); - // dt = (double)R.parseEval("mysetup$requested_dt"); - - /* setup local variables */ - pkg_to_send = wp_sizes_vector.size(); - pkg_to_recv = wp_sizes_vector.size(); - double *work_pointer = mpi_buffer; - free_workers = world_size - 1; - i_pkgs = 0; - - /* end time measurement of sequential part */ - seq_b = MPI_Wtime(); - seq_t += seq_b - seq_a; - - /* start time measurement of chemistry time needed for send/recv loop */ - worker_chemistry_a = MPI_Wtime(); - - /* start send/recv loop */ - // while there are still packages to recv - while (pkg_to_recv > 0) { - // print a progressbar to stdout - printProgressbar((int)i_pkgs, (int)wp_sizes_vector.size()); - // while there are still packages to send - if (pkg_to_send > 0) { - // send packages to all free workers ... - sendPkgs(pkg_to_send, i_pkgs, free_workers, work_pointer, dt, iteration); - } - // ... and try to receive them from workers who has finished their work - recvPkgs(pkg_to_recv, pkg_to_send > 0, free_workers); - } - - // Just to complete the progressbar - cout << endl; - - /* stop time measurement of chemistry time needed for send/recv loop */ - worker_chemistry_b = MPI_Wtime(); - worker_t = worker_chemistry_b - worker_chemistry_a; - - /* start time measurement of sequential part */ - seq_c = MPI_Wtime(); - - /* unshuffle grid */ - // grid.importAndUnshuffle(mpi_buffer); - this->unshuffleField(mpi_buffer, this->n_cells_per_prop, - this->prop_names.size(), field); - - /* do master stuff */ - - /* start time measurement of master chemistry */ - sim_e_chemistry = MPI_Wtime(); - - // HACK: We don't need to call master_chemistry here since our result is - // already written to the memory as a data frame - - // R.parseEvalQ("mysetup <- master_chemistry(setup=mysetup, data=result)"); - R["TMP_T"] = Rcpp::wrap(field); - R.parseEval(std::string( - "mysetup$state_C <- setNames(data.frame(matrix(TMP_T, nrow=" + - to_string(this->n_cells_per_prop) + ")), mysetup$grid$props)")); - - /* end time measurement of master chemistry */ - sim_f_chemistry = MPI_Wtime(); - chem_master += sim_f_chemistry - sim_e_chemistry; - - /* end time measurement of sequential part */ - seq_d = MPI_Wtime(); - seq_t += seq_d - seq_c; - - /* end time measurement of whole chemistry simulation */ - chem_b = MPI_Wtime(); - chem_t += chem_b - chem_a; - - /* advise workers to end chemistry iteration */ - for (int i = 1; i < world_size; i++) { - MPI_Send(NULL, 0, MPI_DOUBLE, i, TAG_DHT_ITER, MPI_COMM_WORLD); - } - - this->current_sim_time += dt; -} - -inline void ChemMaster::sendPkgs(int &pkg_to_send, int &count_pkgs, - int &free_workers, double *&work_pointer, - const double &dt, const uint32_t iteration) { - /* declare variables */ - double master_send_a, master_send_b; - int local_work_package_size; - int end_of_wp; - - /* start time measurement */ - master_send_a = MPI_Wtime(); - - /* search for free workers and send work */ - for (int p = 0; p < world_size - 1; p++) { - if (workerlist[p].has_work == 0 && pkg_to_send > 0) /* worker is free */ { - /* to enable different work_package_size, set local copy of - * work_package_size to pre-calculated work package size vector */ - - local_work_package_size = (int)wp_sizes_vector[count_pkgs]; - count_pkgs++; - - /* note current processed work package in workerlist */ - workerlist[p].send_addr = work_pointer; - - /* push work pointer to next work package */ - end_of_wp = local_work_package_size * grid.GetSpeciesCount(); - work_pointer = &(work_pointer[end_of_wp]); - - // fill send buffer starting with work_package ... - std::memcpy(send_buffer, workerlist[p].send_addr, - (end_of_wp) * sizeof(double)); - // followed by: work_package_size - send_buffer[end_of_wp] = (double)local_work_package_size; - // current iteration of simulation - send_buffer[end_of_wp + 1] = (double)iteration; - // size of timestep in seconds - send_buffer[end_of_wp + 2] = dt; - // current time of simulation (age) in seconds - send_buffer[end_of_wp + 3] = current_sim_time; - // placeholder for work_package_count - send_buffer[end_of_wp + 4] = 0.; - - /* ATTENTION Worker p has rank p+1 */ - MPI_Send(send_buffer, end_of_wp + BUFFER_OFFSET, MPI_DOUBLE, p + 1, - TAG_WORK, MPI_COMM_WORLD); - - /* Mark that worker has work to do */ - workerlist[p].has_work = 1; - free_workers--; - pkg_to_send -= 1; - } - } - master_send_b = MPI_Wtime(); - send_t += master_send_b - master_send_a; -} - -inline void ChemMaster::recvPkgs(int &pkg_to_recv, bool to_send, - int &free_workers) { - /* declare most of the variables here */ - int need_to_receive = 1; - double master_recv_a, master_recv_b; - double idle_a, idle_b; - int p, size; - - MPI_Status probe_status; - master_recv_a = MPI_Wtime(); - /* start to loop as long there are packages to recv and the need to receive - */ - while (need_to_receive && pkg_to_recv > 0) { - // only of there are still packages to send and free workers are available - if (to_send && free_workers > 0) - // non blocking probing - MPI_Iprobe(MPI_ANY_SOURCE, TAG_WORK, MPI_COMM_WORLD, &need_to_receive, - &probe_status); - else { - idle_a = MPI_Wtime(); - // blocking probing - MPI_Probe(MPI_ANY_SOURCE, TAG_WORK, MPI_COMM_WORLD, &probe_status); - idle_b = MPI_Wtime(); - master_idle += idle_b - idle_a; - } - - /* if need_to_receive was set to true above, so there is a message to - * receive */ - if (need_to_receive) { - p = probe_status.MPI_SOURCE; - MPI_Get_count(&probe_status, MPI_DOUBLE, &size); - MPI_Recv(workerlist[p - 1].send_addr, size, MPI_DOUBLE, p, TAG_WORK, - MPI_COMM_WORLD, MPI_STATUS_IGNORE); - workerlist[p - 1].has_work = 0; - pkg_to_recv -= 1; - free_workers++; - } - } - master_recv_b = MPI_Wtime(); - recv_t += master_recv_b - master_recv_a; -} - -void ChemMaster::printProgressbar(int count_pkgs, int n_wp, int barWidth) { - /* visual progress */ - double progress = (float)(count_pkgs + 1) / n_wp; - - cout << "["; - int pos = barWidth * progress; - for (int iprog = 0; iprog < barWidth; ++iprog) { - if (iprog < pos) - cout << "="; - else if (iprog == pos) - cout << ">"; - else - cout << " "; - } - std::cout << "] " << int(progress * 100.0) << " %\r"; - std::cout.flush(); - /* end visual progress */ -} - -void ChemMaster::End() { - R["simtime_chemistry"] = chem_t; - R.parseEvalQ("profiling$simtime_chemistry <- simtime_chemistry"); - - Rcpp::NumericVector phreeqc_time; - Rcpp::NumericVector dht_get_time; - Rcpp::NumericVector dht_fill_time; - Rcpp::IntegerVector phreeqc_counts; - Rcpp::NumericVector idle_worker; - - int phreeqc_tmp; - - std::array timings; - std::array dht_perfs; - - int dht_hits = 0; - int dht_miss = 0; - int dht_evictions = 0; - - double idle_worker_tmp; - - /* loop over all workers * - * ATTENTION Worker p has rank p+1 */ - for (int p = 0; p < world_size - 1; p++) { - /* Send termination message to worker */ - MPI_Send(NULL, 0, MPI_DOUBLE, p + 1, TAG_FINISH, MPI_COMM_WORLD); - - /* ... and receive all timings and metrics from each worker */ - MPI_Recv(timings.data(), 3, MPI_DOUBLE, p + 1, TAG_TIMING, MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - phreeqc_time.push_back(timings[0], "w" + to_string(p + 1)); - - MPI_Recv(&phreeqc_tmp, 1, MPI_INT, p + 1, TAG_TIMING, MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - phreeqc_counts.push_back(phreeqc_tmp, "w" + to_string(p + 1)); - - MPI_Recv(&idle_worker_tmp, 1, MPI_DOUBLE, p + 1, TAG_TIMING, MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - idle_worker.push_back(idle_worker_tmp, "w" + to_string(p + 1)); - - if (dht_enabled) { - dht_get_time.push_back(timings[1], "w" + to_string(p + 1)); - dht_fill_time.push_back(timings[2], "w" + to_string(p + 1)); - - MPI_Recv(dht_perfs.data(), 3, MPI_INT, p + 1, TAG_DHT_PERF, - MPI_COMM_WORLD, MPI_STATUS_IGNORE); - dht_hits += dht_perfs[0]; - dht_miss += dht_perfs[1]; - dht_evictions += dht_perfs[2]; - } - } - - /* distribute all data to the R runtime */ - R["simtime_chemistry"] = chem_t; - R.parseEvalQ("profiling$simtime_chemistry <- simtime_chemistry"); - R["simtime_workers"] = worker_t; - R.parseEvalQ("profiling$simtime_workers <- simtime_workers"); - R["simtime_chemistry_master"] = chem_master; - R.parseEvalQ( - "profiling$simtime_chemistry_master <- simtime_chemistry_master"); - - R["seq_master"] = seq_t; - R.parseEvalQ("profiling$seq_master <- seq_master"); - - R["idle_master"] = master_idle; - R.parseEvalQ("profiling$idle_master <- idle_master"); - R["idle_worker"] = idle_worker; - R.parseEvalQ("profiling$idle_worker <- idle_worker"); - - R["phreeqc_time"] = phreeqc_time; - R.parseEvalQ("profiling$phreeqc <- phreeqc_time"); - - R["phreeqc_count"] = phreeqc_counts; - R.parseEvalQ("profiling$phreeqc_count <- phreeqc_count"); - - if (dht_enabled) { - R["dht_hits"] = dht_hits; - R.parseEvalQ("profiling$dht_hits <- dht_hits"); - R["dht_miss"] = dht_miss; - R.parseEvalQ("profiling$dht_miss <- dht_miss"); - R["dht_evictions"] = dht_evictions; - R.parseEvalQ("profiling$dht_evictions <- dht_evictions"); - R["dht_get_time"] = dht_get_time; - R.parseEvalQ("profiling$dht_get_time <- dht_get_time"); - R["dht_fill_time"] = dht_fill_time; - R.parseEvalQ("profiling$dht_fill_time <- dht_fill_time"); - } -} - -void ChemMaster::shuffleField(const std::vector &in_field, - uint32_t size_per_prop, uint32_t prop_count, - double *out_buffer) { - uint32_t wp_count = this->wp_sizes_vector.size(); - uint32_t write_i = 0; - for (uint32_t i = 0; i < wp_count; i++) { - for (uint32_t j = i; j < size_per_prop; j += wp_count) { - for (uint32_t k = 0; k < prop_count; k++) { - out_buffer[(write_i * prop_count) + k] = - in_field[(k * size_per_prop) + j]; - } - write_i++; - } - } -} - -void ChemMaster::unshuffleField(const double *in_buffer, uint32_t size_per_prop, - uint32_t prop_count, - std::vector &out_field) { - uint32_t wp_count = this->wp_sizes_vector.size(); - uint32_t read_i = 0; - - for (uint32_t i = 0; i < wp_count; i++) { - for (uint32_t j = i; j < size_per_prop; j += wp_count) { - for (uint32_t k = 0; k < prop_count; k++) { - out_field[(k * size_per_prop) + j] = - in_buffer[(read_i * prop_count) + k]; - } - read_i++; - } - } -} - -double ChemMaster::getSendTime() { return this->send_t; } - -double ChemMaster::getRecvTime() { return this->recv_t; } - -double ChemMaster::getIdleTime() { return this->master_idle; } - -double ChemMaster::getWorkerTime() { return this->worker_t; } - -double ChemMaster::getChemMasterTime() { return this->chem_master; } - -double ChemMaster::getSeqTime() { return this->seq_t; } diff --git a/src/ChemSeq.cpp b/src/ChemSeq.cpp deleted file mode 100644 index 6c9770872..000000000 --- a/src/ChemSeq.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* -** Copyright (C) 2018-2021 Alexander Lindemann, Max Luebke (University of -** Potsdam) -** -** Copyright (C) 2018-2022 Marco De Lucia, Max Luebke (GFZ Potsdam) -** -** POET is free software; you can redistribute it and/or modify it under the -** terms of the GNU General Public License as published by the Free Software -** Foundation; either version 2 of the License, or (at your option) any later -** version. -** -** POET is distributed in the hope that it will be useful, but WITHOUT ANY -** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -** A PARTICULAR PURPOSE. See the GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License along with -** this program; if not, write to the Free Software Foundation, Inc., 51 -** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "poet/DiffusionModule.hpp" -#include "poet/SimParams.hpp" -#include -#include - -#include - -#include -#include -#include -#include -#include - -using namespace Rcpp; -using namespace poet; - -ChemSeq::ChemSeq(SimParams ¶ms, RInside &R_, Grid &grid_) - : BaseChemModule(params, R_, grid_) { - - this->state = this->grid.RegisterState( - poet::BaseChemModule::CHEMISTRY_MODULE_NAME, this->prop_names); - std::vector &field = this->state->mem; - - field.resize(this->n_cells_per_prop * this->prop_names.size()); - for (uint32_t i = 0; i < this->prop_names.size(); i++) { - std::vector prop_vec = - this->grid.GetSpeciesByName(this->prop_names[i]); - - std::copy(prop_vec.begin(), prop_vec.end(), - field.begin() + (i * this->n_cells_per_prop)); - } -} - -poet::ChemSeq::~ChemSeq() { - if (this->phreeqc_rm) { - delete this->phreeqc_rm; - } -} - -void poet::ChemSeq::InitModule(poet::ChemistryParams &chem_params) { - this->phreeqc_rm = new PhreeqcWrapper(this->n_cells_per_prop); - - this->phreeqc_rm->SetupAndLoadDB(chem_params); - this->phreeqc_rm->InitFromFile(chem_params.input_script); -} - -void ChemSeq::Simulate(double dt) { - double chem_a, chem_b; - - /* start time measuring */ - chem_a = MPI_Wtime(); - - std::vector &field = this->state->mem; - - // HACK: transfer the field into R data structure serving as input for phreeqc - R["TMP_T"] = field; - - R.parseEvalQ("mysetup$state_T <- setNames(data.frame(matrix(TMP_T, " - "ncol=length(mysetup$grid$props), nrow=" + - std::to_string(this->n_cells_per_prop) + - ")), mysetup$grid$props)"); - - this->phreeqc_rm->SetInternalsFromWP(field, this->n_cells_per_prop); - this->phreeqc_rm->SetTime(0); - this->phreeqc_rm->SetTimeStep(dt); - this->phreeqc_rm->RunCells(); - - // HACK: we will copy resulting field into global grid field. Maybe we will - // find a more performant way here ... - std::vector vecSimResult; - this->phreeqc_rm->GetWPFromInternals(vecSimResult, this->n_cells_per_prop); - std::copy(vecSimResult.begin(), vecSimResult.end(), field.begin()); - - R["TMP_C"] = field; - - R.parseEvalQ("mysetup$state_C <- setNames(data.frame(matrix(TMP_C, " - "ncol=length(mysetup$grid$props), nrow=" + - std::to_string(this->n_cells_per_prop) + - ")), mysetup$grid$props)"); - - /* end time measuring */ - chem_b = MPI_Wtime(); - - chem_t += chem_b - chem_a; -} - -void ChemSeq::End() { - R["simtime_chemistry"] = this->chem_t; - R.parseEvalQ("profiling$simtime_chemistry <- simtime_chemistry"); -} - -double ChemSeq::getChemistryTime() { return this->chem_t; } diff --git a/src/ChemWorker.cpp b/src/ChemWorker.cpp deleted file mode 100644 index 1a5fc91e3..000000000 --- a/src/ChemWorker.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* -** Copyright (C) 2018-2021 Alexander Lindemann, Max Luebke (University of -** Potsdam) -** -** Copyright (C) 2018-2021 Marco De Lucia (GFZ Potsdam) -** -** POET is free software; you can redistribute it and/or modify it under the -** terms of the GNU General Public License as published by the Free Software -** Foundation; either version 2 of the License, or (at your option) any later -** version. -** -** POET is distributed in the hope that it will be useful, but WITHOUT ANY -** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -** A PARTICULAR PURPOSE. See the GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License along with -** this program; if not, write to the Free Software Foundation, Inc., 51 -** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "poet/ChemSimPar.hpp" -#include "poet/DHT_Wrapper.hpp" -#include "poet/SimParams.hpp" -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace poet; -using namespace std; -using namespace Rcpp; - -ChemWorker::ChemWorker(SimParams ¶ms, RInside &R_, Grid &grid_, - MPI_Comm dht_comm) - : BaseChemModule(params, R_, grid_) { - t_simparams tmp = params.getNumParams(); - this->wp_size = tmp.wp_size; - this->out_dir = params.getOutDir(); - - this->dt_differ = tmp.dt_differ; - this->dht_enabled = tmp.dht_enabled; - this->dht_size_per_process = tmp.dht_size_per_process; - this->dht_snaps = tmp.dht_snaps; - - this->dht_file = params.getDHTFile(); - - this->mpi_buffer = - new double[(this->wp_size * this->prop_names.size()) + BUFFER_OFFSET]; - this->mpi_buffer_results = new double[wp_size * this->prop_names.size()]; - - if (world_rank == 1) - cout << "CPP: Worker: DHT usage is " << (dht_enabled ? "ON" : "OFF") - << endl; - - if (this->dht_enabled) { - - uint32_t iKeyCount = this->prop_names.size() + (dt_differ); - uint32_t iDataCount = this->prop_names.size(); - - if (world_rank == 1) - cout << "CPP: Worker: data count: " << iDataCount << " entries" << endl - << "CPP: Worker: key count: " << iKeyCount << " entries" << endl - << "CPP: Worker: memory per process " - << params.getNumParams().dht_size_per_process / std::pow(10, 6) - << " MByte" << endl; - - dht = new DHT_Wrapper(params, dht_comm, - params.getNumParams().dht_size_per_process, iKeyCount, - iDataCount); - - if (world_rank == 1) - cout << "CPP: Worker: DHT created!" << endl; - - if (!dht_file.empty()) - readFile(); - // set size - dht_flags.resize(wp_size, true); - } - - this->timing.fill(0.0); -} - -ChemWorker::~ChemWorker() { - delete this->mpi_buffer; - delete this->mpi_buffer_results; - if (dht_enabled) - delete this->dht; - - if (this->phreeqc_rm) { - delete this->phreeqc_rm; - } -} - -void ChemWorker::loop() { - MPI_Status probe_status; - while (1) { - double idle_a = MPI_Wtime(); - MPI_Probe(0, MPI_ANY_TAG, MPI_COMM_WORLD, &probe_status); - double idle_b = MPI_Wtime(); - - /* there is a work package to receive */ - if (probe_status.MPI_TAG == TAG_WORK) { - idle_t += idle_b - idle_a; - doWork(probe_status); - } - /* end of iteration */ - else if (probe_status.MPI_TAG == TAG_DHT_ITER) { - postIter(); - } - /* end of simulation */ - else if (probe_status.MPI_TAG == TAG_FINISH) { - finishWork(); - break; - } - } -} - -void poet::ChemWorker::InitModule(poet::ChemistryParams &chem_params) { - this->phreeqc_rm = new PhreeqcWrapper(this->wp_size); - - this->phreeqc_rm->SetupAndLoadDB(chem_params); - this->phreeqc_rm->InitFromFile(chem_params.input_script); -} - -void ChemWorker::doWork(MPI_Status &probe_status) { - int count; - int local_work_package_size = 0; - - static int counter = 1; - - double dht_get_start, dht_get_end; - double phreeqc_time_start, phreeqc_time_end; - double dht_fill_start, dht_fill_end; - - double dt; - - bool bNoPhreeqc = false; - - /* get number of doubles to be received */ - MPI_Get_count(&probe_status, MPI_DOUBLE, &count); - - /* receive */ - MPI_Recv(mpi_buffer, count, MPI_DOUBLE, 0, TAG_WORK, MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - - /* decrement count of work_package by BUFFER_OFFSET */ - count -= BUFFER_OFFSET; - - /* check for changes on all additional variables given by the 'header' of - * mpi_buffer */ - - // work_package_size - local_work_package_size = mpi_buffer[count]; - - // current iteration of simulation - this->iteration = mpi_buffer[count + 1]; - - // current timestep size - dt = mpi_buffer[count + 2]; - - // current simulation time ('age' of simulation) - current_sim_time = mpi_buffer[count + 3]; - - /* 4th double value is currently a placeholder */ - // placeholder = mpi_buffer[count+4]; - - std::vector vecCurrWP( - mpi_buffer, - mpi_buffer + (local_work_package_size * this->prop_names.size())); - std::vector vecMappingWP(this->wp_size); - - DHT_ResultObject DHT_Results; - - for (uint32_t i = 0; i < local_work_package_size; i++) { - vecMappingWP[i] = i; - } - - if (local_work_package_size != this->wp_size) { - std::vector vecFiller( - (this->wp_size - local_work_package_size) * this->prop_names.size(), 0); - vecCurrWP.insert(vecCurrWP.end(), vecFiller.begin(), vecFiller.end()); - - // set all remaining cells to inactive - for (int i = local_work_package_size; i < this->wp_size; i++) { - vecMappingWP[i] = -1; - } - } - - if (dht_enabled) { - /* check for values in DHT */ - dht_get_start = MPI_Wtime(); - DHT_Results = dht->checkDHT(local_work_package_size, dt, vecCurrWP); - dht_get_end = MPI_Wtime(); - - DHT_Results.ResultsToMapping(vecMappingWP); - } - - phreeqc_time_start = MPI_Wtime(); - this->phreeqc_rm->RunWorkPackage(vecCurrWP, vecMappingWP, current_sim_time, - dt); - phreeqc_time_end = MPI_Wtime(); - - if (dht_enabled) { - DHT_Results.ResultsToWP(vecCurrWP); - } - - /* send results to master */ - MPI_Request send_req; - MPI_Isend(vecCurrWP.data(), count, MPI_DOUBLE, 0, TAG_WORK, MPI_COMM_WORLD, - &send_req); - - if (dht_enabled) { - /* write results to DHT */ - dht_fill_start = MPI_Wtime(); - dht->fillDHT(local_work_package_size, DHT_Results, vecCurrWP); - dht_fill_end = MPI_Wtime(); - - timing[1] += dht_get_end - dht_get_start; - timing[2] += dht_fill_end - dht_fill_start; - } - - timing[0] += phreeqc_time_end - phreeqc_time_start; - - MPI_Wait(&send_req, MPI_STATUS_IGNORE); -} - -void ChemWorker::postIter() { - MPI_Recv(NULL, 0, MPI_DOUBLE, 0, TAG_DHT_ITER, MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - if (dht_enabled) { - dht->printStatistics(); - - if (dht_snaps == 2) { - writeFile(); - } - } - // synchronize all processes - MPI_Barrier(MPI_COMM_WORLD); -} - -void ChemWorker::writeFile() { - cout.flush(); - std::stringstream out; - out << out_dir << "/iter_" << setfill('0') << setw(3) << this->iteration - << ".dht"; - int res = dht->tableToFile(out.str().c_str()); - if (res != DHT_SUCCESS && world_rank == 2) - cerr << "CPP: Worker: Error in writing current state of DHT to file." - << endl; - else if (world_rank == 2) - cout << "CPP: Worker: Successfully written DHT to file " << out.str() - << endl; -} - -void ChemWorker::readFile() { - int res = dht->fileToTable((char *)dht_file.c_str()); - if (res != DHT_SUCCESS) { - if (res == DHT_WRONG_FILE) { - if (world_rank == 1) - cerr << "CPP: Worker: Wrong file layout! Continue with empty DHT ..." - << endl; - } else { - if (world_rank == 1) - cerr << "CPP: Worker: Error in loading current state of DHT from " - "file. Continue with empty DHT ..." - << endl; - } - } else { - if (world_rank == 2) - cout << "CPP: Worker: Successfully loaded state of DHT from file " - << dht_file << endl; - std::cout.flush(); - } -} - -void ChemWorker::finishWork() { - /* before death, submit profiling/timings to master*/ - MPI_Recv(NULL, 0, MPI_DOUBLE, 0, TAG_FINISH, MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - - // timings - MPI_Send(timing.data(), timing.size(), MPI_DOUBLE, 0, TAG_TIMING, - MPI_COMM_WORLD); - - MPI_Send(&phreeqc_count, 1, MPI_INT, 0, TAG_TIMING, MPI_COMM_WORLD); - MPI_Send(&idle_t, 1, MPI_DOUBLE, 0, TAG_TIMING, MPI_COMM_WORLD); - - if (dht_enabled) { - // dht_perf - int dht_perf[3]; - dht_perf[0] = dht->getHits(); - dht_perf[1] = dht->getMisses(); - dht_perf[2] = dht->getEvictions(); - MPI_Send(dht_perf, 3, MPI_INT, 0, TAG_DHT_PERF, MPI_COMM_WORLD); - } - - if (dht_enabled && dht_snaps > 0) - writeFile(); -} diff --git a/src/ChemistryModule/CMakeLists.txt b/src/ChemistryModule/CMakeLists.txt new file mode 100644 index 000000000..5736fbfa6 --- /dev/null +++ b/src/ChemistryModule/CMakeLists.txt @@ -0,0 +1,32 @@ +option(POET_USE_PRM_BACKEND "Use PhreeqcRM parallelization instead of poet's." OFF) +option(POET_DHT_DEBUG "Build with DHT debug info" OFF) + +list(APPEND CHEM_MODEL_SRC "ChemistryModule.cpp" ) + +mark_as_advanced(PHREEQCRM_BUILD_MPI PHREEQCRM_DISABLE_OPENMP) +set(PHREEQCRM_DISABLE_OPENMP ON CACHE BOOL "" FORCE) + +if(POET_USE_PRM_BACKEND) + set(PHREEQCRM_BUILD_MPI ON CACHE BOOL "" FORCE) + target_compile_definitions(poet_lib PRIVATE POET_USE_PRM) +else() + set(PHREEQCRM_BUILD_MPI OFF CACHE BOOL "" FORCE) + list(APPEND CHEM_MODEL_SRC "WorkerFunctions.cpp" "MasterFunctions.cpp" "DHT.c" "DHT_Wrapper.cpp" "HashFunctions.cpp") +endif() + +add_library(ChemistryModule ${CHEM_MODEL_SRC}) +target_include_directories(ChemistryModule PUBLIC ${PROJECT_SOURCE_DIR}/include) +target_link_libraries(ChemistryModule + PUBLIC MPI::MPI_CXX PhreeqcRM + PRIVATE ${MATH_LIBRARY} ${CRYPTO_LIBRARY} + ) + +target_compile_definitions(ChemistryModule PUBLIC OMPI_SKIP_MPICXX) + +if(POET_DHT_DEBUG) + target_compile_definitions(ChemistryModule PRIVATE DHT_STATISTICS) +endif() + +if(POET_USE_PRM_BACKEND) + target_compile_definitions(ChemistryModule PUBLIC POET_USE_PRM) +endif() diff --git a/src/ChemistryModule/ChemistryModule.cpp b/src/ChemistryModule/ChemistryModule.cpp new file mode 100644 index 000000000..c3d22af83 --- /dev/null +++ b/src/ChemistryModule/ChemistryModule.cpp @@ -0,0 +1,362 @@ +#include "poet/ChemistryModule.hpp" +#include "PhreeqcRM.h" +#include "poet/DHT_Wrapper.hpp" + +#include +#include +#include +#include +#include +#include + +#ifndef POET_USE_PRM +poet::ChemistryModule::ChemistryModule(uint32_t nxyz, uint32_t wp_size, + MPI_Comm communicator) + : PhreeqcRM(nxyz, 1), group_comm(communicator), wp_size(wp_size) { + + MPI_Comm_size(communicator, &this->comm_size); + MPI_Comm_rank(communicator, &this->comm_rank); + + this->is_sequential = (this->comm_size == 1); + this->is_master = (this->comm_rank == 0); + + if (!is_sequential && is_master) { + MPI_Bcast(&wp_size, 1, MPI_UINT32_T, 0, this->group_comm); + } +} + +poet::ChemistryModule::~ChemistryModule() { + if (dht) { + delete dht; + } +} + +poet::ChemistryModule +poet::ChemistryModule::createWorker(MPI_Comm communicator) { + uint32_t wp_size; + MPI_Bcast(&wp_size, 1, MPI_UINT32_T, 0, communicator); + + return ChemistryModule(wp_size, wp_size, communicator); +} + +#endif + +void poet::ChemistryModule::RunInitFile(const std::string &input_script_path) { +#ifndef POET_USE_PRM + if (this->is_master) { + int f_type = CHEM_INIT; + PropagateFunctionType(f_type); + + int count = input_script_path.size(); + ChemBCast(&count, 1, MPI_INT); + ChemBCast(const_cast(input_script_path.data()), count, MPI_CHAR); + } +#endif + + this->RunFile(true, true, false, input_script_path); + this->RunString(true, false, false, "DELETE; -all; PRINT; -warnings 0;"); + + this->FindComponents(); + + std::vector props; + this->speciesPerModule.reserve(this->MODULE_COUNT); + + std::vector curr_prop_names = this->GetComponents(); + props.insert(props.end(), curr_prop_names.begin(), curr_prop_names.end()); + this->speciesPerModule.push_back(curr_prop_names.size()); + + curr_prop_names = this->GetEquilibriumPhases(); + props.insert(props.end(), curr_prop_names.begin(), curr_prop_names.end()); + char equilibrium = (curr_prop_names.empty() ? -1 : 1); + this->speciesPerModule.push_back(curr_prop_names.size()); + + curr_prop_names = this->GetExchangeNames(); + props.insert(props.end(), curr_prop_names.begin(), curr_prop_names.end()); + char exchange = (curr_prop_names.empty() ? -1 : 1); + this->speciesPerModule.push_back(curr_prop_names.size()); + + curr_prop_names = this->GetSurfaceNames(); + props.insert(props.end(), curr_prop_names.begin(), curr_prop_names.end()); + char surface = (curr_prop_names.empty() ? -1 : 1); + this->speciesPerModule.push_back(curr_prop_names.size()); + + // curr_prop_names = this->GetGasComponents(); + // props.insert(props.end(), curr_prop_names.begin(), curr_prop_names.end()); + // char gas = (curr_prop_names.empty() ? -1 : 1); + // this->speciesPerModule.push_back(curr_prop_names.size()); + + // curr_prop_names = this->GetSolidSolutionNames(); + // props.insert(props.end(), curr_prop_names.begin(), curr_prop_names.end()); + // char ssolutions = (curr_prop_names.empty() ? -1 : 1); + // this->speciesPerModule.push_back(curr_prop_names.size()); + + curr_prop_names = this->GetKineticReactions(); + props.insert(props.end(), curr_prop_names.begin(), curr_prop_names.end()); + char kinetics = (curr_prop_names.empty() ? -1 : 1); + this->speciesPerModule.push_back(curr_prop_names.size()); + + this->prop_count = props.size(); + +#ifdef POET_USE_PRM + std::vector ic1; + ic1.resize(this->nxyz * 7, -1); + // TODO: hardcoded reaction modules + for (int i = 0; i < nxyz; i++) { + ic1[i] = 1; // Solution 1 + ic1[nxyz + i] = equilibrium; // Equilibrium 1 + ic1[2 * nxyz + i] = exchange; // Exchange none + ic1[3 * nxyz + i] = surface; // Surface none + ic1[4 * nxyz + i] = -1; // Gas phase none + ic1[5 * nxyz + i] = -1; // Solid solutions none + ic1[6 * nxyz + i] = kinetics; // Kinetics 1 + } + + this->InitialPhreeqc2Module(ic1); +#else + if (!this->is_master || this->is_sequential) { + std::vector ic1; + ic1.resize(this->nxyz * 7, -1); + // TODO: hardcoded reaction modules + for (int i = 0; i < nxyz; i++) { + ic1[i] = 1; // Solution 1 + ic1[nxyz + i] = equilibrium; // Equilibrium 1 + ic1[2 * nxyz + i] = exchange; // Exchange none + ic1[3 * nxyz + i] = surface; // Surface none + ic1[4 * nxyz + i] = -1; // Gas phase none + ic1[5 * nxyz + i] = -1; // Solid solutions none + ic1[6 * nxyz + i] = kinetics; // Kinetics 1 + } + + this->InitialPhreeqc2Module(ic1); + } +#endif + this->prop_names = props; +} + +#ifndef POET_USE_PRM +void poet::ChemistryModule::InitializeField( + uint32_t n_cells, const poet::ChemistryModule::SingleCMap &mapped_values) { + if (this->is_master) { + this->field.reserve(this->prop_count * n_cells); + for (const std::string &prop_name : this->prop_names) { + const auto m_it = mapped_values.find(prop_name); + if (m_it == mapped_values.end()) { + throw std::domain_error( + "Prop names vector does not match any key in given map."); + } + + const std::vector field_row(n_cells, m_it->second); + const auto chem_field_end = this->field.end(); + this->field.insert(chem_field_end, field_row.begin(), field_row.end()); + } + this->n_cells = n_cells; + } +} + +void poet::ChemistryModule::InitializeField( + const poet::ChemistryModule::VectorCMap &mapped_values) { + if (this->is_master) { + for (const std::string &prop_name : this->prop_names) { + const auto m_it = mapped_values.find(prop_name); + if (m_it == mapped_values.end()) { + throw std::domain_error( + "Prop names vector does not match any key in given map."); + } + + const auto field_row = m_it->second; + const auto chem_field_end = this->field.end(); + this->field.insert(chem_field_end, field_row.begin(), field_row.end()); + } + this->n_cells = mapped_values.begin()->second.size(); + } +} + +void poet::ChemistryModule::SetDHTEnabled(bool enable, uint32_t size_mb) { + constexpr uint32_t MB_FACTOR = 1E6; + + if (this->is_master) { + int ftype = CHEM_DHT_ENABLE; + PropagateFunctionType(ftype); + ChemBCast(&enable, 1, MPI_CXX_BOOL); + ChemBCast(&size_mb, 1, MPI_UINT32_T); + } + + this->dht_enabled = enable; + + if (enable) { + MPI_Comm dht_comm; + + if (this->is_master) { + MPI_Comm_split(this->group_comm, MPI_UNDEFINED, this->comm_rank, + &dht_comm); + return; + } + + MPI_Comm_split(this->group_comm, 1, this->comm_rank, &dht_comm); + + if (this->dht) { + delete this->dht; + } + + const uint32_t dht_size = size_mb * MB_FACTOR; + + this->dht = + new DHT_Wrapper(dht_comm, dht_size, this->prop_count, this->prop_count); + } +} + +void poet::ChemistryModule::SetDHTSnaps(int type, const std::string &out_dir) { + if (this->is_master) { + int ftype = CHEM_DHT_SNAPS; + PropagateFunctionType(ftype); + + int str_size = out_dir.size(); + + ChemBCast(&type, 1, MPI_INT); + ChemBCast(&str_size, 1, MPI_INT); + ChemBCast(const_cast(out_dir.data()), str_size, MPI_CHAR); + } + + this->dht_file_out_dir = out_dir; +} + +void poet::ChemistryModule::SetDHTSignifVector( + std::vector signif_vec) { + if (this->is_master) { + if (signif_vec.size() != this->prop_count) { + throw std::runtime_error( + "Significant vector sizes mismatches prop count."); + } + + int ftype = CHEM_DHT_SIGNIF_VEC; + PropagateFunctionType(ftype); + ChemBCast(signif_vec.data(), signif_vec.size(), MPI_UINT32_T); + + return; + } + + this->dht->SetSignifVector(signif_vec); +} + +void poet::ChemistryModule::SetDHTPropTypeVector( + std::vector proptype_vec) { + if (this->is_master) { + if (proptype_vec.size() != this->prop_count) { + throw std::runtime_error("Prop type vector sizes mismatches prop count."); + } + + int ftype = CHEM_DHT_PROP_TYPE_VEC; + PropagateFunctionType(ftype); + ChemBCast(proptype_vec.data(), proptype_vec.size(), MPI_UINT32_T); + + return; + } + + this->dht->SetPropTypeVector(proptype_vec); +} + +void poet::ChemistryModule::ReadDHTFile(const std::string &input_file) { + if (this->is_master) { + int ftype = CHEM_DHT_READ_FILE; + PropagateFunctionType(ftype); + int str_size = input_file.size(); + + ChemBCast(&str_size, 1, MPI_INT); + ChemBCast(const_cast(input_file.data()), str_size, MPI_CHAR); + } + + if (!this->dht_enabled) { + throw std::runtime_error("DHT file cannot be read. DHT is not enabled."); + } + + if (!this->is_master) { + WorkerReadDHTDump(input_file); + } +} + +void poet::ChemistryModule::GetWPFromInternals(std::vector &vecWP, + uint32_t wp_size) { + std::vector vecCurrOutput; + + vecWP.clear(); + vecWP.reserve(this->prop_count * wp_size); + + this->GetConcentrations(vecCurrOutput); + vecWP.insert(vecWP.end(), vecCurrOutput.begin(), vecCurrOutput.end()); + + if (this->speciesPerModule[1] != 0) { + this->GetPPhaseMoles(vecCurrOutput); + vecWP.insert(vecWP.end(), vecCurrOutput.begin(), vecCurrOutput.end()); + } + + // NOTE: Block for 'Surface' and 'Exchange' is missing because of missing + // Getters @ PhreeqcRM + // ... + // BLOCK_END + + if (this->speciesPerModule[4] != 0) { + this->GetKineticsMoles(vecCurrOutput); + vecWP.insert(vecWP.end(), vecCurrOutput.begin(), vecCurrOutput.end()); + } +} +void poet::ChemistryModule::SetInternalsFromWP(const std::vector &vecWP, + uint32_t wp_size) { + uint32_t iCurrElements; + + auto itStart = vecWP.begin(); + auto itEnd = itStart; + + // this->SetMappingForWP(iCurrWPSize); + + int nchem = this->GetChemistryCellCount(); + + iCurrElements = this->speciesPerModule[0]; + + itEnd += iCurrElements * wp_size; + this->SetConcentrations(std::vector(itStart, itEnd)); + itStart = itEnd; + + // Equlibirum Phases + if ((iCurrElements = this->speciesPerModule[1]) != 0) { + itEnd += iCurrElements * wp_size; + this->SetPPhaseMoles(std::vector(itStart, itEnd)); + itStart = itEnd; + } + + // // NOTE: Block for 'Surface' and 'Exchange' is missing because of missing + // // setters @ PhreeqcRM + // // ... + // // BLOCK_END + + if ((iCurrElements = this->speciesPerModule[4]) != 0) { + itEnd += iCurrElements * wp_size; + this->SetKineticsMoles(std::vector(itStart, itEnd)); + itStart = itEnd; + } +} + +#else //POET_USE_PRM + +inline void poet::ChemistryModule::PrmToPoetField(std::vector &field) { + GetConcentrations(field); + int col = GetSelectedOutputColumnCount(); + int rows = GetSelectedOutputRowCount(); + + field.reserve(field.size() + 3 * rows); + + std::vector so; + GetSelectedOutput(so); + + for (int j = 0; j < col; j += 2) { + const auto start = so.begin() + (j * rows); + const auto end = start + rows; + field.insert(field.end(), start, end); + } +} + +void poet::ChemistryModule::RunCells() { + PhreeqcRM::RunCells(); + PrmToPoetField(this->field); +} + +#endif diff --git a/src/DHT.c b/src/ChemistryModule/DHT.c similarity index 100% rename from src/DHT.c rename to src/ChemistryModule/DHT.c diff --git a/src/DHT_Wrapper.cpp b/src/ChemistryModule/DHT_Wrapper.cpp similarity index 100% rename from src/DHT_Wrapper.cpp rename to src/ChemistryModule/DHT_Wrapper.cpp diff --git a/src/HashFunctions.cpp b/src/ChemistryModule/HashFunctions.cpp similarity index 100% rename from src/HashFunctions.cpp rename to src/ChemistryModule/HashFunctions.cpp diff --git a/src/ChemistryModule/MasterFunctions.cpp b/src/ChemistryModule/MasterFunctions.cpp new file mode 100644 index 000000000..d5667552f --- /dev/null +++ b/src/ChemistryModule/MasterFunctions.cpp @@ -0,0 +1,360 @@ +#include "PhreeqcRM.h" +#include "poet/ChemistryModule.hpp" + +#include +#include +#include +#include +#include + +std::vector +poet::ChemistryModule::MasterGatherWorkerMetrics(int type) const { + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); + + uint32_t dummy; + std::vector metrics(this->comm_size); + + MPI_Gather(&dummy, 1, MPI_UINT32_T, metrics.data(), 1, MPI_UINT32_T, 0, + this->group_comm); + + metrics.erase(metrics.begin()); + return metrics; +} + +std::vector +poet::ChemistryModule::MasterGatherWorkerTimings(int type) const { + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); + + double dummy; + std::vector timings(this->comm_size); + + MPI_Gather(&dummy, 1, MPI_DOUBLE, timings.data(), 1, MPI_DOUBLE, 0, + this->group_comm); + + timings.erase(timings.begin()); + return timings; +} + +std::vector poet::ChemistryModule::GetWorkerPhreeqcTimings() const { + int type = CHEM_PERF; + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); + return MasterGatherWorkerTimings(WORKER_PHREEQC); +} + +std::vector poet::ChemistryModule::GetWorkerDHTGetTimings() const { + int type = CHEM_PERF; + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); + return MasterGatherWorkerTimings(WORKER_DHT_GET); +} + +std::vector poet::ChemistryModule::GetWorkerDHTFillTimings() const { + int type = CHEM_PERF; + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); + return MasterGatherWorkerTimings(WORKER_DHT_FILL); +} + +std::vector poet::ChemistryModule::GetWorkerIdleTimings() const { + int type = CHEM_PERF; + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); + return MasterGatherWorkerTimings(WORKER_IDLE); +} + +std::vector poet::ChemistryModule::GetWorkerDHTHits() const { + int type = CHEM_PERF; + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); + return MasterGatherWorkerMetrics(WORKER_DHT_HITS); +} + +std::vector poet::ChemistryModule::GetWorkerDHTMiss() const { + int type = CHEM_PERF; + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); + return MasterGatherWorkerMetrics(WORKER_DHT_MISS); +} + +std::vector poet::ChemistryModule::GetWorkerDHTEvictions() const { + int type = CHEM_PERF; + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); + return MasterGatherWorkerMetrics(WORKER_DHT_EVICTIONS); +} + +inline std::vector shuffleField(const std::vector &in_field, + uint32_t size_per_prop, + uint32_t prop_count, + uint32_t wp_count) { + std::vector out_buffer(in_field.size()); + uint32_t write_i = 0; + for (uint32_t i = 0; i < wp_count; i++) { + for (uint32_t j = i; j < size_per_prop; j += wp_count) { + for (uint32_t k = 0; k < prop_count; k++) { + out_buffer[(write_i * prop_count) + k] = + in_field[(k * size_per_prop) + j]; + } + write_i++; + } + } + return out_buffer; +} + +inline void unshuffleField(const std::vector &in_buffer, + uint32_t size_per_prop, uint32_t prop_count, + uint32_t wp_count, std::vector &out_field) { + uint32_t read_i = 0; + + for (uint32_t i = 0; i < wp_count; i++) { + for (uint32_t j = i; j < size_per_prop; j += wp_count) { + for (uint32_t k = 0; k < prop_count; k++) { + out_field[(k * size_per_prop) + j] = + in_buffer[(read_i * prop_count) + k]; + } + read_i++; + } + } +} + +inline void printProgressbar(int count_pkgs, int n_wp, int barWidth = 70) { + /* visual progress */ + double progress = (float)(count_pkgs + 1) / n_wp; + + std::cout << "["; + int pos = barWidth * progress; + for (int iprog = 0; iprog < barWidth; ++iprog) { + if (iprog < pos) + std::cout << "="; + else if (iprog == pos) + std::cout << ">"; + else + std::cout << " "; + } + std::cout << "] " << int(progress * 100.0) << " %\r"; + std::cout.flush(); + /* end visual progress */ +} + +inline void poet::ChemistryModule::MasterSendPkgs( + worker_list_t &w_list, workpointer_t &work_pointer, int &pkg_to_send, + int &count_pkgs, int &free_workers, double dt, uint32_t iteration, + const std::vector &wp_sizes_vector) { + /* declare variables */ + int local_work_package_size; + + /* search for free workers and send work */ + for (int p = 0; p < this->comm_size - 1; p++) { + if (w_list[p].has_work == 0 && pkg_to_send > 0) /* worker is free */ { + /* to enable different work_package_size, set local copy of + * work_package_size to pre-calculated work package size vector */ + + local_work_package_size = (int)wp_sizes_vector[count_pkgs]; + count_pkgs++; + + /* note current processed work package in workerlist */ + w_list[p].send_addr = work_pointer.base(); + + /* push work pointer to next work package */ + const uint32_t end_of_wp = local_work_package_size * this->prop_count; + std::vector send_buffer(end_of_wp + this->BUFFER_OFFSET); + std::copy(work_pointer, work_pointer + end_of_wp, send_buffer.begin()); + + work_pointer += end_of_wp; + + // fill send buffer starting with work_package ... + // followed by: work_package_size + send_buffer[end_of_wp] = (double)local_work_package_size; + // current iteration of simulation + send_buffer[end_of_wp + 1] = (double)iteration; + // size of timestep in seconds + send_buffer[end_of_wp + 2] = dt; + // current time of simulation (age) in seconds + send_buffer[end_of_wp + 3] = this->simtime; + // placeholder for work_package_count + send_buffer[end_of_wp + 4] = 0.; + + /* ATTENTION Worker p has rank p+1 */ + // MPI_Send(send_buffer, end_of_wp + BUFFER_OFFSET, MPI_DOUBLE, p + 1, + // LOOP_WORK, this->group_comm); + MPI_Send(send_buffer.data(), send_buffer.size(), MPI_DOUBLE, p + 1, + LOOP_WORK, this->group_comm); + + /* Mark that worker has work to do */ + w_list[p].has_work = 1; + free_workers--; + pkg_to_send -= 1; + } + } +} + +inline void poet::ChemistryModule::MasterRecvPkgs(worker_list_t &w_list, + int &pkg_to_recv, + bool to_send, + int &free_workers) { + /* declare most of the variables here */ + int need_to_receive = 1; + double idle_a, idle_b; + int p, size; + + MPI_Status probe_status; + // master_recv_a = MPI_Wtime(); + /* start to loop as long there are packages to recv and the need to receive + */ + while (need_to_receive && pkg_to_recv > 0) { + // only of there are still packages to send and free workers are available + if (to_send && free_workers > 0) + // non blocking probing + MPI_Iprobe(MPI_ANY_SOURCE, LOOP_WORK, MPI_COMM_WORLD, &need_to_receive, + &probe_status); + else { + idle_a = MPI_Wtime(); + // blocking probing + MPI_Probe(MPI_ANY_SOURCE, LOOP_WORK, MPI_COMM_WORLD, &probe_status); + idle_b = MPI_Wtime(); + this->idle_t += idle_b - idle_a; + } + + /* if need_to_receive was set to true above, so there is a message to + * receive */ + if (need_to_receive) { + p = probe_status.MPI_SOURCE; + MPI_Get_count(&probe_status, MPI_DOUBLE, &size); + MPI_Recv(w_list[p - 1].send_addr, size, MPI_DOUBLE, p, LOOP_WORK, + this->group_comm, MPI_STATUS_IGNORE); + w_list[p - 1].has_work = 0; + pkg_to_recv -= 1; + free_workers++; + } + } +} + +void poet::ChemistryModule::RunCells() { + if (this->is_sequential) { + MasterRunSequential(); + return; + } + + MasterRunParallel(); +} + +void poet::ChemistryModule::MasterRunSequential() { + SetInternalsFromWP(this->field, this->nxyz); + PhreeqcRM::RunCells(); + GetWPFromInternals(this->field, this->nxyz); +} + +void poet::ChemistryModule::MasterRunParallel() { + /* declare most of the needed variables here */ + double chem_a, chem_b; + double seq_a, seq_b, seq_c, seq_d; + double worker_chemistry_a, worker_chemistry_b; + double sim_e_chemistry, sim_f_chemistry; + int pkg_to_send, pkg_to_recv; + int free_workers; + int i_pkgs; + + int ftype = CHEM_WORK_LOOP; + PropagateFunctionType(ftype); + + MPI_Barrier(this->group_comm); + + double dt = this->PhreeqcRM::GetTimeStep(); + static uint32_t iteration = 0; + + /* start time measurement of whole chemistry simulation */ + chem_a = MPI_Wtime(); + + /* start time measurement of sequential part */ + seq_a = MPI_Wtime(); + + const std::vector wp_sizes_vector = + CalculateWPSizesVector(this->n_cells, this->wp_size); + + /* shuffle grid */ + // grid.shuffleAndExport(mpi_buffer); + std::vector mpi_buffer = shuffleField( + this->field, this->n_cells, this->prop_count, wp_sizes_vector.size()); + + /* setup local variables */ + pkg_to_send = wp_sizes_vector.size(); + pkg_to_recv = wp_sizes_vector.size(); + + workpointer_t work_pointer = mpi_buffer.begin(); + worker_list_t worker_list(this->comm_size - 1); + + free_workers = this->comm_size - 1; + i_pkgs = 0; + + /* end time measurement of sequential part */ + seq_b = MPI_Wtime(); + seq_t += seq_b - seq_a; + + /* start time measurement of chemistry time needed for send/recv loop */ + worker_chemistry_a = MPI_Wtime(); + + /* start send/recv loop */ + // while there are still packages to recv + while (pkg_to_recv > 0) { + // print a progressbar to stdout + printProgressbar((int)i_pkgs, (int)wp_sizes_vector.size()); + // while there are still packages to send + if (pkg_to_send > 0) { + // send packages to all free workers ... + MasterSendPkgs(worker_list, work_pointer, pkg_to_send, i_pkgs, + free_workers, dt, iteration, wp_sizes_vector); + } + // ... and try to receive them from workers who has finished their work + MasterRecvPkgs(worker_list, pkg_to_recv, pkg_to_send > 0, free_workers); + } + + // Just to complete the progressbar + std::cout << std::endl; + + /* stop time measurement of chemistry time needed for send/recv loop */ + worker_chemistry_b = MPI_Wtime(); + this->send_recv_t += worker_chemistry_b - worker_chemistry_a; + + /* start time measurement of sequential part */ + seq_c = MPI_Wtime(); + + /* unshuffle grid */ + // grid.importAndUnshuffle(mpi_buffer); + unshuffleField(mpi_buffer, this->n_cells, this->prop_count, + wp_sizes_vector.size(), this->field); + + /* do master stuff */ + + /* start time measurement of master chemistry */ + sim_e_chemistry = MPI_Wtime(); + + /* end time measurement of sequential part */ + seq_d = MPI_Wtime(); + this->seq_t += seq_d - seq_c; + + /* end time measurement of whole chemistry simulation */ + + /* advise workers to end chemistry iteration */ + for (int i = 1; i < this->comm_size; i++) { + MPI_Send(NULL, 0, MPI_DOUBLE, i, LOOP_END, this->group_comm); + } + chem_b = MPI_Wtime(); + this->chem_t += chem_b - chem_a; + + this->simtime += dt; + iteration++; +} + +void poet::ChemistryModule::MasterLoopBreak() { + int type = CHEM_BREAK_MAIN_LOOP; + MPI_Bcast(&type, 1, MPI_INT, 0, this->group_comm); +} + +std::vector +poet::ChemistryModule::CalculateWPSizesVector(uint32_t n_cells, + uint32_t wp_size) const { + bool mod_pkgs = (n_cells % wp_size) != 0; + uint32_t n_packages = (uint32_t)(n_cells / wp_size) + mod_pkgs; + + std::vector wp_sizes_vector(n_packages, 0); + + for (int i = 0; i < n_cells; i++) { + wp_sizes_vector[i % n_packages] += 1; + } + + return wp_sizes_vector; +} diff --git a/src/ChemistryModule/WorkerFunctions.cpp b/src/ChemistryModule/WorkerFunctions.cpp new file mode 100644 index 000000000..f535cc982 --- /dev/null +++ b/src/ChemistryModule/WorkerFunctions.cpp @@ -0,0 +1,422 @@ +#include "IrmResult.h" +#include "poet/ChemistryModule.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +inline std::string get_string(int root, MPI_Comm communicator) { + int count; + MPI_Bcast(&count, 1, MPI_INT, root, communicator); + + char *buffer = new char[count + 1]; + MPI_Bcast(buffer, count, MPI_CHAR, root, communicator); + + buffer[count] = '\0'; + + std::string ret_str(buffer); + delete[] buffer; + + return ret_str; +} + +void poet::ChemistryModule::WorkerLoop() { + struct worker_s timings; + + // HACK: defining the worker iteration count here, which will increment after + // each CHEM_ITER_END message + uint32_t iteration = 1; + bool loop = true; + + while (loop) { + int func_type; + PropagateFunctionType(func_type); + + switch (func_type) { + case CHEM_INIT: { + RunInitFile(get_string(0, this->group_comm)); + break; + } + case CHEM_DHT_ENABLE: { + bool enable; + ChemBCast(&enable, 1, MPI_CXX_BOOL); + + uint32_t size_mb; + ChemBCast(&size_mb, 1, MPI_UINT32_T); + + SetDHTEnabled(enable, size_mb); + break; + } + case CHEM_DHT_SIGNIF_VEC: { + std::vector input_vec(this->prop_count); + ChemBCast(input_vec.data(), this->prop_count, MPI_UINT32_T); + + SetDHTSignifVector(input_vec); + break; + } + case CHEM_DHT_PROP_TYPE_VEC: { + std::vector input_vec(this->prop_count); + ChemBCast(input_vec.data(), this->prop_count, MPI_UINT32_T); + + SetDHTPropTypeVector(input_vec); + break; + } + case CHEM_DHT_SNAPS: { + int type; + ChemBCast(&type, 1, MPI_INT); + + SetDHTSnaps(type, get_string(0, this->group_comm)); + + break; + } + case CHEM_DHT_READ_FILE: { + ReadDHTFile(get_string(0, this->group_comm)); + break; + } + case CHEM_WORK_LOOP: { + WorkerProcessPkgs(timings, iteration); + break; + } + case CHEM_PERF: { + int type; + ChemBCast(&type, 1, MPI_INT); + if (type < WORKER_DHT_HITS) { + WorkerPerfToMaster(type, timings); + break; + } + WorkerMetricsToMaster(type); + break; + } + case CHEM_BREAK_MAIN_LOOP: { + WorkerPostSim(iteration); + loop = false; + break; + } + default: { + throw std::runtime_error("Worker received unknown tag from master."); + } + } + } +} + +void poet::ChemistryModule::WorkerProcessPkgs(struct worker_s &timings, + uint32_t &iteration) { + MPI_Status probe_status; + bool loop = true; + + MPI_Barrier(this->group_comm); + + while (loop) { + double idle_a = MPI_Wtime(); + MPI_Probe(0, MPI_ANY_TAG, this->group_comm, &probe_status); + double idle_b = MPI_Wtime(); + + switch (probe_status.MPI_TAG) { + case LOOP_WORK: { + timings.idle_t += idle_b - idle_a; + int count; + MPI_Get_count(&probe_status, MPI_DOUBLE, &count); + + WorkerDoWork(probe_status, count, timings); + break; + } + case LOOP_END: { + WorkerPostIter(probe_status, iteration); + iteration++; + loop = false; + break; + } + } + } +} + +void poet::ChemistryModule::WorkerDoWork(MPI_Status &probe_status, + int double_count, + struct worker_s &timings) { + int local_work_package_size = 0; + + static int counter = 1; + + double dht_get_start, dht_get_end; + double phreeqc_time_start, phreeqc_time_end; + double dht_fill_start, dht_fill_end; + + uint32_t iteration; + double dt; + double current_sim_time; + + const uint32_t n_cells_times_props = this->prop_count * this->wp_size; + std::vector vecCurrWP(n_cells_times_props + BUFFER_OFFSET); + int count = double_count; + + /* receive */ + MPI_Recv(vecCurrWP.data(), count, MPI_DOUBLE, 0, LOOP_WORK, this->group_comm, + MPI_STATUS_IGNORE); + + /* decrement count of work_package by BUFFER_OFFSET */ + count -= BUFFER_OFFSET; + + /* check for changes on all additional variables given by the 'header' of + * mpi_buffer */ + + // work_package_size + local_work_package_size = vecCurrWP[count]; + + // current iteration of simulation + iteration = vecCurrWP[count + 1]; + + // current timestep size + dt = vecCurrWP[count + 2]; + + // current simulation time ('age' of simulation) + current_sim_time = vecCurrWP[count + 3]; + + /* 4th double value is currently a placeholder */ + // placeholder = mpi_buffer[count+4]; + + // std::vector vecCurrWP( + // mpi_buffer, + // mpi_buffer + (local_work_package_size * this->prop_names.size())); + vecCurrWP.resize(n_cells_times_props); + std::vector vecMappingWP(this->wp_size); + + DHT_ResultObject DHT_Results; + + for (uint32_t i = 0; i < local_work_package_size; i++) { + vecMappingWP[i] = i; + } + + if (local_work_package_size != this->wp_size) { + // std::vector vecFiller( + // (this->wp_size - local_work_package_size) * prop_count, 0); + // vecCurrWP.insert(vecCurrWP.end(), vecFiller.begin(), vecFiller.end()); + + // set all remaining cells to inactive + for (int i = local_work_package_size; i < this->wp_size; i++) { + vecMappingWP[i] = -1; + } + } + + if (dht_enabled) { + /* check for values in DHT */ + dht_get_start = MPI_Wtime(); + DHT_Results = dht->checkDHT(local_work_package_size, dt, vecCurrWP); + dht_get_end = MPI_Wtime(); + + DHT_Results.ResultsToMapping(vecMappingWP); + } + + phreeqc_time_start = MPI_Wtime(); + + WorkerRunWorkPackage(vecCurrWP, vecMappingWP, current_sim_time, dt); + + phreeqc_time_end = MPI_Wtime(); + + if (dht_enabled) { + DHT_Results.ResultsToWP(vecCurrWP); + } + + /* send results to master */ + MPI_Request send_req; + MPI_Isend(vecCurrWP.data(), count, MPI_DOUBLE, 0, LOOP_WORK, MPI_COMM_WORLD, + &send_req); + + if (dht_enabled) { + /* write results to DHT */ + dht_fill_start = MPI_Wtime(); + dht->fillDHT(local_work_package_size, DHT_Results, vecCurrWP); + dht_fill_end = MPI_Wtime(); + + timings.dht_get += dht_get_end - dht_get_start; + timings.dht_fill += dht_fill_end - dht_fill_start; + } + + timings.phreeqc_t += phreeqc_time_end - phreeqc_time_start; + + MPI_Wait(&send_req, MPI_STATUS_IGNORE); +} + +void poet::ChemistryModule::WorkerPostIter(MPI_Status &prope_status, + uint32_t iteration) { + MPI_Recv(NULL, 0, MPI_DOUBLE, 0, LOOP_END, this->group_comm, + MPI_STATUS_IGNORE); + if (this->dht_enabled) { + this->dht->printStatistics(); + + if (this->dht_snaps_type == DHT_FILES_ITEREND) { + WorkerWriteDHTDump(iteration); + } + } +} +void poet::ChemistryModule::WorkerPostSim(uint32_t iteration) { + /* before death, submit profiling/timings to master*/ + + // double timings_serialized[4]; + // timings_serialized[0] = timings.phreeqc_t; + // timings_serialized[1] = timings.dht_get; + // timings_serialized[2] = timings.dht_fill; + // timings_serialized[3] = timings.idle_t; + + // // timings + // MPI_Send(timings_serialized, 4, MPI_DOUBLE, 0, 0, this->group_comm); + + // // MPI_Send(&phreeqc_count, 1, MPI_INT, 0, TAG_TIMING, MPI_COMM_WORLD); + // // MPI_Send(&idle_t, 1, MPI_DOUBLE, 0, TAG_TIMING, MPI_COMM_WORLD); + + // if (this->dht_enabled) { + // // dht_perf + // int dht_perf[3]; + // dht_perf[0] = dht->getHits(); + // dht_perf[1] = dht->getMisses(); + // dht_perf[2] = dht->getEvictions(); + // MPI_Send(dht_perf, 3, MPI_INT, 0, 0, this->group_comm); + // } + + if (this->dht_enabled && this->dht_snaps_type > DHT_FILES_SIMEND) { + WorkerWriteDHTDump(iteration); + } +} + +void poet::ChemistryModule::WorkerWriteDHTDump(uint32_t iteration) { + std::stringstream out; + out << this->dht_file_out_dir << "/iter_" << std::setfill('0') << std::setw(4) + << iteration << ".dht"; + int res = dht->tableToFile(out.str().c_str()); + if (res != DHT_SUCCESS && this->comm_rank == 2) + std::cerr + << "CPP: Worker: Error in writing current state of DHT to file.\n"; + else if (this->comm_rank == 2) + std::cout << "CPP: Worker: Successfully written DHT to file " << out.str() + << "\n"; +} +void poet::ChemistryModule::WorkerReadDHTDump( + const std::string &dht_input_file) { + int res = dht->fileToTable((char *)dht_input_file.c_str()); + if (res != DHT_SUCCESS) { + if (res == DHT_WRONG_FILE) { + if (this->comm_rank == 1) + std::cerr + << "CPP: Worker: Wrong file layout! Continue with empty DHT ...\n"; + } else { + if (this->comm_rank == 1) + std::cerr << "CPP: Worker: Error in loading current state of DHT from " + "file. Continue with empty DHT ...\n"; + } + } else { + if (this->comm_rank == 2) + std::cout << "CPP: Worker: Successfully loaded state of DHT from file " + << dht_input_file << "\n"; + } +} + +IRM_RESULT +poet::ChemistryModule::WorkerRunWorkPackage(std::vector &vecWP, + std::vector &vecMapping, + double dSimTime, double dTimestep) { + if (this->wp_size != vecMapping.size()) { + return IRM_INVALIDARG; + } + + if ((this->wp_size * this->prop_count) != vecWP.size()) { + return IRM_INVALIDARG; + } + + // check if we actually need to start phreeqc + bool bRunPhreeqc = false; + for (const auto &aMappingNum : vecMapping) { + if (aMappingNum != -1) { + bRunPhreeqc = true; + break; + } + } + + if (!bRunPhreeqc) { + return IRM_OK; + } + + std::vector vecCopy; + + vecCopy = vecWP; + for (uint32_t i = 0; i < this->prop_count; i++) { + for (uint32_t j = 0; j < this->wp_size; j++) { + vecWP[(i * this->wp_size) + j] = vecCopy[(j * this->prop_count) + i]; + } + } + + IRM_RESULT result; + this->PhreeqcRM::CreateMapping(vecMapping); + this->SetInternalsFromWP(vecWP, this->wp_size); + + this->PhreeqcRM::SetTime(dSimTime); + this->PhreeqcRM::SetTimeStep(dTimestep); + + result = this->PhreeqcRM::RunCells(); + + this->GetWPFromInternals(vecWP, this->wp_size); + + vecCopy = vecWP; + for (uint32_t i = 0; i < this->prop_count; i++) { + for (uint32_t j = 0; j < this->wp_size; j++) { + vecWP[(j * this->prop_count) + i] = vecCopy[(i * this->wp_size) + j]; + } + } + + return result; +} + +void poet::ChemistryModule::WorkerPerfToMaster(int type, + const struct worker_s &timings) { + switch (type) { + case WORKER_PHREEQC: { + MPI_Gather(&timings.phreeqc_t, 1, MPI_DOUBLE, NULL, 1, MPI_DOUBLE, 0, + this->group_comm); + break; + } + case WORKER_DHT_GET: { + MPI_Gather(&timings.dht_get, 1, MPI_DOUBLE, NULL, 1, MPI_DOUBLE, 0, + this->group_comm); + break; + } + case WORKER_DHT_FILL: { + MPI_Gather(&timings.dht_fill, 1, MPI_DOUBLE, NULL, 1, MPI_DOUBLE, 0, + this->group_comm); + break; + } + case WORKER_IDLE: { + MPI_Gather(&timings.idle_t, 1, MPI_DOUBLE, NULL, 1, MPI_DOUBLE, 0, + this->group_comm); + break; + } + default: { + throw std::runtime_error("Unknown perf type in master's message."); + } + } +} + +void poet::ChemistryModule::WorkerMetricsToMaster(int type) { + uint32_t value; + switch (type) { + case WORKER_DHT_HITS: { + value = dht->getHits(); + break; + } + case WORKER_DHT_MISS: { + value = dht->getMisses(); + break; + } + case WORKER_DHT_EVICTIONS: { + value = dht->getEvictions(); + break; + } + default: { + throw std::runtime_error("Unknown perf type in master's message."); + } + } + MPI_Gather(&value, 1, MPI_UINT32_T, NULL, 1, MPI_UINT32_T, 0, + this->group_comm); +} diff --git a/src/DiffusionModule.cpp b/src/DiffusionModule.cpp index f6282572e..b5a60ac25 100644 --- a/src/DiffusionModule.cpp +++ b/src/DiffusionModule.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/src/PhreeqcWrapper.cpp b/src/PhreeqcWrapper.cpp deleted file mode 100644 index 3b86d42c6..000000000 --- a/src/PhreeqcWrapper.cpp +++ /dev/null @@ -1,336 +0,0 @@ -#include "poet/PhreeqcWrapper.hpp" -#include "IPhreeqc.hpp" -#include "IrmResult.h" -#include "PhreeqcRM.h" - -#include -#include -#include -#include -#include -#include - -poet::PhreeqcWrapper::PhreeqcWrapper(uint32_t inxyz) - : PhreeqcRM(inxyz, 1), iWPSize(inxyz) { - this->vecDefMapping.resize(inxyz); - - for (uint32_t i = 0; i < inxyz; i++) { - this->vecDefMapping[i] = i; - } -} - -void poet::PhreeqcWrapper::SetupAndLoadDB( - const poet::ChemistryParams &chemPar) { - // TODO: hardcoded options ... - this->SetErrorHandlerMode(1); - this->SetComponentH2O(false); - this->SetRebalanceFraction(0.5); - this->SetRebalanceByCell(true); - this->UseSolutionDensityVolume(false); - this->SetPartitionUZSolids(false); - - // Set concentration units - // 1, mg/L; 2, mol/L; 3, kg/kgs - this->SetUnitsSolution(2); - // 0, mol/L cell; 1, mol/L water; 2 mol/L rock - this->SetUnitsPPassemblage(1); - // 0, mol/L cell; 1, mol/L water; 2 mol/L rock - this->SetUnitsExchange(1); - // 0, mol/L cell; 1, mol/L water; 2 mol/L rock - this->SetUnitsSurface(1); - // 0, mol/L cell; 1, mol/L water; 2 mol/L rock - this->SetUnitsGasPhase(1); - // 0, mol/L cell; 1, mol/L water; 2 mol/L rock - this->SetUnitsSSassemblage(1); - // 0, mol/L cell; 1, mol/L water; 2 mol/L rock - this->SetUnitsKinetics(1); - - // Set representative volume - std::vector rv; - rv.resize(this->iWPSize, 1.0); - this->SetRepresentativeVolume(rv); - - // Set initial porosity - std::vector por; - por.resize(this->iWPSize, 1); - this->SetPorosity(por); - - // Set initial saturation - std::vector sat; - sat.resize(this->iWPSize, 1.0); - this->SetSaturation(sat); - - // Load database - this->LoadDatabase(chemPar.database_path); -} - -void poet::PhreeqcWrapper::InitFromFile(const std::string &strInputFile) { - this->RunFile(true, true, false, strInputFile); - // MDL: this is run only by the workers - this->RunString(true, false, true, "DELETE; -all; PRINT; -warnings 0;"); - - this->FindComponents(); - - std::vector ic1; - ic1.resize(this->iWPSize * 7, -1); - // TODO: hardcoded reaction modules - for (int i = 0; i < nxyz; i++) { - ic1[i] = 1; // Solution 1 - ic1[nxyz + i] = 1; // Equilibrium 1 - ic1[2 * nxyz + i] = -1; // Exchange none - ic1[3 * nxyz + i] = -1; // Surface none - ic1[4 * nxyz + i] = -1; // Gas phase none - ic1[5 * nxyz + i] = -1; // Solid solutions none - ic1[6 * nxyz + i] = 1; // Kinetics 1 - } - - this->InitialPhreeqc2Module(ic1); - - // Initial equilibration of cells - // double time = 0.0; - // double time_step = 0.0; - // this->SetTime(time); - // this->SetTimeStep(time_step); - // this->RunCells(); - - this->InitInternals(); -} - -void poet::PhreeqcWrapper::InitInternals() { - uint32_t sum = 0, curr_count; - std::vector vecCurrSpecies; - - this->vecSpeciesPerModule.reserve(this->MODULE_COUNT); - - vecCurrSpecies = this->GetComponents(); - this->vecSpeciesNames.insert(this->vecSpeciesNames.end(), - vecCurrSpecies.begin(), vecCurrSpecies.end()); - this->vecSpeciesPerModule.push_back(vecCurrSpecies.size()); - - vecCurrSpecies = this->GetEquilibriumPhases(); - this->vecSpeciesNames.insert(this->vecSpeciesNames.end(), - vecCurrSpecies.begin(), vecCurrSpecies.end()); - this->vecSpeciesPerModule.push_back(vecCurrSpecies.size()); - - vecCurrSpecies = this->GetExchangeSpecies(); - this->vecSpeciesNames.insert(this->vecSpeciesNames.end(), - vecCurrSpecies.begin(), vecCurrSpecies.end()); - this->vecSpeciesPerModule.push_back(vecCurrSpecies.size()); - - vecCurrSpecies = this->GetSurfaceSpecies(); - this->vecSpeciesNames.insert(this->vecSpeciesNames.end(), - vecCurrSpecies.begin(), vecCurrSpecies.end()); - this->vecSpeciesPerModule.push_back(vecCurrSpecies.size()); - - vecCurrSpecies = this->GetKineticReactions(); - this->vecSpeciesNames.insert(this->vecSpeciesNames.end(), - vecCurrSpecies.begin(), vecCurrSpecies.end()); - this->vecSpeciesPerModule.push_back(vecCurrSpecies.size()); - - this->iSpeciesCount = this->vecSpeciesNames.size(); -} - -auto poet::PhreeqcWrapper::GetSpeciesCount() -> uint32_t { - return this->iSpeciesCount; -} - -auto poet::PhreeqcWrapper::GetSpeciesCountByModule() - -> const std::vector & { - return this->vecSpeciesPerModule; -} - -auto poet::PhreeqcWrapper::GetSpeciesNamesFull() - -> const std::vector & { - return this->vecSpeciesNames; -} - -void poet::PhreeqcWrapper::SetInternalsFromWP(const std::vector &vecWP, - uint32_t iCurrWPSize) { - uint32_t iCurrElements; - - auto itStart = vecWP.begin(); - auto itEnd = itStart; - - // this->SetMappingForWP(iCurrWPSize); - - int nchem = this->GetChemistryCellCount(); - - iCurrElements = this->vecSpeciesPerModule[0]; - - itEnd += iCurrElements * this->iWPSize; - std::vector out; - this->GetConcentrations(out); - this->SetConcentrations(std::vector(itStart, itEnd)); - itStart = itEnd; - - // Equlibirum Phases - if ((iCurrElements = this->vecSpeciesPerModule[1]) != 0) { - itEnd += iCurrElements * this->iWPSize; - this->GetPPhaseMoles(out); - this->SetPPhaseMoles(std::vector(itStart, itEnd)); - itStart = itEnd; - } - - // // NOTE: Block for 'Surface' and 'Exchange' is missing because of missing - // // setters @ PhreeqcRM - // // ... - // // BLOCK_END - - if ((iCurrElements = this->vecSpeciesPerModule[4]) != 0) { - itEnd += iCurrElements * this->iWPSize; - this->GetKineticsMoles(out); - this->SetKineticsMoles(std::vector(itStart, itEnd)); - itStart = itEnd; - } -} -void poet::PhreeqcWrapper::GetWPFromInternals(std::vector &vecWP, - uint32_t iCurrWPSize) { - std::vector vecCurrOutput; - - vecWP.clear(); - vecWP.reserve(this->iSpeciesCount * this->iWPSize); - - this->GetConcentrations(vecCurrOutput); - vecWP.insert(vecWP.end(), vecCurrOutput.begin(), vecCurrOutput.end()); - - if (this->vecSpeciesPerModule[1] != 0) { - this->GetPPhaseMoles(vecCurrOutput); - vecWP.insert(vecWP.end(), vecCurrOutput.begin(), vecCurrOutput.end()); - } - - // NOTE: Block for 'Surface' and 'Exchange' is missing because of missing - // Getters @ PhreeqcRM - // ... - // BLOCK_END - - if (this->vecSpeciesPerModule[4] != 0) { - this->GetKineticsMoles(vecCurrOutput); - vecWP.insert(vecWP.end(), vecCurrOutput.begin(), vecCurrOutput.end()); - } -} - -auto poet::PhreeqcWrapper::ReplaceTotalsByPotentials( - const std::vector &vecWP, uint32_t iCurrWPSize) - -> std::vector { - - uint32_t iPropsPerCell = this->vecSpeciesNames.size(); - - int iphreeqcResult; - std::vector vecOutput; - vecOutput.reserve((iPropsPerCell - 1) * iCurrWPSize); - - auto itCStart = vecWP.begin(); - auto itCEnd = itCStart + (this->vecSpeciesPerModule[0]); - uint32_t iDiff = iPropsPerCell - this->vecSpeciesPerModule[0]; - - for (uint32_t i = 0; i < iCurrWPSize; i++) { - std::vector vecCIn(itCStart, itCEnd); - double pH, pe; - - // FIXME: Hardcoded temperatures and pressures here! - IPhreeqc *util_ptr = this->Concentrations2Utility( - vecCIn, std::vector(1, 25.0), std::vector(1, 1.0)); - - std::string sInput = "SELECTED_OUTPUT " + std::to_string(this->DHT_SELOUT) + - "; -pH; -pe;RUN_CELLS; -cells 1; -time_step 0"; - iphreeqcResult = util_ptr->RunString(sInput.c_str()); - if (iphreeqcResult != 0) { - throw std::runtime_error("IPhreeqc Utility returned non-zero value: " + - std::to_string(iphreeqcResult)); - } - - util_ptr->SetCurrentSelectedOutputUserNumber(this->DHT_SELOUT); - - int vtype; - static std::string svalue(100, '\0'); - - iphreeqcResult = util_ptr->GetSelectedOutputValue2( - 1, 0, &vtype, &pH, svalue.data(), svalue.capacity()); - if (iphreeqcResult != 0) { - throw std::runtime_error("IPhreeqc Utility returned non-zero value: " + - std::to_string(iphreeqcResult)); - } - - iphreeqcResult = util_ptr->GetSelectedOutputValue2( - 1, 1, &vtype, &pe, svalue.data(), svalue.capacity()); - if (iphreeqcResult != 0) { - throw std::runtime_error("IPhreeqc Utility returned non-zero value: " + - std::to_string(iphreeqcResult)); - } - - vecOutput.insert(vecOutput.end(), vecCIn.begin() + 3, vecCIn.end()); - vecOutput.push_back(pH); - vecOutput.push_back(pe); - vecOutput.insert(vecOutput.end(), itCEnd, itCEnd + iDiff); - - itCStart = itCEnd + iDiff; - itCEnd = itCStart + (this->vecSpeciesPerModule[0]); - } - - return vecOutput; -} - -IRM_RESULT -poet::PhreeqcWrapper::RunWorkPackage(std::vector &vecWP, - std::vector &vecMapping, - double dSimTime, double dTimestep) { - if (this->iWPSize != vecMapping.size()) { - return IRM_INVALIDARG; - } - - if ((this->iWPSize * this->iSpeciesCount) != vecWP.size()) { - return IRM_INVALIDARG; - } - - // check if we actually need to start phreeqc - bool bRunPhreeqc = false; - for (const auto &aMappingNum : vecMapping) { - if (aMappingNum != -1) { - bRunPhreeqc = true; - break; - } - } - - if (!bRunPhreeqc) { - return IRM_OK; - } - - std::vector vecCopy; - - vecCopy = vecWP; - for (uint32_t i = 0; i < this->iSpeciesCount; i++) { - for (uint32_t j = 0; j < this->iWPSize; j++) { - vecWP[(i * this->iWPSize) + j] = vecCopy[(j * this->iSpeciesCount) + i]; - } - } - - IRM_RESULT result; - this->CreateMapping(vecMapping); - this->SetInternalsFromWP(vecWP, this->iWPSize); - this->SetTime(dSimTime); - this->SetTimeStep(dTimestep); - result = this->PhreeqcRM::RunCells(); - this->GetWPFromInternals(vecWP, this->iWPSize); - - vecCopy = vecWP; - for (uint32_t i = 0; i < this->iSpeciesCount; i++) { - for (uint32_t j = 0; j < this->iWPSize; j++) { - vecWP[(j * this->iSpeciesCount) + i] = vecCopy[(i * this->iWPSize) + j]; - } - } - - return result; -} - -inline void poet::PhreeqcWrapper::SetMappingForWP(uint32_t iCurrWPSize) { - if (iCurrWPSize == this->iWPSize) { - this->CreateMapping(this->vecDefMapping); - return; - } - - std::vector vecCurrMapping(this->vecDefMapping); - for (uint32_t i = iCurrWPSize; i < vecCurrMapping.size(); i++) { - vecCurrMapping[i] = -1; - } - this->CreateMapping(vecCurrMapping); -} From 32d35190cbd7a966d98d41bbb9bea0bd69878dd7 Mon Sep 17 00:00:00 2001 From: Max Luebke Date: Tue, 14 Feb 2023 16:37:56 +0100 Subject: [PATCH 4/9] fix: write fields using R after chem simulation --- app/poet.cpp | 1 + include/poet/DiffusionModule.hpp | 2 +- include/poet/Grid.hpp | 3 +++ src/Grid.cpp | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/poet.cpp b/app/poet.cpp index 69999a82d..097c9ff33 100644 --- a/app/poet.cpp +++ b/app/poet.cpp @@ -164,6 +164,7 @@ inline double RunMasterLoop(SimParams ¶ms, RInside &R, Grid &grid, chem.RunCells(); chem_state->mem = chem.GetField(); + grid.WriteFieldsToR(R); grid.PreModuleFieldCopy(tick++); R["req_dt"] = dt; diff --git a/include/poet/DiffusionModule.hpp b/include/poet/DiffusionModule.hpp index ce486b9e2..52d0199d0 100644 --- a/include/poet/DiffusionModule.hpp +++ b/include/poet/DiffusionModule.hpp @@ -39,7 +39,7 @@ namespace poet { * */ -constexpr const char *DIFFUSION_MODULE_NAME = "state_t"; +constexpr const char *DIFFUSION_MODULE_NAME = "state_T"; class DiffusionModule { public: diff --git a/include/poet/Grid.hpp b/include/poet/Grid.hpp index 852220919..2ea2068b9 100644 --- a/include/poet/Grid.hpp +++ b/include/poet/Grid.hpp @@ -22,6 +22,7 @@ #define GRID_H #include "poet/SimParams.hpp" +#include #include #include #include @@ -97,6 +98,8 @@ public: std::string module_name = poet::GRID_MODULE_NAME) const -> std::vector; + void WriteFieldsToR(RInside &R); + private: std::vector flow_vec; diff --git a/src/Grid.cpp b/src/Grid.cpp index 2fe125d32..ba70e16ee 100644 --- a/src/Grid.cpp +++ b/src/Grid.cpp @@ -19,7 +19,9 @@ */ #include "poet/SimParams.hpp" +#include #include +#include #include #include #include @@ -253,3 +255,20 @@ auto poet::Grid::GetSpeciesByName(std::string name, return std::vector(module_memory->mem.begin() + begin_vec, module_memory->mem.begin() + end_vec); } + +// HACK: Helper function +void poet::Grid::WriteFieldsToR(RInside &R) { + + for (auto const &elem : this->state_register) { + std::string field_name = elem.first; + StateMemory *field = elem.second; + + const uint32_t n_cells_per_prop = field->mem.size() / field->props.size(); + + R["TMP"] = Rcpp::wrap(field->mem); + R["TMP_PROPS"] = Rcpp::wrap(field->props); + R.parseEval(std::string( + "mysetup$" + field_name + " <- setNames(data.frame(matrix(TMP, nrow=" + + std::to_string(n_cells_per_prop) + ")), TMP_PROPS)")); + } +} From 27b94065ea9e5ae00af3d0233e293612338cb559 Mon Sep 17 00:00:00 2001 From: Marco De Lucia Date: Wed, 1 Mar 2023 10:57:17 +0100 Subject: [PATCH 5/9] data: added surfex benchmark --- bench/surfex/SMILE_2021_11_01_TH.dat | 3718 ++++++++++++++++++++++++++ bench/surfex/SurfExBase.pqi | 56 + bench/surfex/surfex.R | 139 + 3 files changed, 3913 insertions(+) create mode 100644 bench/surfex/SMILE_2021_11_01_TH.dat create mode 100644 bench/surfex/SurfExBase.pqi create mode 100644 bench/surfex/surfex.R diff --git a/bench/surfex/SMILE_2021_11_01_TH.dat b/bench/surfex/SMILE_2021_11_01_TH.dat new file mode 100644 index 000000000..f9f3e8475 --- /dev/null +++ b/bench/surfex/SMILE_2021_11_01_TH.dat @@ -0,0 +1,3718 @@ +# SMILE Thermodynamic Database (EDH version) +# +# Project: SMILE Version 01-November-2021 +################################################################################################################################## +# +# This thermodynamic database has been developed by Helmholtz-Zentrum Dresden-Rossendorf and GRS Braunschweig for the BMWi founded projects: +# +# - SMILE: "Smart-Kd in der Langzeitsicherheitsanalyse - Anwendungen" (Contract Nos. 02E11668B) +# - WEIMAR: "Further Development of the Smart Kd-Concept for Long-Term Safety Assessment" (Contract Nos. 02 E 10518 + 02 E 11072A) +# - ESTRAL: "Realistic Integrataion of Sorption Processes in Transport Programs for long-term Safety Analysis" (Contract Nos. 02 E 10528 + 02 E 11072B) +# +# For the geochemical calculations within this projects two separate thermodynamic databases were created (dependent on the salinity of the groundwater solutions): +# +# (I) The EDH version for groundwater solutions with ionic strength lower than 0.5 mol L-1. This database +# based on the actual PSI/Nagra Chemical Thermodynamic Database Version 12/07 (PSI/Nagra TDB 12/07) formatted for +# PHREEQC /Thoenen et al. 2014/ considering the Davies approach /Davies, 1962/ to represent ion-ion interactions +# based on the Extended Debye-Hückel Theory (EDH) with updated values from NEA Second Update, Vol. 14 (Grenthe et al. 2020) +# +# (II) The Pitzer version (PIT.dat) for high saline solutions using the Pitzer formalism. +# +# For the projects the site-specific minerals and matrix elements of the sedimentary rock above the repository site Gorleben +# and actinides and fission products relevant in the context of a nuclear waste repository are important and considered in the database. +# So far only parameters for T=298.15 K are provided. Relevant thermodynamic data which are not included or not actual in the PSI/Nagra TDB 12/07 +# were taken from either other databases, original literature or own batch experiments and were clearly commented in the database +# and listed here (not relevent date or not recommended data wer commented out, e.g. Graphite, Molybdenum, Niob, Palladium, Tin). +# +# In general the data can be divided into three groups: +# +# (1) Thermodynamic data for aqueous element species +# - Fe+2/Fe+3 were updated from NEA TDB Vol. 13a [Lemire et al., 2013] +# - Mg+2 (MgPO4-, MgHPO4, MgCl+ & MgH2PO4) complexes were added from the LLNL database +# - MgOH+ was updated from [Brown & Ekberg, 2016] +# - Mn+3, MnO4-2, MnO4- +# - U, Np and Am(III) data were updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) and THEREDA Release 2020 (for U) +# +# (2) Solubility data for site-specific Minerals: +# - Fe+2/Fe+3-solid phases were updated from NEA TDB Vol. 13a [Lemire et al., 2013] +# - Albite, Anorthite, Chlorite, Illite and Montmorillonite were included from the ANDRA Database +# ThermoChimie [Giffaut et al., 2014]. Thereby, for Albite, only Albite-low was used, being stable below +# 700°C with an ordered Si-Al arrangement. +# - K-Feldspar (Orthoclase) were calculated from logK(T)-functions published in Stefánsson and Arnórsson (2000) for Microcline. +# - Muscovite were taken from Richter et al. (2016). +# - generic Gibbsite phase 'Gibbsite(gen)' from own fit to experimental Gorleben data +# - amorphous Gibbsite phase from Lindsay (1979) +# - U, Np and Am(III) data were updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) and THEREDA Release 2020 (for U) +# +# (3) Thermodynamic sorption data for representative sorbates (pair of element and minerals): +# - Surface complexation data (SCM), e.g. protolysis constants (pK-values), stability constants (log K-values) +# and reaction equations were taken from the thermodynamic sorption database RES³T (Rossendorf Expert System +# for Surface and Sorption Thermodynamics; [Brendler et al. 2003], full bibliographic references are +# available at http://www.hzdr.de/res3t). +# - Missing SCM data are derived from batch experiments by GRS & HZDR. +# - primary the Diffuse Double Layer Model (DDL) is preferred, but in case of no/scarce SCM data sets +# additionally other SCM models are used +# - generic sites (»XOH) are prefered (with no differentiation between strong and weak sites) +# - Following Kulik (2006, 2002) the protolysis and stability constants were normalised to a reference surface +# site density of 2.31 nm-2 as recommended by Davis and Kent (1990). +# - All SCM-values were corrected to infinite dilution (ionic strength 0) using the Davies equation (Davies, 1962). +# - The solid surface binding sites are essential components and their abbreviations correspond to the international +# code after Whitney and Evans (2010), e. g. for quartz: =Qz-OH2+. +# +# +# References: +# +# Altmaier, M., Brendler, V, Bosbach, D., Kienzler, B., Marquardt, C. M., Neck, V., Richter, A., 2004. Sichtung, Zusammenstellung +# und Bewertung von Daten zur geochemischen Modellierung. Forschungszentrum Karlsruhe, Report FZK - INE 002/04, (2004), 520 pp. +# +# Brendler, V., Vahle, A., Arnold, T., Bernhard, G., Fanghänel, T., 2003. RES³T-Rossendorf expert system for surface and sorption +# thermodynamics. J. Cont. Hydrol., 61, 281-291. +# +# Brown, P.L., Ekberg, C., 2016, Hydrolysis of Metal Ions, Vol. 1, John Wiley & Sons, 952pp. +# +# Cornell, R.M., Schwertmann, U., 2003. The iron oxides - structure, properties, reactions, occurrences and uses. +# 2nd edition, Wiley-VCH, Weinheim, 185-220. +# +# Davies, C. W., 1962. Ion Association. Butterworths, Washington. +# +# Davis, J.A., Kent, D.B., 1990. Surface complexation modeling in aqueous geochemistry, in: Hochella, M.F., White, A.F. (Eds.), +# Mineral-Water Interface Geochemistry. Reviews in Mineralogy, Vol. 23. MSA, Washington, D.C., pp. 177-258. +# +# Giffaut, E., Grivé, M., Blanc, P., Viellard, P., Colàs, E., Gailhanou, H., Gaboreau, S., Marty, N., Madé, B., Duro, L., (2014). +# Andra thermodynamic database for performance assessment: ThermoChimie. Appl. Geochem. 49, 225-236. +# +# Guillaumont, R., Fanghaenel, T., Fuger, J., Grenthe, I., Neck, V., Palmer, D.A., Rand, M.H. (2003). +# Vol. 5. Update on the Chemical Thermodynamics of Uranium, Neptunium, Plutonium, Americium and Technetium. +# OECD Nuclear Energy Agency Data Bank, Eds., North Holland Elsevier Science Publishers B.V., Amsterdam, The Netherlands. +# +# Grenthe et al. (2020). Vol. 14. Second Update on the Chemical Thermodynamics of Uranium, Neptunium, Plutonium, Americium and Technetium, +# OECD Nuclear Energy Agency Data Bank, Eds., OECD Publications, Paris, France. +# +# Kulik, D.A., 2002. Sorption modelling by Gibbs energy minimisation: Towards a uniform thermodynamic database for surface complexes +# of radionuclides. Radiochim. Acta 90, 815-832. +# +# Kulik, D.A., 2006. Standard molar Gibbs energies and activity coefficients of surface complexes on mineral-water interfaces +# (thermodynamic insights), in: Lützenkirchen, J. (Eds.), Surface complexation modelling. Academic Press, Amsterdam, pp. 171-250. +# +# Lemire, R.J., Berner, U., Musikas, C., Palmer, D.A., Taylor, P., Tochiyama, O. (2013). +# Vol. 13a. Chemical Thermodynamics of Iron, Part 1. OECD Nuclear Energy Agency Data Bank, Eds., OECD Publications, +# Issy-les-Moulineaux, France. +# +# Lindsay, W.L., 1979. Chemical equilibria in soils. John Wiley & Sons, New York. +# +# Richter, C., 2015. Sorption of environmentally relevant radionuclides (U(VI), Np(V)) and lanthanides (Nd(III)) on feldspar and mica. +# Doctoral thesis, TU Dresden (available at: https://www.hzdr.de/db/Cms?pNid=2850). +# +# Richter, C., Müller, K., Drobot, B., Steudtner, R., Großmann, K., Stockmann, M., Brendler, V., 2016. Macroscopic and spectroscopic +# characterization of uranium(VI) sorption onto orthoclase and muscovite and the influence of competing Ca2+. +# Geochim. Cosmochim. Acta, 189, 143-157. +# +# Stefánsson, A., Arnórsson, S., 2000. Feldspar saturation state in natural waters. Geochim. Cosmochim. Acta, 64, 2567-84. +# +# Thoenen T., Hummel W., Berner U., Curti E., 2014. The PSI/Nagra Chemical Thermodynamic Database 12/07, PSI Report 14-04. +# available for download at http://www.psi.ch/les/database +# +# Whitney, D. L., Evans, B. W., 2010. Abbreviations for names of rock-forming minerals. Amer. Mineral. 95, 185-187. +# +# +# +######################################################################################################################## +# Original PSI/NAGRA TDB 12/07 +# +# PSI/Nagra Thermochemical Database Version 12/07 LAST MOD. 11-JUN-2015 +# PSINA_110615_DAV_s.dat +# +# The documentation for this database is available on http://www.psi.ch/les/database. +# +# Change history ----------------------------------------------------------------------------------- +# PSINA_120110_DAV_s.dat +# 12-JAN-2010 : Added Becquerelite and Compreignacite to PSINA_060110_DAV_s.dat +# PSINA_050710_DAV_s.dat +# 05-JULY-2010: Added CmSCN+2 to PSINA_120110_DAV_s.dat +# 05-JULY-2010: Added NpO2SCN to PSINA_120110_DAV_s.dat +# PSINA_050710_rev_DAV_s.dat +# 14-FEB-2011 : Sn: Changed incorrect formula (for conversion from mass to mole units) and +# incorrect elemental gfw in PSINA_050710_DAV.dat and PSINA_050710_DAV_s.dat +# 14-FEB-2011 : Added NpSiO(OH)3+2 to PSINA_050710_DAV_s.dat +# 14-FEB-2011 : Added USiO4(s) to PSINA_050710_DAV_s.dat +# 20-FEB-2011 : corrected log_k for UO2(am,hyd) (from -1.5 to 1.5) in PSINA_050710_DAV.dat +# and PSINA_050710_DAV_s.dat +# PSINA_290714_DAV_s.dat +# 29-JUL-2014 : Added I(+5) to the SOLUTION_MASTER_SPECIES +# corrected log_k for I2 (wrong sign) +# corrected log_k for IO3- (from -122.0400 to -101.0900) +# changed some comments +# PSINA_110615_DAV_s.dat +# 11-JUN-2015: Changed references to PSI reports and added comment concerning documentation, +# deleted warning concerning use of database at temperatures other than 25ûC +#--------------------------------------------------------------------------------------------------- +# +# ACTIVITY COEFFICIENTS: +# +# This version of the database uses the Davies equation for the calculation of activity coefficients. +# -gamma 0.00 0.00 for neutral species ensures that the activity coefficients +# are equal to one. +# +# TEMPERATURE: +# +# This version of the database only contains logK-data for 25ûC +# +# DOCUMENTATION: +# +# NAGRA NTB 91-17: Pearson F.J., Berner U. (1992): Nagra Thermochemical Data Base I. Core Data, +# Nagra NTB 91-17. +# available for download at http://www.nagra.ch/de/downloadcenter.htm +# NAGRA NTB 91-18: Pearson F.J., Berner U., Hummel W. (1992): Nagra Thermochemical Data Base +# II. Supplemental Data, Nagra NTB 91-18. +# available for download at http://www.nagra.ch/de/downloadcenter.htm +# NAGRA NTB 02-16: Hummel W., Berner U., Curti E., Pearson F.J., Thoenen T. (2002): Nagra/PSI +# Chemical Thermodynamic Database 01/01, Nagra NTB 02-16. +# available for download at http://www.nagra.ch/de/downloadcenter.htm +# PSI Report 14-04: Thoenen T., Hummel W., Berner U., Curti E. (2014): The PSI/Nagra Chemical +# Thermodynamic Database 12/07, PSI Report 14-04. +# available for download at http://www.psi.ch/les/database +# +#--------------------------------------------------------------------------------------------------- +# +SOLUTION_MASTER_SPECIES +# +# ATOMIC WEIGHTS +# Naturally occurring elements: IUPAC 1993 Table 1 rounded to 0.001 +# Radioelements: Mass number of longest-lived isotope +# +# +# +# elemen species alk gfw_formula element_gfw atomic Disposition Source of data +# number PMATCHC +# +H H+ -1.0 H 1.008 # 1 Ele NAGRA NTB 91-17 +H(0) H2 0.0 H # Ma(S) NAGRA NTB 91-17 +H(1) H+ -1.0 H # Ma(P) NAGRA NTB 91-17 +E e- 0.0 0.0 0.0 # Ma(P) NAGRA NTB 91-17 +O H2O 0.0 O 15.999 # 8 Ele NAGRA NTB 91-17 +O(0) O2 0.0 O # Ma(S) NAGRA NTB 91-17 +O(-2) H2O 0.0 O # Ma(P) NAGRA NTB 91-17 +Al Al+3 0.0 Al 26.982 # 13 Ele, Ma(P) NAGRA NTB 02-16 +Am Am+3 0.0 Am 243 # 95 Ele, Ma(P) PSI Report 14-04 +Am(3) Am+3 0.0 Am # PSI Report 14-04 +Am(5) AmO2+ 0.0 Am # PSI Report 14-04 +As HAsO4-2 0.0 As 74.922 # 33 Ele NAGRA NTB 91-17 +As(3) As(OH)3 0.0 As # Ma(S) NAGRA NTB 91-17 +As(5) HAsO4-2 1.0 As # Ma(P) NAGRA NTB 91-17 +B B(OH)3 0.0 B 10.812 # 5 Ma(P) NAGRA NTB 91-18 +Ba Ba+2 0.0 Ba 137.328 # 56 Ma(P) NAGRA NTB 91-17 +Br Br- 0.0 Br 79.904 # 35 Ma(P) NAGRA NTB 91-17 +C HCO3- 1.0 C 12.011 # 6 Ele NAGRA NTB 91-17 +C(+4) HCO3- 1.0 HCO3- # Ma(P) NAGRA NTB 91-17 +C(-4) CH4 0.0 CH4 # Ma(S) NAGRA NTB 91-17 +Alkalinity HCO3- 1.0 HCO3- 61.016 # NAGRA NTB 91-17 +Ca Ca+2 0.0 Ca 40.078 # 20 Ma(P) NAGRA NTB 91-17 +Cl Cl- 0.0 Cl 35.453 # 17 Ma(P) NAGRA NTB 91-17 +Cm Cm+3 0.0 Cm 247 # PSI Report 14-04 +Cs Cs+ 0.0 Cs 132.905 # 55 Ma(P) Master Species only +Eu Eu+3 0.0 Eu 151.966 # 63 Ele Replaced with Data from ANDRA TDB ThermoChimie +Eu(2) Eu+2 0.0 Eu # Ma(S) Replaced with Data from ANDRA TDB ThermoChimie +Eu(3) Eu+3 0.0 Eu # Ma(P) Replaced with Data from ANDRA TDB ThermoChimie +F F- 0.0 F 18.998 # 9 Ma(P) NAGRA NTB 91-17 +Fe Fe+2 0.0 Fe 55.845 # 26 Ele NAGRA NTB 91-18 +Fe(2) Fe+2 0.0 Fe # Ma(P) NAGRA NTB 91-18 +Fe(3) Fe+3 0.0 Fe # Ma(S) NAGRA NTB 91-18 +I I- 0.0 I 126.904 # 53 Ele NAGRA NTB 91-18 +I(-1) I- 0.0 I # Ma(P) NAGRA NTB 91-18 +I(0) I2 0.0 I # Ma(S) NAGRA NTB 91-18 +I(+5) IO3- 0.0 I # PSI Report 14-04 +K K+ 0.0 K 39.098 # 19 Ma(P) NAGRA NTB 91-17 +Li Li+ 0.0 Li 6.941 # 6 Ma(P) NAGRA NTB 91-17 +Mg Mg+2 0.0 Mg 24.305 # 12 Ma(P) NAGRA NTB 91-17 & LLNL +Mn Mn+2 0.0 Mn 54.938 # 25 Ma(P) NAGRA NTB 91-18 +Mn(2) Mn+2 0.0 Mn # +#Mn(3) Mn+3 0.0 Mn # +#Mn(4) Mn+4 0.0 Mn # +#Mn(6) MnO4-2 0.0 Mn # +#Mn(7) MnO4- 0.0 Mn # +Mo MoO4-2 0.0 Mo 95.941 # 42 Ma(P) NAGRA NTB 91-18 +N NO3- 0.0 N 14.007 # 7 Ele NAGRA NTB 91-17 +N(-5) HCN 0.0 HCN # PSI Report 14-04 +N(-3) NH4+ 0.0 NH4 # Ma(S) NAGRA NTB 91-17 +N(-1) SCN- 0.0 SCN- # PSI Report 14-04 +N(0) N2 0.0 N2 # Ma(S) NAGRA NTB 91-17 +N(5) NO3- 0.0 NO3 # Ma(P) NAGRA NTB 91-17 +Na Na+ 0.0 Na 22.99 # 11 Ma(P) NAGRA NTB 91-17 +Nb NbO3- 0.0 Nb 92.906 # 41 Ma(P) NAGRA NTB 91-18 +Nd Nd+3 0.0 Nd 144.24 # 60 Included from LLNL database +Nd(3) Nd+3 0.0 Nd Included from LLNL database +Ni Ni+2 0.0 Ni 58.693 # 28 Ele, Ma(P) PSI Report 14-04 +Np NpO2+2 0.0 Np 237 # 93 Ele PSI Report 14-04 +Np(3) Np+3 0.0 Np # Ma(S) PSI Report 14-04 +Np(4) Np+4 0.0 Np # Ma(S) PSI Report 14-04 +Np(5) NpO2+ 0.0 Np # Ma(S) PSI Report 14-04 +Np(6) NpO2+2 0.0 Np # Ma(P) PSI Report 14-04 +P HPO4-2 1.0 P 30.974 # 15 Ma(P) NAGRA NTB 91-17 +Pd Pd+2 0.0 Pd 106.421 # 46 Ele, Ma(P) NAGRA NTB 02-16 +Pu PuO2+2 0.0 Pu 242 # 94 Ele PSI Report 14-04 +Pu(3) Pu+3 0.0 Pu # Ma(S) PSI Report 14-04 +Pu(4) Pu+4 0.0 Pu # Ma(S) PSI Report 14-04 +Pu(5) PuO2+ 0.0 Pu # Ma(S) PSI Report 14-04 +Pu(6) PuO2+2 0.0 Pu # Ma(P) PSI Report 14-04 +Ra Ra+2 0.0 Ra 226 # 88 Ele, Ma(P) NAGRA NTB 02-16 +S SO4-2 0.0 S 32.067 # 16 Ele NAGRA NTB 91-17 +S(-2) HS- 1.0 HS # Ma(S) NAGRA NTB 02-16 +S(2) S2O3-2 0.0 S2O3 # Ma(S) NAGRA NTB 91-18 +S(4) SO3-2 0.0 SO3 # Ma(S) NAGRA NTB 91-18 +S(6) SO4-2 0.0 SO4 # Ma(P) NAGRA NTB 91-18 +Se SeO3-2 0.0 Se 78.963 # 34 Ele PSI Report 14-04 +Se(4) SeO3-2 0.0 Se # Ma(P) PSI Report 14-04 +Se(-2) H2Se 0.0 Se # Ma(S) PSI Report 14-04 +Se(6) HSeO4- 0.0 Se # Ma(S) PSI Report 14-04 +Si Si(OH)4 0.0 Si 28.086 # 14 Ele, Ma(P) PSI Report 14-04 +Tn Tn+2 0.0 Tn 118.711 # 50 Ele, Ma(P) NAGRA NTB 02-16 +Sn Sn(OH)4 0.0 Sn 118.711 # Ma(P) NAGRA NTB 02-16 +Sr Sr+2 0.0 Sr 87.621 # 38 Ma(P) NAGRA NTB 91-17 +Tc TcO4- 0.0 Tc 98 # 43 Ele PSI Report 14-04 +Tc(7) TcO4- 0.0 TcO4 # Ma(P) PSI Report 14-04 +Tc(4) TcO(OH)2 -1.0 TcO(OH)2 # Ma(S) PSI Report 14-04 +Th Th+4 0.0 Th 232.038 # 90 Ele, Ma(P) PSI Report 14-04 +U UO2+2 0.0 U 238.029 # 92 Ele PSI Report 14-04 +U(4) U+4 0.0 U # Ma(S) PSI Report 14-04 +U(5) UO2+ 0.0 U # Ma(S) PSI Report 14-04 +U(6) UO2+2 0.0 UO2 # Ma(P) PSI Report 14-04 +Zr Zr+4 0.0 Zr 91.224 # 40 Ele, Ma(P) PSI Report 14-04 + + +SOLUTION_SPECIES + +# PMATCH MASTER SPECIES +# -Vm values for relevant elements/species are implemented from phreeqc.dat: C, Ca, Cl, K, Mg, Na, S + +H+ = H+ + log_k 0.0 + -dw 9.31e-9 # phreeqc.dat, The dw parameters are defined in ref. 3. + +e- = e- + log_k 0.0 + -gamma 0.00 0.00 + +H2O = H2O + log_k 0.0 + -gamma 0.00 0.00 + +Al+3 = Al+3 + log_k 0.0 + +Am+3 = Am+3 + log_k 0.0 + +HAsO4-2 = HAsO4-2 + log_k 0.0 + +B(OH)3 = B(OH)3 + log_k 0.0 + -gamma 0.00 0.00 + +Ba+2 = Ba+2 + log_k 0.0 + +Br- = Br- + log_k 0.0 + +HCO3- = HCO3- + log_k 0.0 + -Vm 8.472 0 -11.5 0 1.56 0 0 146 3.16e-3 1 # ref. 1 + # from phreeqc.dat: CO3-2 + H+ = HCO3- + -dw 1.18e-9 # phreeqc.dat + +Ca+2 = Ca+2 + log_k 0.0 + -Vm -0.3456 -7.252 6.149 -2.479 1.239 5 1.60 -57.1 -6.12e-3 1 # ref. 1 + -dw 7.93e-10 # phreeqc.dat + +Cl- = Cl- + log_k 0.0 + -Vm 4.465 4.801 4.325 -2.847 1.748 0 -0.331 20.16 0 1 # ref. 1 + -dw 2.03e-9 # phreeqc.dat + +Cm+3 = Cm+3 + log_k 0.0 + +Cs+ = Cs+ + log_k 0.0 + +Eu+3 = Eu+3 + log_k 0.0 + +F- = F- + log_k 0.0 + +Fe+2 = Fe+2 + log_k 0.0 + -dw 7.19e-10 # phreeqc.dat + +I- = I- + log_k 0.0 + +K+ = K+ + log_k 0.0 + -Vm 3.322 -1.473 6.534 -2.712 9.06e-2 3.5 0 29.7 0 1 # ref. 1 + -dw 1.96e-9 # phreeqc.dat + +Li+ = Li+ + log_k 0.0 + +Mg+2 = Mg+2 + log_k 0.0 + -Vm -1.410 -8.6 11.13 -2.39 1.332 5.5 1.29 -32.9 -5.86e-3 1 # ref. 1 + -dw 7.05e-10 # phreeqc.dat + +Mn+2 = Mn+2 + log_k 0.0 + +MoO4-2 = MoO4-2 + log_k 0.0 + +NO3- = NO3- + log_k 0.0 + +Na+ = Na+ + log_k 0.0 + -Vm 2.28 -4.38 -4.1 -0.586 0.09 4 0.3 52 -3.33e-3 0.566 # ref. 1 + -dw 1.33e-9 # phreeqc.dat + +NbO3- = NbO3- + log_k 0.0 + +Nd+3 = Nd+3 + log_k 0.0 + +Ni+2 = Ni+2 + log_k 0.0 + +NpO2+2 = NpO2+2 + log_k 0.0 + +HPO4-2 = HPO4-2 + log_k 0.0 + -dw 6.9e-10 # phreeqc.dat + +Pd+2 = Pd+2 + log_k 0.0 + +PuO2+2 = PuO2+2 + log_k 0.0 + +Ra+2 = Ra+2 + log_k 0.0 + +SO4-2 = SO4-2 + log_k 0.0 + -Vm 8.0 2.3 -46.04 6.245 3.82 0 0 0 0 1 # ref. 1 + -dw 1.07e-9 # phreeqc.dat + +SeO3-2 = SeO3-2 + log_k 0.0 + +Si(OH)4 = Si(OH)4 + log_k 0.0 + -gamma 0.00 0.00 + +Tn+2 = Tn+2 + log_k 0.0 + +Sn(OH)4 = Sn(OH)4 + log_k 0.0 + -gamma 0.00 0.00 + +Sr+2 = Sr+2 + log_k 0.0 + -dw 7.94e-10 # phreeqc.dat + +TcO4- = TcO4- + log_k 0.0 + +Th+4 = Th+4 + log_k 0.0 + +UO2+2 = UO2+2 + log_k 0.0 + -dw 7.659e-10 # Kerisit & Liu (2010) + +Zr+4 = Zr+4 + log_k 0.0 + + +# PMATCH SECONDARY MASTER SPECIES + +# Se Redox +############## + + +1.000SeO3-2 +1.000H2O -1.000H+ -2.000e- = HSeO4- + log_k -26.3000 + + +1.000SeO3-2 +8.000H+ +6.000e- -3.000H2O = H2Se + log_k 57.4000 + -gamma 0.00 0.00 + + +1.000HCN +1.000SeO3-2 +5.000H+ +4.000e- -3.000H2O = SeCN- + log_k 57.3000 + +# Tc Redox +############## + + +1.000TcO4- +4.000H+ +3.000e- -1.000H2O = TcO(OH)2 + log_k 29.4000 + -gamma 0.00 0.00 + +# Eu Redox +############## + + +1.000Eu+3 +1.000e- = Eu+2 + log_k -5.9200 + +# U Redox +############## + + +1.000UO2+2 +4.000H+ +2.000e- -2.000H2O = U+4 + log_k 9.0380 + -dw 7.659e-10 # assumption: analogous to UO2+2, from Kerisit & Liu (2010) + + +1.000UO2+2 +1.000e- = UO2+ + log_k 1.4840 + -dw 7.659e-10 # assumption: analogous to UO2+2, from Kerisit & Liu (2010) + +# Np Redox +############## + + +1.000NpO2+2 +4.000H+ +3.000e- -2.000H2O = Np+3 + log_k 33.5000 + + +1.000NpO2+2 +4.000H+ +2.000e- -2.000H2O = Np+4 + log_k 29.8000 + + +1.000NpO2+2 +1.000e- = NpO2+ + log_k 19.5900 + +# Pu Redox +############## + + +1.000PuO2+2 +4.000H+ +3.000e- -2.000H2O = Pu+3 + log_k 50.9700 + + +1.000PuO2+2 +4.000H+ +2.000e- -2.000H2O = Pu+4 + log_k 33.2800 + + +1.000PuO2+2 +1.000e- = PuO2+ + log_k 15.8200 + +# Am Redox +############## + + +1.000Am+3 +2.000H2O -4.000H+ -2.000e- = AmO2+ + log_k -59.7000 + +# Rest Redox +############## + + +2.000H+ +2.000e- = H2 + log_k -3.1054 + -gamma 0.00 0.00 + -dw 5.13e-9 # phreeqc.dat + + +2.000H2O -4.000H+ -4.000e- = O2 + log_k -85.9841 + -gamma 0.00 0.00 + -Vm 5.7889 6.3536 3.2528 -3.0417 -0.3943 # supcrt + -dw 2.35e-9 # phreeqc.dat + + +1.000HAsO4-2 +4.000H+ +2.000e- -1.000H2O = As(OH)3 + log_k 28.4412 + -gamma 0.00 0.00 + + +1.000HCO3- +9.000H+ +8.000e- -3.000H2O = CH4 + log_k 27.8486 + -gamma 0.00 0.00 + -dw 1.85e-9 # phreeqc.dat + + +2.000NO3- +12.000H+ +10.000e- -6.000H2O = N2 + log_k 207.2627 + -gamma 0.00 0.00 + + +1.000NO3- +10.000H+ +8.000e- -3.000H2O = NH4+ + log_k 119.1344 + + +2.000SO4-2 +10.000H+ +8.000e- -5.000H2O = S2O3-2 + log_k 38.0140 +# bug: log_k entered manually + + +1.000SO4-2 +2.000H+ +2.000e- -1.000H2O = SO3-2 + log_k -3.3970 +# bug: log_k entered manually + + +1.000SO4-2 +9.000H+ +8.000e- -4.000H2O = HS- + log_k 33.6900 + -dw 1.73e-9 # phreeqc.dat + +# +1.000Fe+2 -1.000e- = Fe+3 +# log_k -13.0200 + + +2.000I- -2.000e- = I2 + log_k -20.9500 + -gamma 0.00 0.00 + + +0.500I2 +3.000H2O -6.000H+ -5.000e- = IO3- + log_k -101.0900 + ++13.000H+ +1.000CO3-2 +1.000NO3- +10.000e- -6.000H2O = HCN + log_k 117.3360 + -gamma 0.00 0.00 + + +1.000HCN +1.000HS- -2.000e- -2.000H+ = SCN- + log_k 5.9410 + +# Convenience +############# + + +1.000H2O -1.000H+ = OH- + log_k -13.9995 + -Vm -9.66 28.5 80.0 -22.9 1.89 0 1.09 0 0 1 # ref. 1 + # from phreec.dat: H2O = OH- + H+ + -dw 5.27e-9 # phreeqc.dat + + +1.000H+ -1.000H2O +1.000HCO3- = CO2 + log_k 6.3519 + # -gamma 0.00 0.00 + -Vm 7.29 0.92 2.07 -1.23 -1.60 # ref. 1 + McBride et al. 2015, JCED 60, 171 + # from phreeqc.dat: CO3-2 + 2 H+ = CO2 + H2O + -dw 1.92e-9 # phreeqc.dat + + -1.000H+ +1.000HCO3- = CO3-2 + log_k -10.3289 + -Vm 5.95 0 0 -5.67 6.85 0 1.37 106 -0.0343 1 # ref. 1 + # from phreeqc.dat: CO3-2 = CO3-2 + -dw 8.119e-10 # Kerisit & Liu (2010) + + +1.000HPO4-2 +2.000H+ = H3PO4 + log_k 9.3520 + -gamma 0.00 0.00 + + +1.000HPO4-2 +1.000H+ = H2PO4- + log_k 7.2120 + -dw 8.46e-10 # phreeqc.dat + + +1.000HPO4-2 -1.000H+ = PO4-3 + log_k -12.3500 + -dw 6.12e-10 # phreeqc.dat + + +1.000Si(OH)4 -1.000H+ = SiO(OH)3- + log_k -9.8100 + + +1.000Si(OH)4 -2.000H+ = SiO2(OH)2-2 + log_k -23.1400 + + +1.000Al+3 +4.000H2O -4.000H+ = Al(OH)4- + log_k -22.8791 + + +1.000NH4+ -1.000H+ = NH3 + log_k -9.2370 + -gamma 0.00 0.00 + + +1.000HCN -1.000H+ = CN- + log_k -9.2100 + + +1.000HAsO4-2 -1.000H+ = AsO4-3 + log_k -11.6030 + + +1.000HAsO4-2 +2.000H+ = H3AsO4 + log_k 9.0270 + -gamma 0.00 0.00 +# bug: log_k entered manually + +# Se(VI) RECOMMENDED DATA Convenience +######################################## + + +1.000HSeO4- -1.000H+ = SeO4-2 + log_k -1.7500 + +# Se(IV) RECOMMENDED DATA Convenience +######################################## + + +1.000SeO3-2 +1.000H+ = HSeO3- + log_k 8.3600 + +# Se(-II) RECOMMENDED DATA Convenience +######################################## + + +1.000H2Se -1.000H+ = HSe- + log_k -3.8500 + + +1.000HSe- -1.000H+ = Se-2 + log_k -14.9100 + +# PMATCH PRODUCT SPECIES + +# General RECOMMENDED DATA +############################ + + +1.000I- +1.000I2 = I3- + log_k 2.8700 + + +1.000H+ +1.000IO3- = HIO3 + log_k 0.7880 + -gamma 0.00 0.00 + + +1.000Al+3 +1.000F- = AlF+2 + log_k 7.0800 + + +1.000Al+3 +2.000F- = AlF2+ + log_k 12.7300 + + +1.000Al+3 +3.000F- = AlF3 + log_k 16.7800 + -gamma 0.00 0.00 + + +1.000Al+3 +4.000F- = AlF4- + log_k 19.2900 + + +1.000Al+3 +5.000F- = AlF5-2 + log_k 20.3000 + + +1.000Al+3 +6.000F- = AlF6-3 + log_k 20.3000 + + +1.000Al+3 +1.000H2O -1.000H+ = AlOH+2 + log_k -4.9572 + + +1.000Al+3 +2.000H2O -2.000H+ = Al(OH)2+ + log_k -10.5940 + + +1.000Al+3 +3.000H2O -3.000H+ = Al(OH)3 + log_k -16.4324 + -gamma 0.00 0.00 + + +1.000Al+3 +1.000SO4-2 = AlSO4+ + log_k 3.9000 + + +1.000Al+3 +2.000SO4-2 = Al(SO4)2- + log_k 5.9000 + + +1.000As(OH)3 +1.000H2O -1.000H+ = As(OH)4- + log_k -9.2320 +# bug: log_k entered manually + + +1.000B(OH)3 +1.000H2O -1.000H+ = B(OH)4- + log_k -9.2352 + + +1.000Ba+2 -1.000H+ +1.000HCO3- = BaCO3 + log_k -7.6157 + -gamma 0.00 0.00 + + +1.000Ba+2 +1.000HCO3- = BaHCO3+ + log_k 0.9816 + + +1.000Ba+2 +1.000H2O -1.000H+ = BaOH+ + log_k -13.4700 + + +1.000Ba+2 +1.000SO4-2 = BaSO4 + log_k 2.7000 + -gamma 0.00 0.00 + + +1.000Ca+2 -1.000H+ +1.000HCO3- = CaCO3 + log_k -7.1047 + # -gamma 0.00 0.00 + -Vm -.2430 -8.3748 9.0417 -2.4328 -.0300 # supcrt + # from phreeqc.dat: Ca+2 + CO3-2 = CaCO3 + -dw 4.46e-10 # phreeqc.dat, complexes: calc'd with the Pikal formula + + +1.000Ca+2 +1.000F- = CaF+ + log_k 0.9400 + + +1.000Ca+2 +1.000HCO3- = CaHCO3+ + log_k 1.1057 + -Vm 3.1911 .0104 5.7459 -2.7794 .3084 5.4 # supcrt + # from phreeqc.dat: Ca+2 + CO3-2 + H+ = CaHCO3+ + -dw 5.06e-10 # phreeqc.dat + + +1.000Ca+2 +1.000H2O -1.000H+ = CaOH+ + log_k -12.7800 + + +1.000Ca+2 +1.000SO4-2 = CaSO4 + log_k 2.3000 + # -gamma 0.00 0.00 + -Vm 2.7910 -.9666 6.1300 -2.7390 -.0010 # supcrt + -dw 4.71e-10 # phreeqc.dat + +############################# +# Fe data were updated with data from NEA TDB Vol. 13a [Lemire et al., 2013] +# and commented out here (recommended data are implemented below) +############################# +# +# +2.000H2O -2.000H+ +1.000Fe+3 = Fe(OH)2+ +# log_k -5.6700 +# +# +3.000H2O -3.000H+ +1.000Fe+3 = Fe(OH)3 +# log_k -12.5600 +# -gamma 0.00 0.00 +# +# +4.000H2O -4.000H+ +1.000Fe+3 = Fe(OH)4- +# log_k -21.6000 +# +# +2.000SO4-2 +1.000Fe+3 = Fe(SO4)2- +# log_k 5.3800 +# +# +2.000H2O -2.000H+ +2.000Fe+3 = Fe2(OH)2+4 +# log_k -2.9500 +# +# +4.000H2O -4.000H+ +3.000Fe+3 = Fe3(OH)4+5 +# log_k -6.3000 +# +# +1.000Fe+2 +1.000Cl- = FeCl+ +# log_k 0.1400 +# +# +1.000Cl- +1.000Fe+3 = FeCl+2 +# log_k 1.4800 +# +# +2.000Cl- +1.000Fe+3 = FeCl2+ +# log_k 2.1300 +# +# +3.000Cl- +1.000Fe+3 = FeCl3 +# log_k 1.1300 +# -gamma 0.00 0.00 +# +# +1.000Fe+2 +1.000HCO3- -1.000H+ = FeCO3 +# log_k -5.9490 +# -gamma 0.00 0.00 +# +# +1.000Fe+2 +1.000F- = FeF+ +# log_k 1.0000 +# +# +1.000F- +1.000Fe+3 = FeF+2 +# log_k 6.2000 +# +# +2.000F- +1.000Fe+3 = FeF2+ +# log_k 10.8000 +# +# +3.000F- +1.000Fe+3 = FeF3 +# log_k 14.0000 +# -gamma 0.00 0.00 +# +# +1.000Fe+2 +1.000HCO3- = FeHCO3+ +# log_k 2.0000 +# +# +1.000Fe+2 +1.000H+ +1.000SO4-2 = FeHSO4+ +# log_k 3.0680 +# +# +1.000H+ +1.000SO4-2 +1.000Fe+3 = FeHSO4+2 +# log_k 4.4680 +# +# +1.000Fe+2 +1.000H2O -1.000H+ = FeOH+ +# log_k -9.5000 +# +# +1.000H2O -1.000H+ +1.000Fe+3 = FeOH+2 +# log_k -2.1900 +# +# +1.000SO4-2 +1.000Fe+3 = FeSO4+ +# log_k 4.0400 +# +# +1.000Fe+2 +1.000SO4-2 = FeSO4 +# log_k 2.2500 +# -gamma 0.00 0.00 + + +1.000HAsO4-2 +1.000H+ = H2AsO4- + log_k 6.7640 +# bug: log_k entered manually + + +1.000H+ +1.000F- = HF + log_k 3.1760 + -gamma 0.00 0.00 + + +1.000H+ +2.000F- = HF2- + log_k 3.6200 + + +1.000H+ +1.000SO3-2 = HSO3- + log_k 7.2200 +# bug: log_k entered manually + + +1.000H+ +1.000SO4-2 = HSO4- + log_k 1.9878 + -Vm 8.2 9.2590 2.1108 -3.1618 1.1748 0 -0.3 15 0 1 # ref. 1 + -dw 1.33e-9 # phreeqc.dat + + +1.000K+ +1.000H2O -1.000H+ = KOH + log_k -14.4600 + -gamma 0.00 0.00 + + +1.000K+ +1.000SO4-2 = KSO4- + log_k 0.8500 + -Vm 6.8 7.06 3.0 -2.07 1.1 0 0 0 0 1 # ref. 1 + -dw 1.5e-9 # phreeqc.dat + + +1.000Li+ +1.000H2O -1.000H+ = LiOH + log_k -13.6400 + -gamma 0.00 0.00 + + +1.000Li+ +1.000SO4-2 = LiSO4- + log_k 0.6400 + + +1.000HS- -1.000H+ = S-2 + log_k -19.0000 + -dw 7.31e-9 # phreeqc.dat + + +1.000HS- +1.000H+ = H2S + log_k 6.9900 + -gamma 0.00 0.00 + -dw 2.1e-9 # phreeqc.dat + + +1.000Mg+2 -1.000H+ +1.000HCO3- = MgCO3 + log_k -7.3492 + -gamma 0.00 0.00 + -Vm -.5837 -9.2067 9.3687 -2.3984 -.0300 # supcrt + # from phreeqc.dat: Mg+2 + CO3-2 = MgCO3 + -dw 4.21e-10 # phreeqc.dat + + +1.000Mg+2 +1.000Cl- = MgCl+ + log_k -0.1350 + + +1.000Mg+2 +1.000F- = MgF+ + log_k 1.8200 + + +1.000Mg+2 +1.000HCO3- = MgHCO3+ + log_k 1.0682 + -Vm 2.7171 -1.1469 6.2008 -2.7316 .5985 4 # supcrt + # from phreeqc.dat: Mg+2 + H+ + CO3-2 = MgHCO3+ + -dw 4.78e-10 # phreeqc.dat + + +1.000Mg+2 +1.000HPO4-2 -1.000H+ = MgPO4- + log_k -5.7330 + + +1.000Mg+2 +1.000HPO4-2 = MgHPO4 + log_k 2.9100 + + +1.000Mg+2 +1.000HPO4-2 +1.000H+ = MgH2PO4+ + log_k 1.6600 + + +1.000Mg+2 +1.000H2O -1.000H+ = MgOH+ + log_k -11.7500 + + +1.000Mg+2 +1.000SO4-2 = MgSO4 + log_k 2.3700 + # -gamma 0.00 0.00 + -Vm 2.4 -0.97 6.1 -2.74 # est'd + -dw 4.45e-10 # phreeqc.dat + + +1.000Mn+2 +1.000Cl- = MnCl+ + log_k 0.6100 + + +1.000Mn+2 +2.000Cl- = MnCl2 + log_k 0.2500 + -gamma 0.00 0.00 + + +1.000Mn+2 +3.000Cl- = MnCl3- + log_k -0.3100 + + +1.000Mn+2 +1.000HCO3- -1.000H+ = MnCO3 + log_k -5.4290 + -gamma 0.00 0.00 + + +1.000Mn+2 +1.000F- = MnF+ + log_k 0.8400 + + +1.000Mn+2 +1.000HCO3- = MnHCO3+ + log_k 1.9500 + + +1.000Mn+2 +1.000H2O -1.000H+ = MnOH+ + log_k -10.5900 + + +1.000Mn+2 +1.000SO4-2 = MnSO4 + log_k 2.2500 + -gamma 0.00 0.00 + + +1.000Na+ -1.000H+ +1.000HCO3- = NaCO3- + log_k -9.0590 + -Vm 3.89 -8.23e-4 20 -9.44 3.02 9.05e-3 3.07 0 0.0233 1 # ref. 1 + # But in phreeqc.dat: Na+ + CO3-2 = NaCO3- + -dw 1.2e-9 # phreeqc.dat + + +1.000Na+ +1.000F- = NaF + log_k -0.2400 + -gamma 0.00 0.00 + + +1.000Na+ +1.000HCO3- = NaHCO3 + log_k -0.2500 + # -gamma 0.00 0.00 + -Vm 0.431 # ref. 1 + # from phreeqc.dat: Na+ + HCO3- = NaHCO3 + -dw 6.73e-10 # phreeqc.dat + + +1.000Na+ +1.000H2O -1.000H+ = NaOH + log_k -14.1800 + -gamma 0.00 0.00 + -dw 1.33e-9 # phreeqc.dat + + +1.000Na+ +1.000SO4-2 = NaSO4- + log_k 0.7000 + -Vm 1e-5 16.4 -0.0678 -1.05 4.14 0 6.86 0 0.0242 0.53 # ref. 1 + + +1.000NbO3- +2.000H+ +1.000H2O = Nb(OH)4+ + log_k 6.8955 + + +1.000NbO3- +1.000H+ +2.000H2O = Nb(OH)5 + log_k 7.3440 + -gamma 0.00 0.00 + + +1.000Sr+2 -1.000H+ +1.000HCO3- = SrCO3 + log_k -7.5238 + -gamma 0.00 0.00 + + +1.000Sr+2 +1.000HCO3- = SrHCO3+ + log_k 1.1846 + + +1.000Sr+2 +1.000H2O -1.000H+ = SrOH+ + log_k -13.2900 + + +1.000Sr+2 +1.000SO4-2 = SrSO4 + log_k 2.2900 + -gamma 0.00 0.00 + +# Si(IV) RECOMMENDED DATA +############################ + + +1.000Ca+2 +1.000SiO(OH)3- = CaSiO(OH)3+ + log_k 1.2000 + + +1.000Ca+2 +1.000SiO2(OH)2-2 = CaSiO2(OH)2 + log_k 4.6000 + -gamma 0.00 0.00 + + +1.000Mg+2 +1.000SiO(OH)3- = MgSiO(OH)3+ + log_k 1.5000 + + +1.000Mg+2 +1.000SiO2(OH)2-2 = MgSiO2(OH)2 + log_k 5.7000 + -gamma 0.00 0.00 + + +1.000Al+3 +1.000SiO(OH)3- = AlSiO(OH)3+2 + log_k 7.4000 + + +1.000Fe+3 +1.000SiO(OH)3- = FeSiO(OH)3+2 + log_k 9.7000 + + +4.000Si(OH)4 -4.000H+ -4.000H2O = Si4O8(OH)4-4 + log_k -36.3000 + +# Si(IV) SUPPLEMENTAL DATA +# ========================== + + +1.000Al(OH)4- +1.000SiO2(OH)2-2 -1.000H2O = AlSiO3(OH)4-3 + log_k 0.5300 + +# Ni(II) RECOMMENDED DATA +############################ + + +1.000Ni+2 +1.000H2O -1.000H+ = NiOH+ + log_k -9.5400 + + +1.000Ni+2 +3.000H2O -3.000H+ = Ni(OH)3- + log_k -29.2000 + + +2.000Ni+2 +1.000H2O -1.000H+ = Ni2OH+3 + log_k -10.6000 + + +4.000Ni+2 +4.000H2O -4.000H+ = Ni4(OH)4+4 + log_k -27.5200 + + +1.000Ni+2 +1.000F- = NiF+ + log_k 1.4300 + + +1.000Ni+2 +1.000Cl- = NiCl+ + log_k 0.0800 + + +1.000Ni+2 +1.000SO4-2 = NiSO4 + log_k 2.3500 + -gamma 0.00 0.00 + + +1.000Ni+2 +1.000NO3- = NiNO3+ + log_k 0.5000 + + +1.000Ni+2 +1.000HPO4-2 = NiHPO4 + log_k 3.0500 + -gamma 0.00 0.00 + + +1.000Ni+2 +1.000CO3-2 = NiCO3 + log_k 4.2000 + -gamma 0.00 0.00 + + +1.000Ni+2 +1.000HS- = NiHS+ + log_k 5.5000 + + +1.000Ni+2 +2.000HS- = Ni(HS)2 + log_k 11.1000 + -gamma 0.00 0.00 + + +1.000Ni+2 +1.000HAsO4-2 = NiHAsO4 + log_k 2.9000 + -gamma 0.00 0.00 + + +1.000Ni+2 +4.000CN- = Ni(CN)4-2 + log_k 30.2000 + + +1.000Ni+2 +5.000CN- = Ni(CN)5-3 + log_k 28.5000 + + +1.000Ni+2 +1.000SCN- = NiSCN+ + log_k 1.8100 + + +1.000Ni+2 +2.000SCN- = Ni(SCN)2 + log_k 2.6900 + -gamma 0.00 0.00 + + +1.000Ni+2 +3.000SCN- = Ni(SCN)3- + log_k 3.0200 + +# Ni(II) SUPPLEMENTAL DATA +# ========================== + + +1.000Ni+2 +2.000H2O -2.000H+ = Ni(OH)2 + log_k -18.0000 + -gamma 0.00 0.00 + + +1.000Ni+2 +1.000NH3 = NiNH3+2 + log_k 2.7000 + + +1.000Ni+2 +2.000NH3 = Ni(NH3)2+2 + log_k 4.9000 + + +1.000Ni+2 +3.000NH3 = Ni(NH3)3+2 + log_k 6.5000 + + +1.000Ni+2 +4.000NH3 = Ni(NH3)4+2 + log_k 7.6000 + + +1.000Ni+2 +5.000NH3 = Ni(NH3)5+2 + log_k 8.3000 + + +1.000Ni+2 +6.000NH3 = Ni(NH3)6+2 + log_k 8.2000 + + +1.000Ni+2 +2.000CO3-2 = Ni(CO3)2-2 + log_k 6.0000 + + +1.000Ni+2 +1.000HCO3- = NiHCO3+ + log_k 1.0000 + + +1.000Ni+2 +1.000SiO(OH)3- = NiSiO(OH)3+ + log_k 6.3000 + +# Se(0|-II) RECOMMENDED DATA +############################ + + +2.000Se-2 -2.000e- = Se2-2 + log_k 25.3200 + + +3.000Se-2 -4.000e- = Se3-2 + log_k 49.9700 + + +4.000Se-2 -6.000e- = Se4-2 + log_k 73.0200 + +# Se(0) RECOMMENDED DATA +############################ + + +1.000Ni+2 +1.000SeCN- = NiSeCN+ + log_k 1.7700 + + +1.000Ni+2 +2.000SeCN- = Ni(SeCN)2 + log_k 2.2400 + -gamma 0.00 0.00 + +# Se(IV) RECOMMENDED DATA +############################ + + +1.000HSeO3- +1.000H+ = H2SeO3 + log_k 2.6400 + -gamma 0.00 0.00 + + +1.000Fe+3 +1.000SeO3-2 = FeSeO3+ + log_k 11.1500 + +# Se(VI) RECOMMENDED DATA +############################ + + +1.000Ni+2 +1.000SeO4-2 = NiSeO4 + log_k 2.6700 + -gamma 0.00 0.00 + + +1.000Mn+2 +1.000SeO4-2 = MnSeO4 + log_k 2.4300 + -gamma 0.00 0.00 + + +1.000UO2+2 +1.000SeO4-2 = UO2SeO4 + log_k 2.7400 + -gamma 0.00 0.00 + + +1.000Ca+2 +1.000SeO4-2 = CaSeO4 + log_k 2.0000 + -gamma 0.00 0.00 + +# Se(VI) SUPPLEMENTAL DATA +# ========================== + + +1.000UO2+2 +2.000SeO4-2 = UO2(SeO4)2-2 + log_k 3.1000 + + +1.000Mg+2 +1.000SeO4-2 = MgSeO4 + log_k 2.2000 + -gamma 0.00 0.00 + +# Zr(IV) RECOMMENDED DATA +############################ + + +1.000Zr+4 +1.000H2O -1.000H+ = ZrOH+3 + log_k 0.3200 + + +1.000Zr+4 +4.000H2O -4.000H+ = Zr(OH)4 + log_k -2.1900 + -gamma 0.00 0.00 + + +1.000Zr+4 +2.000F- = ZrF2+2 + log_k 18.5500 + + +1.000Zr+4 +3.000F- = ZrF3+ + log_k 24.7200 + + +1.000Zr+4 +4.000F- = ZrF4 + log_k 30.1100 + -gamma 0.00 0.00 + + +1.000Zr+4 +1.000SO4-2 = ZrSO4+2 + log_k 7.0400 + + +1.000Zr+4 +6.000F- = ZrF6-2 + log_k 38.1100 + + +1.000Zr+4 +1.000F- = ZrF+3 + log_k 10.1200 + + +1.000Zr+4 +5.000F- = ZrF5- + log_k 34.6000 + + +1.000Zr+4 +1.000Cl- = ZrCl+3 + log_k 1.5900 + + +1.000Zr+4 +2.000Cl- = ZrCl2+2 + log_k 2.1700 + + +1.000Zr+4 +2.000SO4-2 = Zr(SO4)2 + log_k 11.5400 + -gamma 0.00 0.00 + + +1.000Zr+4 +3.000SO4-2 = Zr(SO4)3-2 + log_k 14.3000 + + +1.000Zr+4 +1.000NO3- = ZrNO3+3 + log_k 1.5900 + + +1.000Zr+4 +2.000NO3- = Zr(NO3)2+2 + log_k 2.6400 + + +1.000Zr+4 +4.000CO3-2 = Zr(CO3)4-4 + log_k 42.9000 + + +1.000Zr+4 +2.000H2O -2.000H+ = Zr(OH)2+2 + log_k 0.9800 + + +1.000Zr+4 +6.000H2O -6.000H+ = Zr(OH)6-2 + log_k -29.0000 + + +3.000Zr+4 +4.000H2O -4.000H+ = Zr3(OH)4+8 + log_k 0.4000 + + +3.000Zr+4 +9.000H2O -9.000H+ = Zr3(OH)9+3 + log_k 12.1900 + + +4.000Zr+4 +15.000H2O -15.000H+ = Zr4(OH)15+ + log_k 12.5800 + + +4.000Zr+4 +16.000H2O -16.000H+ = Zr4(OH)16 + log_k 8.3900 + -gamma 0.00 0.00 + + +4.000Zr+4 +8.000H2O -8.000H+ = Zr4(OH)8+8 + log_k 6.5200 + + +2.000Ca+2 +1.000Zr+4 +6.000H2O -6.000H+ = Ca2Zr(OH)6+2 + log_k -22.6000 + + +3.000Ca+2 +1.000Zr+4 +6.000H2O -6.000H+ = Ca3Zr(OH)6+4 + log_k -23.2000 + +# Zr(IV) SUPPLEMENTAL DATA +# ========================== + + +1.000Ca+2 +1.000Zr+4 +6.000H2O -6.000H+ = CaZr(OH)6 + log_k -24.6000 + -gamma 0.00 0.00 + +# Tc(IV) RECOMMENDED DATA +############################ + + +1.000TcO(OH)2 +2.000H+ -2.000H2O = TcO+2 + log_k 4.0000 + + +1.000TcO(OH)2 +1.000H+ -1.000H2O = TcO(OH)+ + log_k 2.5000 + + +1.000TcO(OH)2 +1.000H2O -1.000H+ = TcO(OH)3- + log_k -10.9000 + + +1.000TcO(OH)2 +1.000CO3-2 +2.000H+ -1.000H2O = TcCO3(OH)2 + log_k 19.3000 + -gamma 0.00 0.00 + + +1.000TcO(OH)2 +1.000H+ +1.000CO3-2 = TcCO3(OH)3- + log_k 11.0000 + +# Pd(II) RECOMMENDED DATA +############################ + + +1.000Pd+2 +1.000Cl- = PdCl+ + log_k 5.1000 + + +1.000Pd+2 +2.000Cl- = PdCl2 + log_k 8.3000 + -gamma 0.00 0.00 + + +1.000Pd+2 +3.000Cl- = PdCl3- + log_k 10.9000 + + +1.000Pd+2 +4.000Cl- = PdCl4-2 + log_k 11.7000 + + +1.000Pd+2 +1.000NH3 = PdNH3+2 + log_k 9.6000 + + +1.000Pd+2 +2.000NH3 = Pd(NH3)2+2 + log_k 18.5000 + + +1.000Pd+2 +3.000NH3 = Pd(NH3)3+2 + log_k 26.0000 + + +1.000Pd+2 +4.000NH3 = Pd(NH3)4+2 + log_k 32.8000 + + +1.000Pd+2 -2.000H+ +2.000H2O = Pd(OH)2 + log_k -4.0000 + -gamma 0.00 0.00 + + +1.000Pd+2 -3.000H+ +3.000H2O = Pd(OH)3- + log_k -15.5000 + + +1.000Pd+2 +3.000Cl- +1.000H2O -1.000H+ = PdCl3OH-2 + log_k 2.5000 + + +1.000Pd+2 +2.000Cl- +2.000H2O -2.000H+ = PdCl2(OH)2-2 + log_k -7.0000 + +# Tn(II) RECOMMENDED DATA +######################################################## + + +1.000Tn+2 +1.000H2O -1.000H+ = TnOH+ + log_k -3.8000 + + +1.000Tn+2 +3.000H2O -3.000H+ = Tn(OH)3- + log_k -17.5000 + + +3.000Tn+2 +4.000H2O -4.000H+ = Tn3(OH)4+2 + log_k -5.6000 + + +1.000Tn+2 +1.000Cl- = TnCl+ + log_k 1.7000 + + +1.000Tn+2 +3.000Cl- = TnCl3- + log_k 2.1000 + + +1.000Tn+2 +1.000F- = TnF+ + log_k 5.0000 + + +1.000Tn+2 +2.000H2O -2.000H+ = Tn(OH)2 + log_k -7.7000 + -gamma 0.00 0.00 + + +1.000Tn+2 +1.000SO4-2 = TnSO4 + log_k 2.6000 + -gamma 0.00 0.00 + + +1.000Tn+2 +1.000H2O +1.000Cl- -1.000H+ = TnOHCl + log_k -3.1000 + -gamma 0.00 0.00 + + +1.000Tn+2 +2.000Cl- = TnCl2 + log_k 2.3600 + -gamma 0.00 0.00 + +# Sn(IV) RECOMMENDED DATA +############################ + + +1.000Sn(OH)4 +1.000H2O -1.000H+ = Sn(OH)5- + log_k -8.0000 + + +1.000Sn(OH)4 +2.000H2O -2.000H+ = Sn(OH)6-2 + log_k -18.4000 + +# Ra(II) RECOMMENDED DATA +############################ + + +1.000Ra+2 +1.000OH- = RaOH+ + log_k 0.5000 + + +1.000Ra+2 +1.000Cl- = RaCl+ + log_k -0.1000 + + +1.000Ra+2 +1.000CO3-2 = RaCO3 + log_k 2.5000 + -gamma 0.00 0.00 + + +1.000Ra+2 +1.000SO4-2 = RaSO4 + log_k 2.7500 + -gamma 0.00 0.00 + +# Eu(III) RECOMMENDED DATA +############################ + + +1.000Eu+3 +1.000H2O -1.000H+ = EuOH+2 + log_k -7.6400 + + +1.000Eu+3 +2.000H2O -2.000H+ = Eu(OH)2+ + log_k -15.1000 + + +1.000Eu+3 +3.000H2O -3.000H+ = Eu(OH)3 + log_k -23.7000 + -gamma 0.00 0.00 + + +1.000Eu+3 +4.000H2O -4.000H+ = Eu(OH)4- + log_k -36.2000 + + +1.000Eu+3 +1.000CO3-2 = EuCO3+ + log_k 8.1000 + + +1.000Eu+3 +2.000CO3-2 = Eu(CO3)2- + log_k 12.1000 + + +1.000Eu+3 +1.000SO4-2 = EuSO4+ + log_k 3.9500 + + +1.000Eu+3 +2.000SO4-2 = Eu(SO4)2- + log_k 5.7000 + + +1.000Eu+3 +1.000F- = EuF+2 + log_k 3.8000 + + +1.000Eu+3 +2.000F- = EuF2+ + log_k 6.5000 + + +1.000Eu+3 +1.000Cl- = EuCl+2 + log_k 1.1000 + + +1.000Eu+3 +2.000Cl- = EuCl2+ + log_k 1.5000 + + +1.000Eu+3 +1.000SiO(OH)3- = EuSiO(OH)3+2 + log_k 8.1000 + +# Th(IV) RECOMMENDED DATA +############################ + + +1.000Th+4 +1.000H2O -1.000H+ = ThOH+3 + log_k -2.5000 + + +1.000Th+4 +4.000H2O -4.000H+ = Th(OH)4 + log_k -17.4000 + -gamma 0.00 0.00 + + +1.000Th+4 +1.000F- = ThF+3 + log_k 8.8700 + + +1.000Th+4 +2.000F- = ThF2+2 + log_k 15.6300 + + +1.000Th+4 +3.000F- = ThF3+ + log_k 20.6700 + + +1.000Th+4 +4.000F- = ThF4 + log_k 25.5800 + -gamma 0.00 0.00 + + +1.000Th+4 +5.000CO3-2 = Th(CO3)5-6 + log_k 31.0000 + + +1.000Th+4 +2.000SO4-2 = Th(SO4)2 + log_k 9.6900 + -gamma 0.00 0.00 + + +1.000Th+4 +3.000SO4-2 = Th(SO4)3-2 + log_k 10.7480 + + +1.000Th+4 +2.000H2O -2.000H+ = Th(OH)2+2 + log_k -6.2000 + + +2.000Th+4 +2.000H2O -2.000H+ = Th2(OH)2+6 + log_k -5.9000 + + +2.000Th+4 +3.000H2O -3.000H+ = Th2(OH)3+5 + log_k -6.8000 + + +4.000Th+4 +8.000H2O -8.000H+ = Th4(OH)8+8 + log_k -20.4000 + + +4.000Th+4 +12.000H2O -12.000H+ = Th4(OH)12+4 + log_k -26.6000 + + +6.000Th+4 +14.000H2O -14.000H+ = Th6(OH)14+10 + log_k -36.8000 + + +6.000Th+4 +15.000H2O -15.000H+ = Th6(OH)15+9 + log_k -36.8000 + + +1.000Th+4 +1.000Cl- = ThCl+3 + log_k 1.7000 + + +1.000Th+4 +1.000IO3- = ThIO3+3 + log_k 4.1400 + + +1.000Th+4 +2.000IO3- = Th(IO3)2+2 + log_k 6.9700 + + +1.000Th+4 +3.000IO3- = Th(IO3)3+ + log_k 9.8700 + + +1.000Th+4 +1.000SO4-2 = ThSO4+2 + log_k 6.1700 + + +1.000Th+4 +1.000NO3- = ThNO3+3 + log_k 1.3000 + + +1.000Th+4 +2.000NO3- = Th(NO3)2+2 + log_k 2.3000 + + +1.000Th+4 +1.000H3PO4 -1.000H+ = ThH2PO4+3 + log_k 3.4500 + + +1.000Th+4 +1.000H3PO4 = ThH3PO4+4 + log_k 1.8900 + + +1.000Th+4 +2.000H3PO4 -2.000H+ = Th(H2PO4)2+2 + log_k 6.2000 + + +1.000Th+4 +2.000H3PO4 -1.000H+ = Th(H3PO4)(H2PO4)+3 + log_k 5.4200 + + +1.000Th+4 +1.000OH- +4.000CO3-2 = ThOH(CO3)4-5 + log_k 35.6000 + + +1.000Th+4 +2.000OH- +2.000CO3-2 = Th(OH)2(CO3)2-2 + log_k 36.8000 + + +1.000Th+4 +4.000OH- +1.000CO3-2 = Th(OH)4CO3-2 + log_k 40.4000 + + +1.000Th+4 +1.000SCN- = ThSCN+3 + log_k 2.0000 + + +1.000Th+4 +2.000SCN- = Th(SCN)2+2 + log_k 3.4000 + + +4.000Ca+2 +1.000Th+4 +8.000H2O -8.000H+ = Ca4Th(OH)8+4 + log_k -62.4000 + +# Th(IV) SUPPLEMENTAL DATA +# ========================== + + +1.000Th+4 +6.000F- = ThF6-2 + log_k 29.2300 + + +1.000Th+4 +2.000OH- +1.000CO3-2 = Th(OH)2CO3 + log_k 30.5000 + -gamma 0.00 0.00 + + +1.000Th+4 +3.000OH- +1.000CO3-2 = Th(OH)3CO3- + log_k 38.3000 + + +1.000Th+4 +3.000Si(OH)4 +3.000H2O -6.000H+ = Th(OH)3(SiO(OH)3)3-2 + log_k -27.8000 + +# U(IV) RECOMMENDED DATA +############################ + + +1.000U+4 +1.000H2O -1.000H+ = UOH+3 + log_k -0.5400 + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + + +1.000U+4 +4.000H2O -4.000H+ = U(OH)4 + log_k -10.0000 + -gamma 0.00 0.00 + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + + +1.000U+4 +1.000F- = UF+3 + log_k 9.4200 + + +1.000U+4 +2.000F- = UF2+2 + log_k 16.5600 + + +1.000U+4 +3.000F- = UF3+ + log_k 21.8900 + + +1.000U+4 +4.000F- = UF4 + log_k 26.3400 + -gamma 0.00 0.00 + + +1.000U+4 +5.000F- = UF5- + log_k 27.7300 + + +1.000U+4 +6.000F- = UF6-2 + log_k 29.8000 + + +1.000U+4 +1.000Cl- = UCl+3 + log_k 1.7200 + + +1.000U+4 +1.000SO4-2 = USO4+2 + log_k 6.5800 + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + + +1.000U+4 +2.000SO4-2 = U(SO4)2 + log_k 10.5100 + -gamma 0.00 0.00 + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + + +1.000U+4 +1.000NO3- = UNO3+3 + log_k 1.4700 + + +1.000U+4 +2.000NO3- = U(NO3)2+2 + log_k 2.3000 + + +1.000U+4 +4.000CO3-2 = U(CO3)4-4 + log_k 35.2200 + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + + +1.000U+4 +5.000CO3-2 = U(CO3)5-6 + log_k 34.000 +# Original value 34.1 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + + +1.000U+4 +1.000I- = UI+3 + log_k 1.2500 + + +1.000U+4 +1.000SCN- = USCN+3 + log_k 2.9700 + + +1.000U+4 +2.000SCN- = U(SCN)2+2 + log_k 4.2600 + +# U(IV) SUPPLEMENTAL DATA +# ========================== + + +1.000U+4 +2.000H2O -2.000H+ = U(OH)2+2 + log_k -1.9000 +# Original value -1.1 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + + +1.000U+4 +3.000H2O -3.000H+ = U(OH)3+ + log_k -5.2000 +# Original value -4.7 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + + +1.000U+4 +1.000CO3-2 +3.000H2O -3.000H+ = UCO3(OH)3- + log_k 4.0000 + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + +# U(V) RECOMMENDED DATA +############################ + + +1.000UO2+ +3.000CO3-2 = UO2(CO3)3-5 + log_k 6.9500 + -dw 7.66e-10 # assumption: analogous to UO2OH+, Kerisit & Liu (2010) + +# U(VI) RECOMMENDED DATA +############################ + + +1.000UO2+2 +1.000H2O -1.000H+ = UO2OH+ + log_k -5.2500 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +2.000H2O -2.000H+ = UO2(OH)2 + log_k -12.1500 + -gamma 0.00 0.00 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +3.000H2O -3.000H+ = UO2(OH)3- + log_k -20.25 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +4.000H2O -4.000H+ = UO2(OH)4-2 + log_k -32.4 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +2.000UO2+2 +1.000H2O -1.000H+ = (UO2)2OH+3 + log_k -2.7000 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +2.000UO2+2 +2.000H2O -2.000H+ = (UO2)2(OH)2+2 + log_k -5.6200 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +3.000UO2+2 +4.000H2O -4.000H+ = (UO2)3(OH)4+2 + log_k -11.9000 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +3.000UO2+2 +5.000H2O -5.000H+ = (UO2)3(OH)5+ + log_k -15.5500 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +3.000UO2+2 +7.000H2O -7.000H+ = (UO2)3(OH)7- + log_k -32.2000 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +4.000UO2+2 +7.000H2O -7.000H+ = (UO2)4(OH)7+ + log_k -21.9000 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +1.000F- = UO2F+ + log_k 5.1600 + + +1.000UO2+2 +2.000F- = UO2F2 + log_k 8.8300 + -gamma 0.00 0.00 + + +1.000UO2+2 +3.000F- = UO2F3- + log_k 10.9000 + + +1.000UO2+2 +4.000F- = UO2F4-2 + log_k 11.8400 + + +1.000UO2+2 +1.000Cl- = UO2Cl+ + log_k 0.1700 + + +1.000UO2+2 +2.000Cl- = UO2Cl2 + log_k -1.1000 + -gamma 0.00 0.00 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +1.000SO4-2 = UO2SO4 + log_k 3.1500 + -gamma 0.00 0.00 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +2.000SO4-2 = UO2(SO4)2-2 + log_k 4.1400 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +1.000NO3- = UO2NO3+ + log_k 0.3000 + + +1.000UO2+2 +1.000PO4-3 = UO2PO4- + log_k 11.01 +# Original value 13.23 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000UO2+2 +1.000HPO4-2 = UO2HPO4 + log_k 7.2400 + -gamma 0.00 0.00 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +1.000H3PO4 -1.000H+ = UO2H2PO4+ + log_k 1.1200 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +1.000H3PO4 = UO2H3PO4+2 + log_k 0.7600 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +2.000H3PO4 -2.000H+ = UO2(H2PO4)2 + log_k 0.6400 + -gamma 0.00 0.00 + -dw 7.66e-10 # assumption: analogous to UO2+2, Liu et al., 2011 + + +1.000UO2+2 +1.000CO3-2 = UO2CO3 + log_k 9.9400 + -gamma 0.00 0.00 + -dw 6.67e-10 # Kerisit & Liu (2010) + + +1.000UO2+2 +2.000CO3-2 = UO2(CO3)2-2 + log_k 16.6100 + -dw 5.52e-10 # Kerisit & Liu (2010) + + +1.000UO2+2 +3.000CO3-2 = UO2(CO3)3-4 + log_k 21.8400 + -dw 5.566e-10 # Kerisit & Liu (2010) + + +3.000UO2+2 +6.000CO3-2 = (UO2)3(CO3)6-6 + log_k 54.00 + -dw 5.566e-10 # assumption: analogous to UO2(CO3)2-2, Kerisit & Liu (2010) + + +2.000UO2+2 +3.000H2O -3.000H+ +1.000CO3-2 = (UO2)2CO3(OH)3- + log_k -0.855 + -dw 5.566e-10 # assumption: analogous to UO2(CO3)2-2, Kerisit & Liu (2010) + + +1.000UO2+2 +2.000H3PO4 -1.000H+ = UO2H2PO4H3PO4+ + log_k 1.6500 + -dw 5.566e-10 # assumption: analogous to UO2(CO3)2-2, Kerisit & Liu (2010) + + +3.000UO2+2 +1.000CO3-2 +3.000H2O -3.000H+ = (UO2)3O(OH)2HCO3+ + log_k 0.655 + -dw 5.566e-10 # assumption: analogous to UO2(CO3)2-2, Kerisit & Liu (2010) + + +11.000UO2+2 + 6.000CO3-2 + 12H2O - 12H+ = (UO2)11(CO3)6(OH)12-2 + log_k 36.42 + ++1.000UO2+2 +1.000IO3- = UO2IO3+ + log_k 2.0000 + + +1.000UO2+2 +2.000IO3- = UO2(IO3)2 + log_k 3.5900 + -gamma 0.00 0.00 + + +1.000UO2+2 +3.000SO4-2 = UO2(SO4)3-4 + log_k 3.0200 + -dw 5.566e-10 # assumption: analogous to UO2(CO3)2-2, Kerisit & Liu (2010) + + +1.000UO2+2 +1.000HAsO4-2 = UO2HAsO4 + log_k 7.1600 + -gamma 0.00 0.00 + + +1.000UO2+2 +1.000H3AsO4 -1.000H+ = UO2H2AsO4+ + log_k 1.3400 + + +1.000UO2+2 +2.000H3AsO4 -2.000H+ = UO2(H2AsO4)2 + log_k 0.2900 + -gamma 0.00 0.00 + + +1.000UO2+2 +1.000CO3-2 +1.000F- = UO2CO3F- + log_k 13.7500 + + +1.000UO2+2 +1.000CO3-2 +2.000F- = UO2CO3F2-2 + log_k 15.5700 + + +1.000UO2+2 +1.000CO3-2 +3.000F- = UO2CO3F3-3 + log_k 16.3800 + ++1.000UO2+2 +1.000Si(OH)4 = UO2SiO(OH)3+ + H+ + log_k -1.88 +# Original value 7.8 (= -1.84) was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000UO2+2 +1.000SCN- = UO2SCN+ + log_k 1.4000 + + +1.000UO2+2 +2.000SCN- = UO2(SCN)2 + log_k 1.2400 + -gamma 0.00 0.00 + + +1.000UO2+2 +3.000SCN- = UO2(SCN)3- + log_k 2.1000 + +# U(VI) SUPPLEMENTAL DATA +# ========================== + + +1.000Mg+2 +1.000UO2+2 +3.000CO3-2 = MgUO2(CO3)3-2 + log_k 26.2 +# Original value 26.11 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + -dw 5.06e-10 # Kerisit & Liu (2010) + ++2.000Mg+2 +1.000UO2+2 +3.000CO3-2 = Mg2UO2(CO3)3 + log_k 27.1 +# This value was added from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + -dw 4.6e-10 # Kerisit & Liu (2010) analog to Ca2UO2(CO3)3 + + +1.000Ca+2 +1.000UO2+2 +3.000CO3-2 = CaUO2(CO3)3-2 + log_k 27.0 +# Original value 27.18 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + -dw 5.06e-10 # Kerisit & Liu (2010) + + +2.000Ca+2 +1.000UO2+2 +3.000CO3-2 = Ca2UO2(CO3)3 + log_k 30.8 +# Original value 29.22 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + -dw 4.6e-10 # Kerisit & Liu (2010) + + +1.000Sr+2 +1.000UO2+2 +3.000CO3-2 = SrUO2(CO3)3-2 + log_k 25.9 +# Original value 26.86 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + -dw 4.83e-10 # Kerisit & Liu (2010) + + +2.000Sr+2 +1.000UO2+2 +3.000CO3-2 = Sr2UO2(CO3)3 + log_k 29.7 +# This value was added from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + -dw 4.6e-10 # Kerisit & Liu (2010) analog to Ca2UO2(CO3)3 + + +1.000SeO4-2 +1.000UO2+2 = UO2SeO4 + log_k 2.93 +# This value was added from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +2.000SeO4-2 +1.000UO2+2 = UO2(SeO4)2-2 + log_k 4.03 +# This value was added from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000Ba+2 +1.000UO2+2 +3.000CO3-2 = BaUO2(CO3)3-2 + log_k 26.6800 + + +2.000Ba+2 +1.000UO2+2 +3.000CO3-2 = Ba2UO2(CO3)3 + log_k 29.7500 + -gamma 0.00 0.00 + +# Np(III) RECOMMENDED DATA +############################ + + +1.000Np+3 +1.000H2O -1.000H+ = NpOH+2 + log_k -6.8000 + +# Np(III) SUPPLEMENTAL DATA +# ========================== + ++1.000Np+3 +2.000H2O -2.000H+ = Np(OH)2+ + log_k -14.7000 + + +1.000Np+3 +3.000H2O -3.000H+ = Np(OH)3 + log_k -25.8000 + -gamma 0.00 0.00 + + +1.000Np+3 +1.000F- = NpF+2 + log_k 3.4000 + + +1.000Np+3 +2.000F- = NpF2+ + log_k 5.8000 + + +1.000Np+3 +1.000Cl- = NpCl+2 + log_k 0.2400 + + +1.000Np+3 +2.000Cl- = NpCl2+ + log_k -0.7400 + + +1.000Np+3 +1.000SO4-2 = NpSO4+ + log_k 3.3000 + + +1.000Np+3 +2.000SO4-2 = Np(SO4)2- + log_k 3.7000 + + +1.000Np+3 +1.000CO3-2 = NpCO3+ + log_k 8.0000 + + +1.000Np+3 +2.000CO3-2 = Np(CO3)2- + log_k 12.9000 + + +1.000Np+3 +3.000CO3-2 = Np(CO3)3-3 + log_k 15.0000 + + +1.000Np+3 +1.000SiO(OH)3- = NpSiO(OH)3+2 + log_k 8.1000 + +# Np(IV) RECOMMENDED DATA +############################ + + +1.000Np+4 +1.000H2O -1.000H+ = NpOH+3 + log_k 0.5500 + + +1.000Np+4 +4.000H2O -4.000H+ = Np(OH)4 + log_k -8.3000 + -gamma 0.00 0.00 + + +1.000Np+4 +1.000F- = NpF+3 + log_k 8.9600 + + +1.000Np+4 +2.000F- = NpF2+2 + log_k 15.7000 + + +1.000Np+4 +1.000Cl- = NpCl+3 + log_k 1.5000 + + +1.000Np+4 +1.000SO4-2 = NpSO4+2 + log_k 6.8500 + + +1.000Np+4 +2.000SO4-2 = Np(SO4)2 + log_k 11.0500 + -gamma 0.00 0.00 + + +1.000Np+4 +1.000NO3- = NpNO3+3 + log_k 1.9000 + + +1.000Np+4 +4.000CO3-2 = Np(CO3)4-4 + log_k 38.9000 + + +1.000Np+4 +5.000CO3-2 = Np(CO3)5-6 + log_k 37.8000 + + +1.000Np+4 +2.000H2O -2.000H+ = Np(OH)2+2 + log_k 0.3500 + + +1.000Np+4 +1.000I- = NpI+3 + log_k 1.5000 + + +1.000Np+4 +1.000SCN- = NpSCN+3 + log_k 3.0000 + + +1.000Np+4 +2.000SCN- = Np(SCN)2+2 + log_k 4.1000 + + +1.000Np+4 +3.000SCN- = Np(SCN)3+ + log_k 4.8000 + +# Np(IV) SUPPLEMENTAL DATA +# ========================== + + +1.000Np+4 +3.000H2O -3.000H+ = Np(OH)3+ + log_k -2.8000 + + +1.000Np+4 +1.000CO3-2 +3.000H2O -3.000H+ = NpCO3(OH)3- + log_k 2.0000 + + +1.000Np+4 +1.000SiO(OH)3- = NpSiO(OH)3+3 + log_k 11.2000 + +# Np(V) RECOMMENDED DATA +############################ + + +1.000NpO2+ +1.000H2O -1.000H+ = NpO2(OH) + log_k -11.3000 + -gamma 0.00 0.00 + + +1.000NpO2+ +2.000H2O -2.000H+ = NpO2(OH)2- + log_k -23.6000 + + +1.000NpO2+ +1.000F- = NpO2F + log_k 1.2000 + -gamma 0.00 0.00 + + +1.000NpO2+ +1.000SO4-2 = NpO2SO4- + log_k 1.3 +# Original value 0.44 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000NpO2+ +1.000HPO4-2 = NpO2HPO4- + log_k 2.9500 + + +1.000NpO2+ +1.000CO3-2 = NpO2CO3- + log_k 4.9600 + + +1.000NpO2+ +2.000CO3-2 = NpO2(CO3)2-3 + log_k 6.5300 + + +1.000NpO2+ +3.000CO3-2 = NpO2(CO3)3-5 + log_k 5.5000 + + +1.000NpO2+ +2.000CO3-2 +1.000H2O -1.000H+ = NpO2(CO3)2OH-4 + log_k -5.3000 + + +1.000NpO2+ +1.000IO3- = NpO2IO3 + log_k 0.5000 + -gamma 0.00 0.00 + +# Np(V) SUPPLEMENTAL DATA +# ========================== + + +1.000NpO2+ +1.000SiO(OH)3- = NpO2SiO(OH)3 + log_k 7.0000 + -gamma 0.00 0.00 + + +1.000NpO2+ +1.000SCN- = NpO2SCN + log_k 0.0800 + -gamma 0.00 0.00 + ++1.000NpO2+ +1.000Ca+2 +2.000H2O = Ca(NpO2(OH)2)+ +2.000H+ + log_k -20.6 +# New species was added from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + ++1.000NpO2+ +3.000Ca+2 +5.000H2O = Ca3(NpO2(OH)5)+2 +5.000H+ + log_k -54.8 +# New species was added from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +# Np(VI) RECOMMENDED DATA +############################ + + +1.000NpO2+2 +1.000H2O -1.000H+ = NpO2OH+ + log_k -5.1000 + + +2.000NpO2+2 +2.000H2O -2.000H+ = (NpO2)2(OH)2+2 + log_k -6.2700 + + +3.000NpO2+2 +5.000H2O -5.000H+ = (NpO2)3(OH)5+ + log_k -17.1200 + + +1.000NpO2+2 +1.000F- = NpO2F+ + log_k 4.5700 + + +1.000NpO2+2 +2.000F- = NpO2F2 + log_k 7.6000 + -gamma 0.00 0.00 + + +1.000NpO2+2 +1.000Cl- = NpO2Cl+ + log_k 0.4000 + + +1.000NpO2+2 +1.000SO4-2 = NpO2SO4 + log_k 3.2800 + -gamma 0.00 0.00 + + +1.000NpO2+2 +2.000SO4-2 = NpO2(SO4)2-2 + log_k 4.7000 + + +1.000NpO2+2 +1.000CO3-2 = NpO2CO3 + log_k 9.3200 + -gamma 0.00 0.00 + + +1.000NpO2+2 +2.000CO3-2 = NpO2(CO3)2-2 + log_k 16.5200 + + +1.000NpO2+2 +3.000CO3-2 = NpO2(CO3)3-4 + log_k 19.3700 + + +3.000NpO2+2 +6.000CO3-2 = (NpO2)3(CO3)6-6 + log_k 49.8400 + + +2.000NpO2+2 +1.000CO3-2 +3.000H2O -3.000H+ = (NpO2)2CO3(OH)3- + log_k -2.8700 + + +1.000NpO2+2 +1.000HPO4-2 = NpO2HPO4 + log_k 6.2000 + -gamma 0.00 0.00 + + +1.000NpO2+2 +1.000H2PO4- = NpO2H2PO4+ + log_k 3.3200 + + +1.000NpO2+2 +2.000HPO4-2 = NpO2(HPO4)2-2 + log_k 9.5000 + + +1.000NpO2+2 +1.000IO3- = NpO2IO3+ + log_k 1.2000 + +# Np(VI) SUPPLEMENTAL DATA +# ========================== + + +1.000NpO2+2 +3.000H2O -3.000H+ = NpO2(OH)3- + log_k -20.0000 + + +1.000NpO2+2 +4.000H2O -4.000H+ = NpO2(OH)4-2 + log_k -32.0000 + + +1.000NpO2+2 +1.000SiO(OH)3- = NpO2SiO(OH)3+ + log_k 7.2000 + + +1.000NpO2+2 +1.000SiO2(OH)2-2 = NpO2SiO2(OH)2 + log_k 16.5000 + -gamma 0.00 0.00 + +# Pu(III) RECOMMENDED DATA +############################ + + +1.000Pu+3 +1.000H2O -1.000H+ = PuOH+2 + log_k -6.9000 + + +1.000Pu+3 +1.000SO4-2 = PuSO4+ + log_k 3.9000 + + +1.000Pu+3 +2.000SO4-2 = Pu(SO4)2- + log_k 5.7000 + + +1.000Pu+3 +1.000SCN- = PuSCN+2 + log_k 1.3000 + +# Pu(III) SUPPLEMENTAL DATA +# ========================== + + +1.000Pu+3 +2.000H2O -2.000H+ = Pu(OH)2+ + log_k -14.8000 + + +1.000Pu+3 +3.000H2O -3.000H+ = Pu(OH)3 + log_k -25.9000 + -gamma 0.00 0.00 + + +1.000Pu+3 +1.000F- = PuF+2 + log_k 3.4000 + + +1.000Pu+3 +2.000F- = PuF2+ + log_k 5.8000 + + +1.000Pu+3 +1.000Cl- = PuCl+2 + log_k 1.2000 + + +1.000Pu+3 +1.000CO3-2 = PuCO3+ + log_k 8.0000 + + +1.000Pu+3 +2.000CO3-2 = Pu(CO3)2- + log_k 12.9000 + + +1.000Pu+3 +3.000CO3-2 = Pu(CO3)3-3 + log_k 15.0000 + + +1.000Pu+3 +1.000SiO(OH)3- = PuSiO(OH)3+2 + log_k 8.1000 + +# Pu(IV) RECOMMENDED DATA +############################ + + +1.000Pu+4 +1.000H2O -1.000H+ = PuOH+3 + log_k 0.0000 + + +1.000Pu+4 +4.000H2O -4.000H+ = Pu(OH)4 + log_k -9.3000 + -gamma 0.00 0.00 + + +1.000Pu+4 +1.000F- = PuF+3 + log_k 8.8400 + + +1.000Pu+4 +2.000F- = PuF2+2 + log_k 15.7000 + + +1.000Pu+4 +1.000Cl- = PuCl+3 + log_k 1.8000 + + +1.000Pu+4 +1.000SO4-2 = PuSO4+2 + log_k 6.8900 + + +1.000Pu+4 +2.000SO4-2 = Pu(SO4)2 + log_k 11.1400 + -gamma 0.00 0.00 + + +1.000Pu+4 +1.000NO3- = PuNO3+3 + log_k 1.9500 + + +1.000Pu+4 +1.000H3PO4 = PuH3PO4+4 + log_k 2.4000 + + +1.000Pu+4 +4.000CO3-2 = Pu(CO3)4-4 + log_k 37.0000 + + +1.000Pu+4 +5.000CO3-2 = Pu(CO3)5-6 + log_k 35.6500 + + +1.000Pu+4 +2.000H2O -2.000H+ = Pu(OH)2+2 + log_k -1.2000 + + +1.000Pu+4 +3.000H2O -3.000H+ = Pu(OH)3+ + log_k -3.1000 + + +# Pu(IV) SUPPLEMENTAL DATA +# ========================== + + +1.000Pu+4 +1.000SiO(OH)3- = PuSiO(OH)3+3 + log_k 11.8000 + + +4.000Ca+2 +1.000Pu+4 +8.000H2O -8.000H+ = Ca4Pu(OH)8+4 + log_k -55.7000 + + +1.000Pu+4 +1.000CO3-2 +3.000H2O -3.000H+ = PuCO3(OH)3- + log_k 6.0000 + +# Pu(V) RECOMMENDED DATA +############################ + + +1.000PuO2+ +1.000H2O -1.000H+ = PuO2OH + log_k -9.7300 + -gamma 0.00 0.00 + + +1.000PuO2+ +1.000CO3-2 = PuO2CO3- + log_k 5.1200 + + +1.000PuO2+ +3.000CO3-2 = PuO2(CO3)3-5 + log_k 5.0300 + +# Pu(VI) RECOMMENDED DATA +############################ + + +1.000PuO2+2 +1.000H2O -1.000H+ = PuO2OH+ + log_k -5.5000 + + +1.000PuO2+2 +2.000H2O -2.000H+ = PuO2(OH)2 + log_k -13.2000 + -gamma 0.00 0.00 + + +2.000PuO2+2 +2.000H2O -2.000H+ = (PuO2)2(OH)2+2 + log_k -7.5000 + + +1.000PuO2+2 +1.000F- = PuO2F+ + log_k 4.5600 + + +1.000PuO2+2 +2.000F- = PuO2F2 + log_k 7.2500 + -gamma 0.00 0.00 + + +1.000PuO2+2 +1.000Cl- = PuO2Cl+ + log_k 0.2300 + + +1.000PuO2+2 +2.000Cl- = PuO2Cl2 + log_k -1.1500 + -gamma 0.00 0.00 + + +1.000PuO2+2 +1.000SO4-2 = PuO2SO4 + log_k 3.3800 + -gamma 0.00 0.00 + + +1.000PuO2+2 +2.000SO4-2 = PuO2(SO4)2-2 + log_k 4.4000 + + +1.000PuO2+2 +1.000CO3-2 = PuO2CO3 + log_k 9.5000 + -gamma 0.00 0.00 + + +1.000PuO2+2 +2.000CO3-2 = PuO2(CO3)2-2 + log_k 14.7000 + + +1.000PuO2+2 +3.000CO3-2 = PuO2(CO3)3-4 + log_k 18.0000 + +# Pu(VI) SUPPLEMENTAL DATA +# ========================== + + +1.000PuO2+2 +1.000SiO(OH)3- = PuO2SiO(OH)3+ + log_k 6.0000 + + +1.000PuO2+2 +1.000SiO2(OH)2-2 = PuO2SiO2(OH)2 + log_k 12.6000 + -gamma 0.00 0.00 + +# RECOMMENDED DATA +# U(VI) +# Np(VI) Mixed +# Pu(VI) +############################ + + +2.000UO2+2 +1.000NpO2+2 +6.000CO3-2 = (UO2)2NpO2(CO3)6-6 + log_k 53.5900 + + +2.000UO2+2 +1.000PuO2+2 +6.000CO3-2 = (UO2)2PuO2(CO3)6-6 + log_k 52.7000 + +# Am(III) RECOMMENDED DATA +############################ + + +1.000Am+3 +1.000H2O -1.000H+ = AmOH+2 + log_k -7.2000 + + +1.000Am+3 +2.000H2O -2.000H+ = Am(OH)2+ + log_k -15.1000 + + +1.000Am+3 +3.000H2O -3.000H+ = Am(OH)3 + log_k -26.2000 + -gamma 0.00 0.00 + + +1.000Am+3 +1.000F- = AmF+2 + log_k 3.4000 + + +1.000Am+3 +2.000F- = AmF2+ + log_k 5.8000 + + +1.000Am+3 +1.000Cl- = AmCl+2 + log_k 0.2400 + + +1.000Am+3 +2.000Cl- = AmCl2+ + log_k -0.81 +# Original value -0.74 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000Am+3 +1.000SO4-2 = AmSO4+ + log_k 3.5000 +# Original value 3.3 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000Am+3 +2.000SO4-2 = Am(SO4)2- + log_k 5.000 +# Original value 3.7 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000Am+3 +1.000NO3- = AmNO3+2 + log_k 1.2800 +# Original value 1.33 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000Am+3 +2.000NO3- = Am(NO3)2+ + log_k 0.8800 +# This value was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000Am+3 +1.000H2PO4- = AmH2PO4+2 + log_k 2.4600 +# Original value 3.00 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000Am+3 +1.000CO3-2 = AmCO3+ + log_k 8.0000 + + +1.000Am+3 +2.000CO3-2 = Am(CO3)2- + log_k 12.9000 + + +1.000Am+3 +3.000CO3-2 = Am(CO3)3-3 + log_k 15.0000 + + +1.000Am+3 +1.000SiO(OH)3- = AmSiO(OH)3+2 + log_k 8.1300 +# Original value 8.1 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +1.000Am+3 +1.000HCO3- = AmHCO3+2 + log_k 3.1000 + + +1.000Am+3 +1.000SCN- = AmSCN+2 + log_k 1.3000 + +# Am(III) SUPPLEMENTAL DATA +# ========================== + + +1.000Ca+2 +1.000Am+3 +3.000H2O -3.000H+ = CaAm(OH)3+2 + log_k -26.3000 + + +2.000Ca+2 +1.000Am+3 +4.000H2O -4.000H+ = Ca2Am(OH)4+3 + log_k -37.2000 + + +3.000Ca+2 +1.000Am+3 +6.000H2O -6.000H+ = Ca3Am(OH)6+3 + log_k -60.7000 + +# Am(V) RECOMMENDED DATA +############################ + + +1.000AmO2+ +1.000H2O -1.000H+ = AmO2OH + log_k -11.3000 + -gamma 0.00 0.00 + + +1.000AmO2+ +2.000H2O -2.000H+ = AmO2(OH)2- + log_k -23.6000 + + +1.000AmO2+ +1.000CO3-2 = AmO2CO3- + log_k 5.1000 + + +1.000AmO2+ +2.000CO3-2 = AmO2(CO3)2-3 + log_k 6.7000 + + +1.000AmO2+ +3.000CO3-2 = AmO2(CO3)3-5 + log_k 5.1000 + +# Cm(III) RECOMMENDED DATA +############################ + + +1.000Cm+3 +1.000H2O -1.000H+ = CmOH+2 + log_k -7.2000 + + +1.000Cm+3 +2.000H2O -2.000H+ = Cm(OH)2+ + log_k -15.1000 + + +1.000Cm+3 +3.000H2O -3.000H+ = Cm(OH)3 + log_k -26.2000 + -gamma 0.00 0.00 + + +1.000Cm+3 +1.000F- = CmF+2 + log_k 3.4000 + + +1.000Cm+3 +2.000F- = CmF2+ + log_k 5.8000 + + +1.000Cm+3 +1.000Cl- = CmCl+2 + log_k 0.2400 + + +1.000Cm+3 +2.000Cl- = CmCl2+ + log_k -0.7400 + + +1.000Cm+3 +1.000SO4-2 = CmSO4+ + log_k 3.3000 + + +1.000Cm+3 +2.000SO4-2 = Cm(SO4)2- + log_k 3.7000 + + +1.000Cm+3 +1.000NO3- = CmNO3+2 + log_k 1.3300 + + +1.000Cm+3 +1.000H2PO4- = CmH2PO4+2 + log_k 3.0000 + + +1.000Cm+3 +1.000CO3-2 = CmCO3+ + log_k 8.0000 + + +1.000Cm+3 +2.000CO3-2 = Cm(CO3)2- + log_k 12.9000 + + +1.000Cm+3 +3.000CO3-2 = Cm(CO3)3-3 + log_k 15.0000 + + +1.000Cm+3 +1.000HCO3- = CmHCO3+2 + log_k 3.1000 + + +1.000Cm+3 +1.000SiO(OH)3- = CmSiO(OH)3+2 + log_k 8.1000 + + +1.000Cm+3 +1.000SCN- = CmSCN+2 + log_k 1.3000 + +# Cm(III) SUPPLEMENTAL DATA +# ========================== + + +1.000Ca+2 +1.000Cm+3 +3.000H2O -3.000H+ = CaCm(OH)3+2 + log_k -26.3000 + + +2.000Ca+2 +1.000Cm+3 +4.000H2O -4.000H+ = Ca2Cm(OH)4+3 + log_k -37.2000 + + +3.000Ca+2 +1.000Cm+3 +6.000H2O -6.000H+ = Ca3Cm(OH)6+3 + log_k -60.7000 + +################################################################################################################### + +# New implemented aqueous species - Updated values + +################################################################################################################### + +### Iron from NEA TDB, Vol. 13a ################################################################################## + + ++1.000Fe+2 = 1.000Fe+3 + e- + log_k -13.051 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013]; their Fe+2 + H+ = Fe+3 + 0.5H2(g) with logK -13.051 + +### Oxide/Hydroxide ############ + ++1.000Fe+2 + 1.000H2O = FeOH+ + H+ + log_k -9.1 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++1.000Fe+3 + 1.000H2O = FeOH+2 + H+ + log_k -2.1500 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++1.000Fe+3 + 2.000H2O = Fe(OH)2+ + 2H+ + log_k -4.8 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++2.000Fe+3 + 2.000H2O = Fe2(OH)2+4 + 2H+ + log_k -2.82 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +### Carbonate ############ + ++1.000Fe+3 +3.000CO3-2 = Fe(CO3)3-3 + log_k 24.0 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++1.000Fe+3 +1.000CO3-2 + H2O = Fe(OH)CO3 + H+ + log_k 10.7 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++1.000Fe+2 +2.000HCO3- = Fe(CO3)2-2 + 2H+ + log_k -13.62 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013], converted from + # FeCO3(cr) + CO2(g) + H2O(l) = Fe(CO3)2-2 + 2H+ with logK -21.794 + +### Chloride ############ + ++1.000Fe+3 +1.000Cl- = FeCl+2 + log_k 1.52 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++1.000Fe+3 +2.000Cl- = FeCl2+ + log_k 2.2 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++1.000Fe+3 +3.000Cl- = FeCl3 + log_k 1.02 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++1.000Fe+3 +4.000Cl- = FeCl4- + log_k -0.98 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +### Fluoride ############ + ++1.000Fe+2 +1.000F- = FeF+ + log_k 1.7 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +### Sulfate ############ + ++1.000Fe+2 +1.000SO4-2 = FeSO4 + log_k 2.44 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++1.000Fe+3 +1.000SO4-2 = FeSO4+ + log_k 4.25 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + ++1.000Fe+3 +2.000SO4-2 = Fe(SO4)2- + log_k 6.22 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +###################################################################################################################### + +PHASES + +# PMATCH MINERALS + +# Minerals RECOMMENDED DATA + +##### Original PSI/NAGRA TDB 12/07 ################################################################################# + +Anhydrite +CaSO4 = +1.000Ca+2 +1.000SO4-2 + log_k -4.3575 + +Aragonite +CaCO3 = +1.000Ca+2 -1.000H+ +1.000HCO3- + log_k 1.9928 + +As(cr) +As = +1.000HAsO4-2 +7.000H+ +5.000e- -4.000H2O + log_k -40.9892 + +Barite +BaSO4 = +1.000Ba+2 +1.000SO4-2 + log_k -9.9704 + +Brucite +Mg(OH)2 = +1.000Mg+2 +2.000H2O -2.000H+ + log_k 16.8400 + +Calcite +CaCO3 = +1.000Ca+2 -1.000H+ +1.000HCO3- + log_k 1.8490 + +MgCalcite_NT14 # for PW in OPA section at Mt. Russelin (borehole NT-14) +CaCO3 = +1.000Ca+2 -1.000H+ +1.000HCO3- + log_k 2.01672 # calculated by Marco to account for Mg incorporation in pure mineral + +Celestite +SrSO4 = +1.000Sr+2 +1.000SO4-2 + log_k -6.6319 + +Dolomite(dis) +CaMg(CO3)2 = +1.000Ca+2 +1.000Mg+2 -2.000H+ +2.000HCO3- + log_k 4.1180 + +Dolomite(ord) +CaMg(CO3)2 = +1.000Ca+2 +1.000Mg+2 -2.000H+ +2.000HCO3- + log_k 3.5680 + +#Fe(cr) +#Fe = +1.000Fe+2 +2.000e- +# log_k 13.8226 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +Fluorite +CaF2 = +1.000Ca+2 +2.000F- + log_k -10.5997 + +#Goethite +#FeOOH = +2.000H2O -3.000H+ +1.000Fe+3 +# log_k -1.0000 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +Graphite +C = +1.000HCO3- +5.000H+ +4.000e- -3.000H2O + log_k -21.8192 + +Gypsum +CaSO4:2H2O = +1.000Ca+2 +1.000SO4-2 +2.000H2O + log_k -4.5809 + +Hausmannite +MnMn2O4 = +3.000Mn+2 +4.000H2O -8.000H+ -2.000e- + log_k 61.0300 + +Manganite +MnOOH = +1.000Mn+2 +2.000H2O -3.000H+ -1.000e- + log_k 25.3400 + +#Melanterite +#FeSO4:7H2O = +1.000Fe+2 +1.000SO4-2 +7.000H2O +# log_k -2.2093 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +Mo(cr) +Mo = +1.000MoO4-2 +8.000H+ +6.000e- -4.000H2O + log_k 19.6670 +# bug: log_k entered manually + +Tugarinovite +MoO2 = +1.000MoO4-2 +4.000H+ +2.000e- -2.000H2O + log_k 29.9560 +# bug: log_k entered manually + +Molybdite +MoO3 = +1.000MoO4-2 +2.000H+ -1.000H2O + log_k 12.0550 +# bug: log_k entered manually + +Nb2O5(cr) +Nb2O5 = +2.000NbO3- +2.000H+ -1.000H2O + log_k 24.3410 +# bug: log_k entered manually + +NbO2(cr) +NbO2 = +1.000NbO3- +2.000H+ +1.000e- -1.000H2O + log_k 7.9780 +# bug: log_k entered manually + +Portlandite +Ca(OH)2 = +1.000Ca+2 +2.000H2O -2.000H+ + log_k 22.8000 + +Pyrochroite +Mn(OH)2 = +1.000Mn+2 +2.000H2O -2.000H+ + log_k 15.2000 + +Pyrolusite +MnO2 = +1.000Mn+2 +2.000H2O -4.000H+ -2.000e- + log_k 41.3800 + +Rhodochrosite +MnCO3 = +1.000Mn+2 +1.000HCO3- -1.000H+ + log_k -0.8011 + +Rhodochrosite(syn) +MnCO3 = +1.000Mn+2 +1.000HCO3- -1.000H+ + log_k -0.0611 + +#Siderite +#FeCO3 = +1.000Fe+2 +1.000HCO3- -1.000H+ +# log_k -0.5612 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +#FeCO3(pr) +#FeCO3 = +1.000Fe+2 +1.000HCO3- -1.000H+ +# log_k -0.1211 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +Strontianite +SrCO3 = +1.000Sr+2 -1.000H+ +1.000HCO3- + log_k 1.0583 + +Witherite +BaCO3 = +1.000Ba+2 -1.000H+ +1.000HCO3- + log_k 1.7672 + +#Hematite +#Fe2O3 = +3.000H2O -6.000H+ +2.000Fe+3 +# log_k 1.1200 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +Pyrite +FeS2 = +1.000Fe+2 +2.000HS- -2.000H+ -2.000e- + log_k -18.5000 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +#Troilite +#FeS = +1.000Fe+2 +1.000HS- -1.000H+ +# log_k -5.3100 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +Magnesite +MgCO3 = +1.000Mg+2 -1.000H+ +1.000HCO3- + log_k 2.0410 + +S(rhomb) +S = +1.000HS- -1.000H+ -2.000e- + log_k -2.1440 + +#Fe(OH)3(am) +#Fe(OH)3 = +3.000H2O -3.000H+ +1.000Fe+3 +# log_k 5.0000 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +#Fe(OH)3(mic) +#Fe(OH)3 = +3.000H2O -3.000H+ +1.000Fe+3 +# log_k 3.0000 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +#Magnetite +#FeFe2O4 = +1.000Fe+2 +4.000H2O -8.000H+ +2.000Fe+3 +# log_k 10.0200 +# commented out, as we added Fe+3 data from NEA TDB Vol. 13a + +#Gibbsite +#Al(OH)3 = +1.000Al+3 +3.000H2O -3.000H+ +# log_k 7.7559 +# commented out, as we added data for a generic and amorphous gibbsite + + +# Si(IV) RECOMMENDED DATA +############################ + +Quartz +SiO2 = +1.000Si(OH)4 -2.000H2O + log_k -3.7460 + +SiO2(am) +SiO2 = +1.000Si(OH)4 -2.000H2O + log_k -2.7140 + +Kaolinite +Al2Si2O5(OH)4 = +2.000Al+3 +2.000Si(OH)4 +1.000H2O -6.000H+ + log_k 7.4350 + +# Ni(II) RECOMMENDED DATA +############################ + +NiCO3(cr) +NiCO3 = +1.000Ni+2 +1.000CO3-2 + log_k -11.0000 + +Ni(OH)2(cr,beta) +Ni(OH)2 = +1.000Ni+2 +2.000H2O -2.000H+ + log_k 11.0200 + +NiO(cr) +NiO = +1.000Ni+2 +1.000H2O -2.000H+ + log_k 12.4800 + +NiCO3:5.5H2O(s) +NiCO3:5.5H2O = +1.000Ni+2 +1.000CO3-2 +5.500H2O + log_k -7.5300 + +Ni3(AsO4)2:8H2O(s) +Ni3(AsO4)2:8H2O = +3.000Ni+2 +2.000AsO4-3 +8.000H2O + log_k -28.1000 + +# Se(-II) SUPPLEMENTAL DATA +# ========================== + +MnSe(s) +MnSe = +1.000Mn+2 +1.000Se-2 + log_k -16.0000 + +# Se(IV) RECOMMENDED DATA +############################ + +Se(cr) +Se = +1.000SeO3-2 +6.000H+ +4.000e- -3.000H2O + log_k -61.1500 + +NiSeO3:2H2O(cr) +NiSeO3:2H2O = +1.000Ni+2 +1.000SeO3-2 +2.000H2O + log_k -5.8000 + +MnSeO3:2H2O(cr) +MnSeO3:2H2O = +1.000Mn+2 +1.000SeO3-2 +2.000H2O + log_k -7.6000 + +MgSeO3:6H2O(cr) +MgSeO3:6H2O = +1.000Mg+2 +1.000SeO3-2 +6.000H2O + log_k -5.8200 + +CaSeO3:H2O(cr) +CaSeO3:H2O = +1.000Ca+2 +1.000SeO3-2 +1.000H2O + log_k -6.4000 + +SrSeO3(cr) +SrSeO3 = +1.000Sr+2 +1.000SeO3-2 + log_k -6.3000 + +BaSeO3(cr) +BaSeO3 = +1.000Ba+2 +1.000SeO3-2 + log_k -6.5000 + +# Se(VI) RECOMMENDED DATA +############################ + +BaSeO4(cr) +BaSeO4 = +1.000Ba+2 +1.000SeO4-2 + log_k -7.5600 + +# Zr(IV) RECOMMENDED DATA +############################ + +Baddeleyite +ZrO2 = +1.000Zr+4 +2.000H2O -4.000H+ + log_k -7.0000 + +Zr(OH)4(am,fr) +Zr(OH)4 = +1.000Zr+4 +4.000H2O -4.000H+ + log_k -3.2400 + +# Zr(IV) SUPPLEMENTAL DATA +# ========================== + +Zr(HPO4)2:H2O(cr) +Zr(HPO4)2:H2O = +1.000Zr+4 +2.000H3PO4 +1.000H2O -4.000H+ + log_k -22.8000 + + +# Tc(IV) RECOMMENDED DATA +############################ + +TcO2:1.6H2O(s) +TcO2:1.6H2O = +1.000TcO(OH)2 +0.600H2O + log_k -8.4000 + +# Pd(II) RECOMMENDED DATA +############################ + +Pd(cr) +Pd = +1.000Pd+2 +2.000e- + log_k -30.8000 + +Pd(OH)2(s) +Pd(OH)2 = +1.000Pd+2 -2.000H+ +2.000H2O + log_k -3.3000 + +# Tn(II) RECOMMENDED DATA +############################ + +Tn(cr) +Tn = +1.00Tn+2 +2.000e- + log_k 4.6300 + +TnO(s) +TnO = +1.000Tn+2 +1.000H2O -2.000H+ + log_k 2.5000 + +TnS(pr) +TnS = +1.000Tn+2 +1.000HS- -1.000H+ + log_k -14.7000 + +# Sn(IV) RECOMMENDED DATA +############################ + +Cassiterite +SnO2 = +1.000Sn(OH)4 -2.000H2O + log_k -8.0000 + +SnO2(am) +SnO2 = +1.000Sn(OH)4 -2.000H2O + log_k -7.3000 + +CaSn(OH)6(s) +CaSn(OH)6 = +1.000Sn(OH)4 +2.000H2O +1.000Ca+2 -2.000H+ + log_k 8.7000 + +# Ra(II) RECOMMENDED DATA +############################ + +RaCO3(cr) +RaCO3 = +1.000Ra+2 +1.000CO3-2 + log_k -8.3000 + +RaSO4(cr) +RaSO4 = +1.000Ra+2 +1.000SO4-2 + log_k -10.2600 + +# Eu(III) RECOMMENDED DATA +############################ + +Eu(OH)3(cr) +Eu(OH)3 = +1.000Eu+3 +3.000H2O -3.000H+ + log_k 14.9000 + +Eu(OH)3(am) +Eu(OH)3 = +1.000Eu+3 +3.000H2O -3.000H+ + log_k 17.6000 + +Eu2(CO3)3(cr) +Eu2(CO3)3 = +2.000Eu+3 +3.000CO3-2 + log_k -35.0000 + + +EuOHCO3(cr) +EuOHCO3 = +1.000Eu+3 +1.000OH- +1.000CO3-2 + log_k -21.7000 + +EuF3(cr) +EuF3 = +1.000Eu+3 +3.000F- + log_k -17.4000 + +# Th(IV) RECOMMENDED DATA +############################ + +ThO2(am,hyd,fr) +ThO2 = +1.000Th+4 +2.000H2O -4.000H+ + log_k 9.3000 + +ThO2(am,hyd,ag) +ThO2 = +1.000Th+4 +2.000H2O -4.000H+ + log_k 8.5000 + +ThF4(cr,hyd) +ThF4 = +1.000Th+4 +4.000F- + log_k -31.8000 + +Na6Th(CO3)5:12H2O(cr) +Na6Th(CO3)5:12H2O = +6.000Na+ +1.000Th+4 +5.000CO3-2 +12.000H2O + log_k -42.2000 + +# Th(IV) SUPPLEMENTAL DATA +# ========================== + +Th3(PO4)4(s) +Th3(PO4)4 = +3.000Th+4 +4.000PO4-3 + log_k -112.0000 + +# U(IV) RECOMMENDED DATA +############################ + +UF4:2.5H2O(cr) +UF4:2.5H2O = +1.000U+4 +4.000F- +2.500H2O + log_k -30.1200 + +U(OH)2SO4(cr) +U(OH)2SO4 = +1.000U+4 +1.000SO4-2 +2.000H2O -2.000H+ + log_k -3.1700 + +UO2(am,hyd) +UO2 = +1.000U+4 +2.000H2O -4.000H+ + log_k 1.5000 + +# U(IV) SUPPLEMENTAL DATA +# ========================== + +USiO4(s) # Coffinit +USiO4 = +1.000U+4 +1.000Si(OH)4 -4.000H+ + log_k -1.5000 + +# U(VI) RECOMMENDED DATA - With Update from THEREDA database +#################################################################### + +### U(VI)-Oxides ############ + +Metaschoepite +UO3:2H2O = +1.000UO2+2 +3.000H2O -2.000H+ + log_k 5.35 +# Original value 5.96 was updated from THEREDA database [primary reference [ALT/YAL2017]) + +Becquerelite +CaU6O19:11H2O = +1.000Ca+2 +6.000UO2+2 +18.000H2O -14.000H+ + log_k 40.5000 + +Clarkeite +Na2U2O7:H2O = +2.000Na+ +2.000UO2+2 +4.000H2O -6.000H+ + log_k 24.4 +# This value was added from THEREDA database [primary reference [ALT/YAL2017]) + +K2U2O7:1.5H2O(cr) +K2U2O7:1.5H2O = +2.000K+ +2.000UO2+2 +4.500H2O -6.000H+ + log_k 24.0 +# This value was added from THEREDA database [primary reference [CEV/YAL2018]) + +K-Compreignacite +K2U6O19:11H2O = +2.000K+ +6.000UO2+2 +18.000H2O -14.000H+ + log_k 37.8 +# Original value 37.10 was updated from THEREDA database [primary reference [CEV/YAL2018]) + +Na-Compreignacite +Na2(UO2)6O4(OH)6:7H2O = 2.000Na+ + 6.000UO2+2 + 17.000H2O - 14.000H+ + log_k 39.4 +# This value was added from THEREDA database (primary reference [GOR/FEI2008]) + +CaU2O7:3H2O(cr) +CaU2O7:3H2O + 6.000H+ = 1.000Ca+2 +2.000UO2+2 +6.000H2O + log_k 23.4 +# This value was added from THEREDA database (primary reference [ALT/NEC2006]) + + +### U(VI)-Carbonates ########### + +Cejkaite +Na4UO2(CO3)3 = 4.000Na+ + 3.000CO3-2 + 1.000UO2+2 + log_k -27.18 +# This value was added from THEREDA database (primary reference [GUI/FAN2003]) + +Rutherfordine +UO2CO3 = +1.000UO2+2 +1.000CO3-2 + log_k -14.7600 + + +### U(VI)-Silicates ############ + +Boltwoodite +KUO2(SiO3OH):H2O = +1.000K+ +1.000UO2+2 +1.000Si(OH)4 +1.000H2O -3.000H+ + log_k 4.12 +# This value was added from THEREDA database (primary reference [SHV/MAZ2011]) + +Na-Boltwoodite +NaUO2(SiO3OH):H2O = +1.000Na+ +1.000UO2+2 +1.000Si(OH)4 +1.000H2O -3.000H+ + log_k 6.07 +# This value was added from THEREDA database (primary reference [SHV/MAZ2011]) + +Soddyite +(UO2)2SiO4:2H2O = +2.000UO2+2 +1.000Si(OH)4 +2.000H2O -4.000H+ + log_k 5.75 +# Original value 6.2 was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + +Uranophane +Ca(UO2)2(SiO3OH)2:5H2O = +1.000Ca+2 +2.000UO2+2 +2.000Si(OH)4 +5.000H2O -6.000H+ + log_k 10.82 +# This value was added from THEREDA database (primary reference [SHV/MAZ2011]) + +Weeksite +K2(UO2)2(Si2O5)3:4H2O = +2.000K+ +2.000UO2+2 +6.000Si(OH)4 -6.000H+ -5.000H2O + log_k 16.91 +# This value was added from THEREDA database (primary reference [HEM1982]) + +Na-Weeksite +Na2(UO2)2(Si2O5)3:4H2O = +2.000Na+ +2.000UO2+2 +6.000Si(OH)4 -6.000H+ -5.000H2O + log_k 1.5 + +Sklodowskite +Mg(UO2)2(SiO3OH)2:6H2O = +1.000Mg+2 +2.000UO2+2 +2.000Si(OH)4 -6.000H+ +6.000H2O + log_k 14.48 +# This value was added from THEREDA database (primary reference [HEM1982]) + +Haiweeite +Ca(UO2)2(Si2O5)3:5H2O = +1.000Ca+2 +2.000UO2+2 +6.000Si(OH)4 -6.000H+ -4.000H2O + log_k -5.52 +# This value was added from THEREDA database (primary reference [HEM1982]) + +### U(VI)-Sulphates ############# + +UO2SO4:2.5H2O(cr) +UO2SO4:2.5H2O = +1.000UO2+2 +1.000SO4-2 +2.500H2O + log_k -1.589 +# Updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + +UO2SO4:3.0H2O(cr) +UO2SO4:3.0H2O = +1.000UO2+2 +1.000SO4-2 +3.000H2O + log_k -1.50 +# Updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + +UO2SO4:3.5H2O(cr) +UO2SO4:3.5H2O = +1.000UO2+2 +1.000SO4-2 +3.500H2O + log_k -1.585 +# Updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + +Zippeite +K3(UO2)4(SO4)2O3OH:3.3H2O = +3.000K+ + 4.000UO2+2 + 2.000SO4-2 + 7.300H2O -7H+ + log_k 4.14 +# This value was added from THEREDA database (primary reference [SHA/SZY2016]) + + +### U(VI)-Phosphate ########### + +Autunite +Ca(UO2)2(PO4)2:3H2O = 1.000Ca+2 +2.000UO2+2 +2.000PO4-3 +3.000H2O + log_k -48.36 +# This value was added from THEREDA database (primary reference [GOR/SHV2009]) + +Saaleite +Mg(UO2)2(PO4)2 = 1.000Mg+2 +2.000UO2+2 +2.000PO4-3 + log_k -46.32 +# This value was added from THEREDA database (primary reference [MUT/HIR1968]) + +Chernikovite +UO2HPO4:4H2O = +1.000UO2+2 +1.000H3PO4 +4.000H2O -2.000H+ + log_k -2.5000 + +(UO2)3(PO4)2:4H2O(cr) +(UO2)3(PO4)2:4H2O = +3.000UO2+2 +2.000H3PO4 +4.000H2O -6.000H+ + log_k -5.9600 + +(UO2)3(PO4)2:6H2O(cr) +(UO2)3(PO4)2:6H2O = +3.000UO2+2 +2.000PO4-3 +6.000H2O + log_k -49.91 +# This value was added from THEREDA database (primary reference [GUI/FAN2003]) + +UO2(H2PO4)2:3H2O(cr) +UO2(H2PO4)2:3H2O = +1.000UO2+2 +2.000H3PO4 +3.000H2O -2.000H+ + log_k -1.7 +# This value was added from THEREDA database (primary reference [GRE/FUG1992]) + + + +# Np(IV) RECOMMENDED DATA +############################ + +NpO2(am,hyd) +NpO2 = +1.000Np+4 +2.000H2O -4.000H+ + log_k -0.7000 + +# Np(V) RECOMMENDED DATA +############################ + +NpO2OH(am,fr) +NpO2OH = +1.000NpO2+ +1.000H2O -1.000H+ + log_k 5.3000 + +NaNpO2CO3:3.5H2O(cr) +NaNpO2CO3:3.5H2O = +1.000Na+ +1.000NpO2+ +1.000CO3-2 +3.500H2O + log_k -11.0000 + +Na3NpO2(CO3)2(cr) +Na3NpO2(CO3)2 = +3.000Na+ +1.000NpO2+ +2.000CO3-2 + log_k -14.2200 + +KNpO2CO3(s) +KNpO2CO3 = +1.000K+ +1.000NpO2+ +1.000CO3-2 + log_k -13.1500 + +K3NpO2(CO3)2(s) +K3NpO2(CO3)2 = +3.000K+ +1.000NpO2+ +2.000CO3-2 + log_k -15.4600 + +NpO2OH(am,ag) +NpO2OH = +1.000NpO2+ +1.000H2O -1.000H+ + log_k 4.7000 + +# Np(VI) RECOMMENDED DATA +############################ + +NpO2CO3(s) +NpO2CO3 = +1.000NpO2+2 +1.000CO3-2 + log_k -14.6000 + +NpO3:H2O(cr) +NpO3:H2O = +1.000NpO2+2 +2.000H2O -2.000H+ + log_k 5.4700 + +K4NpO2(CO3)3(s) +K4NpO2(CO3)3 = +4.000K+ +1.000NpO2+2 +3.000CO3-2 + log_k -26.4000 + +(NH4)4NpO2(CO3)3(s) +(NH4)4NpO2(CO3)3 = +4.000NH4+ +1.000NpO2+2 +3.000CO3-2 + log_k -26.8100 + +# Pu(III) RECOMMENDED DATA +############################ + +Pu(OH)3(cr) +Pu(OH)3 = +1.000Pu+3 +3.000H2O -3.000H+ + log_k 15.8000 + +PuPO4(s,hyd) +PuPO4 = +1.000Pu+3 +1.000PO4-3 + log_k -24.6000 + +# Pu(IV) RECOMMENDED DATA +############################ + +Pu(HPO4)2(am,hyd) +Pu(HPO4)2 = +1.000Pu+4 +2.000HPO4-2 + log_k -30.4500 + +PuO2(hyd,ag) +PuO2 = +1.000Pu+4 +2.000H2O -4.000H+ + log_k -2.3300 + +# Pu(V) RECOMMENDED DATA +############################ + +PuO2OH(am) +PuO2OH = +1.000PuO2+ +1.000H2O -1.000H+ + log_k 5.0000 + +# Pu(VI) RECOMMENDED DATA +############################ + +PuO2(OH)2:H2O(cr) +PuO2(OH)2:H2O = +1.000PuO2+2 +3.000H2O -2.000H+ + log_k 5.5000 + +PuO2CO3(s) +PuO2CO3 = +1.000PuO2+2 +1.000CO3-2 + log_k -14.6500 + +# Am(III) RECOMMENDED DATA +############################ + +Am(OH)3(cr) +Am(OH)3 = +1.000Am+3 +3.000H2O -3.000H+ + log_k 15.6000 + +Am(OH)3(am) +Am(OH)3 = +1.000Am+3 +3.000H2O -3.000H+ + log_k 16.9000 + +Am(CO3)1.5(am,hyd) +Am(CO3)1.5 = +1.000Am+3 +1.500CO3-2 + log_k -16.7000 + +AmOHCO3:0.5H2O(cr) +AmOHCO3:0.5H2O = +1.000Am+3 +1.000OH- +1.000CO3-2 +0.500H2O + log_k -22.4000 + +AmOHCO3(am,hyd) +AmOHCO3 = +1.000Am+3 +1.000OH- +1.000CO3-2 + log_k -20.2000 + +NaAm(CO3)2:5H2O(cr) +NaAm(CO3)2:5H2O = +1.000Na+ +1.000Am+3 +2.000CO3-2 +5.000H2O + log_k -21.0000 + +Am2(CO3)3(am) +Am2(CO3)3 = +2.000Am+3 +3.000CO3-2 + log_k -33.4 +# This value was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + +AmPO4(am,hyd) +AmPO4 = +1.000Am+3 +1.000PO4-3 + log_k -24.79 +# This value was updated from NEA Second Update, Vol. 14 (Grenthe et al. 2020) + + +# Am(V) RECOMMENDED DATA +############################ + +AmO2OH(am) +AmO2OH = +1.000AmO2+ +1.000H2O -1.000H+ + log_k 5.3000 + +NaAmO2CO3(s) +NaAmO2CO3 = +1.000Na+ +1.000AmO2+ +1.000CO3-2 + log_k -10.9000 + +# Cm(III) SUPPLEMENTAL DATA +# ========================== + +Cm(OH)3(am,coll) +Cm(OH)3 = +1.000Cm+3 +3.000H2O -3.000H+ + log_k 17.2000 + + +################################################################################################################### + +# New implemented solid phases - Updated values + +################################################################################################################### + +### IRON - NEA TDB Vol. 13a ########################################################################################### + +Fe(cr) +Fe = +1.000Fe+2 + 2.000e- + log_k 15.893 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013]; their Fe(cr) + 2.0H+ = Fe+2 + 1.0H2(g) with logK 15.893 + +### Fe-Oxide/Hydroxide ######## + +Hematite +Fe2O3 + 3.000H2O = +2.000Fe+3 + 6.000OH- + log_k -84.11 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013]; there 0.5Fe2O3 logK -42.05 + +Maghemite +Fe2O3 + 3.000H2O = +2.000Fe+3 + 6.000OH- + log_k -81.2 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013]; there 0.5Fe2O3 logK -40.59 + +#Magnetite +#Fe3O4 + 4.000H2O = 1.000Fe+2 +2.000Fe+3 + 8.000OH- # Equation not clear +# log_k -101.67 +# # Data from NEA TDB Vol. 13a [Lemire et al., 2013]; there given only Gibbs-Energy + +Goethite +FeOOH + 1.000H2O = +1.000Fe+3 + 3.000OH- + log_k -41.83 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +Lepidocrosite +FeOOH + 1.000H2O = +1.000Fe+3 + 3.000OH- + log_k -40.13 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +Ferrihydrite +Fe(OH)3 = 1.000Fe+3 + 3.000OH- + log_k -38.97 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +### Fe-Carbonate ######## + +Siderite +FeCO3 = 1.000Fe+2 + 1.000HCO3- - 1.000H+ + log_k -0.349 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013]; calculated from Gibbs-Energy + +### Fe-Chloride ######### + +#FeCl2:H2O(cr) +#FeCl2:H2O = +1.000FeCl2(cr) + 1.000H2O +# log_k 4.38 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +#FeCl2:2H2O(cr) +#FeCl2:2H2O + 2.000H2O = +1.000FeCl2:4H2O +# log_k 4.131 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +#FeCl2:4H2O(cr) +#FeCl2:4H2O = +1.000FeCl2(cr) + 4.000H2O +# log_k -5.921 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +### Fe-Sulfate ######### + +FeS(cr) +FeS + 2.000H+ = +1.000Fe+2 +1.000H2S + log_k 3.8 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +Melanterite +FeSO4:7H2O = 1.000Fe+2 +1.000SO4-2 +7.000H2O + log_k -2.279 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013] + +FeSO4:4H2O(cr) +FeSO4:4H2O = 1.000Fe+2 +1.000SO4-2 +4.000H2O + log_k -1.654 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013]; converted NEA-equation + +FeSO4:H2O(cr) +FeSO4:H2O = 1.000Fe+2 +1.000SO4-2 +1.000H2O + log_k -0.9908 + # Data from NEA TDB Vol. 13a [Lemire et al., 2013]; converted NEA-equation + + +#### ANDRA Database ThermoChimie_PHREEQC_eDH_v9b0.dat ############################################################ + +Albite +NaAlSi3O8 = +1.000Na+ +1.000Al+3 -4.000H+ +3.000Si(OH)4 -4.000H2O + log_k 2.74 # + # delta_h -82.813 kJ/mol # + # Enthalpy of formation: -3936.19 kJ/mol 00ARN/STE + # Type: Albite-low + # Data from ANDRA Data Base ThermoChimie + +Anorthite +CaAl2Si2O8 = +1.000Ca+2 +2.000Al+3 -8.000H+ +2.000Si(OH)4 + log_k 25.31 # + # delta_h -314.358 kJ/mol # + # Enthalpy of formation: -4227.83 kJ/mol 00ARN/STE + # Data from ANDRA Data Base ThermoChimie + +Chlorite +(Mg2.964Fe1.712Fe0.215Al1.116Ca0.011)(Si2.633Al1.367)O10(OH)8 = +0.011Ca+2 +2.964Mg+2 +0.215Fe+3 +1.712Fe+2 +2.483Al+3 -17.468H+ +2.633Si(OH)4 +7.468H2O + log_k 61.23 # + #delta_h -632.836 kJ/mol # + # Enthalpy of formation: -8240.69 kJ/mol 06GAI + # Type: Chlorite-Cca-2 + # Data from ANDRA Data Base ThermoChimie + + +Illite +K0.85Fe0.25Al2.6Si3.15O10(OH)2 = +0.850K+ +0.250Fe+3 +2.600Al+3 -9.400H+ +3.150Si(OH)4 -0.600H2O + log_k 10.07 # + #delta_h -252.345 kJ/mol # + # Enthalpy of formation: -5805.328 kJ/mol 07VIE + # Type: Illite-FeIII + # Data from ANDRA Data Base ThermoChimie + +Montmorillonite +Na0.33Mg0.33Fe0.67Al1.0Si4O10(OH)2 = +0.330Mg+2 +0.330Na+ +0.670Fe+3 +1.000Al+3 -6.000H+ +4.000Si(OH)4 -4.000H2O + log_k 2.89 + #delta_h -137.779 kJ/mol + # Enthalpy of formation: -5368.33 kJ/mol 07VIE + # Type: Fe-Montmorillonite-Na + # Data from ANDRA Data Base ThermoChimie + + +##### Other Literature ###################################################################################### + +Gibbsite(gen) +Al(OH)3 = +1.000Al+3 +3.000H2O -3.000H+ + log_k 10.35 +# Data for a generic Gibbsite phase from fit to experimental Gorleben data + +Gibbsite(am) +Al(OH)3 = +1.000Al+3 +3.000H2O -3.000H+ + log_k 9.6661 +# Data from Lindsay (1979) + +K-feldspar # Orthoclase +KAlSi3O8 + 4H+ + 4H2O = Al+3 + 3Si(OH)4 + K+ + log_k -0.12 +# log_k calculated by logK(T)-functions (Stefánsson and Arnórsson, 2000) using T=298,15K +# microcline, the triclinic form of K-feldspar, was used as an analogue +# published in Richter et al. (2016) + +Muscovite +KAl3Si3O10(OH)2 = +1.000K+ +3.000Al+3 -10.000H+ +3.000Si(OH)4 + log_k 14.15 # ± 0.74 (the error representing 2sigma) # +# only calculations based on formation data are possible and the respective solubility constants were averaged +# published in Richter et al. (2016) + + +####################################################################################################### + + +# PMATCH GASES + +CH4(g) +CH4 = +1.000CH4 + log_k -2.8565 + +CO2(g) +CO2 = +1.000H+ -1.000H2O +1.000HCO3- + log_k -7.8198 + +H2(g) +H2 = +1.000H2 + log_k -3.1056 + +N2(g) +N2 = +1.000N2 + log_k -3.1864 + +O2(g) +O2 = +1.000O2 + log_k -2.8944 + +H2S(g) +H2S = +1.000HS- +1.000H+ + log_k -8.0100 + +H2Se(g) +H2Se = +1.000H2Se + log_k -1.1000 + + +##################################################################################################################### + +# Implemented Exchange Species relevant for the Opalinus Clay + +##################################################################################################################### + +# Genereal Information: +# +# Exchange Daten for all clay minerals (X), Montmorillonite (Y), Illite (Z) +# Allocation CEC -> 25 % Montmorillonite, 75 % Illite (clay minerals: Illite, Illite/Smectite mixed layers = 50 % Illite and 50 % Montmorillonite (Smectite)) + +EXCHANGE_MASTER_SPECIES + X X- # all clay minerals + Z Z- # Illite + Y Y- # Montmorillonite + +EXCHANGE_SPECIES + X- = X- + log_k 0.0 + + X- + H+ = HX # GD-Exp. Wersin et al. (2009), p. 53 + log_k = 0.0 + + X- + Na+ = NaX + log_k = 0.0 + + X- + K+ = KX + log_k = 1.4 # value from Pearson et al. (2011), PC-C Modellierung + + 2X- + Ca+2 = CaX2 + # log_k = 0.7 # value from Pearson et al. (2011) PC-C Modellierung + log_k = 0.8 # value from Wersin et al. (2009), GD-Exp. + + 2X- + Mg+2 = MgX2 + # log_k = 0.7 # value from Pearson et al. (2011) PC-C Modellierung + log_k = 0.8 # value from Wersin et al. (2009), GD-Exp. + + 2X- + Sr+2 = SrX2 + # log_k = 0.7 # value from Pearson et al. (2011) PC-C Modellierung + log_k = 0.8 # value from Wersin et al. (2009), GD-Exp. + + 2X- + Fe+2 = FeX2 + # log_k = 0.7 # value from Pearson et al. (2011) PC-C Modellierung + log_k = 0.8 # value from Wersin et al. (2009), GD-Exp. + +### Illite ### + + Z- = Z- + log_k 0.0 + + Z- + H+ = HZ # GD-Exp. Wersin et al. (2009), p. 53 + log_k = 0.0 + + Z- + Na+ = NaZ + log_k = 0.0 + + Z- + K+ = KZ + log_k = 0.92 # value from Wersin et al. (2009), GD-Experiment, Tab. 3-3 (Illite) + + 2Z- + Ca+2 = CaZ2 + log_k = 0.24 # s.o. + + 2Z- + Mg+2 = MgZ2 + log_k = 0.58 # s.o. + + 2Z- + Sr+2 = SrZ2 + log_k = 0.24 # s.o. + + 2Z- + Fe+2 = FeZ2 + log_k = 0.7 # value from Pearson et al. (2011), PC-C modelling + + 2Z- + UO2+2 = UO2Z2 + log_k = 0.65 # value from M.Stockmann (HZDR); Illite: Kc=4.5 from /BB05/ (PSI-Report) + +### Montmorillonite ### + + Y- = Y- + log_k = 0.0 + Y- + Na+ = NaY + log_k = 0.0 + Y- + H+ = HY # GD-Exp. Wersin et al. (2009), p. 53 + log_k = 0.0 + Y- + K+ = KY + log_k = 1.1 # value from Wersin et al. (2009), GD-Experiment, Tab. 3-3 (Smektite) + 2Y- + Mg+2 = MgY2 + log_k = 0.36 # s.o. + 2Y- + Ca+2 = CaY2 + log_k = 0.42 # s.o. + 2Y- + Sr+2 = SrY2 + log_k = 0.37 # s.o. + 2Y- + Fe+2 = FeY2 + log_k = 0.8 # value from Baeyens & Bradbury (2017), TR 17-13, Kc = 6.3, Tab. F1 and Soltermann (2014) + 2Y- + UO2+2 = UO2Y2 + log_k = 0.15 # value from M.Stockmann (HZDR); Montmorillonite: Kc=1.4 from /BB05a/ + + +###################################################################################################################### + +# Implemented surface species relevant in the context of the WEIMAR project (far-field of a nuclear waste repository) + +###################################################################################################################### + +# General information: +# +# This data compilation focuses on surface complexation parameters (reaction equations, pK- and logK-values) for +# representative sorbates (pair of element and minerals). All values are taken from the +# sorption database RES³T [Brendler et al. 2003] or were fitted from representative published experimental values. +# As SCM Type primary the Diffuse Double Layer Model (DDL) is preferred, but in case of no/low SCM data sets +# additionally other SCM models are used. Generic sites (»XOH) are prefered, with no differentiation between weak +# and strong sites. All pK- and logK-values were normalized to the reference binding site 2.31 sites/nm² and corrected +# to ionic strenght IS=0 using the Davies equation. Full bibliographic references are available in RES³T (www.hzdr.de/res3t). +# +# +# Additions made by Theresa: +# SCM-Daten für Tonminerale (Montmorrillonit = Smektit, Illit, Kaolinit, Chlorit) ergänzt basierend auf Joseph et al. (2013) = JOS13. +# DDL-Modell bevorzugt mit strong (sOH) und weak (wOH) binding sites für Illit und Montmorillonit. +# + +SURFACE_MASTER_SPECIES + + Ill_w Ill_wOH # Ill = Mineral group: 3-layer-clay minerals (illite) + Ill_s Ill_sOH # _w = weak binding sites, _s = strong binding sites + + Kln_a Kln_aOH # Kln = Mineral group: 2-layer clay minerals (kaolinite) + Kln_si Kln_siOH # _a = aluminol sites, _si = silanol sites + + Mll_w Mll_wOH # Mll = Mineral group: 3-layer clay minerals (smectite = montmorillonite) + Mll_s Mll_sOH # _w = weak binding sites, _s = strong binding sites + + Chl ChlOH # Chl = Mineral group: 4-layer clay minerals (chlorite) + +SURFACE_SPECIES + +### Kaolinite ################################################################################################## + +### Aluminol sites ### + + Kln_aOH = Kln_aOH + log_K 0.0 + Kln_aOH + H+ = Kln_aOH2+ + log_K 8.33 # mean of /TS96/ and /CW88/ + Kln_aOH = Kln_aO- + H+ + #log_K -9.09 # mean of /TS96/ and /CW88/ + log_K -9.73 # /JOS13/ + + Kln_aOH + UO2+2 = Kln_aOHUO2+2 +# log_k 9.2 # /PAY92/ (in TS96) + log_k 9.5 # normiert auf 1.155 sites/nm2 + Kln_aOH + UO2+2 = Kln_aOUO2+ + H+ +# log_k 2.18 # /PAY92/ (in TS96) + log_k 2.48 # normiert auf 1.155 sites/nm2 + Kln_aOH + UO2+2 + H2O = Kln_aOUO2(OH) + 2H+ +# log_k -4.74 # /PAY92/ (in TS96) + log_k -4.44 # normiert auf 1.155 sites/nm2 + +### Silanol sites ### + + Kln_siOH = Kln_siOH + log_K 0.0 + + Kln_siOH = Kln_siO- + H+ + log_K -6.9 # /JOS13/ + + Kln_siOH + UO2+2 = Kln_siOHUO2+2 + log_k 6.03 # /JOS13/ + Kln_siOH + UO2+2 = Kln_siOUO2+ + H+ + log_k 1.26 # /JOS13/ + Kln_siOH + UO2+2 + H2O = Kln_siOUO2(OH) + 2H+ + log_k -5.54 # /JOS13/, korrigiert von MS (HZDR) + +### Illite ################################################################################################### + +### weak-binding sites ### + + Ill_wOH = Ill_wOH + log_K 0.0 + Ill_wOH + H+ = Ill_wOH2+ + #log_K 5.12 # mean of /BB97b/, /WMDV98/, and /WAKWW94/ + log_K = 4.59 # /JOS13/ + Ill_wOH = Ill_wO- + H+ + #log_K -7.71 # mean of /BB97b/, /WMDV98/, and /WAKWW94/ + log_K = -7.11 # /JOS13/ + + # U(VI) + Ill_wOH + UO2+2 = Ill_wOUO2+ + H+ + #log_k 0.25 # mean of /BB05a/, /BB05c/ and /MBDSB12/ --> NE Model + log_k = -0.81 # /JOS13/ + Ill_wOH + UO2+2 + H2O = Ill_wOUO2(OH) + 2H+ + #log_k -5.75 # mean of /BB05a/, /BB05c/ and /MBDSB12/ --> NE Model + log_k = -6.21 # /JOS13/ + +### strong-binding sites ### + + Ill_sOH = Ill_sOH + log_K 0.0 + Ill_sOH + H+ = Ill_sOH2+ + #log_K 5.12 # mean of /BB97b/, /WMDV98/, and /WAKWW94/ + log_K = 4.9 # /JOS13/ + Ill_sOH = Ill_sO- + H+ + #log_K -7.71 # mean of /BB97b/, /WMDV98/, and /WAKWW94/ + log_K = -6.8 # /JOS13/ + + # U(VI) + Ill_sOH + UO2+2 = Ill_sOUO2+ + H+ + #log_k 0.25 # mean of /BB05a/, /BB05c/ and /MBDSB12/ --> NE Model + log_k = 2. # /JOS13/ + Ill_sOH + UO2+2 + H2O = Ill_sOUO2(OH) + 2H+ + #log_k -5.75 # mean of /BB05a/, /BB05c/ and /MBDSB12/ --> NE Model + log_k = -4.2 # /JOS13/ + Ill_sOH + UO2+2 + 2H2O = Ill_sOUO2(OH)2-1 + 3H+ + log_k = -10.9 # /JOS13/ + Ill_sOH + UO2+2 + 3H2O = Ill_sOUO2(OH)3-2 + 4H+ + log_k = -18.1 # /JOS13/ + + # U(IV) + # Ill_sOH + U+4 + H2O = Ill_sOUOH+2 + 2H+ + # log_k = 7.1 # TR 17-11, Tab. A-3 + # Ill_sOH + U+4 + 2H2O = Ill_sOU(OH)2+1 + 3H+ + # log_k = 3.6 # TR 17-11, Tab. A-3 + # Ill_sOH + U+4 + 4H2O = Ill_sOU(OH)4 + 5H+ + # log_k = -1.6 # TR 17-11, Tab. A-3 + +### Montmorillonite ################################################################################################ + +### weak-binding sites ### + + Mll_wOH = Mll_wOH + log_K 0.0 + Mll_wOH + H+ = Mll_wOH2+ + log_K = 3.98 # /JOS13/ + Mll_wOH = Mll_wO- + H+ + log_K = -8.42 # /JOS13/ + + # U(VI) + Mll_wOH + UO2+2 = Mll_wOUO2+ + H+ + log_k = 0.18 # /JOS13/ + Mll_wOH + UO2+2 + H2O = Mll_wOUO2(OH) + 2H+ + log_k = -6.22 # /JOS13/ + +### strong-binding sites ### + + Mll_sOH = Mll_sOH + log_K 0.0 + Mll_sOH + H+ = Mll_sOH2+ + log_K = 4.34 # /JOS13/ + Mll_sOH = Mll_sO- + H+ + log_K = -8.06 # /JOS13/ + + # U(VI) + Mll_sOH + UO2+2 = Mll_sOUO2+ + H+ + log_k = 2.94 # /JOS13/ + Mll_sOH + UO2+2 + H2O = Mll_sOUO2(OH) + 2H+ + log_k = -3.56 # /JOS13/ + Mll_sOH + UO2+2 + 2H2O = Mll_sOUO2(OH)2-1 + 3H+ + log_k = -11.16 # /JOS13/ + Mll_sOH + UO2+2 + 3H2O = Mll_sOUO2(OH)3-2 + 4H+ + log_k = -20.66 # /JOS13/ + + # U(IV) + # Mll_sOH + U+4 + H2O = Mll_sOUOH+2 + 2H+ + # log_k = 7.7 # TR 17-11, Tab. A-3 + # Mll_sOH + U+4 + 2H2O = Mll_sOU(OH)2+1 + 3H+ + # log_k = 4.0 # TR 17-11, Tab. A-3 + # Mll_sOH + U+4 + 4H2O = Mll_sOU(OH)4 + 5H+ + # log_k = -1.4 # TR 17-11, Tab. A-3 + +### Chlorite ######################################################################################################### + + ChlOH = ChlOH + log_K 0.0 + ChlOH + H+ = ChlOH2+ + log_K = 10.2 # /JOS13/ + + ChlOH + UO2+2 = ChlOUO2+ + H+ + log_K = 4.51 # /JOS13/ + + +END diff --git a/bench/surfex/SurfExBase.pqi b/bench/surfex/SurfExBase.pqi new file mode 100644 index 000000000..a820d70e0 --- /dev/null +++ b/bench/surfex/SurfExBase.pqi @@ -0,0 +1,56 @@ +## Time-stamp: "Last modified 2023-02-27 14:31:11 delucia" +KNOBS + -logfile false + -iterations 10000 + -convergence_tolerance 1E-12 + -step_size 2 + -pe_step_size 2 +SELECTED_OUTPUT + -reset false + -high_precision true + -solution true + -state true + -step true + -pH true + -pe true + -ionic_strength true + -water true +USER_PUNCH +-head total_o total_h cb C(-4) C(4) Ca Cl Fe(2) Fe(3) H(0) K Mg Na S(-2) S(2) S(4) S(6) Sr U(4) U(5) U(6) UO2(am,hyd) KdU +-start +5 w=TOT("water") +10 PUNCH TOTMOLE("O"), TOTMOLE("H"), CHARGE_BALANCE, w*TOT("C(-4)"), w*TOT("C(4)"), w*TOT("Ca"), w*TOT("Cl"), w*TOT("Fe(2)"), w*TOT("Fe(3)"), w*TOT("H(0)"), w*TOT("K"), w*TOT("Mg"), w*TOT("Na"), w*TOT("S(-2)"), w*TOT("S(2)"), w*TOT("S(4)"), w*TOT("S(6)"), w*TOT("Sr"), w*TOT("U(4)"), w*TOT("U(5)"), w*TOT("U(6)"), EQUI("UO2(am,hyd)") +20 PUNCH ((SURF("U, Ill")+SURF("U, Mll")+SURF("U, Kln")+EDL("U, Ill")+EDL("U, Mll")+EDL("U, Kln"))/((TOT("U")*1.01583)))/(0.002251896406*1000) +-end +SOLUTION 1 +temp 13 +units mol/kgw +pH 7.06355 +pe -2.626517 +C(4) 0.001990694 +Ca 0.02172649 +Cl 0.3227673 charge +Fe 0.0001434717 +K 0.001902357 +Mg 0.01739704 +Na 0.2762882 +S(6) 0.01652701 +Sr 0.0004520361 +U(4) 8.147792e-12 +U(6) 2.237946e-09 +-water 0.00133 +SURFACE 1 +-equil 1 +-sites_units density +-donnan 4.9e-10 + Kln_aOH 1.155 11. 5.0518 + Kln_siOH 1.155 + Ill_sOH 0.05 100. 5.5931 + Ill_wOH 2.26 + Mll_sOH 0.05 100. 1.0825 + Mll_wOH 2.26 +EXCHANGE 1 + -equil 1 + Z 0.0012585 + Y 0.0009418 +END diff --git a/bench/surfex/surfex.R b/bench/surfex/surfex.R new file mode 100644 index 000000000..1810bce83 --- /dev/null +++ b/bench/surfex/surfex.R @@ -0,0 +1,139 @@ +## Time-stamp: "Last modified 2023-02-27 18:33:30 delucia" + +database <- normalizePath("./SMILE_2021_11_01_TH.dat") +input_script <- normalizePath("./SurfExBase.pqi") + +cat(paste(":: R This is a test 1\n")) + +################################################################# +## Section 1 ## +## Grid initialization ## +################################################################# + +n <- 10 +m <- 10 + +types <- c("scratch", "phreeqc", "rds") + +init_cell <- list(H = 1.476571028625e-01, + O = 7.392297218936e-02, + Charge = -1.765225732724e-18, + `C(-4)` = 2.477908970828e-21, + `C(4)` = 2.647623016916e-06, + Ca = 2.889623169138e-05, + Cl = 4.292806181039e-04, + `Fe(2)` =1.908142472666e-07, + `Fe(3)` =3.173306589931e-12, + `H(0)` =2.675642675119e-15, + K = 2.530134809667e-06, + Mg =2.313806319294e-05, + Na =3.674633059628e-04, + `S(-2)` = 8.589766637180e-15, + `S(2)` = 1.205284362720e-19, + `S(4)` = 9.108958772790e-18, + `S(6)` = 2.198092329098e-05, + Sr = 6.012080128154e-07, + `U(4)` = 1.039668623852e-14, + `U(5)` = 1.208394829796e-15, + `U(6)` = 2.976409147150e-12) + + +grid <- list( + n_cells = c(n, m), + s_cells = c(1, 1), + type = "scratch", + init_cell = as.data.frame(init_cell), + database = database, + input_script = input_script +) + + +################################################################## +## Section 2 ## +## Diffusion parameters and boundary conditions ## +################################################################## + +vecinj_diffu <- list( + list(H = 0.147659686316291, + O = 0.0739242798146046, + Charge = 7.46361643222701e-20, + `C(-4)` = 2.92438561098248e-21, + `C(4)` = 2.65160558871092e-06, + Ca = 2.89001071336443e-05, + Cl = 0.000429291158114428, + `Fe(2)` = 1.90823391198114e-07, + `Fe(3)` = 3.10832423034763e-12, + `H(0)` = 2.7888235127385e-15, + K = 2.5301787e-06, + Mg = 2.31391999937907e-05, + Na = 0.00036746969, + `S(-2)` = 1.01376078438546e-14, + `S(2)` = 1.42247026981542e-19, + `S(4)` = 9.49422092568557e-18, + `S(6)` = 2.19812504654191e-05, + Sr = 6.01218519999999e-07, + `U(4)` = 4.82255946569383e-12, + `U(5)` = 5.49050615347901e-13, + `U(6)` = 1.32462838991902e-09) +) + +## diffusion coefficients +alpha_diffu <- c(H = 1E-6, O = 1E-6, Charge = 1E-6, `C(-4)` = 1E-6, + `C(4)` = 1E-6, Ca = 1E-6, Cl = 1E-6, `Fe(2)` = 1E-6, + `Fe(3)` = 1E-6, `H(0)` = 1E-6, K = 1E-6, Mg = 1E-6, + Na = 1E-6, `S(-2)` = 1E-6, `S(2)` = 1E-6, + `S(4)` = 1E-6, `S(6)` = 1E-6, Sr = 1E-6, + `U(4)` = 1E-6, `U(5)` = 1E-6, `U(6)` = 1E-6) + +## list of boundary conditions/inner nodes + +## vecinj_inner <- list( +## list(1,1,1) +## ) + +boundary <- list( + "N" = rep(1, n), + "E" = rep(0, n), + "S" = rep(0, n), + "W" = rep(0, n) +) + +diffu_list <- names(alpha_diffu) + +diffusion <- list( + init = init_cell, + vecinj = do.call(rbind.data.frame, vecinj_diffu), +# vecinj_inner = vecinj_inner, + vecinj_index = boundary, + alpha = alpha_diffu +) + +################################################################# +## Section 3 ## +## Chemistry module (Phreeqc) ## +################################################################# + + +chemistry <- list( + database = database, + input_script = input_script +) + +################################################################# +## Section 4 ## +## Putting all those things together ## +################################################################# + + +iterations <- 10 +dt <- 200 + +setup <- list( + grid = grid, + diffusion = diffusion, + chemistry = chemistry, + iterations = iterations, + timesteps = rep(dt, iterations), + store_result = TRUE, + out_save = c(5, iterations) +) From 2020a254092e3f82553cd4d210baf6b228c71641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20L=C3=BCbke?= Date: Wed, 1 Mar 2023 14:46:56 +0100 Subject: [PATCH 6/9] fix: converting to data frames substitutes special chars to '.' --- bench/surfex/surfex.R | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bench/surfex/surfex.R b/bench/surfex/surfex.R index 1810bce83..cc8e8592e 100644 --- a/bench/surfex/surfex.R +++ b/bench/surfex/surfex.R @@ -37,12 +37,12 @@ init_cell <- list(H = 1.476571028625e-01, `U(5)` = 1.208394829796e-15, `U(6)` = 2.976409147150e-12) - grid <- list( n_cells = c(n, m), s_cells = c(1, 1), type = "scratch", - init_cell = as.data.frame(init_cell), + init_cell = as.data.frame(init_cell, check.names = FALSE), + props = names(init_cell), database = database, input_script = input_script ) @@ -77,6 +77,9 @@ vecinj_diffu <- list( `U(6)` = 1.32462838991902e-09) ) +vecinj <- do.call(rbind.data.frame, vecinj_diffu) +names(vecinj) <- grid$props + ## diffusion coefficients alpha_diffu <- c(H = 1E-6, O = 1E-6, Charge = 1E-6, `C(-4)` = 1E-6, `C(4)` = 1E-6, Ca = 1E-6, Cl = 1E-6, `Fe(2)` = 1E-6, @@ -102,7 +105,7 @@ diffu_list <- names(alpha_diffu) diffusion <- list( init = init_cell, - vecinj = do.call(rbind.data.frame, vecinj_diffu), + vecinj = vecinj, # vecinj_inner = vecinj_inner, vecinj_index = boundary, alpha = alpha_diffu From e04da71f92bfbb75bb580744136da36f0f4c3810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20L=C3=BCbke?= Date: Tue, 7 Mar 2023 10:36:31 +0100 Subject: [PATCH 7/9] doc: update documentation to latest progress --- README.md | 146 ++++++++++++++++++------------------------ docs/CMakeLists.txt | 1 + docs/Input_Scripts.md | 95 +++++++++++++++++++++++++++ docs/Output.md | 12 ++-- 4 files changed, 165 insertions(+), 89 deletions(-) create mode 100644 docs/Input_Scripts.md diff --git a/README.md b/README.md index 774fd3f31..224271910 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ - -**Po**tsdamer **R**eactive **T**ransport - - # Forked Project *PORT* is a fork of [POET](https://doi.org/10.5281/zenodo.4757913) @@ -19,11 +15,20 @@ also applicable for this project. POET is a coupled reactive transport simulator implementing a parallel architecture and a fast, original MPI-based Distributed Hash Table. +## Parsed code documentiation + +A parsed version of POET's documentiation can be found at [Gitlab +pages](https://sec34.git-pages.gfz-potsdam.de/port). + ## External Libraries The following external header library is shipped with POET: - **argh** - https://github.com/adishavit/argh (BSD license) +- **PhreeqcRM** with patches from GFZ - + https://www.usgs.gov/software/phreeqc-version-3 - + https://git.gfz-potsdam.de/mluebke/phreeqcrm-gfz +- **tug** - https://git.gfz-potsdam.de/sec34/tug ## Installation @@ -35,23 +40,18 @@ To compile POET you need several software to be installed: - MPI-Implementation (tested with OpenMPI and MVAPICH) - R language and environment - CMake 3.9+ - -If you want to build documentation during compilation, `doxygen`and -`graphviz` must be provided too. +- *optional*: `doxygen` with `dot` bindings for documentiation The following R libraries must then be installed, which will get the needed dependencies automatically: -- [devtools](https://www.r-project.org/nosvn/pandoc/devtools.html) - [Rcpp](https://cran.r-project.org/web/packages/Rcpp/index.html) - [RInside](https://cran.r-project.org/web/packages/RInside/index.html) -- [RedModRphree](https://git.gfz-potsdam.de/delucia/RedModRphree) -- [Rmufits](https://git.gfz-potsdam.de/delucia/Rmufits) ### Compiling source code -The generation of makefiles is done with CMake. If you obtained POET -from git, you should be able to generate Makefiles by running +The generation of makefiles is done with CMake. You should be able to generate +Makefiles by running: ```sh mkdir build && cd build @@ -62,34 +62,21 @@ This will create the directory `build` and processes the CMake files and generate Makefiles from it. You're now able to run `make` to start build process. -If POET was obtained from the official SVN repository or the redmine -at the branch or tag -to be used have to be set via - -```sh -mkdir build && cd build -cmake -D POET_SET_BRANCH="" .. -``` - -where currently available branches/tags are: - -- dev - If everything went well you'll find the executable at -`build/src/poet`, but it is recommended to install the POET project +`build/app/poet`, but it is recommended to install the POET project structure to a desired `CMAKE_INSTALL_PREFIX` with `make install`. During the generation of Makefiles, various options can be specified -via `cmake -D