From 8dd856df3e89090eea3c5aa58978c4f5b78a8297 Mon Sep 17 00:00:00 2001 From: Anton Rygin Date: Sat, 13 Apr 2024 19:36:41 +0200 Subject: [PATCH] Catalyst Adaptor: Implementation and tests. --- CMakeLists.txt | 5 + src/CMakeLists.txt | 28 ++ src/Catalyst/catalyst_adaptor.hpp | 452 ++++++++++++++++++ src/Catalyst/catalyst_adaptor_grid_dist.hpp | 147 ++++++ src/Catalyst/catalyst_adaptor_vector_dist.hpp | 121 +++++ src/Catalyst/catalyst_unit_test.cpp | 199 ++++++++ test_data/catalyst_grid_dist_ground_truth.png | Bin 0 -> 821524 bytes test_data/catalyst_grid_dist_pipeline.py | 190 ++++++++ .../catalyst_vector_dist_ground_truth.png | Bin 0 -> 821524 bytes test_data/catalyst_vector_dist_pipeline.py | 188 ++++++++ 10 files changed, 1330 insertions(+) create mode 100644 src/Catalyst/catalyst_adaptor.hpp create mode 100644 src/Catalyst/catalyst_adaptor_grid_dist.hpp create mode 100644 src/Catalyst/catalyst_adaptor_vector_dist.hpp create mode 100644 src/Catalyst/catalyst_unit_test.cpp create mode 100644 test_data/catalyst_grid_dist_ground_truth.png create mode 100644 test_data/catalyst_grid_dist_pipeline.py create mode 100644 test_data/catalyst_vector_dist_ground_truth.png create mode 100644 test_data/catalyst_vector_dist_pipeline.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 691476a..e791f1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,10 @@ if(TINYOBJLOADER_FOUND) set(DEFINE_HAVE_TINYOBJLOADER "#define HAVE_TINYOBJLOADER 1") endif() +if(catalyst_FOUND) + set(DEFINE_HAVE_CATALYST "#define HAVE_CATALYST") +endif() + include_directories(SYSTEM ${MPI_INCLUDE_PATH}) add_subdirectory (src) @@ -23,5 +27,6 @@ get_directory_property(hasParent PARENT_DIRECTORY) if(hasParent) set(DEFINE_HAVE_TINYOBJLOADER ${DEFINE_HAVE_TINYOBJLOADER} CACHE INTERNAL "") set(DEFINE_HAVE_HDF5 ${DEFINE_HAVE_HDF5} CACHE INTERNAL "") + set(DEFINE_HAVE_CATALYST ${DEFINE_HAVE_CATALYST} CACHE INTERNAL "") endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f114444..d3416a0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,12 +21,14 @@ if ( CUDA_ON_BACKEND STREQUAL "HIP" AND HIP_FOUND ) hip_add_executable(io main.cpp MetaParser/MetaParser_unit_test.cpp + Catalyst/catalyst_unit_test.cpp ${CUDA_SOURCES} ObjReader/ObjReader_unit_test.cpp CSVReader/tests/CSVReader_unit_test.cpp) else() add_executable(io main.cpp MetaParser/MetaParser_unit_test.cpp + Catalyst/catalyst_unit_test.cpp ${CUDA_SOURCES} ObjReader/ObjReader_unit_test.cpp CSVReader/tests/CSVReader_unit_test.cpp) @@ -66,6 +68,7 @@ target_include_directories (io PUBLIC ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}) target_include_directories (io PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories (io PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../openfpm_devices/src/) target_include_directories (io PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../openfpm_vcluster/src/) +target_include_directories (io PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../openfpm_pdata/src/) target_include_directories (io PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../openfpm_data/src/) target_include_directories (io PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../src/) target_include_directories (io PUBLIC ${CMAKE_BINARY_DIR}/config) @@ -74,6 +77,26 @@ target_include_directories (io PUBLIC ${TINYOBJLOADER_INCLUDE_DIRS} ) target_include_directories (io PUBLIC ${Boost_INCLUDE_DIRS}) target_include_directories (io PUBLIC ${ALPAKA_ROOT}/include) +if (catalyst_FOUND) + target_link_libraries(io catalyst::catalyst) + + # libraries for Catalyst unit tests + target_include_directories (io PUBLIC ${METIS_ROOT}/include) + target_include_directories (io PUBLIC ${PARMETIS_ROOT}/include) + target_include_directories (io PUBLIC ${LIBHILBERT_INCLUDE_DIRS}) + target_link_libraries(io ${PARMETIS_LIBRARIES}) + + if (Vc_FOUND) + target_include_directories (io PUBLIC ${Vc_INCLUDE_DIR}) + target_link_libraries(io ${Vc_LIBRARIES}) + endif() + + if (PNG_FOUND) + target_include_directories (io PUBLIC ${PNG_INCLUDE_DIRS}) + target_link_libraries(io ${PNG_LIBRARIES}) + endif() +endif() + if (PETSC_FOUND) target_include_directories(io PUBLIC ${PETSC_INCLUDES}) endif() @@ -165,3 +188,8 @@ install(FILES Plot/GoogleChart.hpp Plot/util.hpp DESTINATION openfpm_io/include/Plot COMPONENT OpenFPM) +install(FILES Catalyst/catalyst_adaptor.hpp + Catalyst/catalyst_adaptor_grid_dist.hpp + Catalyst/catalyst_adaptor_vector_dist.hpp + DESTINATION openfpm_io/include/Catalyst + COMPONENT OpenFPM) diff --git a/src/Catalyst/catalyst_adaptor.hpp b/src/Catalyst/catalyst_adaptor.hpp new file mode 100644 index 0000000..cd2ddc6 --- /dev/null +++ b/src/Catalyst/catalyst_adaptor.hpp @@ -0,0 +1,452 @@ +#ifndef CATALYST_ADAPTOR_HPP_ +#define CATALYST_ADAPTOR_HPP_ + +#include "config.h" + +#ifdef HAVE_CATALYST +#include +#include + +/** + * @brief Template for setting properties of particles as fields in Counduit node, following Mesh Blueprint. + * + * @tparam T Reserved to pass dimensions of array/vector/tensor structures. + */ +template +struct set_prop_val +{ + /** + * @brief Generic function for property setter to Conduit node. + * + * @param fields Reference to "fields" sub-node in Conduit hierarchical node. + * @param prop_name Name of the property. + * @param prop_storage_row Pointer to the contiguous/interleaved storage of property values. + * @param num_elements Number of particles in simulation. + */ + static void set(conduit_cpp::Node &fields, const std::string &prop_name, void *prop_storage_row, size_t num_elements) + { + // Skip unsupported properties + } +}; + +/** + * @brief Pass scalar `double` property values to Conduit node. + */ +template <> +struct set_prop_val +{ + /** + * scalar `double` properties are saved into `prop_storage` as contiguous `double[n]` array, + * where `n` is the number of particles. + */ + static void set(conduit_cpp::Node &fields, const std::string &prop_name, double &prop_storage_row, size_t num_elements) + { + fields[prop_name + "/association"].set("vertex"); + fields[prop_name + "/topology"].set("mesh"); + fields[prop_name + "/volume_dependent"].set("false"); + fields[prop_name + "/values"].set_external((double *)&prop_storage_row, num_elements); + } +}; + +/** + * @brief Pass scalar `float` property values to Conduit node + */ +template <> +struct set_prop_val +{ + /** + * scalar `float` properties are saved into `prop_storage` as contiguous `float[n]` array, + * where n is the number of particles. + */ + static void set(conduit_cpp::Node &fields, const std::string &prop_name, float &prop_storage_row, size_t num_elements) + { + fields[prop_name + "/association"].set("vertex"); + fields[prop_name + "/topology"].set("mesh"); + fields[prop_name + "/volume_dependent"].set("false"); + fields[prop_name + "/values"].set_external((float *)&prop_storage_row, num_elements); + } +}; + +/** + * @brief Pass `double[2]`/`double[3]` property values to Conduit node. + */ +template +struct set_prop_val +{ + /** + * `double[3]` properties are saved into `prop_storage` as `multi_array[3]`. + * It consists of 3 pointers to contiguous `double[n]` arrays for x/y/z projections: + * `prop_storage_row[0] = [val_0.x, val_1.x, ..., val_n.x]`, + * `prop_storage_row[1] = [val_0.y, val_1.y, ..., val_n.y]`, + * `prop_storage_row[2] = [val_0.z, val_1.z, ..., val_n.z]`, + * where `val_i` is value of given property for i-th particle. + * + * Similarly for `double[2]`. + */ + template + static void set(conduit_cpp::Node &fields, const std::string &prop_name, multi_array prop_storage_row, size_t num_elements) + { + fields[prop_name + "/association"].set("vertex"); + fields[prop_name + "/topology"].set("mesh"); + fields[prop_name + "/volume_dependent"].set("false"); + if constexpr (N == 3) + { + fields[prop_name + "/values/x"].set_external((double *)&prop_storage_row[0], num_elements, /*offset=*/0, /*stride=*/sizeof(double)); + fields[prop_name + "/values/y"].set_external((double *)&prop_storage_row[1], num_elements, /*offset=*/0, /*stride=*/sizeof(double)); + fields[prop_name + "/values/z"].set_external((double *)&prop_storage_row[2], num_elements, /*offset=*/0, /*stride=*/sizeof(double)); + } + else if constexpr (N == 2) + { + fields[prop_name + "/values/x"].set_external((double *)&prop_storage_row[0], num_elements, /*offset=*/0, /*stride=*/sizeof(double)); + fields[prop_name + "/values/y"].set_external((double *)&prop_storage_row[1], num_elements, /*offset=*/0, /*stride=*/sizeof(double)); + } + } +}; + +/** + * @brief Pass `float[2]`/`float[3]` property values to Conduit node. + */ +template +struct set_prop_val +{ + /** + * `float[3]` properties are saved into `prop_storage` as `multi_array[3]`. + * It consists of 3 pointers to contiguous `float[n]` arrays for x/y/z projections: + * `prop_storage_row[0] = [val_0.x, val_1.x, ..., val_n.x]`, + * `prop_storage_row[1] = [val_0.y, val_1.y, ..., val_n.y]`, + * `prop_storage_row[2] = [val_0.z, val_1.z, ..., val_n.z]`, + * where `val_i` is value of given property for i-th particle. + * + * Similarly for `float[2]`. + */ + template + static void set(conduit_cpp::Node &fields, const std::string &prop_name, multi_array prop_storage_row, size_t num_elements) + { + fields[prop_name + "/association"].set("vertex"); + fields[prop_name + "/topology"].set("mesh"); + fields[prop_name + "/volume_dependent"].set("false"); + if constexpr (N == 3) + { + fields[prop_name + "/values/x"].set_external((float *)&prop_storage_row[0], num_elements, /*offset=*/0, /*stride=*/sizeof(float)); + fields[prop_name + "/values/y"].set_external((float *)&prop_storage_row[1], num_elements, /*offset=*/0, /*stride=*/sizeof(float)); + fields[prop_name + "/values/z"].set_external((float *)&prop_storage_row[2], num_elements, /*offset=*/0, /*stride=*/sizeof(float)); + } + else if constexpr (N == 2) + { + fields[prop_name + "/values/x"].set_external((float *)&prop_storage_row[0], num_elements, /*offset=*/0, /*stride=*/sizeof(float)); + fields[prop_name + "/values/y"].set_external((float *)&prop_storage_row[1], num_elements, /*offset=*/0, /*stride=*/sizeof(float)); + } + } +}; + +/** + * @brief Pass `VectorS<2 / 3, double>` property values to Conduit node. + */ +template +struct set_prop_val> +{ + /** + * `VectorS<3, double>` properties are saved into `prop_storage` as sequence of `Point<3, double>` objects. + * x/y/z projections are stored in interleaved fashion: + * `prop_storage_row = [val_0.x, val_0.y, val_0.z, val_1.x, val_1.y, val_1.z, ..., ]` + * where `val_i` is value of given property for i-th particle. + * Iterate over `prop_storage_row` with `stride = 3 * sizeof(double)` and proper offset to extract x/y/z projections. + + * Similarly for `VectorS<2, double>`. + */ + static void set(conduit_cpp::Node &fields, const std::string &prop_name, VectorS &prop_storage_row, size_t num_elements) + { + fields[prop_name + "/association"].set("vertex"); + fields[prop_name + "/topology"].set("mesh"); + fields[prop_name + "/volume_dependent"].set("false"); + if constexpr (N == 3) + { + fields[prop_name + "/values/x"].set_external((double *)&prop_storage_row[0], num_elements, /*offset=*/0, /*stride=*/3 * sizeof(double)); + fields[prop_name + "/values/y"].set_external((double *)&prop_storage_row[0], num_elements, /*offset=*/sizeof(double), /*stride=*/3 * sizeof(double)); + fields[prop_name + "/values/z"].set_external((double *)&prop_storage_row[0], num_elements, /*offset=*/2 * sizeof(double), /*stride=*/3 * sizeof(double)); + } + else if constexpr (N == 2) + { + fields[prop_name + "/values/x"].set_external((double *)&prop_storage_row[0], num_elements, /*offset=*/0, /*stride=*/2 * sizeof(double)); + fields[prop_name + "/values/y"].set_external((double *)&prop_storage_row[0], num_elements, /*offset=*/sizeof(double), /*stride=*/2 * sizeof(double)); + } + } +}; + +/** + * @brief Pass `VectorS<2 / 3, float>` property values to Conduit node. + */ +template +struct set_prop_val> +{ + /** + * `VectorS<3, float>` properties are saved into `prop_storage` as sequence of `Point<3, float>` objects. + * x/y/z projections are stored in interleaved fashion: + * `prop_storage_row = [val_0.x, val_0.y, val_0.z, val_1.x, val_1.y, val_1.z, ..., ]` + * where `val_i` is value of given property for i-th particle. + * Iterate over `prop_storage_row` with `stride = 3 * sizeof(float)` and proper offset to extract x/y/z projections. + + * Similarly for `VectorS<2, float>`. + */ + static void set(conduit_cpp::Node &fields, const std::string &prop_name, VectorS &prop_storage_row, size_t num_elements) + { + fields[prop_name + "/association"].set("vertex"); + fields[prop_name + "/topology"].set("mesh"); + fields[prop_name + "/volume_dependent"].set("false"); + if constexpr (N == 3) + { + fields[prop_name + "/values/x"].set_external((float *)&prop_storage_row[0], num_elements, /*offset=*/0, /*stride=*/3 * sizeof(float)); + fields[prop_name + "/values/y"].set_external((float *)&prop_storage_row[0], num_elements, /*offset=*/sizeof(float), /*stride=*/3 * sizeof(float)); + fields[prop_name + "/values/z"].set_external((float *)&prop_storage_row[0], num_elements, /*offset=*/2 * sizeof(float), /*stride=*/3 * sizeof(float)); + } + else if constexpr (N == 2) + { + fields[prop_name + "/values/x"].set_external((float *)&prop_storage_row[0], num_elements, /*offset=*/0, /*stride=*/2 * sizeof(float)); + fields[prop_name + "/values/y"].set_external((float *)&prop_storage_row[0], num_elements, /*offset=*/sizeof(float), /*stride=*/2 * sizeof(float)); + } + } +}; + +/** + * @brief Pass `double[N][M]` tensor property values to Conduit node, as N*M scalar fields. + */ +template +struct set_prop_val +{ + /** + * `double[N][M]` properties are saved into prop_storage as `multi_array[N][M]`. + * It consists of N*M pointers to contiguous `double[n]` arrays for respective tensor indices projections: + * `prop_storage_row[0][0] = [val_0.0_0, val_1.0_0, ..., val_n.0_0]`, + * `prop_storage_row[0][1] = [val_0.0_1, val_1.0_1, ..., val_n.0_1]`, + * ... + * `prop_storage_row[N-1][M-1] = [val_0.N-1_M-1, val_1.N-1_M-1, ..., val_n.N-1_M-1]`, + * where `val_i` is value of given property for i-th particle. + * + * Tensor is unrolled into N*M scalar properties, named `{tensor}_{i}_{j}`. + */ + template + static void set(conduit_cpp::Node &fields, const std::string &prop_name, multi_array prop_storage_row, size_t num_elements) + { + for (int i = 0; i < N; i++) + { + for (int j = 0; j < M; j++) + { + std::string prop_name_ij = prop_name + "_" + std::to_string(i) + "_" + std::to_string(j); + fields[prop_name_ij + "/association"].set("vertex"); + fields[prop_name_ij + "/topology"].set("mesh"); + fields[prop_name_ij + "/volume_dependent"].set("false"); + fields[prop_name_ij + "/values"].set_external((double *)&prop_storage_row[i][j], num_elements, /*offset=*/0, /*stride=*/sizeof(double)); + } + } + } +}; + +/** + * @brief Pass `float[N][M]` tensor property values to Conduit node, as N*M scalar fields. + */ +template +struct set_prop_val +{ + /** + * `float[N][M]` properties are saved into prop_storage as `multi_array[N][M]`. + * It consists of N*M pointers to contiguous `float[n]` arrays for respective tensor indices projections: + * `prop_storage_row[0][0] = [val_0.0_0, val_1.0_0, ..., val_n.0_0]`, + * `prop_storage_row[0][1] = [val_0.0_1, val_1.0_1, ..., val_n.0_1]`, + * ... + * `prop_storage_row[N-1][M-1] = [val_0.N-1_M-1, val_1.N-1_M-1, ..., val_n.N-1_M-1]`, + * where `val_i` is value of given property for i-th particle. + * + * Tensor is unrolled into N*M scalar properties, named `{tensor}_{i}_{j}`. + */ + template + static void set(conduit_cpp::Node &fields, const std::string &prop_name, multi_array prop_storage_row, size_t num_elements) + { + for (int i = 0; i < N; i++) + { + for (int j = 0; j < M; j++) + { + std::string prop_name_ij = prop_name + "_" + std::to_string(i) + "_" + std::to_string(j); + fields[prop_name_ij + "/association"].set("vertex"); + fields[prop_name_ij + "/topology"].set("mesh"); + fields[prop_name_ij + "/volume_dependent"].set("false"); + fields[prop_name_ij + "/values"].set_external((float *)&prop_storage_row[i][j], num_elements, /*offset=*/0, /*stride=*/sizeof(float)); + } + } + } +}; + +// Pass double[N][M][L] tensor property values to Conduit node, as N*M*L scalar fields. +template +struct set_prop_val +{ + /* + double[N][M][L] properties are saved into prop_storage as multi_array[N][M][L]. + It consists of N*M*L pointers to contiguous double[n] arrays for respective tensor indices projections: + prop_storage_row[0][0][0] = [val_0.0_0_0, val_1.0_0_0, ..., val_n.0_0_0], + prop_storage_row[0][0][1] = [val_0.0_0_1, val_1.0_0_1, ..., val_n.0_0_1], + ... + prop_storage_row[N-1][M-1][L-1] = [val_0.N-1_M-1_L-1, val_1.N-1_M-1_L-1, ..., val_n.N-1_M-1_L-1], + where val_i is value of given property for i-th particle. + + Tensor is unrolled into N*M*L scalar properties, named "{tensor}_{i}_{j}_{k}". + */ + template + static void set(conduit_cpp::Node &fields, const std::string &prop_name, multi_array prop_storage_row, size_t num_elements) + { + for (int i = 0; i < N; i++) + { + for (int j = 0; j < M; j++) + { + for (int k = 0; k < L; k++) + { + std::string prop_name_ijk = prop_name + "_" + std::to_string(i) + "_" + std::to_string(j) + "_" + std::to_string(k); + fields[prop_name_ijk + "/association"].set("vertex"); + fields[prop_name_ijk + "/topology"].set("mesh"); + fields[prop_name_ijk + "/volume_dependent"].set("false"); + fields[prop_name_ijk + "/values"].set_external((double *)&prop_storage_row[i][j][k], num_elements, /*offset=*/0, /*stride=*/sizeof(double)); + } + } + } + } +}; + +// Pass float[N][M][L] tensor property values to Conduit node, as N*M*L scalar fields. +template +struct set_prop_val +{ + /* + float[N][M][L] properties are saved into prop_storage as multi_array[N][M][L]. + It consists of N*M*L pointers to contiguous float[n] arrays for respective tensor indices projections: + prop_storage_row[0][0][0] = [val_0.0_0_0, val_1.0_0_0, ..., val_n.0_0_0], + prop_storage_row[0][0][1] = [val_0.0_0_1, val_1.0_0_1, ..., val_n.0_0_1], + ... + prop_storage_row[N-1][M-1][L-1] = [val_0.N-1_M-1_L-1, val_1.N-1_M-1_L-1, ..., val_n.N-1_M-1_L-1], + where val_i is value of given property for i-th particle. + + Tensor is unrolled into N*M*L scalar properties, named "{tensor}_{i}_{j}_{k}". + */ + template + static void set(conduit_cpp::Node &fields, const std::string &prop_name, multi_array prop_storage_row, size_t num_elements) + { + for (int i = 0; i < N; i++) + { + for (int j = 0; j < M; j++) + { + for (int k = 0; k < L; k++) + { + std::string prop_name_ijk = prop_name + "_" + std::to_string(i) + "_" + std::to_string(j) + "_" + std::to_string(k); + fields[prop_name_ijk + "/association"].set("vertex"); + fields[prop_name_ijk + "/topology"].set("mesh"); + fields[prop_name_ijk + "/volume_dependent"].set("false"); + fields[prop_name_ijk + "/values"].set_external((float *)&prop_storage_row[i][j][k], num_elements, /*offset=*/0, /*stride=*/sizeof(float)); + } + } + } + } +}; + +/** + * @brief For-each functor, applied to each property in `prop_storage` to pass its values array to Conduit node. + * + * @tparam particles_struct Type of particles data structure: `grid_dist` or `vector_dist`. + * @tparam prop_soa Type of `vector_soa` storage of particles properties values. + * @tparam props Indices of properties to be visualized. + */ +template +struct set_prop_val_functor +{ + const openfpm::vector &prop_names; + conduit_cpp::Node &fields; + /** + * `prop_storage` is heterogeneous `vector_soa` + * storing properties values of particles in SoA fashion; + * `prop_object` is list of chosen properties types. + */ + prop_soa &prop_storage; + + typedef typename to_boost_vmpl::type vprp; + + __device__ __host__ inline set_prop_val_functor(const openfpm::vector &prop_names, conduit_cpp::Node &fields, prop_soa &prop_storage) + : prop_names(prop_names), fields(fields), prop_storage(prop_storage){}; + + template + __device__ __host__ inline void operator()(T &t) const + { + typedef typename boost::mpl::at::type prp_val; + typedef typename boost::mpl::at::type prp_type; + + std::string prop_name; + if (prp_val::value < prop_names.size()) + { + prop_name = prop_names.get(prp_val::value); + } + // Unnamed properties + else + { + prop_name = "attr" + std::to_string(prp_val::value); + } + + set_prop_val::set(fields, prop_name, prop_storage.template get(0), prop_storage.size()); + } +}; + +enum catalyst_adaptor_impl +{ + GRID_DIST_IMPL = 0, + VECTOR_DIST_IMPL = 1 +}; + +template +struct vis_props +{ +}; + +/** + * @brief Catalyst Adaptor interface. Specializations define how to translate particles mesh into Conduit node format. + * + * @tparam particles_struct Type of particles data structure: `grid_dist` or `vector_dist`. + * @tparam vis_props Indices of properties to be visualized. + * @tparam impl Specialization of adaptor: `GRID_DIST_IMPL` or `VECTOR_DIST_IMPL`. + * + * @note Currently, properties indices must cover entire [0, N] segment where N is the maximum index + * among the properties chosen for visualization. "Indices trick" fix is required to map M arbitrary + * properties indices onto [0, M-1] numbering to correctly extract their values from `prop_storage`. + */ +template +class catalyst_adaptor +{ + +public: + catalyst_adaptor() + { + } + + // Initialize Catalyst, pass visualization pipeline scripts from ParaView. + void initialize() + { + std::cout << __FILE__ << ":" << __LINE__ << " The implementetion has not been selected or is unknown " << std::endl; + } + + // Finalize Catalyst. + void finalize() + { + std::cout << __FILE__ << ":" << __LINE__ << " The implementetion has not been selected or is unknown " << std::endl; + } + + /** + * Catalyst in-situ visualization, called by either `catalyst_execute` in the script + * or triggered by pre-defined trigger, e.g. each k-th timestep. + * Translates simulation data structures into Conduit Mesh Blueprint format. + */ + void execute(particles_struct &data) + { + std::cout << __FILE__ << ":" << __LINE__ << " The implementetion has not been selected or is unknown " << std::endl; + } +}; + +#include "catalyst_adaptor_grid_dist.hpp" +#include "catalyst_adaptor_vector_dist.hpp" + +#endif /* HAVE_CATALYST */ + +#endif /* CATALYST_ADAPTOR_HPP_ */ \ No newline at end of file diff --git a/src/Catalyst/catalyst_adaptor_grid_dist.hpp b/src/Catalyst/catalyst_adaptor_grid_dist.hpp new file mode 100644 index 0000000..cfffd07 --- /dev/null +++ b/src/Catalyst/catalyst_adaptor_grid_dist.hpp @@ -0,0 +1,147 @@ +#include "config.h" + +#ifdef HAVE_CATALYST +#include "catalyst_adaptor.hpp" + +#include +#include +#include + +/** + * @brief Catalyst Adaptor for structured particles mesh, stored in `grid_dist`-like data structures. + * + * @tparam grid_type Type of particles data structure. + * @tparam props Indices of properties to be visualized. + */ +template +class catalyst_adaptor, catalyst_adaptor_impl::GRID_DIST_IMPL> +{ + // Create typename for chosen properties. + typedef object::type> prop_object; + // SoA storage for chosen properties, split across local sub-grids. + openfpm::vector> prop_storage; + // Type of particles coordinates - float/double. + typedef typename grid_type::stype stype; + +public: + catalyst_adaptor() + { + } + + void initialize(const openfpm::vector &scripts) + { + conduit_cpp::Node node; + + for (int i = 0; i < scripts.size(); i++) + { + node["catalyst/scripts/script" + std::to_string(i)].set_string(scripts.get(i)); + } + + catalyst_status err = catalyst_initialize(conduit_cpp::c_node(&node)); + if (err != catalyst_status_ok) + { + std::cerr << "Failed to initialize Catalyst: " << err << std::endl; + } + } + + void finalize() + { + conduit_cpp::Node node; + catalyst_status err = catalyst_finalize(conduit_cpp::c_node(&node)); + if (err != catalyst_status_ok) + { + std::cerr << "Failed to finalize Catalyst: " << err << std::endl; + } + } + + void execute(grid_type &grid, size_t cycle = 0, double time = 0, const std::string &channel_name = "grid") + { + // Simulation state + conduit_cpp::Node exec_params; + auto state = exec_params["catalyst/state"]; + state["timestep"].set(cycle); + state["time"].set(time); + + // Mesh + auto channel = exec_params["catalyst/channels/" + channel_name]; + channel["type"].set("mesh"); + auto mesh = channel["data"]; + mesh["coordsets/coords/type"].set("uniform"); + + // Particles coordinates and properties + // Iterate over local sub-grids stored on current processor and fill data about coordinates and properties. + prop_storage.resize(grid.getN_loc_grid()); + + for (size_t grid_num = 0; grid_num < grid.getN_loc_grid(); grid_num++) + { + auto box = grid.getLocalGridsInfo().get(grid_num).Dbox; + auto offset = grid.getLocalGridsInfo().get(grid_num).origin; + + // FIXME: What is happening here? + for (int dim = 0; dim < grid_type::dims; dim++) + { + if (offset.get(dim) + box.getHigh(dim) + 1 != grid.size(dim)) + { + box.setHigh(dim, box.getHigh(dim) + 1); + } + } + + // Extract local sub-grid, iterate over its particles and copy (subset of) their properties into SoA storage. + auto &grid_loc = grid.get_loc_grid(grid_num); + prop_storage.get(grid_num).resize(box.getVolumeKey()); + auto &prop_storage_loc = prop_storage.get(grid_num); + + auto it = grid_loc.getIterator(box.getKP1(), box.getKP2()); + size_t particle_idx = 0; + + while (it.isNext()) + { + auto particle = it.get(); + object_s_di(grid_loc.template get_o(particle), prop_storage_loc.template get(particle_idx)); + ++it; + ++particle_idx; + } + + // Set size of sub-grid along XYZ axes. + mesh["coordsets/coords/dims/i"].set(box.getHigh(0) - box.getLow(0) + 1); + mesh["coordsets/coords/dims/j"].set(box.getHigh(1) - box.getLow(1) + 1); + mesh["coordsets/coords/dims/k"].set(box.getHigh(2) - box.getLow(2) + 1); + + // FIXME: What is happening here? + Point poffset; + for (int i = 0; i < grid_type::dims; i++) + { + poffset.get(i) = (offset.get(i) + box.getLow(i)) * grid.getSpacing()[i]; + } + + // Set origin of local sub-grid. + mesh["coordsets/coords/origin/x"].set(poffset.get(0)); + mesh["coordsets/coords/origin/y"].set(poffset.get(1)); + mesh["coordsets/coords/origin/z"].set(poffset.get(2)); + + // Set spacing of local sub-grid. + mesh["coordsets/coords/spacing/dx"].set(grid.getSpacing()[0]); + mesh["coordsets/coords/spacing/dy"].set(grid.getSpacing()[1]); + mesh["coordsets/coords/spacing/dz"].set(grid.getSpacing()[2]); + + // Topology + mesh["topologies/mesh/type"].set("uniform"); + mesh["topologies/mesh/coordset"].set("coords"); + + // Fields + auto fields = mesh["fields"]; + auto &prop_names = grid.getPropNames(); + set_prop_val_functor, props...> prop_setter(prop_names, fields, prop_storage_loc); + boost::mpl::for_each_ref>(prop_setter); + } + + // Execute + catalyst_status err = catalyst_execute(conduit_cpp::c_node(&exec_params)); + if (err != catalyst_status_ok) + { + std::cerr << "Failed to execute Catalyst: " << err << std::endl; + } + } +}; + +#endif /* HAVE_CATALYST */ \ No newline at end of file diff --git a/src/Catalyst/catalyst_adaptor_vector_dist.hpp b/src/Catalyst/catalyst_adaptor_vector_dist.hpp new file mode 100644 index 0000000..778de87 --- /dev/null +++ b/src/Catalyst/catalyst_adaptor_vector_dist.hpp @@ -0,0 +1,121 @@ +#include "config.h" + +#ifdef HAVE_CATALYST +#include "catalyst_adaptor.hpp" + +#include +#include +#include + +/** + * @brief Catalyst Adaptor for unstructured particles mesh, stored in `vector_dist`-like data structures. + * + * @tparam vector_type Type of particles data structure. + * @tparam props Indices of properties to be visualized. + */ +template +class catalyst_adaptor, catalyst_adaptor_impl::VECTOR_DIST_IMPL> +{ + // Create typename for chosen properties. + typedef object::type> prop_object; + // SoA storage for chosen properties. + openfpm::vector_soa prop_storage; + // Type of particles coordinates (float/double). + typedef typename vector_type::stype stype; + +public: + catalyst_adaptor() + { + } + + void initialize(const openfpm::vector &scripts) + { + conduit_cpp::Node node; + + for (int i = 0; i < scripts.size(); i++) + { + node["catalyst/scripts/script" + std::to_string(i)].set_string(scripts.get(i)); + } + + catalyst_status err = catalyst_initialize(conduit_cpp::c_node(&node)); + if (err != catalyst_status_ok) + { + std::cerr << "Failed to initialize Catalyst: " << err << std::endl; + } + } + + void finalize() + { + conduit_cpp::Node node; + catalyst_status err = catalyst_finalize(conduit_cpp::c_node(&node)); + if (err != catalyst_status_ok) + { + std::cerr << "Failed to finalize Catalyst: " << err << std::endl; + } + } + + void execute(vector_type &particles, size_t cycle = 0, double time = 0, const std::string &channel_name = "particles") + { + size_t num_particles = particles.size_local(); + + // Simulation state + conduit_cpp::Node exec_params; + auto state = exec_params["catalyst/state"]; + state["timestep"].set(cycle); + state["time"].set(time); + + // Mesh + auto channel = exec_params["catalyst/channels/" + channel_name]; + channel["type"].set_string("mesh"); + auto mesh = channel["data"]; + mesh["coordsets/coords/type"].set_string("explicit"); + + // Particles coordinates + auto pos_vector = particles.getPosVector(); + + // Coordinates are stored in pos_vector in interleaved fashion: + // pos_vector = [pos_0.x, pos_0.y, pos_0.z, pos_1.x, pos_1.y, pos_1.z, ..., ]. + // Iterate over pos_vector with stride = 3 * sizeof(stype) and proper offset to extract x/y/z projections. + if constexpr (vector_type::dims == 3) { + mesh["coordsets/coords/values/x"].set_external((stype *)&pos_vector.template get<0>(0)[0], num_particles, /*offset=*/0, /*stride=*/3 * sizeof(stype)); + mesh["coordsets/coords/values/y"].set_external((stype *)&pos_vector.template get<0>(0)[0], num_particles, /*offset=*/sizeof(stype), /*stride=*/3 * sizeof(stype)); + mesh["coordsets/coords/values/z"].set_external((stype *)&pos_vector.template get<0>(0)[0], num_particles, /*offset=*/2 * sizeof(stype), /*stride=*/3 * sizeof(stype)); + } else if constexpr (vector_type::dims == 2) { + mesh["coordsets/coords/values/x"].set_external((stype *)&pos_vector.template get<0>(0)[0], num_particles, /*offset=*/0, /*stride=*/2 * sizeof(stype)); + mesh["coordsets/coords/values/y"].set_external((stype *)&pos_vector.template get<0>(0)[0], num_particles, /*offset=*/sizeof(stype), /*stride=*/2 * sizeof(stype)); + } + + // Topology + mesh["topologies/mesh/type"].set_string("unstructured"); + mesh["topologies/mesh/coordset"].set_string("coords"); + mesh["topologies/mesh/elements/shape"].set_string("point"); + // Connectivity is represented by index array of particles. + openfpm::vector connectivity(num_particles); + std::iota(connectivity.begin(), connectivity.end(), 0); + mesh["topologies/mesh/elements/connectivity"].set_external(&connectivity.get(0), connectivity.size()); + + // Fields + auto fields = mesh["fields"]; + // Iterate over particles and copy their chosen properties into SoA storage. + prop_storage.resize(num_particles); + auto prop_vector = particles.getPropVector(); + for (size_t i = 0; i < num_particles; i++) + { + object_s_di(prop_vector.template get(i), prop_storage.template get(i)); + } + + // Iterate over properties and pass respective fields values to Conduit node with for-each functor. + auto &prop_names = particles.getPropNames(); + set_prop_val_functor, props...> prop_setter(prop_names, fields, prop_storage); + boost::mpl::for_each_ref>(prop_setter); + + // Execute + catalyst_status err = catalyst_execute(conduit_cpp::c_node(&exec_params)); + if (err != catalyst_status_ok) + { + std::cerr << "Failed to execute Catalyst: " << err << std::endl; + } + } +}; + +#endif /* HAVE_CATALYST */ \ No newline at end of file diff --git a/src/Catalyst/catalyst_unit_test.cpp b/src/Catalyst/catalyst_unit_test.cpp new file mode 100644 index 0000000..a73e383 --- /dev/null +++ b/src/Catalyst/catalyst_unit_test.cpp @@ -0,0 +1,199 @@ +#define BOOST_TEST_DYN_LINK +#include +#include "Grid/grid_dist_id.hpp" +#include "Vector/vector_dist_subset.hpp" +#include "Grid/map_grid.hpp" + +#include "config.h" + +#ifdef HAVE_CATALYST +#include +#include "Catalyst/catalyst_adaptor.hpp" + +BOOST_AUTO_TEST_SUITE(catalyst_test_suite) + +#ifdef HAVE_PNG +#include +#include + +// Test utilities to compare Catalyst-produced image with ground truth. +struct PixelInserter +{ + openfpm::vector &storage; + + PixelInserter(openfpm::vector &s) : storage(s) {} + + void operator()(boost::gil::rgb8_pixel_t p) const + { + storage.add(boost::gil::at_c<0>(p)); + storage.add(boost::gil::at_c<1>(p)); + storage.add(boost::gil::at_c<2>(p)); + } +}; + +void check_png(const std::string &img1_path, const std::string &img2_path) +{ + auto &v_cl = create_vcluster(); + + if (v_cl.rank() == 0) + { + // Check the file has been generated (we do not check the content) + boost::gil::rgb8_image_t img1, img2; + // FIXME: Ensure both images are accessible + boost::gil::read_image(img1_path.c_str(), img1, boost::gil::png_tag()); + boost::gil::read_image(img2_path.c_str(), img2, boost::gil::png_tag()); + + openfpm::vector storage1, storage2; + + for_each_pixel(const_view(img1), PixelInserter(storage1)); + for_each_pixel(const_view(img2), PixelInserter(storage2)); + + BOOST_REQUIRE(storage1.size() != 0); + BOOST_REQUIRE(storage2.size() != 0); + + BOOST_REQUIRE_EQUAL(storage1.size(), storage2.size()); + + size_t diff = 0; + for (int i = 0; i < storage1.size(); i++) + { + diff += abs(storage1.get(i) - storage2.get(i)); + } + + BOOST_REQUIRE(diff < 3000000); + } +} +#endif + +BOOST_AUTO_TEST_CASE(catalyst_grid_dist_test) +{ + Vcluster<> &v_cl = create_vcluster(); + size_t sz[3] = {40, 40, 40}; + size_t bc[3] = {NON_PERIODIC, NON_PERIODIC, NON_PERIODIC}; + Box<3, double> domain({0, 0, 0}, {2.0, 2.0, 2.0}); + Ghost<3, double> g(3. * 2. / (sz[0] - 1)); + grid_dist_id<3, double, aggregate, double[3][3]>> grid(sz, domain, g); + + openfpm::vector prop_names({"scalar", "vector", "tensor"}); + enum + { + SCALAR, + VECTOR, + TENSOR + }; + grid.setPropNames(prop_names); + + auto it = grid.getDomainIterator(); + while (it.isNext()) + { + auto key = it.get(); + auto pos = it.getGKey(key); + + double x = pos.get(0) * grid.getSpacing()[0]; + double y = pos.get(1) * grid.getSpacing()[1]; + double z = pos.get(2) * grid.getSpacing()[2]; + + grid.get(key) = x + y + z; + + grid.get(key)[0] = sin(x + y); + grid.get(key)[1] = cos(x + y); + grid.get(key)[2] = 0.0; + + grid.get(key)[0][0] = x*x; + grid.get(key)[0][1] = x*y; + grid.get(key)[0][2] = x*z; + + grid.get(key)[1][0] = y*x; + grid.get(key)[1][1] = y*y; + grid.get(key)[1][2] = y*z; + + grid.get(key)[2][0] = z*x; + grid.get(key)[2][1] = z*y; + grid.get(key)[2][2] = z*z; + + + ++it; + } + + grid.map(); + grid.ghost_get(); + // Generate representative dataset to create visualization pipeline in ParaView + //grid.write("grid_dist_test_data"); + + // Visualization with Catalyst Adaptor + // Images are written to ./datasets/ folder by default + catalyst_adaptor, catalyst_adaptor_impl::GRID_DIST_IMPL> adaptor; + openfpm::vector scripts({{"./test_data/catalyst_grid_dist_pipeline.py"}}); + adaptor.initialize(scripts); + adaptor.execute(grid); + adaptor.finalize(); + +#ifdef HAVE_PNG + check_png("./datasets/catalyst_grid_dist.png", "./test_data/catalyst_grid_dist_ground_truth.png"); +#endif +} + +BOOST_AUTO_TEST_CASE(catalyst_vector_dist_test) +{ + Vcluster<> &v_cl = create_vcluster(); + size_t sz[3] = {40, 40, 40}; + size_t bc[3] = {NON_PERIODIC, NON_PERIODIC, NON_PERIODIC}; + Box<3, double> domain({0, 0, 0}, {2.0, 2.0, 2.0}); + Ghost<3, double> g(3. * 2. / (sz[0] - 1)); + vector_dist_ws<3, double, aggregate, double[3][3]>> particles(0, domain, bc, g); + openfpm::vector prop_names({"scalar", "vector", "tensor"}); + enum + { + SCALAR, + VECTOR, + TENSOR + }; + particles.setPropNames(prop_names); + + // Assign scalar = x + y + z, vector = {sin(x + y), cos(x + y), 0}, tensor = {{x^2, xy}, {-xy, y^2}} properties to the particles + auto it = particles.getGridIterator(sz); + while (it.isNext()) + { + particles.add(); + auto key = it.get(); + double x = key.get(0) * it.getSpacing(0); + particles.getLastPos()[0] = x; + double y = key.get(1) * it.getSpacing(1); + particles.getLastPos()[1] = y; + double z = key.get(2) * it.getSpacing(2); + particles.getLastPos()[2] = z; + + particles.getLastProp() = x + y + z; + + particles.getLastProp()[0] = sin(x + y); + particles.getLastProp()[1] = cos(x + y); + particles.getLastProp()[2] = 0.0; + + particles.getLastProp()[0][0] = x*x; + particles.getLastProp()[0][1] = x*y; + particles.getLastProp()[1][0] = -x*y; + particles.getLastProp()[1][1] = y*y; + + ++it; + } + + particles.map(); + particles.ghost_get(); + // Generate representative dataset to create visualization pipeline in ParaView + //particles.write("vector_dist_test_data"); + + // Visualization with Catalyst Adaptor + // Images are written to ./datasets/ folder by default + catalyst_adaptor, catalyst_adaptor_impl::VECTOR_DIST_IMPL> adaptor; + openfpm::vector scripts({{"./test_data/catalyst_vector_dist_pipeline.py"}}); + adaptor.initialize(scripts); + adaptor.execute(particles); + adaptor.finalize(); + +#ifdef HAVE_PNG + check_png("./datasets/catalyst_vector_dist.png", "./test_data/catalyst_vector_dist_ground_truth.png"); +#endif +} + +BOOST_AUTO_TEST_SUITE_END() + +#endif /* HAVE_CATALYST */ \ No newline at end of file diff --git a/test_data/catalyst_grid_dist_ground_truth.png b/test_data/catalyst_grid_dist_ground_truth.png new file mode 100644 index 0000000000000000000000000000000000000000..85dc2a4d1f5a528cb1377ed6f047a8fa0ec1fbbb GIT binary patch literal 821524 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