mirror of
https://git.gfz-potsdam.de/naaice/poet.git
synced 2025-12-15 20:38:23 +01:00
Merge branch 'implement-field-class' into 'main'
feat: add Field data structure as substitution of field declaration See merge request sec34/port!18
This commit is contained in:
commit
29344a3ff0
@ -17,6 +17,7 @@ options:
|
||||
- doc
|
||||
- chore
|
||||
- ci
|
||||
- test
|
||||
commit_groups:
|
||||
title_maps:
|
||||
feat: Features
|
||||
@ -29,6 +30,7 @@ options:
|
||||
doc: Documentation
|
||||
chore: Householding and Cleanup
|
||||
ci: CI
|
||||
test: Software Testing
|
||||
header:
|
||||
pattern: "^(\\w*)\\:\\s(.*)$"
|
||||
pattern_maps:
|
||||
|
||||
@ -21,6 +21,7 @@ image: git.gfz-potsdam.de:5000/sec34/port:builder
|
||||
stages: # List of stages for jobs, and their order of execution
|
||||
- build
|
||||
- release
|
||||
- test
|
||||
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
@ -34,6 +35,13 @@ build-poet: # This job runs in the build stage, which runs first.
|
||||
- cmake ..
|
||||
- make -j$(nproc)
|
||||
|
||||
test-poet:
|
||||
stage: test
|
||||
script:
|
||||
- mkdir build_test && cd build_test
|
||||
- cmake -DPOET_ENABLE_TESTING=ON ..
|
||||
- make -j$(nproc) check
|
||||
|
||||
archive-sources: # This job runs in the build stage, which runs first.
|
||||
image: python:3
|
||||
stage: release
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -5,3 +5,6 @@
|
||||
[submodule "ext/phreeqcrm"]
|
||||
path = ext/phreeqcrm
|
||||
url = ../../mluebke/phreeqcrm-gfz.git
|
||||
[submodule "ext/doctest"]
|
||||
path = ext/doctest
|
||||
url = https://github.com/doctest/doctest.git
|
||||
|
||||
@ -24,9 +24,19 @@ add_subdirectory(data)
|
||||
add_subdirectory(app)
|
||||
add_subdirectory(bench/dolo_diffu_inner)
|
||||
|
||||
# as tug will also pull in doctest as a dependency
|
||||
set(TUG_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
||||
|
||||
add_subdirectory(ext/tug EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(ext/phreeqcrm EXCLUDE_FROM_ALL)
|
||||
|
||||
option(POET_ENABLE_TESTING "Build test suite for POET" OFF)
|
||||
|
||||
if (POET_ENABLE_TESTING)
|
||||
add_subdirectory(ext/doctest EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
option(BUILD_DOC "Build documentation with doxygen" OFF)
|
||||
|
||||
if(BUILD_DOC)
|
||||
|
||||
1
ext/doctest
Submodule
1
ext/doctest
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8fdfd113dcb4ad1a31705ff8ddb13a21a505bad8
|
||||
151
include/poet/Field.hpp
Normal file
151
include/poet/Field.hpp
Normal file
@ -0,0 +1,151 @@
|
||||
#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);
|
||||
|
||||
/**
|
||||
* 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 { return this->props; };
|
||||
|
||||
/**
|
||||
* Return the requested vector size.
|
||||
*
|
||||
* \return Requested vector size set in the instanciation of the object.
|
||||
*/
|
||||
auto GetRequestedVecSize() const { 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:
|
||||
const uint32_t req_vec_size;
|
||||
|
||||
std::vector<std::string> props;
|
||||
};
|
||||
|
||||
} // namespace poet
|
||||
#endif // FIELD_H_
|
||||
@ -1,3 +1,5 @@
|
||||
add_subdirectory(DataStructures)
|
||||
|
||||
file(GLOB poet_lib_SRC
|
||||
CONFIGURE_DEPENDS
|
||||
"*.cpp" "*.c")
|
||||
@ -10,7 +12,7 @@ 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)
|
||||
MPI::MPI_C ${MATH_LIBRARY} ${CRYPTO_LIBRARY} RRuntime tug PhreeqcRM DataStructures)
|
||||
target_compile_definitions(poet_lib PUBLIC STRICT_R_HEADERS OMPI_SKIP_MPICXX)
|
||||
|
||||
if(POET_DHT_DEBUG)
|
||||
|
||||
6
src/DataStructures/CMakeLists.txt
Normal file
6
src/DataStructures/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
file(GLOB DataStructures_SRC
|
||||
CONFIGURE_DEPENDS
|
||||
"*.cpp" "*.c")
|
||||
|
||||
add_library(DataStructures ${DataStructures_SRC})
|
||||
target_include_directories(DataStructures PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
134
src/DataStructures/Field.cpp
Normal file
134
src/DataStructures/Field.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
#include "poet/Field.hpp"
|
||||
#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.");
|
||||
}
|
||||
this->insert({*(name_it++), in_vec});
|
||||
}
|
||||
|
||||
this->props = prop_vec;
|
||||
}
|
||||
|
||||
void poet::Field::InitFromVec(const std::vector<double> &input,
|
||||
const std::vector<std::string> &prop_vec) {
|
||||
const uint32_t expected_size = prop_vec.size() * req_vec_size;
|
||||
|
||||
if (expected_size != input.size()) {
|
||||
throw std::runtime_error(
|
||||
"Input vector have more (or less) elements than expected.");
|
||||
}
|
||||
|
||||
auto name_it = prop_vec.begin();
|
||||
|
||||
for (uint32_t i = 0; i < expected_size; i += req_vec_size) {
|
||||
auto input_pair = std::make_pair(
|
||||
*(name_it++), std::vector<double>(&input[i], &input[i + req_vec_size]));
|
||||
this->insert(input_pair);
|
||||
}
|
||||
|
||||
this->props = prop_vec;
|
||||
}
|
||||
|
||||
auto poet::Field::AsVector() const -> poet::FieldColumn {
|
||||
const uint32_t min_size = req_vec_size * this->size();
|
||||
|
||||
poet::FieldColumn output;
|
||||
output.reserve(min_size);
|
||||
|
||||
for (const auto &elem : props) {
|
||||
const auto map_it = this->find(elem);
|
||||
|
||||
const auto start = map_it->second.begin();
|
||||
const auto end = map_it->second.end();
|
||||
|
||||
output.insert(output.end(), start, end);
|
||||
}
|
||||
|
||||
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) {
|
||||
for (const auto &input_elem : input) {
|
||||
auto it_self = this->find(input_elem.first);
|
||||
if (it_self == this->end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
it_self->second = input_elem.second;
|
||||
}
|
||||
}
|
||||
|
||||
auto poet::Field::As2DVector() const -> std::vector<poet::FieldColumn> {
|
||||
std::vector<poet::FieldColumn> output;
|
||||
output.reserve(this->size());
|
||||
|
||||
for (const auto &elem : props) {
|
||||
const auto map_it = this->find(elem);
|
||||
output.push_back(map_it->second);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
poet::FieldColumn &poet::Field::operator[](const std::string &key) {
|
||||
if (this->find(key) == this->end()) {
|
||||
props.push_back(key);
|
||||
}
|
||||
|
||||
return std::unordered_map<std::string, FieldColumn>::operator[](key);
|
||||
}
|
||||
11
test/CMakeLists.txt
Normal file
11
test/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
file(GLOB test_SRC
|
||||
CONFIGURE_DEPENDS
|
||||
"*.cpp" "*.c")
|
||||
|
||||
add_executable(testPOET ${test_SRC})
|
||||
target_link_libraries(testPOET doctest poet_lib)
|
||||
|
||||
add_custom_target(check
|
||||
COMMAND $<TARGET_FILE:testPOET>
|
||||
DEPENDS testPOET
|
||||
)
|
||||
2
test/setup.cpp
Normal file
2
test/setup.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
175
test/testDataStructures.cpp
Normal file
175
test/testDataStructures.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <doctest/doctest.h>
|
||||
#include <iostream>
|
||||
#include <poet/Field.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace poet;
|
||||
|
||||
#define CHECK_AND_FAIL_LOOP(val1, val2) \
|
||||
if (val1 != val2) { \
|
||||
FAIL_CHECK("Value differs: ", val1, " != ", val2); \
|
||||
}
|
||||
|
||||
TEST_CASE("Field") {
|
||||
constexpr uint32_t VEC_SIZE = 5;
|
||||
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));
|
||||
|
||||
SUBCASE("Initialize field with 2D vector") {
|
||||
dut.InitFromVec(init_values, names);
|
||||
|
||||
auto props = dut.GetProps();
|
||||
|
||||
CHECK_EQ(names.size(), props.size());
|
||||
|
||||
const auto res = dut["C"];
|
||||
|
||||
CHECK_EQ(res.size(), VEC_SIZE);
|
||||
|
||||
for (const auto &elem : res) {
|
||||
CHECK_AND_FAIL_LOOP(elem, INIT_VAL);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Initialize field with 2D vector") {
|
||||
std::vector<double> init_values(VEC_SIZE * VEC_COUNT, 1);
|
||||
dut.InitFromVec(init_values, names);
|
||||
|
||||
auto props = dut.GetProps();
|
||||
|
||||
CHECK_EQ(names.size(), props.size());
|
||||
|
||||
const auto res = dut["C"];
|
||||
|
||||
CHECK_EQ(res.size(), VEC_SIZE);
|
||||
|
||||
for (const auto &elem : res) {
|
||||
CHECK_AND_FAIL_LOOP(elem, INIT_VAL);
|
||||
}
|
||||
}
|
||||
|
||||
dut.InitFromVec(init_values, names);
|
||||
|
||||
SUBCASE("Return vector") {
|
||||
std::vector<double> output = dut.AsVector();
|
||||
|
||||
CHECK(output.size() == VEC_SIZE * VEC_COUNT);
|
||||
}
|
||||
|
||||
constexpr double NEW_VAL = 2.;
|
||||
std::vector<double> new_val_vec(VEC_SIZE, NEW_VAL);
|
||||
|
||||
dut["C"] = new_val_vec;
|
||||
|
||||
SUBCASE("Check manipulation of column") {
|
||||
|
||||
auto test_it = new_val_vec.begin();
|
||||
|
||||
for (const auto &val : dut["C"]) {
|
||||
CHECK_EQ(val, *test_it);
|
||||
test_it++;
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Check correctly manipulated values of 1D vector") {
|
||||
auto out_res = dut.AsVector();
|
||||
auto out_it = out_res.begin();
|
||||
|
||||
for (uint32_t i = 0; i < VEC_SIZE; i++, out_it++) {
|
||||
CHECK_AND_FAIL_LOOP(NEW_VAL, *out_it);
|
||||
}
|
||||
|
||||
for (; out_it != out_res.end(); out_it++) {
|
||||
CHECK_AND_FAIL_LOOP(INIT_VAL, *out_it);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> new_field(VEC_SIZE * VEC_COUNT);
|
||||
|
||||
for (uint32_t i = 0; i < VEC_COUNT; i++) {
|
||||
for (uint32_t j = 0; j < VEC_SIZE; j++) {
|
||||
new_field[j + (i * VEC_SIZE)] = (double)(i + 1) / (double)(j + 1);
|
||||
}
|
||||
}
|
||||
|
||||
dut.SetFromVector(new_field);
|
||||
|
||||
SUBCASE("SetFromVector return correct field vector") {
|
||||
auto ret_vec = dut.AsVector();
|
||||
|
||||
auto ret_it = ret_vec.begin();
|
||||
auto new_it = new_field.begin();
|
||||
|
||||
for (; ret_it != ret_vec.end(); ret_it++, new_it++) {
|
||||
CHECK_AND_FAIL_LOOP(*ret_it, *new_it);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Get single column with new values") {
|
||||
auto new_it = new_field.begin() + 2 * VEC_SIZE;
|
||||
auto ret_vec = dut["Na"];
|
||||
auto ret_it = ret_vec.begin();
|
||||
|
||||
CHECK_EQ(ret_vec.size(), VEC_SIZE);
|
||||
|
||||
for (; ret_it != ret_vec.end(); ret_it++, new_it++) {
|
||||
CHECK_AND_FAIL_LOOP(*ret_it, *new_it);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> mg_vec = {1, 2, 3, 4, 5};
|
||||
dut["Mg"] = mg_vec;
|
||||
|
||||
SUBCASE("Operator creates new element and places it at the end") {
|
||||
|
||||
auto new_props = dut.GetProps();
|
||||
|
||||
CHECK_EQ(new_props.size(), 4);
|
||||
CHECK_EQ(new_props[3], "Mg");
|
||||
|
||||
auto ret_vec = dut.As2DVector();
|
||||
auto mg_vec_it = mg_vec.begin();
|
||||
|
||||
for (const auto &val : ret_vec[3]) {
|
||||
CHECK_AND_FAIL_LOOP(val, *(mg_vec_it++));
|
||||
}
|
||||
}
|
||||
|
||||
// reset field
|
||||
names = dut.GetProps();
|
||||
dut.SetFromVector(
|
||||
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);
|
||||
|
||||
SUBCASE("Update existing field from another field") {
|
||||
dut.UpdateFromField(some_other_field);
|
||||
|
||||
auto ret_vec = dut.As2DVector();
|
||||
auto ret_it = ret_vec.begin();
|
||||
|
||||
for (const auto &prop : names) {
|
||||
const auto &curr_vec = *(ret_it++);
|
||||
|
||||
for (const auto &val : curr_vec) {
|
||||
CHECK_AND_FAIL_LOOP((prop == "Na" ? SOME_OTHER_VAL : INIT_VAL), val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user