feat: implement SEXP export of Field structure

feat: implement NamedVector based on Rcpp::NumericVector

feat: remove hard coded checks and substitute by R hook functions,
defined in the input script

refactor: modify API of DHT_Wrapper/InterpolationModule to expect a work
package structure, where input/output values are stored as 2D vectors

test: add tests for NamedVector

test: extend tests for Field
This commit is contained in:
Max Luebke 2023-08-17 10:51:54 +02:00
parent 3897cc6bf8
commit c5a991c4c9
27 changed files with 931 additions and 505 deletions

View File

@ -21,12 +21,12 @@
#include <Rcpp.h>
#include <cstdint>
#include <cstdlib>
#include <poet/Macros.hpp>
#include <poet/ChemistryModule.hpp>
#include <poet/DiffusionModule.hpp>
#include <poet/Grid.hpp>
#include <poet/Macros.hpp>
#include <poet/RInsidePOET.hpp>
#include <poet/SimParams.hpp>
#include <poet/ChemistryModule.hpp>
#include <cstring>
#include <iostream>
@ -153,13 +153,15 @@ inline double RunMasterLoop(SimParams &params, RInside &R,
/* displaying iteration number, with C++ and R iterator */
MSG("Going through iteration " + std::to_string(iter));
MSG("R's $iter: " + std::to_string((uint32_t)(R.parseEval("mysetup$iter"))) + ". Iteration");
MSG("R's $iter: " +
std::to_string((uint32_t)(R.parseEval("mysetup$iter"))) +
". Iteration");
/* run transport */
// TODO: transport to diffusion
diffusion.simulate(dt);
chem.getField().UpdateFromField(diffusion.getField());
chem.getField().update(diffusion.getField());
MSG("Chemistry step");
@ -167,7 +169,7 @@ inline double RunMasterLoop(SimParams &params, RInside &R,
chem.RunCells();
writeFieldsToR(R, diffusion.getField(), chem.GetField());
diffusion.getField().UpdateFromField(chem.GetField());
diffusion.getField().update(chem.GetField());
R["req_dt"] = dt;
R["simtime"] = (sim_time += dt);
@ -181,7 +183,8 @@ inline double RunMasterLoop(SimParams &params, RInside &R,
// store_result is TRUE)
R.parseEvalQ("mysetup <- master_iteration_end(setup=mysetup)");
MSG("End of *coupling* iteration " + std::to_string(iter) + "/" + std::to_string(maxiter));
MSG("End of *coupling* iteration " + std::to_string(iter) + "/" +
std::to_string(maxiter));
MSG();
// MPI_Barrier(MPI_COMM_WORLD);
@ -258,15 +261,16 @@ int main(int argc, char *argv[]) {
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
RInsidePOET &R = RInsidePOET::getInstance();
if (world_rank == 0) {
MSG("Running POET version " + std::string(poet_version));
MSG("Running POET version " + std::string(poet_version));
}
if (world_rank > 0) {
{
SimParams params(world_rank, world_size);
int pret = params.parseFromCmdl(argv, RInsidePOET::getInstance());
int pret = params.parseFromCmdl(argv, R);
if (pret == poet::PARSER_ERROR) {
MPI_Finalize();
@ -286,7 +290,7 @@ int main(int argc, char *argv[]) {
}
MPI_Barrier(MPI_COMM_WORLD);
MSG("finished, cleanup of process " + std::to_string(world_rank));
MPI_Finalize();
@ -294,8 +298,6 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
RInsidePOET &R = RInsidePOET::getInstance();
/*Loading Dependencies*/
// TODO: kann raus
std::string r_load_dependencies = "source('../R_lib/kin_r_library.R');";
@ -326,12 +328,12 @@ int main(int argc, char *argv[]) {
// MDL: store all parameters
if (world_rank == 0) {
MSG("Calling R Function to store calling parameters");
R.parseEvalQ("StoreSetup(setup=mysetup)");
MSG("Calling R Function to store calling parameters");
R.parseEvalQ("StoreSetup(setup=mysetup)");
}
if (world_rank == 0) {
MSG("Init done on process with rank " + std::to_string(world_rank));
MSG("Init done on process with rank " + std::to_string(world_rank));
}
// MPI_Barrier(MPI_COMM_WORLD);
@ -351,7 +353,8 @@ int main(int argc, char *argv[]) {
r_vis_code = "saveRDS(profiling, file=paste0(fileout,'/timings.rds'));";
R.parseEval(r_vis_code);
MSG("Done! Results are stored as R objects into <" + params.getOutDir() + "/timings.rds>");
MSG("Done! Results are stored as R objects into <" + params.getOutDir() +
"/timings.rds>");
MPI_Barrier(MPI_COMM_WORLD);
@ -359,7 +362,7 @@ int main(int argc, char *argv[]) {
MPI_Finalize();
if (world_rank == 0) {
MSG("done, bye!");
MSG("done, bye!");
}
exit(0);

View File

@ -1,4 +1,4 @@
## Time-stamp: "Last modified 2023-08-02 13:59:02 mluebke"
## Time-stamp: "Last modified 2023-08-16 17:04:42 mluebke"
database <- normalizePath("../share/poet/bench/dolo/phreeqc_kin.dat")
input_script <- normalizePath("../share/poet/bench/dolo/dolo_inner.pqi")
@ -62,34 +62,34 @@ alpha_diffu <- c(
## list of boundary conditions/inner nodes
vecinj_diffu <- list(
list(
"H" = 110.683,
"O" = 55.3413,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0,
"Cl" = 0.002,
"Mg" = 0.001
),
list(
"H" = 110.683,
"O" = 55.3413,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0.0,
"Cl" = 0.004,
"Mg" = 0.002
)
list(
"H" = 110.683,
"O" = 55.3413,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0,
"Cl" = 0.002,
"Mg" = 0.001
),
list(
"H" = 110.683,
"O" = 55.3413,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0.0,
"Cl" = 0.004,
"Mg" = 0.002
)
)
vecinj_inner <- list(
l1 = c(1,20,20),
l2 = c(2,80,80),
l3 = c(2,60,80)
l1 = c(1, 20, 20),
l2 = c(2, 80, 80),
l3 = c(2, 60, 80)
)
boundary <- list(
# "N" = c(1, rep(0, n-1)),
# "N" = c(1, rep(0, n-1)),
"N" = rep(0, n),
"E" = rep(0, n),
"S" = rep(0, n),
@ -116,21 +116,59 @@ diffusion <- list(
## # Needed when using DHT
dht_species <- c("H" = 10,
"O" = 10,
"Charge" = 3,
"C(4)" = 5,
"Ca" = 5,
"Cl" = 5,
"Mg" = 5,
"Calcite" = 5,
"Dolomite" =5
)
dht_species <- c(
"H" = 10,
"O" = 10,
"Charge" = 3,
"C(4)" = 5,
"Ca" = 5,
"Cl" = 5,
"Mg" = 5,
"Calcite" = 5,
"Dolomite" = 5
)
check_sign_cal_dol_dht <- function(old, new) {
if ((old["Calcite"] == 0) != (new["Calcite"] == 0)) {
return(TRUE)
}
if ((old["Dolomite"] == 0) != (new["Dolomite"] == 0)) {
return(TRUE)
}
return(FALSE)
}
fuzz_input_dht_keys <- function(input) {
return(input[names(dht_species)])
}
check_sign_cal_dol_interp <- function(to_interp, data_set) {
data_set <- as.data.frame(do.call(rbind, data_set), check.names = FALSE, optional = TRUE)
names(data_set) <- names(dht_species)
cal <- (data_set$Calcite == 0) == (to_interp["Calcite"] == 0)
dol <- (data_set$Dolomite == 0) == (to_interp["Dolomite"] == 0)
cal_dol_same_sig <- cal == dol
return(rev(which(!cal_dol_same_sig)))
}
check_neg_cal_dol <- function(result) {
neg_sign <- (result["Calcite"] <- 0) || (result["Dolomite"] < 0)
return(any(neg_sign))
}
hooks <- list(
dht_fill = check_sign_cal_dol_dht,
dht_fuzz = fuzz_input_dht_keys,
interp_pre_func = check_sign_cal_dol_interp,
interp_post_func = check_neg_cal_dol
)
chemistry <- list(
database = database,
input_script = input_script,
dht_species = dht_species
dht_species = dht_species,
hooks = hooks
)
#################################################################

View File

@ -1,4 +1,4 @@
## Time-stamp: "Last modified 2023-08-02 13:59:12 mluebke"
## Time-stamp: "Last modified 2023-08-16 17:05:04 mluebke"
database <- normalizePath("../share/poet/bench/dolo/phreeqc_kin.dat")
input_script <- normalizePath("../share/poet/bench/dolo/dolo_inner.pqi")
@ -62,34 +62,34 @@ alpha_diffu <- c(
## list of boundary conditions/inner nodes
vecinj_diffu <- list(
list(
"H" = 0.0001540445,
"O" = 0.002148006,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0,
"Cl" = 0.002,
"Mg" = 0.001
),
list(
"H" = 0.0001610193,
"O" = 0.002386934,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0.0,
"Cl" = 0.004,
"Mg" = 0.002
)
list(
"H" = 0.0001540445,
"O" = 0.002148006,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0,
"Cl" = 0.002,
"Mg" = 0.001
),
list(
"H" = 0.0001610193,
"O" = 0.002386934,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0.0,
"Cl" = 0.004,
"Mg" = 0.002
)
)
vecinj_inner <- list(
l1 = c(1,400,200),
l2 = c(2,1400,800),
l3 = c(2,1600,800)
l1 = c(1, 400, 200),
l2 = c(2, 1400, 800),
l3 = c(2, 1600, 800)
)
boundary <- list(
# "N" = c(1, rep(0, n-1)),
# "N" = c(1, rep(0, n-1)),
"N" = rep(0, n),
"E" = rep(0, m),
"S" = rep(0, n),
@ -115,21 +115,59 @@ diffusion <- list(
#################################################################
## # Needed when using DHT
dht_species <- c("H" = 10,
"O" = 10,
"Charge" = 3,
"C(4)" = 5,
"Ca" = 5,
"Cl" = 5,
"Mg" = 5,
"Calcite" = 5,
"Dolomite" =5
)
dht_species <- c(
"H" = 10,
"O" = 10,
"Charge" = 3,
"C(4)" = 5,
"Ca" = 5,
"Cl" = 5,
"Mg" = 5,
"Calcite" = 5,
"Dolomite" = 5
)
check_sign_cal_dol_dht <- function(old, new) {
if ((old["Calcite"] == 0) != (new["Calcite"] == 0)) {
return(TRUE)
}
if ((old["Dolomite"] == 0) != (new["Dolomite"] == 0)) {
return(TRUE)
}
return(FALSE)
}
fuzz_input_dht_keys <- function(input) {
return(input[names(dht_species)])
}
check_sign_cal_dol_interp <- function(to_interp, data_set) {
data_set <- as.data.frame(do.call(rbind, data_set), check.names = FALSE, optional = TRUE)
names(data_set) <- names(dht_species)
cal <- (data_set$Calcite == 0) == (to_interp["Calcite"] == 0)
dol <- (data_set$Dolomite == 0) == (to_interp["Dolomite"] == 0)
cal_dol_same_sig <- cal == dol
return(rev(which(!cal_dol_same_sig)))
}
check_neg_cal_dol <- function(result) {
neg_sign <- (result["Calcite"] <- 0) || (result["Dolomite"] < 0)
return(any(neg_sign))
}
hooks <- list(
dht_fill = check_sign_cal_dol_dht,
dht_fuzz = fuzz_input_dht_keys,
interp_pre_func = check_sign_cal_dol_interp,
interp_post_func = check_neg_cal_dol
)
chemistry <- list(
database = database,
input_script = input_script,
dht_species = dht_species
dht_species = dht_species,
hooks = hooks
)
#################################################################
@ -148,5 +186,5 @@ setup <- list(
iterations = iterations,
timesteps = rep(dt, iterations),
store_result = TRUE,
out_save = seq(5, iterations, by=5)
out_save = seq(5, iterations, by = 5)
)

View File

@ -1,4 +1,4 @@
## Time-stamp: "Last modified 2023-08-02 13:47:06 mluebke"
## Time-stamp: "Last modified 2023-08-16 14:57:25 mluebke"
database <- normalizePath("../share/poet/bench/dolo/phreeqc_kin.dat")
input_script <- normalizePath("../share/poet/bench/dolo/dolo_inner.pqi")
@ -62,25 +62,25 @@ alpha_diffu <- c(
## list of boundary conditions/inner nodes
vecinj_diffu <- list(
list(
"H" = 1.110124E+02,
"O" = 5.550796E+01,
"Charge" = -3.230390327801E-08,
"C(4)" = 0,
"Ca" = 0,
"Cl" = 0.002,
"Mg" = 0.001
),
list(
"H" = 110.683,
"O" = 55.3413,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0.0,
"Cl" = 0.004,
"Mg" = 0.002
),
init_diffu
list(
"H" = 1.110124E+02,
"O" = 5.550796E+01,
"Charge" = -3.230390327801E-08,
"C(4)" = 0,
"Ca" = 0,
"Cl" = 0.002,
"Mg" = 0.001
),
list(
"H" = 110.683,
"O" = 55.3413,
"Charge" = 1.90431e-16,
"C(4)" = 0,
"Ca" = 0.0,
"Cl" = 0.004,
"Mg" = 0.002
),
init_diffu
)
vecinj_inner <- list(
@ -132,19 +132,56 @@ dht_species <- c(
## # Optional when using Interpolation (example with less key species and custom
## # significant digits)
#pht_species <- c(
# pht_species <- c(
# "C(4)" = 3,
# "Ca" = 3,
# "Mg" = 2,
# "Calcite" = 2,
# "Dolomite" = 2
#)
# )
check_sign_cal_dol_dht <- function(old, new) {
if ((old["Calcite"] == 0) != (new["Calcite"] == 0)) {
return(TRUE)
}
if ((old["Dolomite"] == 0) != (new["Dolomite"] == 0)) {
return(TRUE)
}
return(FALSE)
}
fuzz_input_dht_keys <- function(input) {
return(input[names(dht_species)])
}
check_sign_cal_dol_interp <- function(to_interp, data_set) {
data_set <- as.data.frame(do.call(rbind, data_set), check.names = FALSE, optional = TRUE)
names(data_set) <- names(dht_species)
cal <- (data_set$Calcite == 0) == (to_interp["Calcite"] == 0)
dol <- (data_set$Dolomite == 0) == (to_interp["Dolomite"] == 0)
cal_dol_same_sig <- cal == dol
return(rev(which(!cal_dol_same_sig)))
}
check_neg_cal_dol <- function(result) {
neg_sign <- (result["Calcite"] <- 0) || (result["Dolomite"] < 0)
return(any(neg_sign))
}
hooks <- list(
dht_fill = check_sign_cal_dol_dht,
dht_fuzz = fuzz_input_dht_keys,
interp_pre_func = check_sign_cal_dol_interp,
interp_post_func = check_neg_cal_dol
)
chemistry <- list(
database = database,
input_script = input_script,
dht_species = dht_species
# pht_species = pht_species
dht_species = dht_species,
hooks = hooks
# pht_species = pht_species
)
#################################################################

View File

@ -1,11 +1,10 @@
// Time-stamp: "Last modified 2023-08-08 13:15:49 mluebke"
// Time-stamp: "Last modified 2023-08-15 14:36:28 mluebke"
#ifndef CHEMISTRYMODULE_H_
#define CHEMISTRYMODULE_H_
#include "DHT_Wrapper.hpp"
#include "DataStructures.hpp"
#include "Field.hpp"
#include "Interpolation.hpp"
#include "IrmResult.h"
#include "PhreeqcRM.h"
@ -382,7 +381,7 @@ protected:
uint32_t prop_count = 0;
std::vector<std::string> prop_names;
Field chem_field{0};
Field chem_field;
static constexpr int MODULE_COUNT = 5;

View File

@ -1,4 +1,4 @@
// Time-stamp: "Last modified 2023-08-08 18:01:12 mluebke"
// Time-stamp: "Last modified 2023-08-15 14:57:51 mluebke"
/*
** Copyright (C) 2018-2021 Alexander Lindemann, Max Luebke (University of
@ -25,6 +25,8 @@
#include "DataStructures.hpp"
#include "LookupKey.hpp"
#include "RInsidePOET.hpp"
#include "SimParams.hpp"
#include "enums.hpp"
#include "poet/HashFunctions.hpp"
#include "poet/LookupKey.hpp"
@ -85,6 +87,8 @@ public:
DHT_Wrapper(MPI_Comm dht_comm, std::uint64_t dht_size,
const NamedVector<std::uint32_t> &key_species,
const std::vector<std::int32_t> &key_indices,
const std::vector<std::string> &output_names,
const ChemistryParams::Chem_Hook_Functions &hooks,
uint32_t data_count);
/**
* @brief Destroy the dht wrapper object
@ -213,7 +217,11 @@ public:
double dt) {
dht_results.keys.resize(input_values.size());
for (std::size_t i = 0; i < input_values.size(); i++) {
dht_results.keys[i] = fuzzForDHT(input_values[i], dt);
if (this->hooks.dht_fuzz.isValid()) {
dht_results.keys[i] = fuzzForDHT_R(input_values[i], dt);
} else {
dht_results.keys[i] = fuzzForDHT(input_values[i], dt);
}
}
}
@ -225,6 +233,7 @@ private:
MPI_Comm communicator;
LookupKey fuzzForDHT(const std::vector<double> &cell, double dt);
LookupKey fuzzForDHT_R(const std::vector<double> &cell, double dt);
std::vector<double>
outputToInputAndRates(const std::vector<double> &old_results,
@ -242,6 +251,10 @@ private:
std::vector<std::uint32_t> dht_prop_type_vector;
std::vector<std::int32_t> input_key_elements;
const std::vector<std::string> &output_names;
const ChemistryParams::Chem_Hook_Functions &hooks;
DHT_ResultObject dht_results;
std::array<double, 2> base_totals{0};

View File

@ -3,12 +3,16 @@
#include "enums.hpp"
#include <Rcpp.h>
#include <cassert>
#include <cinttypes>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <list>
#include <string>
#include <utility>
#include <vector>
namespace poet {
struct WorkPackage {
@ -24,28 +28,193 @@ struct WorkPackage {
}
};
template <typename value_type> class NamedVector {
template <typename T> class NamedVector : public Rcpp::NumericVector {
public:
void insert(std::pair<std::string, value_type> to_insert) {
this->names.push_back(to_insert.first);
this->values.push_back(to_insert.second);
container_size++;
NamedVector() : Rcpp::NumericVector(){};
NamedVector(const std::vector<std::string> &in_names,
const std::vector<T> &in_values)
: Rcpp::NumericVector(Rcpp::wrap(in_values)) {
this->names() = Rcpp::CharacterVector(Rcpp::wrap(in_names));
}
bool empty() const { return this->container_size == 0; }
NamedVector(const SEXP &s) : Rcpp::NumericVector(s){};
std::size_t size() const { return this->container_size; }
bool empty() const { return (this->size() == 0); }
value_type &operator[](std::size_t i) { return values[i]; }
std::vector<std::string> getNames() const {
return Rcpp::as<std::vector<std::string>>(this->names());
}
std::vector<T> getValues() const { return Rcpp::as<std::vector<T>>(*this); }
};
const std::vector<std::string> &getNames() const { return this->names; }
const std::vector<value_type> &getValues() const { return this->values; }
using FieldColumn = std::vector<double>;
/**
* Stores data for input/output of a module. The class keeps track of all
* defined properties with name and import/export to 1D and 2D STL vectors.
* Also, it eases the update process of a field with an input from another
* field.
*
* It can be seen as an R-like data frame, but with less access to the members.
* Species values are stored as "columns", where column is a STL vector.
*/
class Field : private std::unordered_map<std::string, FieldColumn> {
public:
Field(){};
/**
* Creates a new instance of a field with fixed expected vector size.
*
* \param vec_s expected vector size of each component/column.
*/
Field(std::uint32_t vec_s) : req_vec_size(vec_s){};
/**
* Initializes instance with a 2D vector and according names for each columnn.
* There is no check if names were given multiple times. The order of name
* vector also defines the ordering of the output.
*
* \param vec_s expected vector size of each component/column.
* \param input 2D vector using STL semantic describing the current state of
* the field.
* \param prop_vec Name of each vector in the input. Shall match
* the count of vectors.
*
* \exception std::runtime_error If prop_vec size doesn't match input vector
* size or column vectors size doesn't match expected vector size.
*/
Field(std::uint32_t vec_s, const std::vector<std::vector<double>> &input,
const std::vector<std::string> &prop_vec, bool row_major = false);
/**
* Initializes instance with a 1D continious memory vector and according names
* for each columnn. There is no check if names were given multiple times. The
* order of name vector also defines the ordering of the output.
*
* \param vec_s expected vector size of each component/column.
* \param input 1D vector using STL semantic describing the current state of
* the field storing each column starting at index *i times requested vector
* size*.
* \param prop_vec Name of each vector in the input. Shall match the
* count of vectors.
*
* \exception std::runtime_error If prop_vec size doesn't match input vector
* size or column vectors size doesn't match expected vector size.
*/
Field(std::uint32_t vec_s, const std::vector<double> &input,
const std::vector<std::string> &prop_vec);
Field(const SEXP &s_init) { fromSEXP(s_init); }
Field &operator=(const Field &rhs) {
this->req_vec_size = rhs.req_vec_size;
this->props = rhs.props;
this->clear();
for (const auto &pair : rhs) {
this->insert(pair);
}
return *this;
}
/**
* Read in a (previously exported) 1D vector. It has to have the same
* dimensions as the current column count times the requested vector size of
* this instance. Import order is given by the species name vector.
*
* \param cont_field 1D field as vector.
*
* \exception std::runtime_error Input vector does not match the expected
* size.
*/
Field &operator=(const FieldColumn &cont_field);
/**
* Read in a (previously exported) 2D vector. It has to have the same
* dimensions as the current column count of this instance and each vector
* must have the size of the requested vector size. Import order is given by
* the species name vector.
*
* \param cont_field 2D field as vector.
*
* \exception std::runtime_error Input vector has more or less elements than
* the instance or a column vector does not match expected vector size.
*/
Field &operator=(const std::vector<FieldColumn> &cont_field);
Field &operator=(const SEXP &s_rhs) {
fromSEXP(s_rhs);
return *this;
}
/**
* Returns a reference to the column vector with given name. Creates a new
* vector if prop was not found. The prop name will be added to the end of the
* list.
*
* \param key Name of the prop.
*
* \return Reference to the column vector.
*/
FieldColumn &operator[](const std::string &key);
/**
* Returns the names of all species defined in the instance.
*
* \return Vector containing all species names in output order.
*/
auto GetProps() const -> std::vector<std::string> { return this->props; }
/**
* Return the requested vector size.
*
* \return Requested vector size set in the instanciation of the object.
*/
auto GetRequestedVecSize() const -> std::uint32_t {
return this->req_vec_size;
};
/**
* Updates all species with values from another field. If one element of the
* input field doesn't match the names of the calling instance it will get
* skipped.
*
* \param input Field to update the current instance's columns.
*/
void update(const Field &input);
/**
* Builds a new 1D vector with the current state of the instance. The output
* order is given by the given species name vector set earlier and/or added
* values using the [] operator.
*
* \return 1D STL vector stored each column one after another.
*/
auto AsVector() const -> FieldColumn;
/**
* Builds a new 2D vector with the current state of the instance. The output
* order is given by the given species name vector set earlier and/or added
* values using the [] operator.
*
* \return 2D STL vector stored each column one after anothe in a new vector
* element.
*/
auto As2DVector() const -> std::vector<FieldColumn>;
SEXP asSEXP() const;
std::size_t cols() const { return this->props.size(); }
private:
std::size_t container_size{0};
std::uint32_t req_vec_size{0};
std::vector<std::string> names{};
std::vector<value_type> values{};
std::vector<std::string> props{{}};
void fromSEXP(const SEXP &s_rhs);
};
} // namespace poet
#endif // DATASTRUCTURES_H_

View File

@ -21,7 +21,7 @@
#ifndef DIFFUSION_MODULE_H
#define DIFFUSION_MODULE_H
#include "Field.hpp"
#include "DataStructures.hpp"
#include "SimParams.hpp"
#include "poet/SimParams.hpp"
#include <array>
@ -94,7 +94,8 @@ private:
enum { DIM_1D = 1, DIM_2D };
void initialize(const poet::DiffusionParams &args);
void initialize(const poet::DiffusionParams &args,
std::uint32_t n_grid_cells);
uint8_t dim;

View File

@ -1,159 +0,0 @@
#ifndef FIELD_H_
#define FIELD_H_
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <string>
#include <unordered_map>
#include <vector>
namespace poet {
using FieldColumn = std::vector<double>;
/**
* Stores data for input/output of a module. The class keeps track of all
* defined properties with name and import/export to 1D and 2D STL vectors.
* Also, it eases the update process of a field with an input from another
* field.
*
* It can be seen as an R-like data frame, but with less access to the members.
* Species values are stored as "columns", where column is a STL vector.
*/
class Field : private std::unordered_map<std::string, FieldColumn> {
public:
/**
* Creates a new instance of a field with fixed expected vector size.
*
* \param vec_s expected vector size of each component/column. \
*/
Field(uint32_t vec_s) : req_vec_size(vec_s){};
/**
* Initializes instance with a 2D vector and according names for each columnn.
* There is no check if names were given multiple times. The order of name
* vector also defines the ordering of the output.
*
* \param input 2D vector using STL semantic describing the current state of
* the field.
* \param prop_vec Name of each vector in the input. Shall match
* the count of vectors.
*
* \exception std::runtime_error If prop_vec size doesn't match input vector
* size or column vectors size doesn't match expected vector size.
*/
void InitFromVec(const std::vector<std::vector<double>> &input,
const std::vector<std::string> &prop_vec);
/**
* Initializes instance with a 1D continious memory vector and according names
* for each columnn. There is no check if names were given multiple times. The
* order of name vector also defines the ordering of the output.
*
* \param input 1D vector using STL semantic describing the current state of
* the field storing each column starting at index *i times requested vector
* size*.
* \param prop_vec Name of each vector in the input. Shall match the
* count of vectors.
*
* \exception std::runtime_error If prop_vec size doesn't match input vector
* size or column vectors size doesn't match expected vector size.
*/
void InitFromVec(const std::vector<double> &input,
const std::vector<std::string> &prop_vec);
Field &operator=(const Field &rhs) {
this->req_vec_size = rhs.req_vec_size;
this->props = rhs.props;
return *this;
}
/**
* Returns a reference to the column vector with given name. Creates a new
* vector if prop was not found. The prop name will be added to the end of the
* list.
*
* \param key Name of the prop.
*
* \return Reference to the column vector.
*/
FieldColumn &operator[](const std::string &key);
/**
* Returns the names of all species defined in the instance.
*
* \return Vector containing all species names in output order.
*/
auto GetProps() const -> std::vector<std::string> { return this->props; }
/**
* Return the requested vector size.
*
* \return Requested vector size set in the instanciation of the object.
*/
auto GetRequestedVecSize() const -> std::uint32_t {
return this->req_vec_size;
};
/**
* Updates all species with values from another field. If one element of the
* input field doesn't match the names of the calling instance it will get
* skipped.
*
* \param input Field to update the current instance's columns.
*/
void UpdateFromField(const Field &input);
/**
* Builds a new 1D vector with the current state of the instance. The output
* order is given by the given species name vector set earlier and/or added
* values using the [] operator.
*
* \return 1D STL vector stored each column one after another.
*/
auto AsVector() const -> FieldColumn;
/**
* Builds a new 2D vector with the current state of the instance. The output
* order is given by the given species name vector set earlier and/or added
* values using the [] operator.
*
* \return 2D STL vector stored each column one after anothe in a new vector
* element.
*/
auto As2DVector() const -> std::vector<FieldColumn>;
/**
* Read in a (previously exported) 1D vector. It has to have the same
* dimensions as the current column count times the requested vector size of
* this instance. Import order is given by the species name vector.
*
* \param cont_field 1D field as vector.
*
* \exception std::runtime_error Input vector does not match the expected
* size.
*/
void SetFromVector(const FieldColumn &cont_field);
/**
* Read in a (previously exported) 2D vector. It has to have the same
* dimensions as the current column count of this instance and each vector
* must have the size of the requested vector size. Import order is given by
* the species name vector.
*
* \param cont_field 2D field as vector.
*
* \exception std::runtime_error Input vector has more or less elements than
* the instance or a column vector does not match expected vector size.
*/
void SetFromVector(const std::vector<FieldColumn> &cont_field);
private:
std::uint32_t req_vec_size;
std::vector<std::string> props;
};
} // namespace poet
#endif // FIELD_H_

View File

@ -1,4 +1,4 @@
// Time-stamp: "Last modified 2023-08-09 12:52:37 mluebke"
// Time-stamp: "Last modified 2023-08-16 16:49:31 mluebke"
#ifndef INTERPOLATION_H_
#define INTERPOLATION_H_
@ -9,6 +9,7 @@
#include "LookupKey.hpp"
#include "poet/DHT_Wrapper.hpp"
#include "poet/Rounding.hpp"
#include "poet/SimParams.hpp"
#include <cassert>
#include <iostream>
#include <list>
@ -46,7 +47,7 @@ public:
std::vector<std::vector<double>> in_values;
std::vector<std::vector<double>> out_values;
std::uint32_t getSize() const {
std::uint32_t getMemSize() const {
std::uint32_t sum{0};
for (const auto &results : out_values) {
sum += results.size() * sizeof(double);
@ -65,9 +66,9 @@ public:
void writeLocationToPHT(LookupKey key, DHT_Location location);
const PHT_Result &query(const LookupKey &key,
const std::uint32_t min_entries_needed,
const std::uint32_t input_count,
const std::uint32_t output_count);
std::uint32_t min_entries_needed,
std::uint32_t input_count,
std::uint32_t output_count);
std::uint64_t getLocations(const LookupKey &key);
@ -163,7 +164,9 @@ public:
std::uint64_t size_per_process,
std::uint32_t min_entries_needed, DHT_Wrapper &dht,
const NamedVector<std::uint32_t> &interp_key_signifs,
const std::vector<std::int32_t> &dht_key_indices);
const std::vector<std::int32_t> &dht_key_indices,
const std::vector<std::string> &out_names,
const ChemistryParams::Chem_Hook_Functions &hooks);
enum result_status { RES_OK, INSUFFICIENT_DATA, NOT_NEEDED };
@ -258,6 +261,10 @@ private:
return out_key;
}
const ChemistryParams::Chem_Hook_Functions &hooks;
const std::vector<std::string> &out_names;
const std::vector<std::string> dht_names;
};
} // namespace poet

View File

@ -1,4 +1,4 @@
// Time-stamp: "Last modified 2023-07-26 10:20:10 mluebke"
// Time-stamp: "Last modified 2023-08-11 10:12:52 mluebke"
#ifndef LOOKUPKEY_H_
#define LOOKUPKEY_H_
@ -27,8 +27,6 @@ union Lookup_Keyelement {
class LookupKey : public std::vector<Lookup_Keyelement> {
public:
explicit LookupKey(size_type count);
using std::vector<Lookup_Keyelement>::vector;
std::vector<double> to_double() const;

View File

@ -1,11 +1,11 @@
#ifndef RPOET_H_
#define RPOET_H_
#include "DataStructures.hpp"
#include <RInside.h>
#include <Rcpp.h>
#include <cstddef>
#include <exception>
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
@ -28,34 +28,32 @@ public:
this->parseEval("'" + R_name + "' %in% names(" + where + ")"));
}
template <class T> inline T getSTL(const std::string &R_name) {
return Rcpp::as<T>(RInside::operator[](R_name));
}
template <typename value_type>
poet::NamedVector<value_type> wrapNamedVector(const std::string &R_name) {
std::size_t name_size = this->parseEval("length(names(" + R_name + "))");
if (name_size == 0) {
throw std::runtime_error("wrapNamedVector: " + R_name +
" is not a named vector!");
}
auto names = Rcpp::as<std::vector<std::string>>(
this->parseEval("names(" + R_name + ")"));
auto values = Rcpp::as<Rcpp::NumericVector>(this->parseEval(R_name));
poet::NamedVector<value_type> ret_map;
for (std::size_t i = 0; i < names.size(); i++) {
ret_map.insert(
std::make_pair(names[i], static_cast<value_type>(values[i])));
}
return ret_map;
}
private:
RInsidePOET() : RInside(){};
};
template <typename T> class RHookFunction {
public:
RHookFunction() {}
RHookFunction(RInside &R, const std::string &f_name) {
try {
this->func = Rcpp::Function(Rcpp::as<SEXP>(R.parseEval(f_name.c_str())));
} catch (const std::exception &e) {
}
}
template <typename... Args> T operator()(Args... args) const {
if (func.has_value()) {
return (Rcpp::as<T>(this->func.value()(args...)));
} else {
throw std::exception();
}
}
bool isValid() const { return this->func.has_value(); }
private:
std::optional<Rcpp::Function> func;
};
#endif // RPOET_H_

View File

@ -22,13 +22,14 @@
#define PARSER_H
#include <cstdint>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "Macros.hpp"
#include "DataStructures.hpp"
#include "Macros.hpp"
#include "RInsidePOET.hpp"
#include "argh.hpp" // Argument handler https://github.com/adishavit/argh
#include <RInside.h>
@ -124,6 +125,13 @@ struct ChemistryParams {
std::uint32_t interp_min_entries;
NamedVector<std::uint32_t> pht_signifs;
struct Chem_Hook_Functions {
RHookFunction<bool> dht_fill;
RHookFunction<std::vector<double>> dht_fuzz;
RHookFunction<std::vector<std::size_t>> interp_pre;
RHookFunction<bool> interp_post;
} hooks;
void initFromR(RInsidePOET &R);
};
@ -240,8 +248,8 @@ public:
private:
std::list<std::string> validateOptions(argh::parser cmdl);
const std::set<std::string> flaglist{
"ignore-result", "dht", "P", "progress", "interp"};
const std::set<std::string> flaglist{"ignore-result", "dht", "P", "progress",
"interp"};
const std::set<std::string> paramlist{
"work-package-size", "dht-strategy",
"dht-size", "dht-snaps",

View File

@ -291,7 +291,6 @@ void poet::ChemistryModule::initializeField(const Field &trans_field) {
if (is_master) {
this->n_cells = trans_field.GetRequestedVecSize();
chem_field = Field(n_cells);
std::vector<std::vector<double>> phreeqc_dump(this->nxyz);
this->getDumpedField(phreeqc_dump);
@ -305,7 +304,7 @@ void poet::ChemistryModule::initializeField(const Field &trans_field) {
const auto tmp_buffer{init_vec};
this->unshuffleField(tmp_buffer, n_cells, prop_count, 1, init_vec);
chem_field.InitFromVec(init_vec, prop_names);
this->chem_field = Field(n_cells, init_vec, prop_names);
return;
}
@ -325,7 +324,7 @@ void poet::ChemistryModule::initializeField(const Field &trans_field) {
initial_values.push_back(std::move(init));
}
chem_field.InitFromVec(initial_values, prop_names);
this->chem_field = Field(n_cells, initial_values, prop_names);
} else {
ChemBCast(base_totals.data(), 2, MPI_DOUBLE);
}
@ -366,11 +365,9 @@ void poet::ChemistryModule::initializeDHT(
auto map_copy = key_species;
if (key_species.empty()) {
const auto &all_species = this->prop_names;
for (std::size_t i = 0; i < all_species.size(); i++) {
map_copy.insert(std::make_pair(all_species[i],
DHT_Wrapper::DHT_KEY_SIGNIF_DEFAULT));
}
std::vector<std::uint32_t> default_signif(
this->prop_names.size(), DHT_Wrapper::DHT_KEY_SIGNIF_DEFAULT);
map_copy = NamedVector<std::uint32_t>(this->prop_names, default_signif);
}
auto key_indices = parseDHTSpeciesVec(key_species, this->prop_names);
@ -381,8 +378,9 @@ void poet::ChemistryModule::initializeDHT(
const std::uint64_t dht_size = size_mb * MB_FACTOR;
this->dht = new DHT_Wrapper(dht_comm, dht_size, map_copy, key_indices,
this->prop_count);
this->dht =
new DHT_Wrapper(dht_comm, dht_size, map_copy, key_indices,
this->prop_names, params.hooks, this->prop_count);
this->dht->setBaseTotals(base_totals.at(0), base_totals.at(1));
}
}
@ -488,8 +486,8 @@ void poet::ChemistryModule::initializeInterp(
const uint64_t pht_size = size_mb * MB_FACTOR;
interp = std::make_unique<poet::InterpolationModule>(
bucket_size, pht_size, min_entries, *(this->dht), map_copy,
key_indices);
bucket_size, pht_size, min_entries, *(this->dht), map_copy, key_indices,
this->prop_names, this->params.hooks);
interp->setInterpolationFunction(inverseDistanceWeighting);
}

View File

@ -342,7 +342,7 @@ void poet::ChemistryModule::MasterRunSequential() {
std::vector<double> out_vec{shuffled_field};
unshuffleField(shuffled_field, n_cells, prop_count, 1, out_vec);
chem_field.SetFromVector(out_vec);
chem_field = out_vec;
}
void poet::ChemistryModule::MasterRunParallel() {
@ -423,7 +423,7 @@ void poet::ChemistryModule::MasterRunParallel() {
std::vector<double> out_vec{mpi_buffer};
unshuffleField(mpi_buffer, this->n_cells, this->prop_count,
wp_sizes_vector.size(), out_vec);
chem_field.SetFromVector(out_vec);
chem_field = out_vec;
/* do master stuff */

View File

@ -1,4 +1,4 @@
// Time-stamp: "Last modified 2023-08-09 14:05:01 mluebke"
// Time-stamp: "Last modified 2023-08-16 16:44:17 mluebke"
/*
** Copyright (C) 2018-2021 Alexander Lindemann, Max Luebke (University of
@ -27,7 +27,10 @@
#include "poet/Rounding.hpp"
#include "poet/enums.hpp"
#include "poet/RInsidePOET.hpp"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdint>
@ -43,10 +46,12 @@ namespace poet {
DHT_Wrapper::DHT_Wrapper(MPI_Comm dht_comm, std::uint64_t dht_size,
const NamedVector<std::uint32_t> &key_species,
const std::vector<std::int32_t> &key_indices,
const std::vector<std::string> &_output_names,
const ChemistryParams::Chem_Hook_Functions &_hooks,
uint32_t data_count)
: key_count(key_indices.size()), data_count(data_count),
input_key_elements(key_indices), communicator(dht_comm),
key_species(key_species) {
key_species(key_species), output_names(_output_names), hooks(_hooks) {
// initialize DHT object
// key size = count of key elements + timestep
uint32_t key_size = (key_count + 1) * sizeof(Lookup_Keyelement);
@ -128,12 +133,13 @@ void DHT_Wrapper::fillDHT(const WorkPackage &work_package) {
// check if calcite or dolomite is absent and present, resp.n and vice
// versa in input/output. If this is the case -> Do not write to DHT!
// HACK: hardcoded, should be fixed!
if ((work_package.input[i][7] == 0) != (work_package.output[i][7] == 0)) {
continue;
}
if (hooks.dht_fill.isValid()) {
NamedVector<double> old_values(output_names, work_package.input[i]);
NamedVector<double> new_values(output_names, work_package.output[i]);
if ((work_package.input[i][9] == 0) != (work_package.output[i][9] == 0)) {
continue;
if (hooks.dht_fill(old_values, new_values)) {
continue;
}
}
uint32_t proc, index;
@ -228,6 +234,38 @@ void DHT_Wrapper::printStatistics() {
}
}
LookupKey DHT_Wrapper::fuzzForDHT_R(const std::vector<double> &cell,
double dt) {
const auto c_zero_val = std::pow(10, AQUEOUS_EXP);
NamedVector<double> input_nv(this->output_names, cell);
const std::vector<double> eval_vec = hooks.dht_fuzz(input_nv);
assert(eval_vec.size() == this->key_count);
LookupKey vecFuzz(this->key_count + 1, {.0});
DHT_Rounder rounder;
int totals_i = 0;
// introduce fuzzing to allow more hits in DHT
// loop over every variable of grid cell
for (std::uint32_t i = 0; i < eval_vec.size(); i++) {
double curr_key = eval_vec[i];
if (curr_key != 0) {
if (this->dht_prop_type_vector[i] == DHT_TYPE_TOTAL) {
curr_key -= base_totals[totals_i++];
}
vecFuzz[i] =
rounder.round(curr_key, dht_signif_vector[i],
this->dht_prop_type_vector[i] == DHT_TYPE_TOTAL);
}
}
// add timestep to the end of the key as double value
vecFuzz[this->key_count].fp_element = dt;
return vecFuzz;
}
LookupKey DHT_Wrapper::fuzzForDHT(const std::vector<double> &cell, double dt) {
const auto c_zero_val = std::pow(10, AQUEOUS_EXP);

View File

@ -1,10 +1,13 @@
// Time-stamp: "Last modified 2023-08-11 11:01:11 delucia"
// Time-stamp: "Last modified 2023-08-16 17:02:31 mluebke"
#include "poet/DHT_Wrapper.hpp"
#include "poet/DataStructures.hpp"
#include "poet/HashFunctions.hpp"
#include "poet/Interpolation.hpp"
#include "poet/LookupKey.hpp"
#include "poet/Rounding.hpp"
#include <Rcpp/vector/instantiation.h>
#include <Rinternals.h>
#include <algorithm>
#include <array>
#include <cassert>
@ -27,9 +30,13 @@ InterpolationModule::InterpolationModule(
std::uint32_t entries_per_bucket, std::uint64_t size_per_process,
std::uint32_t min_entries_needed, DHT_Wrapper &dht,
const NamedVector<std::uint32_t> &interp_key_signifs,
const std::vector<std::int32_t> &dht_key_indices)
const std::vector<std::int32_t> &dht_key_indices,
const std::vector<std::string> &_out_names,
const ChemistryParams::Chem_Hook_Functions &_hooks)
: dht_instance(dht), key_signifs(interp_key_signifs),
key_indices(dht_key_indices), min_entries_needed(min_entries_needed) {
key_indices(dht_key_indices), min_entries_needed(min_entries_needed),
dht_names(dht.getKeySpecies().getNames()), out_names(_out_names),
hooks(_hooks) {
initPHT(this->key_signifs.size(), entries_per_bucket, size_per_process,
dht.getCommunicator());
@ -63,45 +70,41 @@ void InterpolationModule::tryInterpolation(WorkPackage &work_package) {
const auto dht_results = this->dht_instance.getDHTResults();
for (int i = 0; i < work_package.size; i++) {
if (work_package.mapping[i] != CHEM_PQC) {
interp_result.status[i] = NOT_NEEDED;
for (int wp_i = 0; wp_i < work_package.size; wp_i++) {
if (work_package.mapping[wp_i] != CHEM_PQC) {
interp_result.status[wp_i] = NOT_NEEDED;
continue;
}
const auto rounded_key = roundKey(dht_results.keys[i]);
const auto rounded_key = roundKey(dht_results.keys[wp_i]);
auto pht_result =
pht->query(rounded_key, this->min_entries_needed,
dht_instance.getInputCount(), dht_instance.getOutputCount());
int pht_i = 0;
if (pht_result.size < this->min_entries_needed) {
interp_result.status[wp_i] = INSUFFICIENT_DATA;
continue;
}
if (hooks.interp_pre.isValid()) {
NamedVector<double> nv_in(this->out_names, work_package.input[wp_i]);
auto rm_indices = hooks.interp_pre(nv_in, pht_result.in_values);
pht_result.size -= rm_indices.size();
while (pht_i < pht_result.size) {
if (pht_result.size < this->min_entries_needed) {
break;
}
auto in_it = pht_result.in_values.begin() + pht_i;
auto out_it = pht_result.out_values.begin() + pht_i;
bool same_sig_calcite = (pht_result.in_values[pht_i][7] == 0) ==
(work_package.input[i][7] == 0);
bool same_sig_dolomite = (pht_result.in_values[pht_i][8] == 0) ==
(work_package.input[i][9] == 0);
if (!same_sig_calcite || !same_sig_dolomite) {
pht_result.size -= 1;
pht_result.in_values.erase(in_it);
pht_result.out_values.erase(out_it);
interp_result.status[wp_i] = INSUFFICIENT_DATA;
continue;
}
pht_i += 1;
}
if (pht_result.size < this->min_entries_needed) {
interp_result.status[i] = INSUFFICIENT_DATA;
continue;
for (const auto &index : rm_indices) {
pht_result.in_values.erase(
std::next(pht_result.in_values.begin(), index - 1));
pht_result.out_values.erase(
std::next(pht_result.out_values.begin(), index - 1));
}
}
#ifdef POET_PHT_ADD
@ -109,20 +112,17 @@ void InterpolationModule::tryInterpolation(WorkPackage &work_package) {
#endif
double start_fc = MPI_Wtime();
// mean water
// double mean_water = 0;
// for (int out_i = 0; out_i < pht_result.size; out_i++) {
// mean_water += pht_result.out_values[out_i][0];
// }
// mean_water /= pht_result.size;
work_package.output[i] =
f_interpolate(dht_instance.getKeyElements(), work_package.input[i],
work_package.output[wp_i] =
f_interpolate(dht_instance.getKeyElements(), work_package.input[wp_i],
pht_result.in_values, pht_result.out_values);
if (work_package.output[i][7] < 0 || work_package.output[i][9] < 0) {
interp_result.status[i] = INSUFFICIENT_DATA;
continue;
if (hooks.interp_post.isValid()) {
NamedVector<double> nv_result(this->out_names, work_package.output[wp_i]);
if (hooks.interp_post(nv_result)) {
interp_result.status[wp_i] = INSUFFICIENT_DATA;
continue;
}
}
// interp_result.results[i][0] = mean_water;
@ -130,8 +130,8 @@ void InterpolationModule::tryInterpolation(WorkPackage &work_package) {
this->interpolations++;
work_package.mapping[i] = CHEM_INTERP;
interp_result.status[i] = RES_OK;
work_package.mapping[wp_i] = CHEM_INTERP;
interp_result.status[wp_i] = RES_OK;
}
}

View File

@ -1,4 +1,4 @@
// Time-stamp: "Last modified 2023-08-09 13:32:11 mluebke"
// Time-stamp: "Last modified 2023-08-15 14:50:59 mluebke"
#include "poet/DHT_Wrapper.hpp"
#include "poet/HashFunctions.hpp"
@ -203,7 +203,7 @@ void ProximityHashTable::Cache::operator()(const LookupKey &key,
if (this->free_mem < 0) {
const LookupKey &to_del = this->lru_queue.back();
const auto elem_d = this->find(to_del);
this->free_mem += elem_d->second.getSize();
this->free_mem += elem_d->second.getMemSize();
this->erase(to_del);
this->keyfinder.erase(to_del);
this->lru_queue.pop_back();
@ -212,7 +212,7 @@ void ProximityHashTable::Cache::operator()(const LookupKey &key,
this->insert({key, val});
this->lru_queue.emplace_front(key);
this->keyfinder[key] = lru_queue.begin();
this->free_mem -= val.getSize();
this->free_mem -= val.getMemSize();
return;
}

View File

@ -1,4 +1,4 @@
// Time-stamp: "Last modified 2023-08-15 17:33:00 mluebke"
// Time-stamp: "Last modified 2023-08-16 14:50:04 mluebke"
#include "poet/ChemistryModule.hpp"
#include "poet/DHT_Wrapper.hpp"
@ -52,7 +52,7 @@ void poet::ChemistryModule::WorkerLoop() {
break;
}
case CHEM_INIT_SPECIES: {
Field dummy{0};
Field dummy;
initializeField(dummy);
break;
}
@ -220,8 +220,8 @@ void poet::ChemistryModule::WorkerPostIter(MPI_Status &prope_status,
if (this->dht_enabled) {
dht_hits.push_back(dht->getHits());
dht_evictions.push_back(dht->getEvictions());
dht->resetCounter();
dht->resetCounter();
if (this->dht_snaps_type == DHT_SNAPS_ITEREND) {
WorkerWriteDHTDump(iteration);
}
@ -238,8 +238,9 @@ void poet::ChemistryModule::WorkerPostIter(MPI_Status &prope_status,
interp->dumpPHTState(out.str());
}
}
}
RInsidePOET::getInstance().parseEvalQ("gc()");
}
void poet::ChemistryModule::WorkerPostSim(uint32_t iteration) {
if (this->dht_enabled && this->dht_snaps_type >= DHT_SNAPS_ITEREND) {
@ -248,7 +249,7 @@ void poet::ChemistryModule::WorkerPostSim(uint32_t iteration) {
if (this->interp_enabled && this->dht_snaps_type >= DHT_SNAPS_ITEREND) {
std::stringstream out;
out << this->dht_file_out_dir << "/iter_" << std::setfill('0')
<< std::setw(this->file_pad) << iteration << ".pht";
<< std::setw(this->file_pad) << iteration << ".pht";
interp->dumpPHTState(out.str());
}
}
@ -265,7 +266,7 @@ void poet::ChemistryModule::WorkerWriteDHTDump(uint32_t iteration) {
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());

View File

@ -1,31 +1,53 @@
#include "poet/Field.hpp"
#include "poet/DataStructures.hpp"
#include <Rcpp.h>
#include <Rinternals.h>
#include <cstddef>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
void poet::Field::InitFromVec(const std::vector<std::vector<double>> &input,
const std::vector<std::string> &prop_vec) {
if (prop_vec.size() != input.size()) {
throw std::runtime_error("Prop vector shall name all elements.");
}
auto name_it = prop_vec.begin();
for (const auto &in_vec : input) {
if (in_vec.size() != req_vec_size) {
throw std::runtime_error(
"Input vector doesn't match expected vector size.");
poet::Field::Field(std::uint32_t vec_s,
const std::vector<std::vector<double>> &input,
const std::vector<std::string> &prop_vec, bool row_major)
: req_vec_size(vec_s), props(prop_vec) {
if (row_major) {
if (this->props.size() != input[0].size()) {
throw std::runtime_error("Prop vector shall name all elements.");
}
this->insert({*(name_it++), in_vec});
}
this->props = prop_vec;
const std::size_t n_input = input.size();
for (std::size_t i = 0; i < this->props.size(); i++) {
std::vector<double> curr_col(n_input);
for (std::size_t j = 0; j < n_input; j++) {
curr_col[j] = input[j][i];
}
this->insert({this->props[i], std::move(curr_col)});
}
} else {
if (this->props.size() != input.size()) {
throw std::runtime_error("Prop vector shall name all elements.");
}
auto name_it = this->props.begin();
for (const auto &in_vec : input) {
if (in_vec.size() != req_vec_size) {
throw std::runtime_error(
"Input vector doesn't match expected vector size.");
}
this->insert({*(name_it++), in_vec});
}
}
}
void poet::Field::InitFromVec(const std::vector<double> &input,
const std::vector<std::string> &prop_vec) {
poet::Field::Field(std::uint32_t vec_s, const std::vector<double> &input,
const std::vector<std::string> &prop_vec)
: Field(vec_s) {
const uint32_t expected_size = prop_vec.size() * req_vec_size;
if (expected_size != input.size()) {
@ -62,47 +84,7 @@ auto poet::Field::AsVector() const -> poet::FieldColumn {
return output;
}
void poet::Field::SetFromVector(const poet::FieldColumn &cont_field) {
if (cont_field.size() != this->size() * req_vec_size) {
throw std::runtime_error(
"Field::SetFromVector: vector does not match expected size");
}
uint32_t vec_p = 0;
for (const auto &elem : props) {
const auto start = cont_field.begin() + vec_p;
const auto end = start + req_vec_size;
const auto map_it = this->find(elem);
map_it->second = FieldColumn(start, end);
vec_p += req_vec_size;
}
}
void poet::Field::SetFromVector(
const std::vector<poet::FieldColumn> &cont_field) {
if (cont_field.size() != this->size()) {
throw std::runtime_error(
"Input field contains more or less elements than this container.");
}
auto in_field_it = cont_field.begin();
for (const auto &elem : props) {
if (in_field_it->size() != req_vec_size) {
throw std::runtime_error(
"One vector contains more or less elements than expected.");
}
const auto map_it = this->find(elem);
map_it->second = *(in_field_it++);
}
}
void poet::Field::UpdateFromField(const poet::Field &input) {
void poet::Field::update(const poet::Field &input) {
for (const auto &input_elem : input) {
auto it_self = this->find(input_elem.first);
if (it_self == this->end()) {
@ -132,3 +114,118 @@ poet::FieldColumn &poet::Field::operator[](const std::string &key) {
return std::unordered_map<std::string, FieldColumn>::operator[](key);
}
SEXP poet::Field::asSEXP() const {
const std::size_t cols = this->props.size();
SEXP s_names = PROTECT(Rf_allocVector(STRSXP, cols));
SEXP s_output = PROTECT(Rf_allocVector(VECSXP, cols));
for (std::size_t prop_i = 0; prop_i < this->props.size(); prop_i++) {
const auto &name = this->props[prop_i];
SEXP s_values = PROTECT(Rf_allocVector(REALSXP, this->req_vec_size));
const auto values = this->find(name)->second;
SET_STRING_ELT(s_names, prop_i, Rf_mkChar(name.c_str()));
for (std::size_t i = 0; i < this->req_vec_size; i++) {
REAL(s_values)[i] = values[i];
}
SET_VECTOR_ELT(s_output, prop_i, s_values);
UNPROTECT(1);
}
SEXP s_rownames = PROTECT(Rf_allocVector(INTSXP, this->req_vec_size));
for (std::size_t i = 0; i < this->req_vec_size; i++) {
INTEGER(s_rownames)[i] = static_cast<int>(i + 1);
}
Rf_setAttrib(s_output, R_ClassSymbol,
Rf_ScalarString(Rf_mkChar("data.frame")));
Rf_setAttrib(s_output, R_NamesSymbol, s_names);
Rf_setAttrib(s_output, R_RowNamesSymbol, s_rownames);
UNPROTECT(3);
return s_output;
}
poet::Field &poet::Field::operator=(const FieldColumn &cont_field) {
if (cont_field.size() != this->size() * req_vec_size) {
throw std::runtime_error(
"Field::SetFromVector: vector does not match expected size");
}
uint32_t vec_p = 0;
for (const auto &elem : props) {
const auto start = cont_field.begin() + vec_p;
const auto end = start + req_vec_size;
const auto map_it = this->find(elem);
map_it->second = FieldColumn(start, end);
vec_p += req_vec_size;
}
return *this;
}
poet::Field &
poet::Field::operator=(const std::vector<FieldColumn> &cont_field) {
if (cont_field.size() != this->size()) {
throw std::runtime_error(
"Input field contains more or less elements than this container.");
}
auto in_field_it = cont_field.begin();
for (const auto &elem : props) {
if (in_field_it->size() != req_vec_size) {
throw std::runtime_error(
"One vector contains more or less elements than expected.");
}
const auto map_it = this->find(elem);
map_it->second = *(in_field_it++);
}
return *this;
}
void poet::Field::fromSEXP(const SEXP &s_rhs) {
this->clear();
SEXP s_vec = PROTECT(Rf_coerceVector(s_rhs, VECSXP));
SEXP s_names = PROTECT(Rf_getAttrib(s_vec, R_NamesSymbol));
std::size_t cols = static_cast<std::size_t>(Rf_length(s_vec));
this->props.clear();
this->props.reserve(cols);
for (std::size_t i = 0; i < cols; i++) {
const std::string prop_name(CHAR(STRING_ELT(s_names, i)));
this->props.push_back(prop_name);
SEXP s_values = PROTECT(VECTOR_ELT(s_vec, i));
if (i == 0) {
this->req_vec_size = static_cast<std::uint32_t>(Rf_length(s_values));
}
FieldColumn input(this->req_vec_size);
for (std::size_t j = 0; j < this->req_vec_size; j++) {
input[j] = static_cast<double>(REAL(s_values)[j]);
}
UNPROTECT(1);
this->insert({prop_name, input});
}
UNPROTECT(2);
}

View File

@ -18,15 +18,15 @@
** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <poet/Macros.hpp>
#include <poet/SimParams.hpp>
#include <tug/BoundaryCondition.hpp>
#include <tug/Diffusion.hpp>
#include <Rcpp.h>
#include <algorithm>
#include <cstdint>
#include <poet/DiffusionModule.hpp>
#include <poet/Grid.hpp>
#include <poet/Macros.hpp>
#include <poet/SimParams.hpp>
#include <tug/BoundaryCondition.hpp>
#include <tug/Diffusion.hpp>
#include <array>
#include <cassert>
@ -58,17 +58,18 @@ inline const char *convert_bc_to_config_file(uint8_t in) {
DiffusionModule::DiffusionModule(const poet::DiffusionParams &diffu_args,
const poet::GridParams &grid_params)
: t_field{grid_params.total_n}, n_cells_per_prop(grid_params.total_n) {
: n_cells_per_prop(grid_params.total_n) {
this->diff_input.setGridCellN(grid_params.n_cells[0], grid_params.n_cells[1]);
this->diff_input.setDomainSize(grid_params.s_cells[0],
grid_params.s_cells[1]);
this->dim = grid_params.dim;
this->initialize(diffu_args);
this->initialize(diffu_args, grid_params.total_n);
}
void DiffusionModule::initialize(const poet::DiffusionParams &args) {
void DiffusionModule::initialize(const poet::DiffusionParams &args,
std::uint32_t n_grid_cells) {
// const poet::DiffusionParams args(this->R);
// name of props
@ -87,7 +88,7 @@ void DiffusionModule::initialize(const poet::DiffusionParams &args) {
this->alpha.push_back(args.alpha[this->prop_names[i]]);
const double val = args.initial_t[prop_names[i]];
std::vector<double> init_val(t_field.GetRequestedVecSize(), val);
std::vector<double> init_val(n_grid_cells, val);
initial_values.push_back(std::move(init_val));
if (this->dim == this->DIM_2D) {
@ -100,7 +101,7 @@ void DiffusionModule::initialize(const poet::DiffusionParams &args) {
}
}
t_field.InitFromVec(initial_values, prop_names);
this->t_field = Field(n_grid_cells, initial_values, prop_names);
// apply boundary conditions to each ghost node
uint8_t bc_size = (this->dim == this->DIM_1D ? 2 : 4);
@ -156,30 +157,30 @@ void DiffusionModule::simulate(double dt) {
MSG_NOENDL("DiffusionModule::simulate(): Starting diffusion ...");
std::cout << std::flush;
std::vector<std::vector<double>> field_2d = t_field.As2DVector();
this->diff_input.setTimestep(dt);
for (int i = 0; i < field_2d.size(); i++) {
std::vector<double> in_alpha(this->n_cells_per_prop, this->alpha[i]);
this->diff_input.setBoundaryCondition(this->bc_vec[i]);
if (this->dim == this->DIM_1D) {
tug::diffusion::BTCS_1D(this->diff_input, field_2d[i].data(),
in_alpha.data());
} else {
tug::diffusion::ADI_2D(this->diff_input, field_2d[i].data(),
in_alpha.data());
}
std::vector<double> in_alpha(this->n_cells_per_prop, this->alpha[i]);
this->diff_input.setBoundaryCondition(this->bc_vec[i]);
if (this->dim == this->DIM_1D) {
tug::diffusion::BTCS_1D(this->diff_input, field_2d[i].data(),
in_alpha.data());
} else {
tug::diffusion::ADI_2D(this->diff_input, field_2d[i].data(),
in_alpha.data());
}
}
t_field.SetFromVector(field_2d);
std::cout << " done!" << std::endl;
t_field = field_2d;
std::cout << " done!\n";
sim_a_transport = MPI_Wtime();
transport_t += sim_a_transport - sim_b_transport;
}

View File

@ -83,14 +83,22 @@ void poet::ChemistryParams::initFromR(RInsidePOET &R) {
Rcpp::as<std::string>(R.parseEval("mysetup$chemistry$input_script"));
if (R.checkIfExists("dht_species", "mysetup$chemistry")) {
this->dht_signifs =
R.wrapNamedVector<std::uint32_t>("mysetup$chemistry$dht_species");
this->dht_signifs = Rcpp::as<NamedVector<std::uint32_t>>(
R.parseEval(("mysetup$chemistry$dht_species")));
}
if (R.checkIfExists("pht_species", "mysetup$chemistry")) {
this->pht_signifs =
R.wrapNamedVector<std::uint32_t>("mysetup$chemistry$pht_species");
this->pht_signifs = Rcpp::as<NamedVector<std::uint32_t>>(
R.parseEval(("mysetup$chemistry$pht_species")));
}
this->hooks.dht_fill =
RHookFunction<bool>(R, "mysetup$chemistry$hooks$dht_fill");
this->hooks.dht_fuzz =
RHookFunction<std::vector<double>>(R, "mysetup$chemistry$hooks$dht_fuzz");
this->hooks.interp_pre = RHookFunction<std::vector<std::size_t>>(
R, "mysetup$chemistry$hooks$interp_pre_func");
this->hooks.interp_post =
RHookFunction<bool>(R, "mysetup$chemistry$hooks$interp_post_func");
}
SimParams::SimParams(int world_rank_, int world_size_) {

View File

@ -5,6 +5,10 @@ file(GLOB test_SRC
add_executable(testPOET ${test_SRC})
target_link_libraries(testPOET doctest poet_lib)
get_filename_component(TEST_RInsideSourceFile "RInsidePOET_funcs.R" REALPATH)
configure_file(testDataStructures.hpp.in testDataStructures.hpp)
target_include_directories(testPOET PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
add_custom_target(check
COMMAND $<TARGET_FILE:testPOET>
DEPENDS testPOET

22
test/RInsidePOET_funcs.R Normal file
View File

@ -0,0 +1,22 @@
simple_named_vec <- function(input) {
input["Ca"] <- 0
return(input)
}
extended_named_vec <- function(input, additional) {
return(input["H"] + additional["H"])
}
bool_named_vec <- function(input) {
return(input["H"] == 0)
}
simple_field <- function(field) {
field$Na <- 0
return(field)
}
extended_field <- function(field, additional) {
return(field + additional)
}

View File

@ -0,0 +1,6 @@
#ifndef TESTRINSIDEPOET_H_
#define TESTRINSIDEPOET_H_
inline const char *RInside_source_file = "@TEST_RInsideSourceFile@";
#endif // TESTRINSIDEPOET_H_

View File

@ -1,12 +1,17 @@
#include <Rcpp.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <doctest/doctest.h>
#include <iostream>
#include <poet/Field.hpp>
#include <poet/DataStructures.hpp>
#include <poet/RInsidePOET.hpp>
#include <string>
#include <vector>
#include "testDataStructures.hpp"
using namespace poet;
#define CHECK_AND_FAIL_LOOP(val1, val2) \
@ -19,14 +24,17 @@ TEST_CASE("Field") {
constexpr uint32_t VEC_COUNT = 3;
constexpr double INIT_VAL = 1;
Field dut(VEC_SIZE);
std::vector<std::string> names = {"C", "Ca", "Na"};
std::vector<FieldColumn> init_values(names.size(),
FieldColumn(VEC_SIZE, INIT_VAL));
RInsidePOET &R = RInsidePOET::getInstance();
R["sourcefile"] = RInside_source_file;
R.parseEval("source(sourcefile)");
SUBCASE("Initialize field with 2D vector") {
dut.InitFromVec(init_values, names);
Field dut(VEC_SIZE, init_values, names);
auto props = dut.GetProps();
@ -43,7 +51,7 @@ TEST_CASE("Field") {
SUBCASE("Initialize field with 2D vector") {
std::vector<double> init_values(VEC_SIZE * VEC_COUNT, 1);
dut.InitFromVec(init_values, names);
Field dut(VEC_SIZE, init_values, names);
auto props = dut.GetProps();
@ -58,7 +66,7 @@ TEST_CASE("Field") {
}
}
dut.InitFromVec(init_values, names);
Field dut(VEC_SIZE, init_values, names);
SUBCASE("Return vector") {
std::vector<double> output = dut.AsVector();
@ -66,6 +74,37 @@ TEST_CASE("Field") {
CHECK(output.size() == VEC_SIZE * VEC_COUNT);
}
SUBCASE("SEXP return") {
R["test"] = dut.asSEXP();
Field field_constructed = R.parseEval("test");
Rcpp::DataFrame R_df = R.parseEval("test");
Rcpp::CharacterVector R_names = R_df.names();
for (std::size_t i = 0; i < VEC_COUNT; i++) {
const auto r_values = Rcpp::as<std::vector<double>>(R_df[i]);
CHECK_EQ(r_values, dut[names[i]]);
CHECK_EQ(R_names[i], names[i]);
CHECK_EQ(field_constructed[names[i]], dut[names[i]]);
}
}
SUBCASE("Apply R function (set Na to zero)") {
RHookFunction<Field> to_call(R, "simple_field");
Field field_proc = to_call(dut.asSEXP());
CHECK_EQ(field_proc["Na"], FieldColumn(dut.GetRequestedVecSize(), 0));
}
SUBCASE("Apply R function (add two fields)") {
RHookFunction<Field> to_call(R, "extended_field");
Field field_proc = to_call(dut.asSEXP(), dut.asSEXP());
CHECK_EQ(field_proc["Na"],
FieldColumn(dut.GetRequestedVecSize(), INIT_VAL + INIT_VAL));
}
constexpr double NEW_VAL = 2.;
std::vector<double> new_val_vec(VEC_SIZE, NEW_VAL);
@ -102,7 +141,7 @@ TEST_CASE("Field") {
}
}
dut.SetFromVector(new_field);
dut = new_field;
SUBCASE("SetFromVector return correct field vector") {
auto ret_vec = dut.AsVector();
@ -147,19 +186,18 @@ TEST_CASE("Field") {
// reset field
names = dut.GetProps();
dut.SetFromVector(
std::vector<FieldColumn>(names.size(), FieldColumn(VEC_SIZE, INIT_VAL)));
dut = std::vector<FieldColumn>(names.size(), FieldColumn(VEC_SIZE, INIT_VAL));
constexpr double SOME_OTHER_VAL = -0.5;
Field some_other_field(VEC_SIZE);
std::vector<std::string> some_other_props = {"Na", "Cl"};
std::vector<double> some_other_values(VEC_SIZE * some_other_props.size(),
SOME_OTHER_VAL);
some_other_field.InitFromVec(some_other_values, some_other_props);
Field some_other_field(VEC_SIZE, some_other_values, some_other_props);
SUBCASE("Update existing field from another field") {
dut.UpdateFromField(some_other_field);
dut.update(some_other_field);
auto ret_vec = dut.As2DVector();
auto ret_it = ret_vec.begin();

63
test/testNamedVector.cpp Normal file
View File

@ -0,0 +1,63 @@
#include <Rcpp.h>
#include <cstddef>
#include <doctest/doctest.h>
#include <poet/DataStructures.hpp>
#include <poet/RInsidePOET.hpp>
#include <utility>
#include <vector>
#include "testDataStructures.hpp"
TEST_CASE("NamedVector") {
RInsidePOET &R = RInsidePOET::getInstance();
R["sourcefile"] = RInside_source_file;
R.parseEval("source(sourcefile)");
const std::vector<std::string> names{{"H", "O", "Ca"}};
const std::vector<double> values{{0.1, 0.2, 0.3}};
poet::NamedVector<double> nv(names, values);
SUBCASE("SEXP conversion") {
R["test"] = nv;
const Rcpp::NumericVector R_value = R.parseEval("test");
const Rcpp::CharacterVector R_names = R_value.names();
const poet::NamedVector<double> nv_constructed = R["test"];
for (std::size_t i = 0; i < R_value.size(); i++) {
CHECK_EQ(R_value[i], values[i]);
CHECK_EQ(R_names[i], names[i]);
CHECK_EQ(nv_constructed[i], values[i]);
CHECK_EQ(nv_constructed.getNames()[i], names[i]);
}
}
SUBCASE("Apply R function (set to zero)") {
RHookFunction<poet::NamedVector<double>> to_call(R, "simple_named_vec");
nv = to_call(nv);
CHECK_EQ(nv[2], 0);
}
SUBCASE("Apply R function (second NamedVector)") {
RHookFunction<poet::NamedVector<double>> to_call(R, "extended_named_vec");
const std::vector<std::string> names{{"C", "H", "Mg"}};
const std::vector<double> values{{0, 1, 2}};
poet::NamedVector<double> second_nv(names, values);
nv = to_call(nv, second_nv);
CHECK_EQ(nv[0], 1.1);
}
SUBCASE("Apply R function (check if zero)") {
RHookFunction<bool> to_call(R, "bool_named_vec");
CHECK_FALSE(to_call(nv));
}
}