refactor: rename ChemSim to ChemSeq

refactor: introduce new base class BaseChemModule for both sequential
and parallel chemistry module

refactor: iterative loop of simulations in poet application via template
function

This should make the application code more readable.
This commit is contained in:
Max Luebke 2022-11-28 13:23:32 +01:00 committed by Max Lübke
parent aed9cb3395
commit 8d13748545
8 changed files with 197 additions and 180 deletions

View File

@ -20,6 +20,7 @@
#include <RInside.h>
#include <Rcpp.h>
#include <cstdint>
#include <poet/ChemSimPar.hpp>
#include <poet/ChemSimSeq.hpp>
#include <poet/DiffusionModule.hpp>
@ -32,11 +33,69 @@
#include <vector>
#include <poet.h>
#include <mpi.h>
using namespace std;
using namespace poet;
using namespace Rcpp;
template <class ChemistryInstance>
inline double RunMasterLoop(SimParams &params, RInside &R, Grid &grid,
ChemistryParams &chem_params) {
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");
ChemistryInstance C(params, R, grid);
C.InitModule(chem_params);
/* SIMULATION LOOP */
double ret_time = MPI_Wtime();
for (uint32_t iter = 1; iter < maxiter + 1; iter++) {
// cout << "CPP: Evaluating next time step" << endl;
// R.parseEvalQ("mysetup <- master_iteration_setup(mysetup)");
double dt = Rcpp::as<double>(
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);
cout << "CPP: Chemistry" << endl;
C.Simulate(dt);
// 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()");
C.End();
diffusion.end();
return ret_time;
}
int main(int argc, char *argv[]) {
double sim_start, sim_end;
@ -135,61 +194,11 @@ int main(int argc, char *argv[]) {
/* THIS IS EXECUTED BY THE MASTER */
if (world_rank == 0) {
ChemMaster master(params, R, grid, chem_params);
DiffusionModule diffusion(poet::DiffusionParams(R), grid);
// diffusion.initialize();
sim_start = MPI_Wtime();
/* Iteration Count is dynamic, retrieving value from R (is only needed by
* master for the following loop) */
uint32_t maxiter = R.parseEval("mysetup$iterations");
/* SIMULATION LOOP */
for (uint32_t iter = 1; iter < maxiter + 1; iter++) {
// cout << "CPP: Evaluating next time step" << endl;
// R.parseEvalQ("mysetup <- master_iteration_setup(mysetup)");
double dt = Rcpp::as<double>(
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);
cout << "CPP: Chemistry" << endl;
/* Fallback for sequential execution */
// TODO: use new grid
if (world_size == 1) {
master.simulate(dt);
}
/* otherwise run parallel */
else {
master.masterLoop(dt);
}
// 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
if (world_size == 1) {
sim_start = RunMasterLoop<ChemSeq>(params, R, grid, chem_params);
} else {
sim_start = RunMasterLoop<ChemMaster>(params, R, grid, chem_params);
}
cout << "CPP: finished simulation loop" << endl;
@ -197,19 +206,9 @@ int main(int argc, char *argv[]) {
cout << "CPP: start timing profiling" << endl;
R.parseEvalQ("profiling <- list()");
R["simtime"] = sim_end - sim_start;
R.parseEvalQ("profiling$simtime <- simtime");
diffusion.end();
if (world_size == 1) {
master.end();
} else {
master.endLoop();
}
string r_vis_code;
r_vis_code = "saveRDS(profiling, file=paste0(fileout,'/timings.rds'));";
R.parseEval(r_vis_code);
@ -219,7 +218,8 @@ int main(int argc, char *argv[]) {
}
/* THIS IS EXECUTED BY THE WORKERS */
else {
ChemWorker worker(params, R, grid, dht_comm, chem_params);
ChemWorker worker(params, R, grid, dht_comm);
worker.InitModule(chem_params);
worker.loop();
}

View File

@ -59,7 +59,7 @@ namespace poet {
* a loop to send and recv pkgs from workers is implemented.
*
*/
class ChemMaster : public ChemSim {
class ChemMaster : public BaseChemModule {
public:
/**
* @brief Construct a new ChemMaster object
@ -73,8 +73,7 @@ public:
* @param R_ R runtime
* @param grid_ Grid object
*/
ChemMaster(SimParams &params, RInside &R_, Grid &grid_,
poet::ChemistryParams chem_params);
ChemMaster(SimParams &params, RInside &R_, Grid &grid_);
/**
* @brief Destroy the ChemMaster object
@ -100,7 +99,7 @@ public:
* The main tasks are instrumented with time measurements.
*
*/
void masterLoop(double dt);
void Simulate(double dt);
/**
* @brief End chemistry simulation.
@ -111,7 +110,7 @@ public:
* Finally he will write all data to the R runtime and return this function.
*
*/
void endLoop();
void End();
/**
* @brief Get the send time
@ -170,15 +169,16 @@ public:
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);
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<double> &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<double> &out_field);
uint32_t wp_size;
bool dht_enabled;
unsigned int wp_size;
double send_t = 0.f;
double recv_t = 0.f;
@ -195,6 +195,8 @@ private:
worker_struct *workerlist;
double *send_buffer;
double *mpi_buffer;
std::vector<uint32_t> wp_sizes_vector;
poet::StateMemory *state;
};
/**
@ -203,7 +205,7 @@ private:
* Providing mainly a function to loop and wait for messages from the master.
*
*/
class ChemWorker : public ChemSim {
class ChemWorker : public BaseChemModule {
public:
/**
* @brief Construct a new ChemWorker object
@ -219,9 +221,9 @@ public:
* @param grid_ Grid object
* @param dht_comm Communicator addressing all processes marked as worker
*/
ChemWorker(SimParams &params, RInside &R_, Grid &grid_, MPI_Comm dht_comm,
poet::ChemistryParams chem_params);
ChemWorker(SimParams &params, RInside &R_, Grid &grid_, MPI_Comm dht_comm);
void InitModule(poet::ChemistryParams &chem_params);
/**
* @brief Destroy the ChemWorker object
*
@ -246,22 +248,27 @@ private:
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;
unsigned int dht_size_per_process;
std::vector<bool> dht_flags;
double *mpi_buffer_results;
DHT_Wrapper *dht;
std::array<double, 3> timing;
double idle_t = 0.f;
int phreeqc_count = 0;
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

View File

@ -27,6 +27,7 @@
#include "RInside.h"
#include "SimParams.hpp"
#include <bits/stdint-uintn.h>
#include <cstddef>
#include <cstdint>
#include <mpi.h>
@ -55,9 +56,28 @@ namespace poet {
* containing basic parameters for simulation.
*
*/
constexpr const char *CHEMISTRY_MODULE_NAME = "state_c";
class BaseChemModule {
public:
BaseChemModule(SimParams &params, RInside &R_, Grid &grid_);
class ChemSim {
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<std::string> prop_names;
};
class ChemSeq : public BaseChemModule {
public:
/**
@ -70,9 +90,11 @@ public:
* @param R_ R runtime
* @param grid_ Initialized grid
*/
ChemSim(SimParams &params, RInside &R_, Grid &grid_,
poet::ChemistryParams chem_params);
ChemSeq(SimParams &params, RInside &R_, Grid &grid_);
~ChemSeq();
void InitModule(poet::ChemistryParams &chem_params);
/**
* @brief Run iteration of simulation in sequential mode
*
@ -82,7 +104,7 @@ public:
* @todo change function name. Maybe 'slave' to 'seq'.
*
*/
virtual void simulate(double dt);
void Simulate(double dt);
/**
* @brief End simulation
@ -91,7 +113,7 @@ public:
* R runtime.
*
*/
virtual void end();
void End();
/**
* @brief Get the Chemistry Time
@ -100,30 +122,9 @@ public:
*/
double getChemistryTime();
protected:
double current_sim_time = 0;
int iteration = 0;
int world_rank;
int world_size;
unsigned int wp_size;
RInside &R;
Grid &grid;
std::string out_dir;
double chem_t = 0.f;
private:
poet::StateMemory *state;
uint32_t n_cells_per_prop;
std::vector<std::string> prop_names;
std::vector<uint32_t> wp_sizes_vector;
PhreeqcWrapper *phreeqc_rm;
PhreeqcWrapper *phreeqc_rm = std::nullptr_t();
};
} // namespace poet

12
src/BaseChemModule.cpp Normal file
View File

@ -0,0 +1,12 @@
#include <poet/ChemSimSeq.hpp>
poet::BaseChemModule::BaseChemModule(SimParams &params, 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();
}

View File

@ -18,10 +18,12 @@
** Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "poet/ChemSimSeq.hpp"
#include "poet/DiffusionModule.hpp"
#include "poet/SimParams.hpp"
#include <Rcpp.h>
#include <array>
#include <bits/stdint-uintn.h>
#include <iostream>
@ -34,23 +36,20 @@ using namespace poet;
using namespace std;
using namespace Rcpp;
ChemMaster::ChemMaster(SimParams &params, RInside &R_, Grid &grid_,
poet::ChemistryParams chem_params)
: ChemSim(params, R_, grid_, chem_params) {
ChemMaster::ChemMaster(SimParams &params, 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;
this->out_dir = params.getOutDir();
uint32_t grid_size = grid.getTotalCellCount() * this->prop_names.size();
/* allocate memory */
workerlist = (worker_struct *)calloc(world_size - 1, sizeof(worker_struct));
send_buffer = (double *)calloc(
(wp_size * this->prop_names.size()) + BUFFER_OFFSET, sizeof(double));
mpi_buffer = (double *)calloc(grid_size, sizeof(double));
this->workerlist = new worker_struct[this->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_size % this->wp_size;
@ -65,11 +64,12 @@ ChemMaster::ChemMaster(SimParams &params, RInside &R_, Grid &grid_,
}
ChemMaster::~ChemMaster() {
free(mpi_buffer);
free(workerlist);
delete this->workerlist;
delete this->send_buffer;
delete this->mpi_buffer;
}
void ChemMaster::masterLoop(double dt) {
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;
@ -78,6 +78,7 @@ void ChemMaster::masterLoop(double dt) {
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();
@ -139,7 +140,7 @@ void ChemMaster::masterLoop(double dt) {
// 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);
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);
@ -194,7 +195,7 @@ void ChemMaster::masterLoop(double dt) {
inline void ChemMaster::sendPkgs(int &pkg_to_send, int &count_pkgs,
int &free_workers, double *work_pointer,
const double &dt) {
const double &dt, const uint32_t iteration) {
/* declare variables */
double master_send_a, master_send_b;
int local_work_package_size;
@ -308,13 +309,9 @@ void ChemMaster::printProgressbar(int count_pkgs, int n_wp, int barWidth) {
/* end visual progress */
}
void ChemMaster::endLoop() {
/* call end() from base class */
ChemSim::end();
/* now we get to the part of the master */
double *timings;
int *dht_perfs;
void ChemMaster::End() {
R["simtime_chemistry"] = chem_t;
R.parseEvalQ("profiling$simtime_chemistry <- simtime_chemistry");
Rcpp::NumericVector phreeqc_time;
Rcpp::NumericVector dht_get_time;
@ -324,16 +321,13 @@ void ChemMaster::endLoop() {
int phreeqc_tmp;
timings = (double *)calloc(3, sizeof(double));
std::array<double, 3> timings;
std::array<int, 3> dht_perfs;
int dht_hits = 0;
int dht_miss = 0;
int dht_evictions = 0;
if (dht_enabled) {
dht_perfs = (int *)calloc(3, sizeof(int));
}
double idle_worker_tmp;
/* loop over all workers *
@ -343,7 +337,7 @@ void ChemMaster::endLoop() {
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, 3, MPI_DOUBLE, p + 1, TAG_TIMING, MPI_COMM_WORLD,
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));
@ -359,8 +353,8 @@ void ChemMaster::endLoop() {
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, 3, MPI_INT, p + 1, TAG_DHT_PERF, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
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];
@ -402,12 +396,6 @@ void ChemMaster::endLoop() {
R["dht_fill_time"] = dht_fill_time;
R.parseEvalQ("profiling$dht_fill_time <- dht_fill_time");
}
/* do some cleanup */
free(timings);
if (dht_enabled)
free(dht_perfs);
}
void ChemMaster::shuffleField(const std::vector<double> &in_field,

View File

@ -34,20 +34,11 @@
using namespace Rcpp;
using namespace poet;
ChemSim::ChemSim(SimParams &params, RInside &R_, Grid &grid_,
poet::ChemistryParams chem_params)
: R(R_), grid(grid_) {
t_simparams tmp = params.getNumParams();
this->world_rank = tmp.world_rank;
this->world_size = tmp.world_size;
this->wp_size = tmp.wp_size;
this->out_dir = params.getOutDir();
ChemSeq::ChemSeq(SimParams &params, RInside &R_, Grid &grid_)
: BaseChemModule(params, R_, grid_) {
this->prop_names = this->grid.getPropNames();
this->n_cells_per_prop = this->grid.getTotalCellCount();
this->state =
this->grid.registerState(poet::CHEMISTRY_MODULE_NAME, this->prop_names);
this->state = this->grid.registerState(
poet::BaseChemModule::CHEMISTRY_MODULE_NAME, this->prop_names);
std::vector<double> &field = this->state->mem;
field.resize(this->n_cells_per_prop * this->prop_names.size());
@ -58,13 +49,23 @@ ChemSim::ChemSim(SimParams &params, RInside &R_, Grid &grid_,
std::copy(prop_vec.begin(), prop_vec.end(),
field.begin() + (i * this->n_cells_per_prop));
}
}
poet::ChemSeq::~ChemSeq() {
this->grid.deregisterState(poet::BaseChemModule::CHEMISTRY_MODULE_NAME);
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 ChemSim::simulate(double dt) {
void ChemSeq::Simulate(double dt) {
double chem_a, chem_b;
/* start time measuring */
@ -116,9 +117,9 @@ void ChemSim::simulate(double dt) {
chem_t += chem_b - chem_a;
}
void ChemSim::end() {
R["simtime_chemistry"] = chem_t;
void ChemSeq::End() {
R["simtime_chemistry"] = this->chem_t;
R.parseEvalQ("profiling$simtime_chemistry <- simtime_chemistry");
}
double ChemSim::getChemistryTime() { return this->chem_t; }
double ChemSeq::getChemistryTime() { return this->chem_t; }

View File

@ -21,6 +21,7 @@
#include "poet/SimParams.hpp"
#include <Rcpp.h>
#include <cstdint>
#include <iostream>
#include <ostream>
#include <string>
@ -33,9 +34,11 @@ using namespace std;
using namespace Rcpp;
ChemWorker::ChemWorker(SimParams &params, RInside &R_, Grid &grid_,
MPI_Comm dht_comm, poet::ChemistryParams chem_args)
: ChemSim(params, R_, grid_, chem_args) {
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;
@ -44,16 +47,15 @@ ChemWorker::ChemWorker(SimParams &params, RInside &R_, Grid &grid_,
this->dht_file = params.getDHTFile();
mpi_buffer = (double *)calloc(
(wp_size * (this->prop_names.size())) + BUFFER_OFFSET, sizeof(double));
mpi_buffer_results =
(double *)calloc(wp_size * (this->prop_names.size()), sizeof(double));
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 (dht_enabled) {
if (this->dht_enabled) {
int data_size = this->prop_names.size() * sizeof(double);
int key_size =
this->prop_names.size() * sizeof(double) + (dt_differ * sizeof(double));
@ -80,18 +82,18 @@ ChemWorker::ChemWorker(SimParams &params, RInside &R_, Grid &grid_,
dht_flags.assign(wp_size, true);
}
timing[0] = 0.0;
timing[1] = 0.0;
timing[2] = 0.0;
this->timing.fill(0.0);
}
ChemWorker::~ChemWorker() {
free(mpi_buffer);
free(mpi_buffer_results);
delete this->mpi_buffer;
delete this->mpi_buffer_results;
if (dht_enabled)
delete dht;
delete this->dht;
delete this->dht;
if (this->phreeqc_rm) {
delete this->phreeqc_rm;
}
}
void ChemWorker::loop() {
@ -118,6 +120,13 @@ void ChemWorker::loop() {
}
}
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;
@ -151,11 +160,9 @@ void ChemWorker::doWork(MPI_Status &probe_status) {
}
// current iteration of simulation
if (mpi_buffer[count + 1] != iteration) {
iteration = mpi_buffer[count + 1];
R["iter"] = iteration;
R.parseEvalQ("mysetup$iter <- iter");
}
this->iteration = mpi_buffer[count + 1];
R["iter"] = this->iteration;
R.parseEvalQ("mysetup$iter <- iter");
// current timestep size
if (mpi_buffer[count + 2] != dt) {
@ -293,7 +300,8 @@ void ChemWorker::postIter() {
void ChemWorker::writeFile() {
cout.flush();
std::stringstream out;
out << out_dir << "/iter_" << setfill('0') << setw(3) << iteration << ".dht";
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."

View File

@ -175,7 +175,7 @@ void DiffusionModule::simulate(double dt) {
for (uint32_t i = 0; i < this->prop_names.size(); i++) {
try {
std::vector<double> t_prop_vec = this->grid.getSpeciesByName(
this->prop_names[i], poet::CHEMISTRY_MODULE_NAME);
this->prop_names[i], poet::BaseChemModule::CHEMISTRY_MODULE_NAME);
std::copy(t_prop_vec.begin(), t_prop_vec.end(),
curr_field.begin() + (i * this->n_cells_per_prop));