From e1a540a71e82c1edd64ab407590f0aa982564b61 Mon Sep 17 00:00:00 2001 From: Menno Fraters Date: Sat, 26 Oct 2024 14:42:01 +0200 Subject: [PATCH 1/8] Add properties function to wrapper_c --- include/world_builder/wrapper_c.h | 92 ++++++++++++++++++++++ source/world_builder/wrapper_c.cc | 123 ++++++++++++++++++++++-------- 2 files changed, 182 insertions(+), 33 deletions(-) diff --git a/include/world_builder/wrapper_c.h b/include/world_builder/wrapper_c.h index e1598ab66..d7386b867 100644 --- a/include/world_builder/wrapper_c.h +++ b/include/world_builder/wrapper_c.h @@ -33,6 +33,98 @@ extern "C" { */ void create_world(void **ptr_ptr_world, const char *world_builder_file, const bool *has_output_dir, const char *output_dir, const unsigned long random_number_seed); + +/** + * @brief This function returns 2D properties. + * + * The properties input decides what each entry means, and the output is generated in the + * same order as the properties input. The properties input consists of + * a 3D array, where the first entry identifies the property and the last two entries + * provide extra information about that property. + * + * Temperature is identified by 1 and no extra information is needed. So temperature + * input usually looks like {1,0,0}. A temperature query prodoces one entry in the output + * vector. + * + * Composition is identified by 2. This produces one + * value in the output. The second entry identifies the composition number and the third + * number is not used. So a composition query asking about composition 1 looks like this: + * {2,1,0}. A composition query prodoces one entry in the output vector. + * + * Grains are identified by 3. The second entry is the grain composition number and the third + * entry is the number of grains. A query about the grains, where it asks about composition 1 + * (for example enstatite) and 500 grains, looks like this: {3,1,500}. + * A composition query prodoces n_grains*10 entries in the output vector. The first n_grains + * entries are the sizes of all the grains, and the other 9 entries are sets of rotation + * matrices. The rotation matrix entries are ordered [0][0],[0][1],[0][2],[1][0],[1][1],etc. + * + * The tag is identified by 4 and no extra information is needed. So the tag + * input usually looks like {4,0,0}. A tag query prodoces one entry in the output + * vector, representing the index of the tag of the last/dominant feature. + * + * @param ptr_ptr_world a pointer to the world + * @param x The x position of the point + * @param z The z position of the point + * @param depth The depth of the point + * @param properties an array of properties, which each property is an array of three integers. + * @param n_properties number of properties. + * @param values are the return values as a pointer to an array of doubles + */ +void properties_2d(void *ptr_ptr_world, + const double x, + const double z, + const double depth, + const unsigned int properties[][3], + const unsigned int n_properties, + double values[]); + + +/** + * @brief This function returns 3D properties. + * + * The properties input decides what each entry means, and the output is generated in the + * same order as the properties input. The properties input consists of + * a 3D array, where the first entry identifies the property and the last two entries + * provide extra information about that property. + * + * Temperature is identified by 1 and no extra information is needed. So temperature + * input usually looks like {1,0,0}. A temperature query prodoces one entry in the output + * vector. + * + * Composition is identified by 2. This produces one + * value in the output. The second entry identifies the composition number and the third + * number is not used. So a composition query asking about composition 1 looks like this: + * {2,1,0}. A composition query prodoces one entry in the output vector. + * + * Grains are identified by 3. The second entry is the grain composition number and the third + * entry is the number of grains. A query about the grains, where it asks about composition 1 + * (for example enstatite) and 500 grains, looks like this: {3,1,500}. + * A composition query prodoces n_grains*10 entries in the output vector. The first n_grains + * entries are the sizes of all the grains, and the other 9 entries are sets of rotation + * matrices. The rotation matrix entries are ordered [0][0],[0][1],[0][2],[1][0],[1][1],etc. + * + * The tag is identified by 4 and no extra information is needed. So the tag + * input usually looks like {4,0,0}. A tag query prodoces one entry in the output + * vector, representing the index of the tag of the last/dominant feature. + * + * @param ptr_ptr_world a pointer to the world + * @param x The x position of the point + * @param x The y position of the point + * @param z The z position of the point + * @param depth The depth of the point + * @param properties an array of properties, which each property is an array of three integers. + * @param n_properties number of properties. + * @param values are the return values as a pointer to an array of doubles + */ +void properties_3d(void *ptr_ptr_world, + const double x, + const double y, + const double z, + const double depth, + const unsigned int properties[][3], + const unsigned int n_properties, + double values[]); + /** * This function return the temperature at a specific location given x, z, depth and * gravity. diff --git a/source/world_builder/wrapper_c.cc b/source/world_builder/wrapper_c.cc index e0b95e6e4..be37bc651 100644 --- a/source/world_builder/wrapper_c.cc +++ b/source/world_builder/wrapper_c.cc @@ -19,7 +19,9 @@ #include "world_builder/wrapper_c.h" +#include "world_builder/assert.h" #include "world_builder/world.h" +#include extern "C" { /** @@ -27,7 +29,9 @@ extern "C" { * to it. This pointer can then be used to call the temperature and composition * functions. When done call the release world function to destroy the object. */ - void create_world(void **ptr_ptr_world, const char *world_builder_file, const bool *has_output_dir_, const char *output_dir_, const unsigned long random_number_seed) + void create_world(void **ptr_ptr_world, const char *world_builder_file, + const bool *has_output_dir_, const char *output_dir_, + const unsigned long random_number_seed) { bool has_output_dir = false; @@ -42,67 +46,120 @@ extern "C" { output_dir = *output_dir_; } - WorldBuilder::World *a = new WorldBuilder::World(std::string(world_builder_file), has_output_dir, output_dir,random_number_seed); + WorldBuilder::World *a = + new WorldBuilder::World(std::string(world_builder_file), has_output_dir, + output_dir, random_number_seed); *ptr_ptr_world = reinterpret_cast(a); } + void properties_2d(void *ptr_ptr_world, const double x, const double z, + const double depth, const unsigned int properties_[][3], + const unsigned int n_properties, double values[]) + { + WorldBuilder::World *a = + reinterpret_cast(ptr_ptr_world); + const std::array position = {{x, z}}; + std::vector> properties(n_properties); + for (size_t i = 0; i < n_properties; ++i) + { + properties[i][0] = properties_[i][0]; + properties[i][1] = properties_[i][1]; + properties[i][2] = properties_[i][2]; + } + std::vector returned_values = a->properties(position, depth, properties); + for (unsigned i = 0; i < returned_values.size(); ++i) + { + values[i] = returned_values[i]; + } + } - /** - * This function return the temperature at a specific location given x, z, depth and - * gravity. - */ - void temperature_2d(void *ptr_ptr_world, double x, double z, double depth, double *temperature) + void properties_3d(void *ptr_ptr_world, + const double x, + const double y, + const double z, + const double depth, + const unsigned int properties_[][3], + const unsigned int n_properties, + double values[]) { WorldBuilder::World *a = reinterpret_cast(ptr_ptr_world); - const std::array position = {{x,z}}; - *temperature = a->temperature(position,depth); + const std::array position = {{x,y,z}}; + std::vector> properties(n_properties); + for (size_t i = 0; i < n_properties; ++i) + { + properties[i][0] = properties_[i][0]; + properties[i][1] = properties_[i][1]; + properties[i][2] = properties_[i][2]; + } + std::vector returned_values = a->properties(position, depth, properties); + for (unsigned i = 0; i < returned_values.size(); ++i) + { + values[i] = returned_values[i]; + } } - /** - * This function return the temperature at a specific location given x, y, z, depth and - * gravity. + * This function return the temperature at a specific location given x, z, depth + * and gravity. */ - void temperature_3d(void *ptr_ptr_world, double x, double y, double z, double depth, double *temperature) + void temperature_2d(void *ptr_ptr_world, double x, double z, double depth, + double *temperature) { - WorldBuilder::World *a = reinterpret_cast(ptr_ptr_world); - const std::array position = {{x,y,z}}; - *temperature = a->temperature(position,depth); + WorldBuilder::World *a = + reinterpret_cast(ptr_ptr_world); + const std::array position = {{x, z}}; + *temperature = a->temperature(position, depth); } - /** - * This function return the composition at a specific location given x, z, depth and - * composition number. + * This function return the temperature at a specific location given x, y, z, + * depth and gravity. */ - void composition_2d(void *ptr_ptr_world, double x, double z, double depth, unsigned int composition_number, double *composition) + void temperature_3d(void *ptr_ptr_world, double x, double y, double z, + double depth, double *temperature) { - WorldBuilder::World *a = reinterpret_cast(ptr_ptr_world); - const std::array position = {{x,z}}; - *composition = a->composition(position,depth,composition_number); + WorldBuilder::World *a = + reinterpret_cast(ptr_ptr_world); + const std::array position = {{x, y, z}}; + *temperature = a->temperature(position, depth); } - /** - * This function return the composition at a specific location given x, y, z, depth and - * composition number. + * This function return the composition at a specific location given x, z, depth + * and composition number. */ - void composition_3d(void *ptr_ptr_world, double x, double y, double z, double depth, unsigned int composition_number, double *composition) + void composition_2d(void *ptr_ptr_world, double x, double z, double depth, + unsigned int composition_number, double *composition) { - WorldBuilder::World *a = reinterpret_cast(ptr_ptr_world); - const std::array position = {{x,y,z}}; - *composition = a->composition(position,depth,composition_number); + WorldBuilder::World *a = + reinterpret_cast(ptr_ptr_world); + const std::array position = {{x, z}}; + *composition = a->composition(position, depth, composition_number); } + /** + * This function return the composition at a specific location given x, y, z, + * depth and composition number. + */ + void composition_3d(void *ptr_ptr_world, double x, double y, double z, + double depth, unsigned int composition_number, + double *composition) + { + WorldBuilder::World *a = + reinterpret_cast(ptr_ptr_world); + const std::array position = {{x, y, z}}; + *composition = a->composition(position, depth, composition_number); + } /** - * The destructor for the world builder class. Call this function when done with the - * world builder. + * The destructor for the world builder class. Call this function when done with + * the world builder. */ void release_world(void *ptr_ptr_world) { - WorldBuilder::World *a = reinterpret_cast(ptr_ptr_world); + WorldBuilder::World *a = + reinterpret_cast(ptr_ptr_world); delete a; } } From 61d22e8a8816a50f908a0572ff8f8846c77682ca Mon Sep 17 00:00:00 2001 From: Menno Fraters Date: Sat, 26 Oct 2024 14:42:35 +0200 Subject: [PATCH 2/8] Add unit tests for wrapper c. --- tests/unit_tests/unit_test_world_builder.cc | 68 +++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/unit_tests/unit_test_world_builder.cc b/tests/unit_tests/unit_test_world_builder.cc index 408d39b56..da285d730 100644 --- a/tests/unit_tests/unit_test_world_builder.cc +++ b/tests/unit_tests/unit_test_world_builder.cc @@ -737,24 +737,78 @@ TEST_CASE("WorldBuilder C wrapper") temperature_2d(*ptr_ptr_world, 1, 2, 0, &temperature); approval_tests.emplace_back(temperature); + { + unsigned int properties[1][3] = {{1,0,0}}; + double values[1]; + properties_2d(*ptr_ptr_world, 1, 2, 0, properties, 1, values); + CHECK(temperature == Approx(values[0])); + } + temperature_3d(*ptr_ptr_world, 1, 2, 3, 0, &temperature); approval_tests.emplace_back(temperature); + { + unsigned int properties[1][3] = {{1,0,0}}; + double values[1]; + properties_3d(*ptr_ptr_world, 1, 2,3, 0, properties, 1, values); + CHECK(temperature == Approx(values[0])); + } + temperature_2d(*ptr_ptr_world, 550e3, 0, 0, &temperature); approval_tests.emplace_back(temperature); + { + unsigned int properties[1][3] = {{1,0,0}}; + double values[1]; + properties_2d(*ptr_ptr_world, 550e3, 2, 0, properties, 1, values); + CHECK(temperature == Approx(values[0])); + } + temperature_3d(*ptr_ptr_world, 120e3, 500e3, 0, 0, &temperature); approval_tests.emplace_back(temperature); + { + unsigned int properties[1][3] = {{1,0,0}}; + double values[1]; + properties_3d(*ptr_ptr_world, 120e3, 500e3, 2, 0, properties, 1, values); + CHECK(temperature == Approx(values[0])); + } // Test the compositions double composition = 0.0; composition_2d(*ptr_ptr_world, 1, 2, 0, 2, &composition); approval_tests.emplace_back(composition); + { + unsigned int properties[1][3] = {{2,2,0}}; + double values[1]; + properties_2d(*ptr_ptr_world, 1, 2, 0, properties, 1, values); + CHECK(composition == Approx(values[0])); + } + composition_3d(*ptr_ptr_world, 1, 2, 3, 0, 2, &composition); approval_tests.emplace_back(composition); + { + unsigned int properties[1][3] = {{2,2,0}}; + double values[1]; + properties_3d(*ptr_ptr_world, 1, 2,3, 0, properties, 1, values); + CHECK(composition == Approx(values[0])); + } + composition_2d(*ptr_ptr_world, 550e3, 0, 0, 3, &composition); approval_tests.emplace_back(composition); + { + unsigned int properties[1][3] = {{2,3,0}}; + double values[1]; + properties_2d(*ptr_ptr_world, 550e3, 2, 0, properties, 1, values); + CHECK(composition == Approx(values[0])); + } + composition_3d(*ptr_ptr_world, 120e3, 500e3, 0, 0, 3, &composition); approval_tests.emplace_back(composition); + { + unsigned int properties[1][3] = {{2,3,0}}; + double values[1]; + properties_3d(*ptr_ptr_world, 120e3, 500e3,3, 0, properties, 1, values); + CHECK(composition == Approx(values[0])); + } release_world(*ptr_ptr_world); @@ -773,8 +827,15 @@ TEST_CASE("WorldBuilder C wrapper") "variable in the world builder file has been set. Dim is 3.")); temperature_3d(*ptr_ptr_world, 1, 2, 3, 0, &temperature); approval_tests.emplace_back(temperature); + temperature_3d(*ptr_ptr_world, 120e3, 500e3, 0, 0, &temperature); approval_tests.emplace_back(temperature); + { + unsigned int properties[1][3] = {{1,0,0}}; + double values[1]; + properties_3d(*ptr_ptr_world, 120e3, 500e3, 3, 0, properties, 1, values); + CHECK(temperature == Approx(values[0])); + } // Test the compositions CHECK_THROWS_WITH(composition_2d(*ptr_ptr_world, 1, 2, 0, 2, &composition), @@ -783,8 +844,15 @@ TEST_CASE("WorldBuilder C wrapper") composition_3d(*ptr_ptr_world, 1, 2, 3, 0, 2, &composition); approval_tests.emplace_back(composition); + composition_3d(*ptr_ptr_world, 120e3, 500e3, 0, 0, 3, &composition); approval_tests.emplace_back(composition); + { + unsigned int properties[1][3] = {{2,3,0}}; + double values[1]; + properties_3d(*ptr_ptr_world, 120e3, 500e3,3, 0, properties, 1, values); + CHECK(composition == Approx(values[0])); + } release_world(*ptr_ptr_world); ApprovalTests::Approvals::verifyAll("TITLE", approval_tests); From f8e37e3de31e68bb74890acbf35cf00e47939298 Mon Sep 17 00:00:00 2001 From: Menno Fraters Date: Sat, 26 Oct 2024 14:46:55 +0200 Subject: [PATCH 3/8] Add properties functions to c example test. --- tests/C/example.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/C/example.c b/tests/C/example.c index 65d1ea3d7..beb0c1a9d 100644 --- a/tests/C/example.c +++ b/tests/C/example.c @@ -30,6 +30,7 @@ int main(int argc, char *argv[]) { create_world(&ptr_world, argv[1], &has_output_dir, output_dir, random_number_seed); + // simple specific interfaces printf("2d temperature: \n"); temperature_2d(ptr_world,x,z,depth,&temperature); printf("temperature in C = %f \n", temperature); @@ -46,6 +47,20 @@ int main(int argc, char *argv[]) { composition_3d(ptr_world,x,y,z,depth,composition_number,&composition); printf("composition in C = %f \n", composition); + // High performance generic interface for getting many properties at the same time + unsigned int properties[6][3] = { + {1,0,0}, // temmperature + {2,0,0}, // composition 0 + {2,1,0}, // composition 1 + {3,0,10}, // grain compositon 0, 10 grains + {4,0,0}, // tag + {5,0,0} // velocity (3 values) + }; + double values[1]; + printf("2d temperature: \n"); + properties_2d(ptr_world,x,z,depth,properties,1,values); + printf("temperature in C = %f \n", values[0]); + release_world(ptr_world); return 0; From 7f22e5fe62193ba802a3aa53c9f0cc2012a9a9e5 Mon Sep 17 00:00:00 2001 From: Menno Fraters Date: Sat, 26 Oct 2024 23:20:13 +0200 Subject: [PATCH 4/8] Add properties_output_size function to world.cc/world.h --- include/world_builder/world.h | 9 ++++++++ source/world_builder/world.cc | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/world_builder/world.h b/include/world_builder/world.h index 85f0a6155..056023f61 100644 --- a/include/world_builder/world.h +++ b/include/world_builder/world.h @@ -78,6 +78,15 @@ namespace WorldBuilder */ void parse_entries(Parameters &prm); + /** + * Return the size of the output vector returned by the properties function for a given properties vector. + * + * @param properties The properties parameter from the properties function. See the documentation of that + * function for more info. + * @return unsigned int Return the size of the output vector returned by the properties function for a given properties vector. + */ + unsigned int properties_output_size(const std::vector> &properties) const; + /** * Returns different values at a single point in one go stored in a vector of doubles. * diff --git a/source/world_builder/world.cc b/source/world_builder/world.cc index 910b8973e..5bfd422f4 100644 --- a/source/world_builder/world.cc +++ b/source/world_builder/world.cc @@ -270,6 +270,47 @@ namespace WorldBuilder + unsigned int + World::properties_output_size(const std::vector> &properties) const + { + unsigned int n_output_entries = 0; + for (auto property : properties) + { + switch (property[0]) + { + case 1: // Temperature + n_output_entries += 1; + break; + case 2: // composition + n_output_entries += 1; + break; + case 3: // grains (10 entries per grain) + { + n_output_entries += property[2]*10; + break; + } + case 4: // tag + { + n_output_entries += 1; + break; + } + case 5: // velocity (3 entries) + { + n_output_entries += 3; + break; + } + default: + WBAssertThrow(false, + "Internal error: Unimplemented property provided. " << + "Only temperature (1), composition (2), grains (3), tag (4) or velocity (5) are allowed. " + "Provided property number was: " << property[0]); + } + } + return n_output_entries; + } + + + std::vector World::properties(const std::array &point, const double depth, From 51c38b041685b8ab0cff96e6a60a6646fc10dd15 Mon Sep 17 00:00:00 2001 From: Menno Fraters Date: Sat, 26 Oct 2024 23:20:44 +0200 Subject: [PATCH 5/8] Add unit tests for properties_output_size function. --- tests/unit_tests/unit_test_world_builder.cc | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/unit_tests/unit_test_world_builder.cc b/tests/unit_tests/unit_test_world_builder.cc index da285d730..dfd56e54f 100644 --- a/tests/unit_tests/unit_test_world_builder.cc +++ b/tests/unit_tests/unit_test_world_builder.cc @@ -735,12 +735,19 @@ TEST_CASE("WorldBuilder C wrapper") double temperature = 0.; + { + unsigned int properties[1][3] = {{100,0,0}}; + CHECK_THROWS_WITH(properties_output_size(*ptr_ptr_world, properties,1), + Contains("Unimplemented property provided.")); + } + temperature_2d(*ptr_ptr_world, 1, 2, 0, &temperature); approval_tests.emplace_back(temperature); { unsigned int properties[1][3] = {{1,0,0}}; double values[1]; properties_2d(*ptr_ptr_world, 1, 2, 0, properties, 1, values); + CHECK(properties_output_size(ptr_ptr_world, properties, 1) == 1); CHECK(temperature == Approx(values[0])); } @@ -750,6 +757,7 @@ TEST_CASE("WorldBuilder C wrapper") unsigned int properties[1][3] = {{1,0,0}}; double values[1]; properties_3d(*ptr_ptr_world, 1, 2,3, 0, properties, 1, values); + CHECK(properties_output_size(ptr_ptr_world, properties, 1) == 1); CHECK(temperature == Approx(values[0])); } @@ -759,6 +767,7 @@ TEST_CASE("WorldBuilder C wrapper") unsigned int properties[1][3] = {{1,0,0}}; double values[1]; properties_2d(*ptr_ptr_world, 550e3, 2, 0, properties, 1, values); + CHECK(properties_output_size(ptr_ptr_world, properties, 1) == 1); CHECK(temperature == Approx(values[0])); } @@ -768,6 +777,7 @@ TEST_CASE("WorldBuilder C wrapper") unsigned int properties[1][3] = {{1,0,0}}; double values[1]; properties_3d(*ptr_ptr_world, 120e3, 500e3, 2, 0, properties, 1, values); + CHECK(properties_output_size(ptr_ptr_world, properties, 1) == 1); CHECK(temperature == Approx(values[0])); } @@ -780,6 +790,7 @@ TEST_CASE("WorldBuilder C wrapper") unsigned int properties[1][3] = {{2,2,0}}; double values[1]; properties_2d(*ptr_ptr_world, 1, 2, 0, properties, 1, values); + CHECK(properties_output_size(ptr_ptr_world, properties, 1) == 1); CHECK(composition == Approx(values[0])); } @@ -789,6 +800,7 @@ TEST_CASE("WorldBuilder C wrapper") unsigned int properties[1][3] = {{2,2,0}}; double values[1]; properties_3d(*ptr_ptr_world, 1, 2,3, 0, properties, 1, values); + CHECK(properties_output_size(ptr_ptr_world, properties, 1) == 1); CHECK(composition == Approx(values[0])); } @@ -798,6 +810,7 @@ TEST_CASE("WorldBuilder C wrapper") unsigned int properties[1][3] = {{2,3,0}}; double values[1]; properties_2d(*ptr_ptr_world, 550e3, 2, 0, properties, 1, values); + CHECK(properties_output_size(ptr_ptr_world, properties, 1) == 1); CHECK(composition == Approx(values[0])); } @@ -807,6 +820,7 @@ TEST_CASE("WorldBuilder C wrapper") unsigned int properties[1][3] = {{2,3,0}}; double values[1]; properties_3d(*ptr_ptr_world, 120e3, 500e3,3, 0, properties, 1, values); + CHECK(properties_output_size(ptr_ptr_world, properties, 1) == 1); CHECK(composition == Approx(values[0])); } @@ -825,6 +839,7 @@ TEST_CASE("WorldBuilder C wrapper") CHECK_THROWS_WITH(temperature_2d(*ptr_ptr_world, 1, 2, 0, &temperature), Contains("This function can only be called when the cross section " "variable in the world builder file has been set. Dim is 3.")); + temperature_3d(*ptr_ptr_world, 1, 2, 3, 0, &temperature); approval_tests.emplace_back(temperature); @@ -929,6 +944,36 @@ TEST_CASE("WorldBuilder interface") CHECK_THROWS_WITH(world.properties({{1,2,3}},1., {{{{10,0,0}}}}),Contains("Unimplemented property provided. Only ")); CHECK_THROWS_WITH(world.properties(std::array({{1,2}}),1., {{{{10,0,0}}}}),Contains("Unimplemented property provided. Only ")); + std::vector> properties = {{{1,0,0}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + + properties = {{{2,0,0}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + + properties = {{{2,1,0}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + + properties = {{{3,0,10}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + + properties = {{{3,1,20}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + + properties = {{{4,0,0}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + + properties = {{{5,0,0}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + + properties = {{{{1,0,0}},{{5,0,0}}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + + properties = {{{{2,1,0}},{{3,1,15}}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + + properties = {{{{1,0,0}},{{2,0,0}},{{2,1,0}},{{3,0,15}},{{3,1,15}},{{4,0,0}},{{5,0,0}}}}; + CHECK(world.properties_output_size(properties) == world.properties({{1,2,3}},1., properties).size()); + approval_tests_grains.emplace_back(world.grains(std::array {{750e3,250e3,100e3}},10e3,0,3)); approval_tests_grains.emplace_back(world.grains(std::array {{750e3,100e3}},10e3,0,3)); ApprovalTests::Approvals::verifyAll("TITLE", approval_tests_grains); From 5e4144ccffadc36df1e6796d3e848cf1cbb15588 Mon Sep 17 00:00:00 2001 From: Menno Fraters Date: Sat, 26 Oct 2024 23:21:13 +0200 Subject: [PATCH 6/8] Add properties_output_size function to wrapper c --- include/world_builder/wrapper_c.h | 13 +++++++++++++ source/world_builder/wrapper_c.cc | 15 +++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/include/world_builder/wrapper_c.h b/include/world_builder/wrapper_c.h index d7386b867..001457469 100644 --- a/include/world_builder/wrapper_c.h +++ b/include/world_builder/wrapper_c.h @@ -34,6 +34,19 @@ extern "C" { void create_world(void **ptr_ptr_world, const char *world_builder_file, const bool *has_output_dir, const char *output_dir, const unsigned long random_number_seed); + +/** + * Return the size of the output vector returned by the properties function for a given properties vector. + * + * @param properties The properties parameter from the properties function. See the documentation of that + * function for more info. + * @param n_properties number of properties + * @return unsigned int Return the size of the output vector returned by the properties function for a given properties vector. + */ +unsigned int properties_output_size(void *ptr_ptr_world, + const unsigned int properties[][3], + const unsigned int n_properties); + /** * @brief This function returns 2D properties. * diff --git a/source/world_builder/wrapper_c.cc b/source/world_builder/wrapper_c.cc index be37bc651..70c794376 100644 --- a/source/world_builder/wrapper_c.cc +++ b/source/world_builder/wrapper_c.cc @@ -53,6 +53,21 @@ extern "C" { *ptr_ptr_world = reinterpret_cast(a); } + unsigned int properties_output_size(void *ptr_ptr_world,const unsigned int properties_[][3], + const unsigned int n_properties) + { + WorldBuilder::World *a = + reinterpret_cast(ptr_ptr_world); + std::vector> properties(n_properties); + for (size_t i = 0; i < n_properties; ++i) + { + properties[i][0] = properties_[i][0]; + properties[i][1] = properties_[i][1]; + properties[i][2] = properties_[i][2]; + } + return a->properties_output_size(properties); + } + void properties_2d(void *ptr_ptr_world, const double x, const double z, const double depth, const unsigned int properties_[][3], const unsigned int n_properties, double values[]) From 79f556a9a631172f4174951529c1125fd619e5f3 Mon Sep 17 00:00:00 2001 From: Menno Fraters Date: Sun, 27 Oct 2024 00:29:09 +0200 Subject: [PATCH 7/8] add properties to run_simple_C_example. --- tests/C/example.c | 22 +++++++-- tests/C/run_simple_C_example.log | 78 ++++++++++++++++++++++++++++++++ tests/data/continental_plate.wb | 15 ++++-- 3 files changed, 107 insertions(+), 8 deletions(-) diff --git a/tests/C/example.c b/tests/C/example.c index beb0c1a9d..2e4b20a05 100644 --- a/tests/C/example.c +++ b/tests/C/example.c @@ -1,6 +1,7 @@ #include "world_builder/wrapper_c.h" #include +#include int main(int argc, char *argv[]) { // Declare the types which will be needed. @@ -52,14 +53,25 @@ int main(int argc, char *argv[]) { {1,0,0}, // temmperature {2,0,0}, // composition 0 {2,1,0}, // composition 1 - {3,0,10}, // grain compositon 0, 10 grains + {3,0,3}, // grain composition 0, 10 grains {4,0,0}, // tag {5,0,0} // velocity (3 values) }; - double values[1]; - printf("2d temperature: \n"); - properties_2d(ptr_world,x,z,depth,properties,1,values); - printf("temperature in C = %f \n", values[0]); + unsigned int n_properties_outputs = properties_output_size(ptr_world, properties, 6); + printf("\nn_properties_outputs = %i \n",n_properties_outputs); + double *values = (double*)malloc(sizeof(double)*n_properties_outputs); + printf("2d properties: \n"); + + properties_2d(ptr_world,x,z,depth,properties,6,values); + for(unsigned int i = 0; i < n_properties_outputs; ++i){ + printf("properties output %i = %f \n", i, values[i]); + } + + printf("3d properties: \n"); + properties_3d(ptr_world,x,y,z,depth,properties,6,values); + for(unsigned int i = 0; i < n_properties_outputs; ++i){ + printf("properties output %i = %f \n", i, values[i]); + } release_world(ptr_world); diff --git a/tests/C/run_simple_C_example.log b/tests/C/run_simple_C_example.log index 060b62594..ff3b62605 100644 --- a/tests/C/run_simple_C_example.log +++ b/tests/C/run_simple_C_example.log @@ -7,3 +7,81 @@ temperature in C = 150.000000 composition in C = 0.000000 3d composition: composition in C = 1.000000 + +n_properties_outputs = 37 +2d properties: +properties output 0 = 1600.000000 +properties output 1 = 0.000000 +properties output 2 = 0.000000 +properties output 3 = 0.000000 +properties output 4 = 0.000000 +properties output 5 = 0.000000 +properties output 6 = 0.000000 +properties output 7 = 0.000000 +properties output 8 = 0.000000 +properties output 9 = 0.000000 +properties output 10 = 0.000000 +properties output 11 = 0.000000 +properties output 12 = 0.000000 +properties output 13 = 0.000000 +properties output 14 = 0.000000 +properties output 15 = 0.000000 +properties output 16 = 0.000000 +properties output 17 = 0.000000 +properties output 18 = 0.000000 +properties output 19 = 0.000000 +properties output 20 = 0.000000 +properties output 21 = 0.000000 +properties output 22 = 0.000000 +properties output 23 = 0.000000 +properties output 24 = 0.000000 +properties output 25 = 0.000000 +properties output 26 = 0.000000 +properties output 27 = 0.000000 +properties output 28 = 0.000000 +properties output 29 = 0.000000 +properties output 30 = 0.000000 +properties output 31 = 0.000000 +properties output 32 = 0.000000 +properties output 33 = -1.000000 +properties output 34 = 0.000000 +properties output 35 = 0.000000 +properties output 36 = 0.000000 +3d properties: +properties output 0 = 150.000000 +properties output 1 = 0.000000 +properties output 2 = 0.000000 +properties output 3 = 0.333333 +properties output 4 = 0.333333 +properties output 5 = 0.333333 +properties output 6 = 1.000000 +properties output 7 = 2.000000 +properties output 8 = 3.000000 +properties output 9 = 4.000000 +properties output 10 = 5.000000 +properties output 11 = 6.000000 +properties output 12 = 7.000000 +properties output 13 = 8.000000 +properties output 14 = 9.000000 +properties output 15 = 1.000000 +properties output 16 = 2.000000 +properties output 17 = 3.000000 +properties output 18 = 4.000000 +properties output 19 = 5.000000 +properties output 20 = 6.000000 +properties output 21 = 7.000000 +properties output 22 = 8.000000 +properties output 23 = 9.000000 +properties output 24 = 1.000000 +properties output 25 = 2.000000 +properties output 26 = 3.000000 +properties output 27 = 4.000000 +properties output 28 = 5.000000 +properties output 29 = 6.000000 +properties output 30 = 7.000000 +properties output 31 = 8.000000 +properties output 32 = 9.000000 +properties output 33 = 0.000000 +properties output 34 = 1.200000 +properties output 35 = 2.300000 +properties output 36 = 3.400000 diff --git a/tests/data/continental_plate.wb b/tests/data/continental_plate.wb index 3084cbce8..c7550859b 100644 --- a/tests/data/continental_plate.wb +++ b/tests/data/continental_plate.wb @@ -8,7 +8,14 @@ {"model":"continental plate", "name":"First continental plate", "max depth":250e3, "coordinates":[[-1e3,500e3],[500e3,500e3],[500e3,1000e3],[-1e3,1000e3]], "temperature models":[{"model":"uniform", "max depth":75e3, "temperature":150}, {"model":"uniform", "min depth":75e3, "max depth":150e3, "temperature":100}, - {"model":"uniform", "min depth":150e3, "max depth":225e3, "temperature":50}]}, + {"model":"uniform", "min depth":150e3, "max depth":225e3, "temperature":50}], + "grains models":[ + {"model":"uniform", "compositions":[0,1], // olivine and enstatie + "rotation matrices":[[[11,21,31],[41,51,61],[71,81,91]],[[101,111,121],[131,141,151],[161,171,181]]], + "grain sizes":[0.3,-1]} + ], + "velocity models": [{"model": "uniform raw", "velocity": [1.1,2.2,3.3]}] +}, {"model":"continental plate", "name":"Second continental plate", "max depth":250e3, "coordinates":[[2000e3,2000e3],[1000e3,2000e3],[1000e3,1000e3],[2000e3,1000e3]], "temperature models":[{"model":"adiabatic", "potential mantle temperature":20}], @@ -17,7 +24,8 @@ {"model":"uniform", "compositions":[0,1], // olivine and enstatie "rotation matrices":[[[10,20,30],[40,50,60],[70,80,90]],[[100,110,120],[130,140,150],[160,170,180]]], "grain sizes":[0.3,-1]} -]}, + ] + }, {"model":"continental plate", "name":"Third continental plate", "max depth":250e3, "coordinates":[[-1e3,500e3],[500e3,500e3],[500e3,1000e3],[-1e3,1000e3]], "composition models":[{"model":"uniform", "compositions":[3]}], @@ -25,7 +33,8 @@ {"model":"uniform", "compositions":[0,1], // olivine and enstatie "rotation matrices":[[[1,2,3],[4,5,6],[7,8,9]],[[10,11,12],[13,14,15],[16,17,18]]], "grain sizes":[-1,0.2]} -]}, + ], + "velocity models": [{"model": "uniform raw", "velocity": [1.2,2.3,3.4]}]}, {"model":"continental plate", "name":"Fourth continental plate", "max depth":250e3, "coordinates":[[-1e3,1500e3],[500e3,1500e3],[500e3,2000e3],[-1e3,2000e3]], "temperature models":[{"model":"linear", "max depth":250e3}], From 8844806bc403bd2e446bf9a1b0139d066f0bda13 Mon Sep 17 00:00:00 2001 From: Menno Fraters Date: Sun, 27 Oct 2024 11:16:15 +0100 Subject: [PATCH 8/8] Add changelog entries for adding the properties_output_size function and adding the properties functions to the C-wrapper. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8cdc36cf..64114bb09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Added a velocities system where each feature can define a velocity field and the gwb can return the resulting velocity field. \[Menno Fraters; 2024-10-20; [#761](https://github.com/GeodynamicWorldBuilder/WorldBuilder/pull/761)\] - There is now a CMake variable to automatically update the reference files for failed tests (gdb-dat and gwb-grid tests) \[Menno Fraters; 2024-10-20; [#761](https://github.com/GeodynamicWorldBuilder/WorldBuilder/pull/761)\] +- There is now a properties_output_size function, which returns the size of the output vector/array returned by the properties functions for a given properties input vector \[Menno Fraters; 2024-10-27; [#765](https://github.com/GeodynamicWorldBuilder/WorldBuilder/pull/765)\] +- Added 2d and 3d versions of the properties function to the C wrapper \[Menno Fraters; 2024-10-27; [#765](https://github.com/GeodynamicWorldBuilder/WorldBuilder/pull/765)\] ## Changed