diff --git a/.gitignore b/.gitignore index 864fe9916962506878322b713591137cdaeaf98b..49c1312eaf3bb0318bfc10f6c20c93c72825df22 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ org.eclipse.core.resources.prefs *.json .vscode +# Exception for 'CMakePresets.json' +!CMakePresets.json + #ignore temporary files tmp/ *.tmp diff --git a/CMakeLists.txt b/CMakeLists.txt index a7edb3e175a44e72e68bd1c63083da301e16209b..c8b38ec880b3f52e8bdf60ec9a021c9336c50504 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,15 @@ -cmake_minimum_required (VERSION 3.18.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.18.0 FATAL_ERROR) + # # Project Declaration -#-------------------- +# -------------------- project(scalfmm CXX) include(CMakePrintHelpers) # check if compiling into source directories string(COMPARE EQUAL "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" insource) + if(insource) message(FATAL_ERROR "${PROJECT_NAME} requires an out of source build. Goto ./Build and tapes cmake ../") endif(insource) @@ -19,7 +21,7 @@ endif(insource) set(${CMAKE_PROJECT_NAME}_MAJOR_VERSION 3) set(${CMAKE_PROJECT_NAME}_MINOR_VERSION 0) set(${CMAKE_PROJECT_NAME}_PATCH_VERSION 0-pre-alpha) -set(${CMAKE_PROJECT_NAME}_VERSION "${${CMAKE_PROJECT_NAME}_MAJOR_VERSION}.${${CMAKE_PROJECT_NAME}_MINOR_VERSION}.${${CMAKE_PROJECT_NAME}_PATCH_VERSION}" ) +set(${CMAKE_PROJECT_NAME}_VERSION "${${CMAKE_PROJECT_NAME}_MAJOR_VERSION}.${${CMAKE_PROJECT_NAME}_MINOR_VERSION}.${${CMAKE_PROJECT_NAME}_PATCH_VERSION}") set(CMAKE_CXX_STANDARD 17) cmake_print_variables(BLA_VENDOR) @@ -196,6 +198,17 @@ if(${CMAKE_PROJECT_NAME}_BUILD_BENCH) add_subdirectory(bench) endif() +# +# Build Python interface +# ---------------------- +option(${CMAKE_PROJECT_NAME}_BUILD_PYFMM "Set to ON to build Python interface." OFF) +message(STATUS "${CMAKE_PROJECT_NAME}_BUILD_PYFMM = ${${CMAKE_PROJECT_NAME}_BUILD_PYFMM}") + +if(${CMAKE_PROJECT_NAME}_BUILD_PYFMM) + add_subdirectory(python_interface) +endif() + +# # Build check/debug # -------------- option(${CMAKE_PROJECT_NAME}_BUILD_CHECK "Set to ON to build scalfmm3 tools." ON) @@ -215,10 +228,8 @@ message(STATUS "${CMAKE_PROJECT_NAME}_BUILD_DOC = ${${CMAKE_PROJECT_NAME}_BUILD_ if(${CMAKE_PROJECT_NAME}_BUILD_DOC) add_subdirectory(docs) endif() + # # Export Library # -------------- include(cmake/export.cmake) - - - diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000000000000000000000000000000000000..4c642c2dd1ae088a27b652fdf2935861c9dc43c4 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,137 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 30, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "generator": "Unix Makefiles", + "binaryDir": "${sourceDir}/build-${presetName}", + "description": "Configure in Release mode with optimization enabled", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_FLAGS": "-O3 -march=native", + "scalfmm_BUILD_UNITS": true + } + }, + { + "name": "default", + "inherits": "base", + "displayName": "Default config (OpenBLAS).", + "description": "Release mode + optimization enabled + OpenBLAS", + "cacheVariables": { + "BLA_VENDOR": "OpenBLAS" + } + }, + { + "name": "mkl", + "inherits": "base", + "displayName": "Default config (MKL).", + "description": "Release mode + optimization enabled + MKL", + "cacheVariables": { + "scalfmm_USE_MKL": true + } + } + ], + "buildPresets": [ + { + "name": "base", + "hidden": true, + "description": "Build all targets in Release mode.", + "targets": ["all", "units", "tools", "examples"] + }, + { + "name": "build-default", + "inherits": "base", + "displayName": "Default build (OpenBLAS).", + "description": "Build all targets in Release mode with OpenBLAS", + "configurePreset": "default" + }, + { + "name": "build-mkl", + "inherits": "base", + "displayName": "Default build (MKL).", + "description": "Build all targets in Release mode with MKL", + "configurePreset": "mkl" + } + ], + "testPresets": [ + { + "name": "base", + "hidden": true, + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "error", + "stopOnFailure": false + } + }, + { + "name": "sequential", + "hidden": true, + "inherits": "base", + "filter": { + "exclude": { + "name": "_omp$" + } + } + }, + { + "name": "openmp", + "hidden": true, + "inherits": "base", + "filter": { + "include": { + "name": "_omp$" + } + } + }, + { + "name": "test-default", + "inherits": "base", + "displayName": "Run all tests (OpenBLAS)", + "description": "Run all the tests with OpenBLAS", + "configurePreset": "default" + }, + { + "name": "test-mkl", + "inherits": "base", + "displayName": "Run all tests (MKL)", + "description": "Run all the tests with the MKL", + "configurePreset": "mkl" + }, + { + "name": "test-default-seq", + "inherits": "sequential", + "displayName": "Run sequential tests (OpenBLAS)", + "description": "Run only the sequential tests with OpenBLAS", + "configurePreset": "default" + }, + { + "name": "test-mkl-seq", + "inherits": "sequential", + "displayName": "Run sequential tests (MKL)", + "description": "Run only the sequential tests with the MKL", + "configurePreset": "mkl" + }, + { + "name": "test-default-omp", + "inherits": "openmp", + "displayName": "Run OpenMP tests (OpenBLAS)", + "description": "Run only the OpenMP tests with OpenBLAS", + "configurePreset": "default" + }, + { + "name": "test-mkl-omp", + "inherits": "openmp", + "displayName": "Run OpenMP tests (MKL)", + "description": "Run only the OpenMP tests with the MKL", + "configurePreset": "mkl" + } + ] +} diff --git a/README.md b/README.md index 27fb76996003ece3a378c90bb284c48bb60e1957..2b6283641677d570fc9f429c5534f1f37b4a4992 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The following are optional: - [Doxygen](https://www.doxygen.nl/index.html) to build the documentation. - [Sphinx](https://www.sphinx-doc.org/en/master/) and extensions (see the doc section) to build the documentation. In this case, we need the following packages: breathe, exhale, recommonmark and sphinx-rtd-theme. - - + - - An MPI implementation to build the distributed files. - [StarPU](http://starpu.gforge.inria.fr/) for the relevant FMM implementations. @@ -44,18 +44,18 @@ We also retrieve [`cpp_tools`](https://gitlab.inria.fr/compose/legacystack/cpp_t - scalfmm-3.0 the release of the 3.0 version - scalfmm-2.0 the release of the 2.0 version (no evolution) - scalfmm-1.5 the release of the 1.5 version (no evolution) - -### Features + +### Features - scalfmm-3.0 the release of the 3.0 version - barycentric, Chebyshev, uniform interpolation - uniform tree - task based OpenMP,MPI - scalfmm-2.0 the release of the 2.0 version - - adaptive and uniform tree + - adaptive and uniform tree - Chebyshev, uniform interpolation - MPI, OpenMP, StarPU (Chebyshev P2P and M2L operators for GPU) - scalfmm-1.5 the release of the 1.5 version - - uniform tree + - uniform tree - Chebyshev, uniform interpolation - MPI, OpenMP, StarPU (Chebyshev P2P and M2L operators for GPU) ### Cloning @@ -73,11 +73,11 @@ cd ScalFMM git submodule init git submodule update -``` +``` ### Building The project has been tested with the following compiler both on Linux and OSX - - GCC 11 and above - - LLVM 17 and above + - GCC 11 and above + - LLVM 17 and above ScalFMM uses iterators on task dependencies (OpenMP 5.x) the following compilers do not support them yet - Intel OneAPI 2022 and lower @@ -89,7 +89,7 @@ To keep a clean source tree, do an out-of-source build by creating a `build` fol ```bash cd /path/to/build/ # Use cmake, with relevant options -cmake .. # -Dscalfmm_USE_MKL=ON +cmake .. # -Dscalfmm_USE_MKL=ON ``` The build may be configured after the first CMake invocation using, for instance, `ccmake` or `cmake-gui`. @@ -106,22 +106,22 @@ To use Clang's native compiler, you must first install the omp library and use t ```bash brew install libomp ``` -and to compile +and to compile ```bash cd /path/to/build/ # Use cmake, with relevant options -cmake -DCMAKE_CXX_FLAGS= `-Xclang -fopenmp` -S ../ +cmake -DCMAKE_CXX_FLAGS= `-Xclang -fopenmp` -S ../ ``` #### Optimization Customization or optimized options: - - If you plan to use Intel MKL, please set `scalfmm_USE_MKL` to `ON`. This will also choose MKL for the FFTs and prevent unusual behavior with multithreaded MKL version. + - If you plan to use Intel MKL, please set `scalfmm_USE_MKL` to `ON`. This will also choose MKL for the FFTs and prevent unusual behavior with multithreaded MKL version. - To use a specific version of labpack/blas you can specify it using the BLA_VENDOR variable in cmake. For example `-DBLA_VENDOR=OpenBLAS` for OpenBLAS lapack and blas library - - if we are interested in periodic boundary condition, the option `scalfmm_BUILD_PBC` has to be set to `ON`. - - + - if we are interested in periodic boundary condition, the option `scalfmm_BUILD_PBC` has to be set to `ON`. + - The binaries are then compiled by calling `make` (or `ninja` if you specified it at the configure step). -The `all` target only build the tools binaries. These are binaries that can generate particle distributions, +The `all` target only build the tools binaries. These are binaries that can generate particle distributions, perform direct fmm computation, etc. Invoke `make help` to see the available targets. @@ -158,7 +158,7 @@ When building ScalFMM, remember that performance depends on SIMD support. If you are targeting a specific architecture, add the appropriate SIMD flag to your version flags. For example, if you are targeting an AVX2 processor, add `-mavx2` to your flags. And if you know the microarchitecture, such as cascade lake for example, you can also add the `-march=cascadelake` flag. -When you add architecture flags, SIMD flags are automatically triggered. +When you add architecture flags, SIMD flags are automatically triggered. ## Using ScalFMM in your project @@ -211,11 +211,70 @@ A quick start file (~QUICKSTART.md~) is available, as well as user documentation ```bash emacs docs/user_guide.org --batch -f org-html-export-to-html --kill ``` -of for pdf file +of for pdf file ```bash emacs docs/user_guide.org --batch -f org-latex-export-to-pdf --kill ``` +## Using Guix + +Work in progress... + +### Workflow for developers + +#### Using OpenBLAS + +``` bash +guix shell -C -D scalfmm --preserve="^TERM$" +cmake -B build -DBLA_VENDOR=OpenBLAS -Dscalfmm_BUILD_UNITS=ON +cmake --build build --target units +ctest --test-dir build # run the unit tests +``` + +#### Using Intel MKL + +``` bash +guix shell -C -D scalfmm-mkl --preserve="^TERM$" +export MKLROOT=$GUIX_ENVIRONMENT # to help cmake to find the required *.cmake files +cmake -B build -Dscalfmm_USE_MKL=ON -Dscalfmm_BUILD_UNITS=ON +cmake --build build --target units +ctest --test-dir build # run the unit tests +``` + +### Workflow for users + +``` bash +guix shell -C scalfmm <other packages> +``` + +Alternatively (using Intel MKL) + +``` bash +guix shell -C scalfmm-mkl <other packages> +``` + +### Installation via guix + +``` bash +guix install scalfmm +``` + +Alternatively (using Intel MKL) + +``` bash +guix install scalfmm-mkl +``` + +### Run reproducible benchmarks + +TODO: provide instructions to run reproducible benchmarks using `guix time-machine` and `guix shell` + +``` bash +guix time-machine -C .guix/scalfmm-channels.scm -- shell -C -m .guix/scalfmm-manifest-openblas.scm +``` + +We provide several manifest files: `scalfmm-manifest-openblas.scm`, `scalfmm-manifest-mkl.scm`. + ## Contributing and development guidelines ### Gitlab flow diff --git a/apply-clang-format.sh b/apply-clang-format.sh new file mode 100755 index 0000000000000000000000000000000000000000..ab4568bc8032f03483c0a3c2fade3c22d1aa859b --- /dev/null +++ b/apply-clang-format.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +SRC=$(find "include" "examples" "units" "checks" "bench" "tools" \( -name "*.cpp" -o -name "*.h" -o -name "*.hpp" -o -name "*.cc" \)) + +for f in $SRC; do + echo -e "- apply clang-format to file $f" + clang-format -i $f +done + +echo -e "--- clang-format DONE ---" diff --git a/bench/fmm_generic.hpp b/bench/fmm_generic.hpp index fdfafab203777557efc12a0fffcca6d5b5bd9840..b32fb4cc181edf62f43e424a3d08531c759431a3 100644 --- a/bench/fmm_generic.hpp +++ b/bench/fmm_generic.hpp @@ -311,7 +311,7 @@ void select_interpolator(const int dimension, const std::string& input_file, con << '\n'; } #ifdef SCALFMM_MK_ONE_OVER_R - using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; + using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; #else using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r<dim>; #endif diff --git a/bench/loop_leaf.cpp b/bench/loop_leaf.cpp index a4d16f9ac6c1212a44d5a36c7351d690b6e60a20..6ee2101cb9a7e0579c480b2b2e47cc107a586550 100644 --- a/bench/loop_leaf.cpp +++ b/bench/loop_leaf.cpp @@ -226,8 +226,8 @@ auto sum_output_2(Tree const& tree, const Value_type& Q, const int iter, Vect& r } // template<int dimension, typename value_type> -auto run_loop(const int& tree_height, const int& group_size, int const& order, int& seed, const int& Nb_particles) - -> int +auto run_loop(const int& tree_height, const int& group_size, int const& order, int& seed, + const int& Nb_particles) -> int { // bool display_container = false; // bool display_tree = false; diff --git a/bench/sequential.cpp b/bench/sequential.cpp index 0be17bc6736a38b8b408f39d6859abdafc22f786..2de82549e48a98b6c5c5a4c0f021394633319481 100644 --- a/bench/sequential.cpp +++ b/bench/sequential.cpp @@ -4,4 +4,3 @@ static auto option_fmm = scalfmm::options::_s(scalfmm::options::seq_timit); #include "fmm_generic.hpp" - diff --git a/bench/tasks_dep_omp.cpp b/bench/tasks_dep_omp.cpp index d772ee2a82e770e76d37a0df0ae817d5576530a2..26edc1ab1defb31d813054b730d4bb36f7274db7 100644 --- a/bench/tasks_dep_omp.cpp +++ b/bench/tasks_dep_omp.cpp @@ -4,4 +4,3 @@ static auto option_fmm = scalfmm::options::_s(scalfmm::options::omp_timit); #include "fmm_generic.hpp" - diff --git a/checks/CMakeLists.txt b/checks/CMakeLists.txt index d6904d9981d025cddbc45614a3c1ef8713208089..80a382d34a103749319c1225ad46f2c8f1445bf9 100644 --- a/checks/CMakeLists.txt +++ b/checks/CMakeLists.txt @@ -8,7 +8,7 @@ set(source_check_files check_interaction_lists.cpp # check_max_morton_level.cpp - # debug & check + # debug & check count_particles_seq.cpp count_particles_st_seq.cpp count_particles_omp.cpp @@ -37,6 +37,10 @@ set(source_check_files check_source-target.cpp test_p2p_inner_non_mutual.cpp + + check_reset.cpp + check_points.cpp + check_block.cpp ) if(${CMAKE_PROJECT_NAME}_BUILD_PBC) diff --git a/checks/check_1d.cpp b/checks/check_1d.cpp index 3ffe6ca187474ef6a0f0efb9e33b3a10702f8026..d4dc4a8c58254c575e52b393a12adece890ec86f 100644 --- a/checks/check_1d.cpp +++ b/checks/check_1d.cpp @@ -10,7 +10,7 @@ * */ // make check_periodic -// ./check/Release/check1d --input-file check_per.fma --d 2 --per 1,1 -o 4 --tree-height 4 -gs 2 +// ./checks/Release/check_1d --input-file ../data/units/test_1d_ref.fma -o 4 --tree-height 4 -gs 2 // // @FUSE_FFTW // @FUSE_CBLAS @@ -97,7 +97,7 @@ namespace local_args flagged }; }; -} // namespace local_args +} // namespace local_args template<typename Container> auto read_data(const std::string& filename) @@ -181,26 +181,6 @@ auto check_output(Container const& part, Tree const& tree) -> scalfmm::utils::ac } std::cout << std::endl; } - // const auto& container = leaf.cparticles(); - // const auto nb_elt = container.size(); - // for(std::size_t i = 0; i < nb_elt; ++i) - // { - // const auto& p = container.particle(i); - // const auto& idx = std::get<0>(p.variables()); - - // auto output = p.outputs(); - // auto output_ref = part.particle(idx).outputs(); - // std::cout << i << " p_tree " << p.position() << " p_ref " - // << part.particle(idx).position() << " "; - - // for(int n{0}; n < nb_out; ++n) - // { - // std::cout << " (" << output_ref.at(n) << " " << output.at(n) - // << " ) "; - // error.add(output_ref.at(n), output.at(n)); - // } - // std::cout << std::endl; - // } }); return error; } @@ -250,6 +230,12 @@ auto run(const int& tree_height, const int& group_size, const std::size_t order, box_type box(box_width, box_center); bool sorted = false; group_tree_type tree(tree_height, order, box, group_size, group_size, container, sorted); + // + std::cout << "numbers of\n" + << " particles " << tree.number_of_particles() << std::endl + << " leaves " << tree.number_of_leaves() << std::endl + << " cells " << tree.number_of_cells() << std::endl; + // //////////////////////////////////////////////////// @@ -272,12 +258,13 @@ auto run(const int& tree_height, const int& group_size, const std::size_t order, // const int separation_criterion = fmm_operator.near_field().separation_criterion(); const bool mutual = true; - scalfmm::list::sequential::build_interaction_lists(tree, tree, separation_criterion, mutual); auto operator_to_proceed = scalfmm::algorithms::all; + scalfmm::list::sequential::build_interaction_lists(tree, tree, separation_criterion, mutual); + auto operator_to_proceed = scalfmm::algorithms::all; scalfmm::algorithms::sequential::sequential(tree, fmm_operator, operator_to_proceed); if(check) { - // scalfmm::algorithms::full_direct(std::begin(container), std::end(container), mk_near); + // scalfmm::algorithms::full_direct(std::begin(container), std::end(container), mk_near); scalfmm::algorithms::full_direct(container, mk_near); auto error1 = check_output(container, tree); @@ -403,8 +390,7 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int args::order{}, // args::thread_count{}, //, args::log_file{}, args::log_level{} local_args::matrix_kernel{}, // periodicity - /*local_args::dimension{}, */ local_args::check{}, local_args::displayParticles{}, local_args::displayCells{} - ); + /*local_args::dimension{}, */ local_args::check{}, local_args::displayParticles{}, local_args::displayCells{}); parser.parse(argc, argv); const int tree_height{parser.get<args::tree_height>()}; diff --git a/checks/check_2d.cpp b/checks/check_2d.cpp index 9cb7a8f128650cadd6f1b5a08515d70219e788f0..667fc174491f8a35f91dcfa5dbe46dcb50f9cce4 100644 --- a/checks/check_2d.cpp +++ b/checks/check_2d.cpp @@ -65,7 +65,7 @@ namespace local_args struct matrix_kernel { cpp_tools::cl_parser::str_vec flags = {"--kernel", "-k"}; - const char *description = "Matrix kernels: \n 0) 1/r, 1) grad(ln r), " + const char* description = "Matrix kernels: \n 0) 1/r, 1) grad(ln r), " "2) shift(ln r)-> grad 3) val_grad( 1/r)"; using type = int; type def = 0; @@ -74,7 +74,7 @@ namespace local_args struct check { cpp_tools::cl_parser::str_vec flags = {"--check"}; - const char *description = "Check with p2p "; + const char* description = "Check with p2p "; using type = bool; /// The parameter is a flag, it doesn't expect a following value enum @@ -85,7 +85,7 @@ namespace local_args struct displayParticles { cpp_tools::cl_parser::str_vec flags = {"--display-particles", "-dp"}; - const char *description = "Display all particles "; + const char* description = "Display all particles "; using type = bool; /// The parameter is a flag, it doesn't expect a following value enum @@ -96,7 +96,7 @@ namespace local_args struct displayCells { cpp_tools::cl_parser::str_vec flags = {"--display-cells", "-dc"}; - const char *description = "Display the cells at leaf level "; + const char* description = "Display the cells at leaf level "; using type = bool; /// The parameter is a flag, it doesn't expect a following value enum @@ -104,9 +104,10 @@ namespace local_args flagged }; }; -} // namespace local_args +} // namespace local_args -template <typename Container> auto read_data(const std::string &filename) +template<typename Container> +auto read_data(const std::string& filename) { using container_type = Container; using particle_type = typename Container::value_type; @@ -117,15 +118,12 @@ template <typename Container> auto read_data(const std::string &filename) scalfmm::io::FFmaGenericLoader<value_type, dimension> loader(filename, verbose); const std::size_t number_of_particles{loader.getNumberOfParticles()}; - std::cout << cpp_tools::colors::yellow - << "[file][n_particles] : " << number_of_particles + std::cout << cpp_tools::colors::yellow << "[file][n_particles] : " << number_of_particles << cpp_tools::colors::reset << '\n'; const auto width{loader.getBoxWidth()}; - std::cout << cpp_tools::colors::yellow << "[file][box_width] : " << width - << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::yellow << "[file][box_width] : " << width << cpp_tools::colors::reset << '\n'; const auto center{loader.getBoxCenter()}; - std::cout << cpp_tools::colors::yellow << "[file][box_centre] : " << center - << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::yellow << "[file][box_centre] : " << center << cpp_tools::colors::reset << '\n'; auto nb_val_to_red_per_part = loader.getNbRecordPerline(); // could be a problem for binary file (float double) @@ -133,23 +131,23 @@ template <typename Container> auto read_data(const std::string &filename) container_type container(number_of_particles); - for (std::size_t idx = 0; idx < number_of_particles; ++idx) + for(std::size_t idx = 0; idx < number_of_particles; ++idx) + { + loader.fillParticle(values_to_read.data(), nb_val_to_red_per_part); + particle_type p; + std::size_t ii{0}; + for(auto& e: p.position()) + { + e = values_to_read[ii++]; + } + for(auto& e: p.inputs()) { - loader.fillParticle(values_to_read.data(), nb_val_to_red_per_part); - particle_type p; - std::size_t ii{0}; - for (auto &e : p.position()) - { - e = values_to_read[ii++]; - } - for (auto &e : p.inputs()) - { - e = values_to_read[ii++]; - } - // p.variables(values_to_read[ii++], idx, 1); - p.variables(idx); - container.insert_particle(idx, p); + e = values_to_read[ii++]; } + // p.variables(values_to_read[ii++], idx, 1); + p.variables(idx); + container.insert_particle(idx, p); + } return std::make_tuple(container, center, width); } @@ -160,9 +158,8 @@ template <typename Container> auto read_data(const std::string &filename) * @param ref */ -template <typename Tree, typename Container> -auto check_output(Container const &part, Tree const &tree) - -> scalfmm::utils::accurater<double> +template<typename Tree, typename Container> +auto check_output(Container const& part, Tree const& tree) -> scalfmm::utils::accurater<double> { scalfmm::utils::accurater<double> error; @@ -198,15 +195,12 @@ auto check_output(Container const &part, Tree const &tree) }); return error; } -template <int Dimension, typename value_type, class FMM_OPERATOR_TYPE> -auto run(const int &tree_height, const int &group_size, const std::size_t order, - const std::string &input_file, const std::string &output_file, - const bool check, const bool displayCells, const bool displayParticles) - -> int +template<int Dimension, typename value_type, class FMM_OPERATOR_TYPE> +auto run(const int& tree_height, const int& group_size, const std::size_t order, const std::string& input_file, + const std::string& output_file, const bool check, const bool displayCells, const bool displayParticles) -> int { - using near_matrix_kernel_type = - typename FMM_OPERATOR_TYPE::near_field_type::matrix_kernel_type; + using near_matrix_kernel_type = typename FMM_OPERATOR_TYPE::near_field_type::matrix_kernel_type; static constexpr std::size_t nb_inputs_near{near_matrix_kernel_type::km}; static constexpr std::size_t nb_outputs_near{near_matrix_kernel_type::kn}; @@ -215,23 +209,18 @@ auto run(const int &tree_height, const int &group_size, const std::size_t order, // number of input values and type // number of output values // variables original index - using particle_type = - scalfmm::container::particle<value_type, Dimension, value_type, - nb_inputs_near, value_type, - nb_outputs_near, std::size_t>; - using container_type = - scalfmm::container::particle_container<particle_type>; + using particle_type = scalfmm::container::particle<value_type, Dimension, value_type, nb_inputs_near, value_type, + nb_outputs_near, std::size_t>; + using container_type = scalfmm::container::particle_container<particle_type>; using position_type = typename particle_type::position_type; // - using near_matrix_kernel_type = - typename FMM_OPERATOR_TYPE::near_field_type::matrix_kernel_type; + using near_matrix_kernel_type = typename FMM_OPERATOR_TYPE::near_field_type::matrix_kernel_type; using far_field_type = typename FMM_OPERATOR_TYPE::far_field_type; using interpolator_type = typename far_field_type::approximation_type; - using far_matrix_kernel_type = - typename interpolator_type::matrix_kernel_type; + using far_matrix_kernel_type = typename interpolator_type::matrix_kernel_type; // using leaf_type = scalfmm::component::leaf_view<particle_type>; using box_type = scalfmm::component::box<position_type>; @@ -242,8 +231,7 @@ auto run(const int &tree_height, const int &group_size, const std::size_t order, position_type box_center{}; value_type box_width{}; container_type container{}; - std::tie(container, box_center, box_width) = - read_data<container_type>(input_file); + std::tie(container, box_center, box_width) = read_data<container_type>(input_file); // read_data<Dimension>(input_file, container, box_center, box_width); // @@ -251,8 +239,7 @@ auto run(const int &tree_height, const int &group_size, const std::size_t order, // Build tree box_type box(box_width, box_center); bool sorted = false; - group_tree_type tree(tree_height, order, box, group_size, group_size, - container, sorted); + group_tree_type tree(tree_height, order, box, group_size, group_size, container, sorted); // //////////////////////////////////////////////////// @@ -267,7 +254,7 @@ auto run(const int &tree_height, const int &group_size, const std::size_t order, near_matrix_kernel_type mk_near{}; const bool mutual_near = true; - typename FMM_OPERATOR_TYPE::near_field_type near_field(mk_near,mutual_near); + typename FMM_OPERATOR_TYPE::near_field_type near_field(mk_near, mutual_near); // std::cout << cpp_tools::colors::blue << "Fmm with kernels: " << std::endl << " near " << mk_near.name() << std::endl @@ -277,8 +264,8 @@ auto run(const int &tree_height, const int &group_size, const std::size_t order, FMM_OPERATOR_TYPE fmm_operator(near_field, far_field); // // Build interaction lists - int const & separation_criterion = fmm_operator.near_field().separation_criterion(); - bool const & mutual = fmm_operator.near_field().mutual(); + int const& separation_criterion = fmm_operator.near_field().separation_criterion(); + bool const& mutual = fmm_operator.near_field().mutual(); scalfmm::list::sequential::build_interaction_lists(tree, tree, separation_criterion, mutual); auto operator_to_proceed = scalfmm::algorithms::all; @@ -291,21 +278,21 @@ auto run(const int &tree_height, const int &group_size, const std::size_t order, scalfmm::algorithms::sequential::sequential(tree, fmm_operator, operator_to_proceed); if(check) { - scalfmm::algorithms::full_direct(container, mk_near); + scalfmm::algorithms::full_direct(container, mk_near); - auto error1 = check_output(container, tree); - std::cout << error1 << std::endl; + auto error1 = check_output(container, tree); + std::cout << error1 << std::endl; } - if(displayParticles) - { - scalfmm::component::for_each_leaf(std::begin(tree), std::end(tree), - [&box, tree_height](auto& leaf) - { - scalfmm::io::print_leaf(leaf, box, tree_height - 1); - std::cout << "-----\n\n"; - }); - } - + if(displayParticles) + { + scalfmm::component::for_each_leaf(std::begin(tree), std::end(tree), + [&box, tree_height](auto& leaf) + { + scalfmm::io::print_leaf(leaf, box, tree_height - 1); + std::cout << "-----\n\n"; + }); + } + if(!output_file.empty()) { std::cout << "Write outputs in " << output_file << std::endl; @@ -333,12 +320,10 @@ auto run(const int &tree_height, const int &group_size, const std::size_t order, return 1; } -template <int Dimension, typename value_type> -auto run_general(const int &tree_height, const int &group_size, - const std::size_t order, const int kernel, - const std::string &input_file, const std::string &output_file, - const bool check, const bool displayCells, - const bool displayParticles) -> int +template<int Dimension, typename value_type> +auto run_general(const int& tree_height, const int& group_size, const std::size_t order, const int kernel, + const std::string& input_file, const std::string& output_file, const bool check, + const bool displayCells, const bool displayParticles) -> int { // @@ -363,78 +348,51 @@ auto run_general(const int &tree_height, const int &group_size, return run<Dimension, value_type, fmm_operators_type>(tree_height, group_size, order, input_file, output_file, check, displayCells, displayParticles); } - else if (kernel == 1) - { // grad_ln_r - using far_matrix_kernel_type = - scalfmm::matrix_kernels::laplace::grad_ln_2d; - using near_matrix_kernel_type = - scalfmm::matrix_kernels::laplace::grad_ln_2d; - using near_field_type = scalfmm::operators::near_field_operator< - near_matrix_kernel_type>; - // - using interpolation_type = - scalfmm::interpolation::interpolator< - value_type, Dimension, far_matrix_kernel_type, options>; - using far_field_type = - scalfmm::operators::far_field_operator<interpolation_type, - false>; - - using fmm_operators_type = - scalfmm::operators::fmm_operators<near_field_type, - far_field_type>; - - return run<Dimension, value_type, fmm_operators_type>( - tree_height, group_size, order, input_file, output_file, check, - displayCells, displayParticles); - } - else if (kernel == 2) - { // shift_ln_r - using far_matrix_kernel_type = - scalfmm::matrix_kernels::laplace::ln_2d; - using near_matrix_kernel_type = - scalfmm::matrix_kernels::laplace::grad_ln_2d; - using near_field_type = scalfmm::operators::near_field_operator< - near_matrix_kernel_type>; - // - using interpolation_type = - scalfmm::interpolation::interpolator< - value_type, Dimension, far_matrix_kernel_type, options>; - using far_field_type = - scalfmm::operators::far_field_operator<interpolation_type, - true>; - - using fmm_operators_type = - scalfmm::operators::fmm_operators<near_field_type, - far_field_type>; - - return run<Dimension, value_type, fmm_operators_type>( - tree_height, group_size, order, input_file, output_file, check, - displayCells, displayParticles); - } - else if (kernel == 3) - { // val_grad_one_over_r - using far_matrix_kernel_type = - scalfmm::matrix_kernels::laplace::val_grad_one_over_r<2>; - using near_matrix_kernel_type = - scalfmm::matrix_kernels::laplace::val_grad_one_over_r<2>; - using near_field_type = scalfmm::operators::near_field_operator< - near_matrix_kernel_type>; - // - using interpolation_type = - scalfmm::interpolation::interpolator< - value_type, Dimension, far_matrix_kernel_type, options>; - using far_field_type = - scalfmm::operators::far_field_operator<interpolation_type, - false>; - - using fmm_operators_type = - scalfmm::operators::fmm_operators<near_field_type, - far_field_type>; - - return run<Dimension, value_type, fmm_operators_type>( - tree_height, group_size, order, input_file, output_file, check, - displayCells, displayParticles); - } + else if(kernel == 1) + { // grad_ln_r + using far_matrix_kernel_type = scalfmm::matrix_kernels::laplace::grad_ln_2d; + using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::grad_ln_2d; + using near_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; + // + using interpolation_type = + scalfmm::interpolation::interpolator<value_type, Dimension, far_matrix_kernel_type, options>; + using far_field_type = scalfmm::operators::far_field_operator<interpolation_type, false>; + + using fmm_operators_type = scalfmm::operators::fmm_operators<near_field_type, far_field_type>; + + return run<Dimension, value_type, fmm_operators_type>(tree_height, group_size, order, input_file, output_file, + check, displayCells, displayParticles); + } + else if(kernel == 2) + { // shift_ln_r + using far_matrix_kernel_type = scalfmm::matrix_kernels::laplace::ln_2d; + using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::grad_ln_2d; + using near_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; + // + using interpolation_type = + scalfmm::interpolation::interpolator<value_type, Dimension, far_matrix_kernel_type, options>; + using far_field_type = scalfmm::operators::far_field_operator<interpolation_type, true>; + + using fmm_operators_type = scalfmm::operators::fmm_operators<near_field_type, far_field_type>; + + return run<Dimension, value_type, fmm_operators_type>(tree_height, group_size, order, input_file, output_file, + check, displayCells, displayParticles); + } + else if(kernel == 3) + { // val_grad_one_over_r + using far_matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r<2>; + using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r<2>; + using near_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; + // + using interpolation_type = + scalfmm::interpolation::interpolator<value_type, Dimension, far_matrix_kernel_type, options>; + using far_field_type = scalfmm::operators::far_field_operator<interpolation_type, false>; + + using fmm_operators_type = scalfmm::operators::fmm_operators<near_field_type, far_field_type>; + + return run<Dimension, value_type, fmm_operators_type>(tree_height, group_size, order, input_file, output_file, + check, displayCells, displayParticles); + } else { return 0; @@ -442,46 +400,39 @@ auto run_general(const int &tree_height, const int &group_size, // scalfmm::matrix_kernels::laplace::one_over_r; // scalfmm::matrix_kernels::laplace::grad_ln_2d; } -auto main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[]) -> int -{ // +auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int +{ // // Parameter handling auto parser = cpp_tools::cl_parser::make_parser( - cpp_tools::cl_parser::help{}, args::input_file(), args::output_file(), - args::tree_height{}, args::block_size{}, - args::order{}, // args::thread_count{}, - //, args::log_file{}, args::log_level{} - local_args::matrix_kernel{}, // periodicity - /*local_args::dimension{}, */ local_args::check{}, - local_args::displayParticles{}, local_args::displayCells{} + cpp_tools::cl_parser::help{}, args::input_file(), args::output_file(), args::tree_height{}, args::block_size{}, + args::order{}, // args::thread_count{}, + //, args::log_file{}, args::log_level{} + local_args::matrix_kernel{}, // periodicity + /*local_args::dimension{}, */ local_args::check{}, local_args::displayParticles{}, local_args::displayCells{} ); parser.parse(argc, argv); const int tree_height{parser.get<args::tree_height>()}; - std::cout << cpp_tools::colors::blue - << "<params> Tree height : " << tree_height - << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Tree height : " << tree_height << cpp_tools::colors::reset + << '\n'; const int group_size{parser.get<args::block_size>()}; - std::cout << cpp_tools::colors::blue - << "<params> Group Size : " << group_size - << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Group Size : " << group_size << cpp_tools::colors::reset << '\n'; const std::string input_file{parser.get<args::input_file>()}; - if (!input_file.empty()) - { - std::cout << cpp_tools::colors::blue - << "<params> Input file : " << input_file - << cpp_tools::colors::reset << '\n'; - } + if(!input_file.empty()) + { + std::cout << cpp_tools::colors::blue << "<params> Input file : " << input_file << cpp_tools::colors::reset + << '\n'; + } const std::string output_file{parser.get<args::output_file>()}; - if (!output_file.empty()) - { - std::cout << cpp_tools::colors::blue - << "<params> Output file : " << output_file - << cpp_tools::colors::reset << '\n'; - } + if(!output_file.empty()) + { + std::cout << cpp_tools::colors::blue << "<params> Output file : " << output_file << cpp_tools::colors::reset + << '\n'; + } const auto order{parser.get<args::order>()}; bool check{parser.exists<local_args::check>()}; diff --git a/checks/check_block.cpp b/checks/check_block.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e4309b30b45a0de67b38855050b4505868b7e42c --- /dev/null +++ b/checks/check_block.cpp @@ -0,0 +1,82 @@ + +#include "scalfmm/container/particle.hpp" +#include "scalfmm/container/point.hpp" +#include "scalfmm/interpolation/interpolation.hpp" +#include "scalfmm/matrix_kernels/laplace.hpp" +#include "scalfmm/meta/is_valid.hpp" +#include "scalfmm/meta/type_pack.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/operators/fmm_operators.hpp" +#include "scalfmm/operators/l2p.hpp" +#include "scalfmm/tree/cell.hpp" +#include "scalfmm/tree/group_of_views.hpp" +#include "scalfmm/tree/leaf_view.hpp" +// #include "scalfmm/tree/leaf.hpp" +#include "scalfmm/utils/generate.hpp" + +#include <iostream> +#include <random> +#include <vector> + +template<typename OutputStream, typename ParticleType> +inline auto operator<<(OutputStream& os, std::vector<ParticleType>& particles) -> OutputStream& +{ + for(auto const& part: particles) + { + os << part << std::endl; + } + return os; +} + +int main() +{ + using value_type = double; + static constexpr std::size_t nb_inputs = 2; + static constexpr std::size_t nb_outputs = 4; + static constexpr std::size_t dimension = 3; + using particle_type = + scalfmm::container::particle<value_type, dimension, value_type, nb_inputs, value_type, nb_outputs, std::size_t>; + + using leaf_type = scalfmm::component::leaf_view<particle_type>; + using block_type = typename leaf_type::storage_type; + + using container_type = std::vector<particle_type>; + + const std::size_t N{10}; + container_type particles(N); + + for(auto& part: particles) + { + int value = 0; + scalfmm::meta::for_each(part.position(), [&value](auto& v) { v = ++value; }); + scalfmm::meta::for_each(part.inputs(), [&value](auto& v) { v = ++value; }); + scalfmm::meta::for_each(part.outputs(), [&value](auto& v) { v = ++value; }); + scalfmm::meta::for_each(part.variables(), [&value](auto& v) { v = ++value; }); + } + + std::cout << particles << std::endl; + + block_type block(particles, 2); + + std::cout << block << std::endl; + + leaf_type leaf1(std::make_pair(std::begin(block), std::begin(block) + N / 2), block.symbolics()); + leaf_type leaf2(std::make_pair(std::begin(block) + N / 2, std::end(block)), block.symbolics()); + + auto print_leaf = [](auto const& leaf) + { + for(auto const p_ref: leaf) + { + const auto p = typename leaf_type::const_proxy_type(p_ref); + std::cout << p << std::endl; + } + }; + + std::cout << "\n - leaf 1: " << std::endl; + print_leaf(leaf1); + + std::cout << "\n - leaf 2: " << std::endl; + print_leaf(leaf2); + + return 0; +} diff --git a/checks/check_interaction_lists.cpp b/checks/check_interaction_lists.cpp index ad1c1e2887a76ae804cbe21edbb1250e4d77ab5e..a74de97cb8089cce627557762eda3280d7287b5d 100644 --- a/checks/check_interaction_lists.cpp +++ b/checks/check_interaction_lists.cpp @@ -25,8 +25,8 @@ #include "scalfmm/tree/leaf_view.hpp" #include "scalfmm/tree/utils.hpp" // Lists -#include "scalfmm/lists/policies.hpp" #include "scalfmm/lists/omp.hpp" +#include "scalfmm/lists/policies.hpp" #include "scalfmm/lists/sequential.hpp" #include "scalfmm/lists/utils.hpp" // @@ -392,7 +392,7 @@ auto fmm_run(const std::string& input_source_file, const std::string& input_targ // const bool mutual = false; //tree_target.build_interaction_lists(*tree, separation_criterion, mutual, scalfmm::list::policies::omp); // scalfmm::list::sequential::build_interaction_lists(*tree, tree_target, separation_criterion, mutual); - scalfmm::list::omp::build_interaction_lists(*tree, tree_target, separation_criterion, mutual); + scalfmm::list::omp::build_interaction_lists(*tree, tree_target, separation_criterion, mutual); if(mutual) { diff --git a/checks/check_interpolation.cpp b/checks/check_interpolation.cpp index 2ed6d1d23c19cddee1b62422c5423ce8416667e0..118aa5a88754836f16d01412004b3bde71be0f40 100644 --- a/checks/check_interpolation.cpp +++ b/checks/check_interpolation.cpp @@ -1,115 +1,248 @@ +#include <array> #include <random> +#include <typeinfo> #include <vector> - -#include <cpp_tools/timers/simple_timer.hpp> +#include <xsimd/xsimd.hpp> #include "scalfmm/interpolation/interpolation.hpp" #include "scalfmm/matrix_kernels/laplace.hpp" +#include "scalfmm/utils/parameters.hpp" +#include "xsimd/xsimd.hpp" + +#include <cpp_tools/cl_parser/tcli.hpp> +#include <cpp_tools/colors/colorized.hpp> +#include <cpp_tools/timers/simple_timer.hpp> + +using value_type = double; +static constexpr std::size_t dimension{3}; +using namespace scalfmm; -int main() +namespace local_args { - int res{0}; - constexpr int dimension{3}; - using value_type = float; - using matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r<dimension>; + struct number_of_points + { + cpp_tools::cl_parser::str_vec flags = {"--number-of-points", "--N"}; + std::string description = "The number of points at which the polynomials are evaluated."; + using type = int; + type def = 100; + }; +} // namespace local_args - // scalfmm::interpolation::chebyshev_interpolator - using interpolator_cheb_type = - scalfmm::interpolation::chebyshev_interpolator<value_type, dimension, matrix_kernel_type, - scalfmm::options::low_rank_>; - using interpolator_unif_type = - scalfmm::interpolation::uniform_interpolator<value_type, dimension, matrix_kernel_type, - scalfmm::options::fft_>; - using interpolator_unif_dense_type = - scalfmm::interpolation::uniform_interpolator<value_type, dimension, matrix_kernel_type, - scalfmm::options::dense_>;matrix_kernel_type mk; - int order{6}; - int N = 10000; - std::vector<value_type> points(N, 0.0); - const auto seed{0}; - std::mt19937_64 gen(seed); - std::uniform_real_distribution<> dist(-1, 1); - for(auto& v: points) +// Function that checks an interpolator +template<typename InterpolatorType> +void process_interpolator(const int& N, const int& order) +{ + using value_type = typename InterpolatorType::value_type; + using simd_type = xsimd::simd_type<value_type>; + constexpr std::size_t simd_size = simd_type::size; + + cpp_tools::timers::timer<std::chrono::nanoseconds> time{}; + + std::size_t tree_height{3}; + value_type root_cell_width{1.}; + + InterpolatorType interp(order, tree_height, root_cell_width); + xt::xarray<value_type> roots = interp.roots_impl(); + std::size_t vec_size = order - order % simd_size; + + std::cout << std::fixed << std::endl; + + // print roots + std::cout << cpp_tools::colors::green; + std::cout << "roots: " << std::endl; + std::cout << cpp_tools::colors::reset; + std::cout << std::showpos; + for(const auto& r: roots) { - v = dist(gen); + std::cout << r << " "; } + std::cout << std::noshowpos << std::endl; - cpp_tools::timers::timer<std::chrono::nanoseconds> time{}; + // evaluate each polynomial at each root + std::cout << cpp_tools::colors::green; + std::cout << "P_o(roots): " << std::endl; + std::cout << cpp_tools::colors::reset; + std::cout << std::showpos; + for(unsigned int o = 0; o < order; ++o) + { + for(const auto& r: roots) + { + std::cout << interp.polynomials_impl(r, o) << " "; + } + std::cout << std::endl; + } + std::cout << std::noshowpos; - interpolator_cheb_type inter_cheb(mk, order); - interpolator_unif_type inter_unif(mk, order); - interpolator_unif_dense_type inter_unif_dense(mk, order); + // evaluate each polynomial at each root (using simd) + std::cout << cpp_tools::colors::green; + std::cout << "P_o(roots) [simd]: " << std::endl; + std::cout << cpp_tools::colors::reset; + std::cout << std::showpos; + for(unsigned int o = 0; o < order; ++o) + { + for(std::size_t k = 0; k < vec_size; k += simd_size) + { + simd_type roots_simd = xsimd::load_aligned(&roots[k]); + std::cout << interp.polynomials_impl(roots_simd, o) << " "; + } + for(std::size_t k = vec_size; k < order; ++k) + { + std::cout << interp.polynomials_impl(roots[k], o) << " "; + } + std::cout << std::endl; + } + std::cout << std::noshowpos; - value_type val{}; - time.tic(); - for(auto v: points) + // evaluate each derivative at each root + std::cout << cpp_tools::colors::green; + std::cout << "dP_o(roots)/dx: " << std::endl; + std::cout << cpp_tools::colors::reset; + std::cout << std::showpos; + for(unsigned int o = 0; o < order; ++o) { - val = inter_cheb.polynomials_impl(v, order - 1); + for(const auto& r: roots) + { + std::cout << interp.derivative_impl(r, o) << " "; + } + std::cout << std::endl; } - time.tac(); - std::cout << " cheb polynomials_impl in " << time.elapsed() << " ms" << std::endl; - std::cout << " val " << val << std::endl; + std::cout << std::noshowpos; - time.tic(); - for(auto v: points) + // evaluate each derivative at each root (using simd) + std::cout << cpp_tools::colors::green; + std::cout << "dP_o(roots)/dx [simd]: " << std::endl; + std::cout << cpp_tools::colors::reset; + std::cout << std::showpos; + for(unsigned int o = 0; o < order; ++o) { - val = inter_cheb.derivative_impl(v, order - 1); + for(std::size_t k = 0; k < vec_size; k += simd_size) + { + simd_type roots_simd = xsimd::load_aligned(&roots[k]); + std::cout << interp.derivative_impl(roots_simd, o) << " "; + } + for(std::size_t k = vec_size; k < order; ++k) + { + std::cout << interp.derivative_impl(roots[k], o) << " "; + } + std::cout << std::endl; } - time.tac(); - std::cout << " cheb derivative_impl in " << time.elapsed() << " ms" << std::endl; - std::cout << " val " << val << std::endl; - ////////////////////////////////////////// - // Uniform - time.tic(); - for(auto v: points) + std::cout << std::noshowpos; + + // check wether the sums of the derivatives vanish + std::cout << cpp_tools::colors::green; + std::cout << "sums: " << std::endl; + std::cout << cpp_tools::colors::reset; + value_type current_sum{}, current_root{}; + std::cout << std::showpos; + for(unsigned int o = 0; o < order; ++o) { - val = inter_unif_dense.polynomials_impl(v, order - 1); + current_sum = 0.; + current_root = roots[o]; + for(unsigned int k = 0; k < order; ++k) + { + current_sum += interp.derivative_impl(current_root, k); + } + std::cout << "o = " << o << ": " << current_sum << std::endl; } - time.tac(); - auto timeold = time.elapsed(); - std::cout << " dense unif polynomials_impl in " << timeold << " ms" << std::endl; - std::cout << " val " << val << std::endl; + std::cout << std::noshowpos; - time.tic(); - for(auto v: points) + // evaluation at random points (not necessarily roots) + std::cout << cpp_tools::colors::green; + std::cout << "random evaluations: " << std::endl; + std::cout << cpp_tools::colors::reset; + std::array<value_type, 3> values{-0.12748145601659977, 0.4579302226486055, 0.24907712585461272}; + + std::cout << std::showpos; + for(unsigned int o = 0; o < order; ++o) { - val = inter_unif.polynomials_impl(v, order - 1); + for(const auto& v: values) + { + std::cout << "P_" << o << "(" << v << ") = " << interp.polynomials_impl(v, o) << "\t"; + } + std::cout << std::endl; } - time.tac(); - auto timenew = time.elapsed(); - std::cout << " fft unif polynomials_impl in " << timenew << " ms"<< " ratio " <<double(timeold)/double(timenew) << std::endl; - std::cout << " val " << val << std::endl; + for(unsigned int o = 0; o < order; ++o) + { + for(const auto& v: values) + { + std::cout << "dP_" << o << "(" << v << ")/dx = " << interp.derivative_impl(v, o) << "\t"; + } + std::cout << std::endl; + } + std::cout << std::noshowpos; + // assess computation time + std::vector<value_type> points(N, 0.0); + const auto seed{0}; + std::mt19937_64 gen(seed); + std::uniform_real_distribution<> dist(-1, 1); + for(auto& v: points) + { + v = dist(gen); + } + + std::cout << cpp_tools::colors::green; + std::cout << "polynomials: " << std::endl; + std::cout << cpp_tools::colors::reset; + value_type val{}; time.tic(); for(auto v: points) { - val = inter_unif.derivative_impl1(v, order - 1); + val = interp.polynomials_impl(v, order - 1); } time.tac(); - timeold = time.elapsed(); - std::cout << "old unif derivative_impl in " << timeold << " ms" << std::endl; - std::cout << " val " << val << std::endl; + std::cout << "- elapsed time (over " << N << " iterations): " << time.elapsed() << " ns" << std::endl; + std::cout << cpp_tools::colors::green; + std::cout << "derivative: " << std::endl; + std::cout << cpp_tools::colors::reset; time.tic(); for(auto v: points) { - val = inter_unif.derivative_impl(v, order - 1); + val = interp.derivative_impl(v, order - 1); } time.tac(); - timenew = time.elapsed() ; - std::cout << " unif derivative_impl in " << time.elapsed() << " ms" << " ratio " <<double(timeold)/double(timenew)<< std::endl; - std::cout << " val " << val << std::endl; - ///////////////// - std::cout << " Check function\n"; - auto val1 = inter_unif.polynomials_impl(points[10], order - 1); - auto val2 = inter_unif_dense.polynomials_impl(points[10], order - 1); - auto val3 = inter_cheb.polynomials_impl(points[10], order - 1); - std::cout << " val(unif_opt)" << val1 << " val(inf_dense) " << val2<< " val(cheb) " << val3 << std::endl; - std::cout << " Check derivative\n"; - auto val4 = inter_unif.derivative_impl(points[10], order - 1); - auto val5 = inter_unif_dense.derivative_impl(points[10], order - 1); - auto val6 = inter_cheb.derivative_impl(points[10], order - 1); - std::cout << " val(unif_opt) " << val4 << " val(inf_dense) " << val5<< " val(cheb) " << val6 << std::endl; - - return res; -} \ No newline at end of file + std::cout << "- elapsed time (over " << N << " iterations): " << time.elapsed() << " ns" << std::endl; +} + +auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int +{ + // Parameter handling + auto parser = + cpp_tools::cl_parser::make_parser(cpp_tools::cl_parser::help{}, args::order{}, local_args::number_of_points{}); + parser.parse(argc, argv); + + // Getting command line parameters + const int N{parser.get<local_args::number_of_points>()}; + std::cout << cpp_tools::colors::blue << "<params> Number of points : " << N << cpp_tools::colors::reset << '\n'; + const auto order{parser.get<args::order>()}; + std::cout << cpp_tools::colors::blue << "<params> order : " << order << " (degree = " << (order - 1) << ")" + << cpp_tools::colors::reset << '\n'; + + using matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r<dimension>; + + // Interpolator aliases + using interpolator_cheb_type = + interpolation::chebyshev_interpolator<value_type, dimension, matrix_kernel_type, scalfmm::options::low_rank_>; + using interpolator_unif_type = + interpolation::uniform_interpolator<value_type, dimension, matrix_kernel_type, scalfmm::options::low_rank_>; + using interpolator_barycentric_type = + interpolation::barycentric_interpolator<value_type, dimension, matrix_kernel_type, scalfmm::options::low_rank_>; + using interpolator_unif_fft_type = + interpolation::uniform_interpolator<value_type, dimension, matrix_kernel_type, scalfmm::options::fft_>; + + // Compare interpolators + std::cout << cpp_tools::colors::cyan << "\n--- CHEBYSHEV ---" << std::endl; + process_interpolator<interpolator_cheb_type>(N, order); + + std::cout << cpp_tools::colors::cyan << "\n--- BARYCENTRIC ---" << std::endl; + process_interpolator<interpolator_barycentric_type>(N, order); + + std::cout << cpp_tools::colors::cyan << "\n--- UNIFORM (fft) ---" << std::endl; + process_interpolator<interpolator_unif_fft_type>(N, order); + + std::cout << cpp_tools::colors::cyan << "\n--- UNIFORM ---" << std::endl; + process_interpolator<interpolator_unif_type>(N, order); + + return 0; +} diff --git a/checks/check_l2p.cpp b/checks/check_l2p.cpp index 080fc9485bf8a9564e9cda7429a1bfa8e22340b2..703d27134d11e8101f1105f189bb2555b5a00c90 100644 --- a/checks/check_l2p.cpp +++ b/checks/check_l2p.cpp @@ -32,46 +32,34 @@ using value_type = double; namespace functions { constexpr value_type fourpi = 12.56637; - auto cos = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return std::cos(fourpi * (x * y + std::pow(z, 4))); - }; - auto derx_cos = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return -fourpi * y * std::sin(fourpi * (x * y + std::pow(z, 4))); - }; - auto dery_cos = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return -fourpi * x * std::sin(fourpi * (x * y + std::pow(z, 4))); - }; - auto derz_cos = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return -fourpi * value_type(4.0) * std::pow(z, 3) * std::sin(fourpi * (x * y + std::pow(z, 4))); - }; + auto cos = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return std::cos(fourpi * (x * y + std::pow(z, 4))); }; + auto derx_cos = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return -fourpi * y * std::sin(fourpi * (x * y + std::pow(z, 4))); }; + auto dery_cos = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return -fourpi * x * std::sin(fourpi * (x * y + std::pow(z, 4))); }; + auto derz_cos = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return -fourpi * value_type(4.0) * std::pow(z, 3) * std::sin(fourpi * (x * y + std::pow(z, 4))); }; constexpr value_type alpha = 4.0; - auto exp = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return std::exp(-alpha * (x + y + z)); - }; - auto derx_exp = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return -alpha * std::exp(-alpha * (x + y + z)); - }; - auto dery_exp = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return -alpha * std::exp(-alpha * (x + y + z)); - }; - auto derz_exp = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return -alpha * std::exp(-alpha * (x + y + z)); - }; - - auto poly = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return x * x + x * y + (y + 1) * (1 + y) + std::pow(z + 1, 4); - }; - auto derx_poly = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return value_type(2.0) * x + y; - }; - auto dery_poly = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return x + 2 * (y + 1); - }; - auto derz_poly = [](const value_type& x, const value_type& y, const value_type& z) -> value_type { - return value_type(4.0) * std::pow(z + 1, 3); - }; + auto exp = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return std::exp(-alpha * (x + y + z)); }; + auto derx_exp = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return -alpha * std::exp(-alpha * (x + y + z)); }; + auto dery_exp = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return -alpha * std::exp(-alpha * (x + y + z)); }; + auto derz_exp = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return -alpha * std::exp(-alpha * (x + y + z)); }; + + auto poly = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return x * x + x * y + (y + 1) * (1 + y) + std::pow(z + 1, 4); }; + auto derx_poly = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return value_type(2.0) * x + y; }; + auto dery_poly = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return x + 2 * (y + 1); }; + auto derz_poly = [](const value_type& x, const value_type& y, const value_type& z) -> value_type + { return value_type(4.0) * std::pow(z + 1, 3); }; } // namespace functions template<typename far_field_type> auto run(const std::string& title, const std::string& input_file, const int& tree_height, const int& group_size, @@ -184,7 +172,8 @@ auto run(const std::string& title, const std::string& input_file, const int& tre scalfmm::component::for_each_cell_leaf( std::begin(gtree), std::end(gtree), static_cast<std::size_t>(tree_height), - [&pos, &order, &roots_x, &roots_y, &roots_z, &global_idx, &far_field](auto& cell, auto& leaf) { + [&pos, &order, &roots_x, &roots_y, &roots_z, &global_idx, &far_field](auto& cell, auto& leaf) + { auto func = functions::poly; auto derx_func = functions::derx_poly; auto dery_func = functions::dery_poly; @@ -275,15 +264,16 @@ struct chebInterp auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int { // Parameter handling - auto parser = cpp_tools::cl_parser::make_parser(args::tree_height{}, args::order{}, args::input_file{}, chebInterp{}, - cpp_tools::cl_parser::help{}); + auto parser = cpp_tools::cl_parser::make_parser(args::tree_height{}, args::order{}, args::input_file{}, + chebInterp{}, cpp_tools::cl_parser::help{}); parser.parse(argc, argv); std::cout << cpp_tools::colors::blue << "Entering tree test...\n" << cpp_tools::colors::reset; const int tree_height{parser.get<args::tree_height>()}; - std::cout << cpp_tools::colors::blue << "<params> Tree height : " << tree_height << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Tree height : " << tree_height << cpp_tools::colors::reset + << '\n'; const int group_size{1}; std::cout << cpp_tools::colors::blue << "<params> Group Size : " << group_size << cpp_tools::colors::reset << '\n'; @@ -291,7 +281,8 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int const std::string input_file{parser.get<args::input_file>()}; if(!input_file.empty()) { - std::cout << cpp_tools::colors::blue << "<params> Input file : " << input_file << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Input file : " << input_file << cpp_tools::colors::reset + << '\n'; } const bool useCheb(parser.exists<chebInterp>()); diff --git a/checks/check_morton_sort.cpp b/checks/check_morton_sort.cpp index d55903ce966d11c4a27650db8841386fbc0a6c87..cac04a8b458b410cade7a632da8e726f9c46f000 100644 --- a/checks/check_morton_sort.cpp +++ b/checks/check_morton_sort.cpp @@ -189,7 +189,7 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int } if(check_different_morton_index) { - const int neighbour_separation = 1; + const int neighbour_separation = 1; for(std::size_t i = 0; i < number_of_particles; ++i) { position_type nodes = container->position(i); diff --git a/checks/check_mpi.cpp b/checks/check_mpi.cpp index 5ca4fea82cf61b647f7333ff1ea5a17782ad7811..915d27dcab39a8e2437e1b07710c80b02ed704ab 100644 --- a/checks/check_mpi.cpp +++ b/checks/check_mpi.cpp @@ -239,15 +239,18 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int if(para.io_master()) { - std::cout << cpp_tools::colors::blue << "<params> Tree height: " << tree_height << cpp_tools::colors::reset << '\n'; - std::cout << cpp_tools::colors::blue << "<params> Group Size: " << group_size << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Tree height: " << tree_height << cpp_tools::colors::reset + << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Group Size: " << group_size << cpp_tools::colors::reset + << '\n'; std::cout << cpp_tools::colors::blue << "<params> order: " << order << cpp_tools::colors::reset << '\n'; if(!input_file.empty()) { std::cout << cpp_tools::colors::blue << "<params> Input file: " << input_file << cpp_tools::colors::reset << '\n'; } - std::cout << cpp_tools::colors::blue << "<params> Output file: " << output_file << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Output file: " << output_file << cpp_tools::colors::reset + << '\n'; std::cout << cpp_tools::colors::blue << "<params> Particle Distribution: " << std::boolalpha << use_particle_distribution << cpp_tools::colors::reset << '\n'; std::cout << cpp_tools::colors::blue << "<params> Leaf Distribution: " << std::boolalpha diff --git a/checks/check_p2p.cpp b/checks/check_p2p.cpp index 722d5a83587d230be854338db3160b617f768185..b8015c84cd53e4174f136f286db8d54ac1b04bfd 100644 --- a/checks/check_p2p.cpp +++ b/checks/check_p2p.cpp @@ -1,8 +1,6 @@ -#include <chrono> -#include <cassert> -#include <cpp_tools/colors/colorized.hpp> -#include <cpp_tools/timers/simple_timer.hpp> +// Be carefull with this header file +#include "scalfmm/container/access.hpp" #include "scalfmm/container/particle.hpp" #include "scalfmm/container/particle_container.hpp" @@ -11,117 +9,169 @@ #include "scalfmm/operators/p2p.hpp" #include "scalfmm/tools/fma_loader.hpp" #include "scalfmm/tree/box.hpp" -#include "scalfmm/tree/leaf.hpp" +// #include "scalfmm/tree/leaf.hpp" +#include "scalfmm/tree/group_of_views.hpp" +#include "scalfmm/tree/leaf_view.hpp" #include "scalfmm/utils/generate.hpp" #include "scalfmm/utils/parameters.hpp" -namespace paramP2P +#include <cpp_tools/colors/colorized.hpp> +#include <cpp_tools/timers/simple_timer.hpp> + +#include <cassert> +#include <chrono> + +namespace local_args { struct nbParticles : cpp_tools::cl_parser::required_tag { - cpp_tools::cl_parser::str_vec flags = { "--number_particles", "-n", "-N"}; + cpp_tools::cl_parser::str_vec flags = {"--number_particles", "-n", "-N"}; std::string description = "Number of particles to generate"; using type = int; std::string input_hint = "int"; /*!< The input hint */ }; -struct dimensionSpace : cpp_tools::cl_parser::required_tag + struct dimensionSpace : cpp_tools::cl_parser::required_tag { - cpp_tools::cl_parser::str_vec flags = {"--dimension", "--dim", "-d"}; + cpp_tools::cl_parser::str_vec flags = {"--dimension", "--dim", "-d"}; std::string description = "Dimension of the space (1,2 or 3)"; using type = int; std::string input_hint = "int"; /*!< The input hint */ }; +} // namespace local_args + +template<typename LeafType, typename ContainerType> +auto init_leaf(LeafType& leaf, ContainerType const& container) -> void +{ + auto leaf_container_begin = leaf.particles().first; + + for(std::size_t index_part = 0; index_part < leaf.size(); ++index_part) + { + *leaf_container_begin = container.at(index_part).as_tuple(); + ++leaf_container_begin; + } } using timer_type = cpp_tools::timers::timer<std::chrono::microseconds>; - template<int Dimension, typename value_type, typename matrix_kernel_type> -void run_inner(const int nb_exp, const int nb_particles, matrix_kernel_type & near) +auto run_inner(const int nb_exp, const std::size_t nb_particles, matrix_kernel_type& near) -> void { - static constexpr std::size_t nb_inputs_near{matrix_kernel_type::km}; static constexpr std::size_t nb_outputs_near{matrix_kernel_type::kn}; // --------------------------------------- using particle_type = scalfmm::container::particle<value_type, Dimension, value_type, nb_inputs_near, value_type, nb_outputs_near, value_type>; using point_type = scalfmm::container::point<value_type, Dimension>; - using leaf_type = scalfmm::component::leaf<particle_type>; + // using leaf_type = scalfmm::component::leaf<particle_type>; + + using leaf_type = scalfmm::component::leaf_view<particle_type>; + using storage_type = typename leaf_type::storage_type; point_type center{scalfmm::utils::get_center<value_type, Dimension>()}; const value_type width{0.125}; - auto container{scalfmm::utils::generate_particles<particle_type>(nb_particles, center, width)}; + auto target_container{scalfmm::utils::generate_particles<particle_type>(nb_particles, center, width)}; //std::cout << container << std::endl; - leaf_type target_leaf(std::cbegin(container), std::cend(container), - container.size(), center, width, 0); - - point_type shift(0.25) ; - center += shift ; - // shift container - auto container1{scalfmm::utils::generate_particles<particle_type>(nb_particles/2, center, width)}; - leaf_type source_leaf(std::cbegin(container), std::cend(container), - container.size(), center, width, 0); + // leaf_type target_leaf(std::cbegin(container), std::cend(container), container.size(), center, width, 0); + + // Create a target leaf view (view on a distinct storage) + storage_type target_storage(nb_particles, 1); + // leaf_type target_leaf(std::make_pair(std::begin(target_storage), std::end(target_storage)), + // target_storage.symbolics()); + leaf_type target_leaf(std::make_pair(std::begin(target_storage), std::begin(target_storage) + nb_particles), + target_storage.symbolics()); + target_leaf.center() = center; + target_leaf.width() = width; + init_leaf(target_leaf, target_container); + + // Shift center of the first leaf + point_type shift(0.25); + center += shift; + + // shifted container + std::size_t nb_source_particles = nb_particles / 2; + auto source_container{scalfmm::utils::generate_particles<particle_type>(nb_source_particles, center, width)}; + // leaf_type source_leaf(std::cbegin(container), std::cend(container), container.size(), center, width, 0); + + // Create a source leaf view (view on a distinct storage) + storage_type source_storage(nb_particles / 2, 1); + // leaf_type source_leaf(std::make_pair(std::begin(source_storage), std::end(source_storage)), + // source_storage.symbolics()); + leaf_type source_leaf(std::make_pair(std::begin(source_storage), std::begin(source_storage) + nb_source_particles), + source_storage.symbolics()); + source_leaf.center() = center; + source_leaf.width() = width; + init_leaf(source_leaf, source_container); + timer_type timer{}; timer.tic(); - for (int i= 0; i < nb_exp ; ++i){ - scalfmm::operators::p2p_inner(near, target_leaf); + for(int i = 0; i < nb_exp; ++i) + { + scalfmm::operators::p2p_inner(near, target_leaf); } timer.tac(); - std::cout << "time for p2p_inner " << timer.elapsed() << std::endl; + std::cout << "time for p2p_inner " << timer.elapsed() << std::endl; + const std::array<bool, 3> pbc{false, false, false}; - std::array<leaf_type*,1> neighbors{&source_leaf}; + std::array<leaf_type*, 1> neighbors{&source_leaf}; timer.tic(); - for (int i= 0; i < nb_exp ; ++i){ - scalfmm::operators::p2p_full_mutual(near, target_leaf, neighbors, 1, pbc, width); + for(int i = 0; i < nb_exp; ++i) + { + scalfmm::operators::p2p_full_mutual(near, target_leaf, neighbors, 1, pbc, width); } timer.tac(); - auto t1 = timer.elapsed() ; + auto t1 = timer.elapsed(); + std::cout << "time for p2p_full_mutual " << t1 << std::endl; - std::cout << "time for p2p_full_mutual " << t1 << std::endl; timer.tic(); - for (int i= 0; i < nb_exp ; ++i){ + for(int i = 0; i < nb_exp; ++i) + { scalfmm::operators::p2p_outer(near, target_leaf, source_leaf, shift); scalfmm::operators::p2p_outer(near, source_leaf, target_leaf, shift); } timer.tac(); - auto t2 = timer.elapsed() ; - std::cout << "time for 2 p2p_outer = p2p_full_mutual " << t2 << " gain: " << t2-t1 << " % " << 1-double(t2)/double(t1) << std::endl; + auto t2 = timer.elapsed(); + std::cout << "time for 2 p2p_outer = p2p_full_mutual " << t2 << " gain: " << t2 - t1 << " % " + << 1 - double(t2) / double(t1) << std::endl; timer.tic(); - for (int i= 0; i < nb_exp ; ++i){ + for(int i = 0; i < nb_exp; ++i) + { scalfmm::operators::p2p_outer(near, target_leaf, source_leaf, shift); } timer.tac(); - std::cout << "time for p2p_outer " << timer.elapsed() << std::endl; + std::cout << "time for p2p_outer " << timer.elapsed() << std::endl; // } + auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int { // Parameter handling - auto parser = cpp_tools::cl_parser::make_parser(paramP2P::nbParticles{}, paramP2P::dimensionSpace{}, - cpp_tools::cl_parser::help{}); + auto parser = cpp_tools::cl_parser::make_parser(local_args::nbParticles{}, local_args::dimensionSpace{}, + cpp_tools::cl_parser::help{}); parser.parse(argc, argv); std::cout << cpp_tools::colors::blue << "Entering P2P test...\n" << cpp_tools::colors::reset; - const int nb_particles{parser.get<paramP2P::nbParticles>()}; - std::cout << cpp_tools::colors::blue << "<params> nbParticles : " << nb_particles << cpp_tools::colors::reset << '\n'; - const int dimension{parser.get<paramP2P::dimensionSpace>()}; + const int nb_particles{parser.get<local_args::nbParticles>()}; + std::cout << cpp_tools::colors::blue << "<params> nbParticles : " << nb_particles << cpp_tools::colors::reset + << '\n'; + const int dimension{parser.get<local_args::dimensionSpace>()}; std::cout << cpp_tools::colors::blue << "<params> dim : " << dimension << cpp_tools::colors::reset << '\n'; int nb_experiments = 50; - if(dimension==3){ + if(dimension == 3) + { using matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r<3>; matrix_kernel_type mk{}; - run_inner<3,double>(nb_experiments, nb_particles, mk); + run_inner<3, double>(nb_experiments, nb_particles, mk); } - else{ - throw std::invalid_argument("The dimension is wrong (1,2 or 3)"); + else + { + throw std::invalid_argument("The dimension is wrong (only dimension = 3)"); } - } diff --git a/checks/check_points.cpp b/checks/check_points.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df4e2a5f4f48ae1eeeb4ab2075a715e8e275d570 --- /dev/null +++ b/checks/check_points.cpp @@ -0,0 +1,75 @@ +/** + * @file check_points.cpp + * @author Antoine Gicquel + * @brief This file checks the equality operator of the points. + * + * @date 2025-06-01 + * + */ + +#include "scalfmm/container/point.hpp" + +template<typename ValueType, std::size_t Dimension> +auto run_equality_point() -> void +{ + using value_type = ValueType; + static constexpr std::size_t dimension = Dimension; + + std::cout << std::boolalpha; + + // Test equality operator with classical points + using point_impl_type = scalfmm::container::point<value_type, dimension>; + + point_impl_type p1(1); + point_impl_type p2(1); + point_impl_type p3(2); + + std::cout << "- p1 = " << p1 << std::endl; + std::cout << "- p2 = " << p2 << std::endl; + std::cout << "- p3 = " << p3 << std::endl; + std::cout << "- p1 == p2 ? : " << (p1 == p2) << std::endl; + std::cout << "- p1 == p3 ? : " << (p1 == p3) << std::endl; + std::cout << std::endl; + + // Test equality operator with proxy points (points of references) + using proxy_point_type = scalfmm::container::point<value_type&, dimension>; + + proxy_point_type p4(p1); + proxy_point_type p5(p2); + proxy_point_type p6(p3); + + std::cout << "- p4 = " << p4 << std::endl; + std::cout << "- p5 = " << p5 << std::endl; + std::cout << "- p6 = " << p6 << std::endl; + std::cout << "- p4 == p5 ? : " << (p4 == p5) << std::endl; + std::cout << "- p4 == p6 ? : " << (p4 == p6) << std::endl; + std::cout << std::endl; + + // Test equality operator with points of SIMD type + using simd_type = xsimd::simd_type<value_type>; + using point_simd_type = scalfmm::container::point<simd_type, dimension>; + + simd_type batch1(1); + simd_type batch2(1); + simd_type batch3(2); + + point_simd_type p7(batch1); + point_simd_type p8(batch2); + point_simd_type p9(batch3); + + std::cout << "- p7 = " << p7 << std::endl; + std::cout << "- p8 = " << p8 << std::endl; + std::cout << "- p9 = " << p9 << std::endl; + std::cout << "- p7 == p8 ? : " << (p7 == p8) << std::endl; + std::cout << "- p7 == p9 ? : " << (p7 == p9) << std::endl; + std::cout << std::endl; +} + +auto main() -> int +{ + run_equality_point<int, 3>(); + run_equality_point<float, 3>(); + run_equality_point<double, 3>(); + + return 0; +} diff --git a/checks/check_reset.cpp b/checks/check_reset.cpp new file mode 100644 index 0000000000000000000000000000000000000000..89eccb577c304d6653ca5e5b3b5f505dafec4bf5 --- /dev/null +++ b/checks/check_reset.cpp @@ -0,0 +1,271 @@ +/** + * @file check_reset.cpp + * @author Antoine Gicquel + * @brief This file checks the reset functions of various containers. + * + * @date 2025-05-01 + * + */ + +#include "scalfmm/container/particle.hpp" +#include "scalfmm/container/point.hpp" +#include "scalfmm/interpolation/interpolation.hpp" +#include "scalfmm/matrix_kernels/laplace.hpp" +#include "scalfmm/meta/is_valid.hpp" +#include "scalfmm/meta/type_pack.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/operators/fmm_operators.hpp" +#include "scalfmm/operators/l2p.hpp" +#include "scalfmm/tree/cell.hpp" +#include "scalfmm/tree/group_of_views.hpp" +// #include "scalfmm/tree/leaf.hpp" // Old leaf format +#include "scalfmm/tree/leaf_view.hpp" +#include "scalfmm/utils/generate.hpp" + +#include <algorithm> +#include <iostream> +#include <random> +#include <vector> + +/** + * @brief + * + * @tparam LeafType + * @tparam ParticleType + * @param container + */ +template<typename ParticleType, typename ContainerType> +auto init_leaf(scalfmm::component::leaf_view<ParticleType>& leaf, ContainerType const& container) -> void +{ + auto leaf_container_begin = leaf.particles().first; + + for(std::size_t index_part = 0; index_part < leaf.size(); ++index_part) + { + *leaf_container_begin = container.at(index_part).as_tuple(); + ++leaf_container_begin; + } +} + +/** + * @brief + * + * @tparam ParticleType + * @param container + */ +template<typename ParticleType> +auto init_container(std::vector<ParticleType>& container) -> void +{ + using particle_type = ParticleType; + const std::size_t N{container.size()}; + + for(std::size_t idx = 0; idx < N; ++idx) + { + int value = 0; + particle_type& p = container[idx]; + scalfmm::meta::for_each(p.position(), [&value](auto& v) { return v = ++value; }); + scalfmm::meta::for_each(p.inputs(), [&value](auto& v) { return v = ++value; }); + scalfmm::meta::for_each(p.outputs(), [&value](auto& v) { return v = ++value; }); + scalfmm::meta::for_each(p.variables(), [&value](auto& v) { return v = ++value; }); + } +} + +/** + * @brief + * + * @tparam ParticleType + * @param container + */ +template<typename ParticleType> +auto init_container(scalfmm::container::particle_container<ParticleType>& container) -> void +{ + const std::size_t N{container.size()}; + + auto it = std::begin(container); + for(std::size_t idx = 0; idx < N; ++idx) + { + int value = 0; + scalfmm::meta::for_each(*it, [&value](auto& v) { return v = ++value; }); + ++it; + } +} + +/** + * @brief + * + * @tparam OutputStreamType + * @tparam ParticleType + * @param os + * @param particles + */ +template<typename OutputStreamType, typename ParticleType> +auto operator<<(OutputStreamType& os, const std::vector<ParticleType>& particles) -> OutputStreamType& +{ + for(auto const& part: particles) + { + os << part << std::endl; + + return os; + } +} + +/** + * @brief + * + * @tparam ParticleType + * @param N + */ +// template<typename ParticleType> +// auto run_old_leaf(std::size_t N) -> void +// { +// using particle_type = ParticleType; +// static constexpr std::size_t dimension = particle_type::dimension; + +// using value_type = typename particle_type::position_value_type; +// using point_type = typename particle_type::position_type; +// using leaf_type = scalfmm::component::leaf<particle_type>; +// using container_type = scalfmm::container::particle_container<particle_type>; + +// // init base container +// container_type leaf_container(N); +// init_container(leaf_container); + +// // create leaf (old format) from container +// const point_type center{scalfmm::utils::get_center<value_type, dimension>()}; +// const value_type width{0.25}; +// leaf_type leaf(std::cbegin(leaf_container), std::cend(leaf_container), N, center, width, 0); + +// std::cout << leaf.particles() << std::endl; + +// // reset positions in the leaf +// leaf.reset_positions(); +// std::cout << leaf.particles() << std::endl; + +// // reset inputs in the leaf +// leaf.reset_inputs(); +// std::cout << leaf.particles() << std::endl; + +// // reset outputs in the leaf +// leaf.reset_outputs(); +// std::cout << leaf.particles() << std::endl; + +// // reset variables in the leaf +// leaf.reset_variables(); +// std::cout << leaf.particles() << std::endl; + +// // create a new leaf again from container +// leaf = leaf_type(std::cbegin(leaf_container), std::cend(leaf_container), N, center, width, 0); +// std::cout << leaf.particles() << std::endl; + +// // rest all particles in the leaf +// leaf.reset_particles(); +// std::cout << leaf.particles() << std::endl; +// } + +/** + * @brief + * + * @tparam ParticleType + * @param N + */ +template<typename ParticleType> +auto run_leaf_view(std::size_t N) -> void +{ + using particle_type = ParticleType; + using leaf_type = scalfmm::component::leaf_view<particle_type>; + using container_type = std::vector<particle_type>; + using storage_type = typename leaf_type::storage_type; + + // init container + container_type container(N); + init_container(container); + + // create block storage and leaf view on this storage + storage_type storage(N, 1); + leaf_type leaf(std::make_pair(std::begin(storage), std::begin(storage) + N), storage.symbolics()); + + // init leaf (and the underlying storage) from the container + init_leaf(leaf, container); + std::cout << storage << std::endl; + + // create a leaf view only on the first half of the storage + leaf_type first_leaf(std::make_pair(std::begin(storage), std::begin(storage) + N / 2), storage.symbolics()); + + // reset the positions on the first half of the storage + first_leaf.reset_positions(); + std::cout << storage << std::endl; + + // reset the inputs on the first half of the storage + first_leaf.reset_inputs(); + std::cout << storage << std::endl; + + // reset the outputs on the first half of the storage + first_leaf.reset_outputs(); + std::cout << storage << std::endl; + + // reset the variables on the first half of the storage + first_leaf.reset_variables(); + std::cout << storage << std::endl; + + // init leaf (and the underlying storage) from the container + init_leaf(leaf, container); + std::cout << storage << std::endl; + + // reset the particles on the first half of the storage + first_leaf.reset_particles(); + std::cout << storage << std::endl; +} + +template<typename ParticleType> +auto run_variadic_container(std::size_t N) -> void +{ + using particle_type = ParticleType; + using container_type = scalfmm::container::particle_container<particle_type>; + + // init container + container_type container(N); + init_container(container); + std::cout << container << std::endl; + + // reset positions in the container + container.reset_positions(); + std::cout << container << std::endl; + + // reset the inputs in the container + container.reset_inputs(); + std::cout << container << std::endl; + + // reset the outputs in the container + container.reset_outputs(); + std::cout << container << std::endl; + + // reset the variables in the container + container.reset_variables(); + std::cout << container << std::endl; + + // init again the container + init_container(container); + std::cout << container << std::endl; + + // reset the particles in the container + container.reset_particles(); + std::cout << container << std::endl; +} + +auto main() -> int +{ + using value_type = double; + static constexpr std::size_t dimension = 3; + static constexpr std::size_t nb_inputs = 4; + static constexpr std::size_t nb_outputs = 4; + + std::size_t N{10}; + + using particle_type = scalfmm::container::particle<value_type, dimension, value_type, nb_inputs, value_type, + nb_outputs, std::size_t, float, double, int>; + + // run_old_leaf<particle_type>(N); + run_leaf_view<particle_type>(N); + run_variadic_container<particle_type>(N); + + return 0; +} diff --git a/checks/count_particles_gen.hpp b/checks/count_particles_gen.hpp index 016d9c92440a7a84a9dc0ffe13e875d72b8ed9d2..82be3ea72fb6d11858970f6d3ed7b3a6d2998a09 100644 --- a/checks/count_particles_gen.hpp +++ b/checks/count_particles_gen.hpp @@ -224,7 +224,7 @@ auto run(const int& tree_height, const int& group_size, Array const& pbc, const count_kernels::particles::count_far_field<Dimension> ff{}; fmm_operator_type fmm_operator(nf, ff); // auto operator_to_proceed = scalfmm::algorithms::all; - auto operator_to_proceed = scalfmm::algorithms::farfield; + auto operator_to_proceed = scalfmm::algorithms::farfield; auto separation_criterion = fmm_operator.near_field().separation_criterion(); scalfmm::list::sequential::build_interaction_lists(tree, tree, separation_criterion, mutual); diff --git a/checks/count_particles_mpi.cpp b/checks/count_particles_mpi.cpp index ed584a4dc0891ce474ae4a28c720614ccf4ef4b2..52b147c0b04c8498d17a3dd5104211e059160859 100644 --- a/checks/count_particles_mpi.cpp +++ b/checks/count_particles_mpi.cpp @@ -286,11 +286,10 @@ auto run(cpp_tools::parallel_manager::parallel_manager& para, const int& tree_he { std::cout << cpp_tools::colors::blue << "Print tree distribution\n"; letTree.print_distrib(std::cout); - std::cout << "\n trace 2\n" << std::flush; + std::cout << "\n trace 2\n" << std::flush; - scalfmm::io::trace(std::cout, letTree, 2); + scalfmm::io::trace(std::cout, letTree, 2); std::cout << cpp_tools::colors::reset; - } #ifdef SCALFMM_BUILD_PBC @@ -314,16 +313,16 @@ auto run(cpp_tools::parallel_manager::parallel_manager& para, const int& tree_he scalfmm::list::sequential::build_interaction_lists(letTree, letTree, separation_criterion, mutual); std::cout << cpp_tools::colors::red << "trace \n" << cpp_tools::colors::reset << std::flush; // if(para.io_master()) - { - std::cout << cpp_tools::colors::red << "trace 4\n" << cpp_tools::colors::reset << std::flush; + { + std::cout << cpp_tools::colors::red << "trace 4\n" << cpp_tools::colors::reset << std::flush; - scalfmm::io::trace(std::cout, letTree, 4); - } + scalfmm::io::trace(std::cout, letTree, 4); + } - // auto operator_to_proceed = scalfmm::algorithms::operators_to_proceed::farfield; - // auto operator_to_proceed = scalfmm::algorithms::operators_to_proceed::nearfield; - auto operator_to_proceed = scalfmm::algorithms::operators_to_proceed::all; - // auto operator_to_proceed = (scalfmm::algorithms::operators_to_proceed::p2m | scalfmm::algorithms::operators_to_proceed::m2l); + // auto operator_to_proceed = scalfmm::algorithms::operators_to_proceed::farfield; + // auto operator_to_proceed = scalfmm::algorithms::operators_to_proceed::nearfield; + auto operator_to_proceed = scalfmm::algorithms::operators_to_proceed::all; + // auto operator_to_proceed = (scalfmm::algorithms::operators_to_proceed::p2m | scalfmm::algorithms::operators_to_proceed::m2l); // auto operator_to_proceed = (scalfmm::algorithms::operators_to_proceed::p2m | scalfmm::algorithms::operators_to_proceed::m2m | scalfmm::algorithms::operators_to_proceed::m2l) ; scalfmm::algorithms::mpi::proc_task(letTree, fmm_operator, operator_to_proceed); @@ -366,12 +365,12 @@ auto run(cpp_tools::parallel_manager::parallel_manager& para, const int& tree_he std::cout << "wrong number of particles - nb particles (min) " << nb_particles_min << " (max) " << nb_particles_max << " (expected) " << number_of_particles << std::endl; - if(para.io_master()) - std::cout << "Save Tree in parallel\n"; - // std::string outName("saveTree_" + std::to_string(rank) + ".bin"); - std::string outName("saveTreeLet.bin"); - std::string header("CHEBYSHEV LOW RANK "); - scalfmm::tools::io::save(para, outName, letTree, header); + if(para.io_master()) + std::cout << "Save Tree in parallel\n"; + // std::string outName("saveTree_" + std::to_string(rank) + ".bin"); + std::string outName("saveTreeLet.bin"); + std::string header("CHEBYSHEV LOW RANK "); + scalfmm::tools::io::save(para, outName, letTree, header); } return 0; @@ -467,7 +466,8 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int use_leaf_distribution, use_particle_distribution); } else if(dimension == 2) - */ { + */ + { run<2>(para, tree_height, group_size, pbc, nb_level_above_root, readFile, input_file, interaction, use_leaf_distribution, use_particle_distribution); } diff --git a/checks/count_particles_st_gen.hpp b/checks/count_particles_st_gen.hpp index fc9224aa8c599362523b711bfb57719aebebec56..360454c565eb35fefec1830eb7a22ae2f08c36c6 100644 --- a/checks/count_particles_st_gen.hpp +++ b/checks/count_particles_st_gen.hpp @@ -160,13 +160,12 @@ auto read_data(const std::string& filename) p.variables(idx); container[idx] = p; } - return std::make_tuple(container, center, width); + return std::make_tuple(container, center, width); } template<int Dimension, typename value_type /*, typename Array*/> auto run(const int& tree_height, const int& group_size, /*Array const& pbc, const int nb_level_above_root,*/ - const bool readFile, std::string& input_source_file, std::string& input_target_file, const bool mutual) - ->int + const bool readFile, std::string& input_source_file, std::string& input_target_file, const bool mutual) -> int { const auto order = 1; // Parameter handling @@ -242,11 +241,11 @@ auto run(const int& tree_height, const int& group_size, /*Array const& pbc, cons { box_width_target = value_type(1.0); box_width_source = value_type(1.0); - container_target = - scalfmm::utils::generate_particle_per_leaf<container_target_type>(box_width_target, tree_height, value_type(+1.0)); - container_source = - scalfmm::utils::generate_particle_per_leaf<container_source_type>(box_width_source, tree_height, value_type(-1.0)); - std::cout << " particles_target \n"; + container_target = scalfmm::utils::generate_particle_per_leaf<container_target_type>( + box_width_target, tree_height, value_type(+1.0)); + container_source = scalfmm::utils::generate_particle_per_leaf<container_source_type>( + box_width_source, tree_height, value_type(-1.0)); + std::cout << " particles_target \n"; } box_type box_source(box_width_source, box_center_source); box_type box_target(box_width_target, box_center_target); @@ -331,16 +330,16 @@ auto run(const int& tree_height, const int& group_size, /*Array const& pbc, cons auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int { // Parameter handling - auto parser = cpp_tools::cl_parser::make_parser( - cpp_tools::cl_parser::help{}, local_args::dimension(), local_args::input_source_file(), - local_args::input_target_file(), args::tree_height{}, args::block_size{}, + auto parser = cpp_tools::cl_parser::make_parser(cpp_tools::cl_parser::help{}, local_args::dimension(), + local_args::input_source_file(), local_args::input_target_file(), + args::tree_height{}, args::block_size{}, #ifdef COUNT_USE_OPENMP - args::thread_count{}, + args::thread_count{}, #endif - args::log_file{}, args::log_level{} // #ifdef scalfmm_BUILD_PBC - // ,args::pbc{}, - // args::extended_tree_height{} // periodicity - // #endif + args::log_file{}, args::log_level{} // #ifdef scalfmm_BUILD_PBC + // ,args::pbc{}, + // args::extended_tree_height{} // periodicity + // #endif ); parser.parse(argc, argv); diff --git a/checks/debug_omp.cpp b/checks/debug_omp.cpp index 19d63ee2dd28f2e42eaf2af7619f93fe447e3e58..d3e2acf594a9fbe565bf13311aa180dca8bc52b7 100644 --- a/checks/debug_omp.cpp +++ b/checks/debug_omp.cpp @@ -240,8 +240,8 @@ auto run(const std::string& input_file, const int& tree_height, const int& group container_omp.reset_outputs(); group_tree_type tree_omp(static_cast<std::size_t>(tree_height), order, box, static_cast<std::size_t>(group_size), static_cast<std::size_t>(group_size), container_omp); - scalfmm::algorithms::fmm[scalfmm::options::_s(scalfmm::options::omp, scalfmm::options::timit)](tree_omp, fmm_operator, - operator_to_proceed); + scalfmm::algorithms::fmm[scalfmm::options::_s(scalfmm::options::omp, scalfmm::options::timit)]( + tree_omp, fmm_operator, operator_to_proceed); // std::cout << "Direct particles \n"; // std::cout << container_direct << std::endl; @@ -255,7 +255,6 @@ auto run(const std::string& input_file, const int& tree_height, const int& group { std::cout << cpp_tools::colors::red << " Test is WRONG !! the error must be around " << eps << " \n " << cpp_tools::colors::reset; - } delete container; return 0; @@ -283,9 +282,9 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int catch(std::exception& e) { std::cerr << "Exception has occurred!" << std::endl; - std::cerr << " use --help to see the parameters\n"; + std::cerr << " use --help to see the parameters\n"; std::cerr << e.what() << std::endl; - throw ; + throw; } // Getting command line parameters const int tree_height{parser.get<args::tree_height>()}; diff --git a/checks/interactions_results.hpp b/checks/interactions_results.hpp index 5ce2bc36cfb6d7e0b8cca032f8d75c35ba10aa36..7dc10f27c431341f87b1e7729fd46b9b93f7852f 100644 --- a/checks/interactions_results.hpp +++ b/checks/interactions_results.hpp @@ -3,7 +3,7 @@ #include <array> // circle-100_target.fma ; circle-100_source.fma -auto st_get_symbolic_list_p2p(std::size_t morton) +auto st_get_symbolic_list_p2p(std::size_t morton) { using array_type = std::array<std::size_t, 9>; switch(morton) @@ -156,7 +156,7 @@ auto st_get_symbolic_list_m2l(std::size_t morton, const int level) return array_type{0, 1, 2, 4, 5, 7, 13, 14, 15}; case 11: return array_type{0, 1, 2, 4, 5, 7, 13, 15}; - case 13: + case 13: return array_type{0, 1, 2, 4, 5, 8, 10, 11}; case 14: return array_type{0, 1, 2, 4, 5, 7, 8, 10}; @@ -171,9 +171,8 @@ auto st_get_symbolic_list_m2l(std::size_t morton, const int level) } } - -// circle-100_target.fma -auto t_get_symbolic_list_p2p(std::size_t morton) +// circle-100_target.fma +auto t_get_symbolic_list_p2p(std::size_t morton) { using array_type = std::array<std::size_t, 9>; switch(morton) @@ -220,8 +219,8 @@ auto t_get_symbolic_list_p2p(std::size_t morton) return array_type{41, 43, 47}; case 47: return array_type{46, 58}; - case 53: - return array_type{31, 55}; + case 53: + return array_type{31, 55}; case 55: return array_type{53, 60, 61}; case 58: diff --git a/checks/test_block.cpp b/checks/test_block.cpp index 7cbe14f26d0187e5fba52cc7a72b7c8426afabd5..5bdd5a30c13e72eb8fa83be6e0a119dd0145e50d 100644 --- a/checks/test_block.cpp +++ b/checks/test_block.cpp @@ -1,11 +1,11 @@ -#include <utility> #include <array> -#include <xtensor/xtensor.hpp> #include <scalfmm/container/block.hpp> #include <scalfmm/container/particle.hpp> #include <scalfmm/tree/group_of_views.hpp> #include <scalfmm/tree/leaf_view.hpp> #include <scalfmm/tree/utils.hpp> +#include <utility> +#include <xtensor/xtensor.hpp> template<typename B> auto print(B const& b_) -> void @@ -21,19 +21,20 @@ auto print(B const& b_) -> void std::cout << "ptr : " << ptr << '\n'; } - auto main() -> int { using namespace scalfmm; using particle_type = container::particle<float, 2, float, 3, float, 3, std::size_t>; - using proxy_particle_type = typename particle_type::proxy_type; //container::particle<float&, 2, float&, 3, float&, 3, std::size_t&>; - using const_proxy_particle_type = typename particle_type::const_proxy_type;//container::particle<float&, 2, float&, 3, float&, 3, std::size_t&>; + using proxy_particle_type = + typename particle_type::proxy_type; //container::particle<float&, 2, float&, 3, float&, 3, std::size_t&>; + using const_proxy_particle_type = + typename particle_type::const_proxy_type; //container::particle<float&, 2, float&, 3, float&, 3, std::size_t&>; using leaf_type = component::leaf_view<particle_type>; using group_type = component::group_of_particles<leaf_type, particle_type>; //using particle_block_type = container::particles_block<float, particle_type>; - group_type g1_(0,5,4,8,144,true); + group_type g1_(0, 5, 4, 8, 144, true); print(g1_.storage()); std::cout << "starting_index : " << g1_.csymbolics().starting_index << '\n'; @@ -57,9 +58,9 @@ auto main() -> int { std::cout << "-----\n"; auto leaf_sym_ptr = &particles_storage.symbolics(leaf_index_in_group); - leaf_view = - component::leaf_view<particle_type>(std::make_pair(particles_storage.begin() + cnt_particles, particles_storage.begin() + cnt_particles + 2), - leaf_sym_ptr); + leaf_view = component::leaf_view<particle_type>( + std::make_pair(particles_storage.begin() + cnt_particles, particles_storage.begin() + cnt_particles + 2), + leaf_sym_ptr); cnt_particles += 2; // accumulate for set the number of particle in group. const auto morton_index_of_leaf = leaf_index_in_group; @@ -70,7 +71,6 @@ auto main() -> int ++leaf_index_in_group; } - for(auto const& leaf_view: leaves_) { std::cout << std::distance(leaf_view.particles().first, leaf_view.particles().second) << '\n'; @@ -90,7 +90,7 @@ auto main() -> int std::get<0>(proxy_particle_type(e).variables()) = 1; }); std::for_each(leaf_view.cparticles().first, leaf_view.cparticles().second, - [](auto const& e) { std::cout << const_proxy_particle_type(e) << '\n'; }); + [](auto const& e) { std::cout << const_proxy_particle_type(e) << '\n'; }); } return 0; diff --git a/checks/test_build_let.cpp b/checks/test_build_let.cpp index fe20c4e089018a3a447ae8dde9d632258610dffe..6d05c7e9da9671546864d3078fc5c1fcf6e95e55 100644 --- a/checks/test_build_let.cpp +++ b/checks/test_build_let.cpp @@ -237,15 +237,18 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int if(para.io_master()) { - std::cout << cpp_tools::colors::blue << "<params> Tree height: " << tree_height << cpp_tools::colors::reset << '\n'; - std::cout << cpp_tools::colors::blue << "<params> Group Size: " << group_size << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Tree height: " << tree_height << cpp_tools::colors::reset + << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Group Size: " << group_size << cpp_tools::colors::reset + << '\n'; std::cout << cpp_tools::colors::blue << "<params> order: " << order << cpp_tools::colors::reset << '\n'; if(!input_file.empty()) { std::cout << cpp_tools::colors::blue << "<params> Input file: " << input_file << cpp_tools::colors::reset << '\n'; } - std::cout << cpp_tools::colors::blue << "<params> Output file: " << output_file << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Output file: " << output_file << cpp_tools::colors::reset + << '\n'; std::cout << cpp_tools::colors::blue << "<params> Particle Distribution: " << std::boolalpha << use_particle_distribution << cpp_tools::colors::reset << '\n'; std::cout << cpp_tools::colors::blue << "<params> Leaf Distribution: " << std::boolalpha diff --git a/checks/test_build_tree.cpp b/checks/test_build_tree.cpp index 102ba72e5973f453670b0adfc8a271186648d132..cf97d76a903548b0ba3fc7285ba11995ecc351c8 100644 --- a/checks/test_build_tree.cpp +++ b/checks/test_build_tree.cpp @@ -216,8 +216,10 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int if(para.io_master()) { - std::cout << cpp_tools::colors::blue << "<params> Tree height: " << tree_height << cpp_tools::colors::reset << '\n'; - std::cout << cpp_tools::colors::blue << "<params> Group Size: " << group_size << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Tree height: " << tree_height << cpp_tools::colors::reset + << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Group Size: " << group_size << cpp_tools::colors::reset + << '\n'; std::cout << cpp_tools::colors::blue << "<params> order: " << order << cpp_tools::colors::reset << '\n'; if(!input_file.empty()) { diff --git a/checks/test_chebyshev.cpp b/checks/test_chebyshev.cpp index d4e987d8c743bd00edcd624a38994ff800dc7123..1a92366f11935747e9ed8aeb2383ff5a8cf4cf28 100644 --- a/checks/test_chebyshev.cpp +++ b/checks/test_chebyshev.cpp @@ -259,8 +259,7 @@ auto run(cpp_tools::cl_parser::parser<Parameters...> const& parser) -> int } template<typename V, std::size_t D, typename MK> -using interpolator_alias = - scalfmm::interpolation::interpolator<V, D, MK, scalfmm::options::chebyshev_<>>; +using interpolator_alias = scalfmm::interpolation::interpolator<V, D, MK, scalfmm::options::chebyshev_<>>; auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int { @@ -274,7 +273,6 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int const std::size_t dimension = parser.get<local_args::dimension>(); - switch(dimension) { case 1: diff --git a/checks/test_compose.cpp b/checks/test_compose.cpp index 5edf125289f96c54170dfafc3ae9b06bde040e5d..cb66e6cc0a20206572d240b8bd2607ffd5fa2c34 100644 --- a/checks/test_compose.cpp +++ b/checks/test_compose.cpp @@ -59,8 +59,9 @@ namespace local_args } // namespace local_args template<std::size_t Dimension, typename ContainerType, typename ValueType> -std::size_t read_data(cpp_tools::parallel_manager::parallel_manager& para, const std::string& filename, ContainerType& container, - scalfmm::container::point<ValueType, Dimension>& Centre, ValueType& width) +std::size_t read_data(cpp_tools::parallel_manager::parallel_manager& para, const std::string& filename, + ContainerType& container, scalfmm::container::point<ValueType, Dimension>& Centre, + ValueType& width) { using particle_type = typename ContainerType::value_type; @@ -94,8 +95,8 @@ std::size_t read_data(cpp_tools::parallel_manager::parallel_manager& para, const return number_of_particles; } template<typename ContainerType, typename Box_type, typename Distrib_type> -void build_distrib(cpp_tools::parallel_manager::parallel_manager& para, ContainerType& particle_container, std::size_t& number_of_particles, - const Box_type& box, const int& level, Distrib_type& index_blocs) +void build_distrib(cpp_tools::parallel_manager::parallel_manager& para, ContainerType& particle_container, + std::size_t& number_of_particles, const Box_type& box, const int& level, Distrib_type& index_blocs) { auto rank = para.get_process_id(); @@ -194,7 +195,8 @@ void build_save_matrix(const std::string& output_file, ContainerType container, file.close(); } template<std::size_t Dimension, typename... Parameters> -auto run(cpp_tools::cl_parser::parser<Parameters...> const& parser, cpp_tools::parallel_manager::parallel_manager& para) -> int +auto run(cpp_tools::cl_parser::parser<Parameters...> const& parser, + cpp_tools::parallel_manager::parallel_manager& para) -> int { //////////////////////////////////////////////// /// @@ -273,8 +275,9 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int para.init(); // // Parameter handling - auto parser = cpp_tools::cl_parser::make_parser(cpp_tools::cl_parser::help{}, local_args::input_file{}, local_args::output_file{}, - local_args::dimension{}, args::tree_height{}); + auto parser = + cpp_tools::cl_parser::make_parser(cpp_tools::cl_parser::help{}, local_args::input_file{}, + local_args::output_file{}, local_args::dimension{}, args::tree_height{}); parser.parse(argc, argv); const std::size_t dimension = parser.get<local_args::dimension>(); diff --git a/checks/test_m2l_sym.cpp b/checks/test_m2l_sym.cpp index b389202070b7e5392b4cdd89643f7f4db1cb1d90..f79634d0b0eceac9272da8e48fb5e415724caa73 100644 --- a/checks/test_m2l_sym.cpp +++ b/checks/test_m2l_sym.cpp @@ -145,86 +145,84 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int bool allok{true}; auto generate_all_perms = [&](auto... is) { + for(std::size_t n{0}; n < kn; ++n) + { + target_sym1.locals().at(n) = xt::zeros_like(target_sym1.clocals().at(n)); + } + scalfmm::operators::m2l(interpolator_nsym, source1, flat_index, target_ref1, std::size_t(3), e_nsym); - for(std::size_t n{0}; n < kn; ++n) - { - target_sym1.locals().at(n) = xt::zeros_like(target_sym1.clocals().at(n)); - } - scalfmm::operators::m2l(interpolator_nsym, source1, flat_index, target_ref1, std::size_t(3), e_nsym); - - auto k = interpolator_sym.interactions_matrices().at(interpolator_sym.symmetry_k_index(flat_index)); + auto k = interpolator_sym.interactions_matrices().at(interpolator_sym.symmetry_k_index(flat_index)); - auto p = xt::eval(xt::view(permutations, flat_index, xt::all())); + auto p = xt::eval(xt::view(permutations, flat_index, xt::all())); - apply_m2l_debug<kn, km>(source1, target_sym1, k, p, - matrix_kernel_sym_type{}.scale_factor(target_sym1.width()), std::size_t(3), - interpolator_sym.nnodes()); + apply_m2l_debug<kn, km>(source1, target_sym1, k, p, + matrix_kernel_sym_type{}.scale_factor(target_sym1.width()), std::size_t(3), + interpolator_sym.nnodes()); - bool ok{true}; - for(std::size_t n{0}; n < kn; ++n) - { - if(xt::allclose(target_ref1.clocals().at(n), target_sym1.clocals().at(n))) - { - ok &= true; - allok &= true; - } - else - { - ok &= false; - } - } - if(ok) + bool ok{true}; + for(std::size_t n{0}; n < kn; ++n) + { + if(xt::allclose(target_ref1.clocals().at(n), target_sym1.clocals().at(n))) { - std::cout << cpp_tools::colors::blue; - std::cout << "Symmetries for flat index " << flat_index << " ok.\n"; + ok &= true; + allok &= true; } else { - std::size_t cmp_k{0}; - std::size_t cmp_p{0}; - bool found{true}; + ok &= false; + } + } + if(ok) + { + std::cout << cpp_tools::colors::blue; + std::cout << "Symmetries for flat index " << flat_index << " ok.\n"; + } + else + { + std::size_t cmp_k{0}; + std::size_t cmp_p{0}; + bool found{true}; - for(std::size_t ik{0}; ik < scalfmm::interpolation::number_of_matrices_in_orthant<dimension>(); - ++ik) + for(std::size_t ik{0}; ik < scalfmm::interpolation::number_of_matrices_in_orthant<dimension>(); ++ik) + { + for(std::size_t i{0}; i < interpolator_sym.m2l_interactions(); ++i) { - for(std::size_t i{0}; i < interpolator_sym.m2l_interactions(); ++i) + for(std::size_t n{0}; n < kn; ++n) { - for(std::size_t n{0}; n < kn; ++n) + target_sym1.locals().at(n) = xt::zeros_like(target_sym1.clocals().at(n)); + } + auto k_search = interpolator_sym.interactions_matrices().at(ik); + auto p_search = xt::eval(xt::view(permutations, i, xt::all())); + apply_m2l_debug<kn, km>(source1, target_sym1, k_search, p_search, + matrix_kernel_sym_type{}.scale_factor(target_sym1.width()), + std::size_t(3), interpolator_sym.nnodes()); + for(std::size_t n{0}; n < kn; ++n) + { + if(xt::allclose(target_ref1.clocals().at(n), target_sym1.clocals().at(n))) { - target_sym1.locals().at(n) = xt::zeros_like(target_sym1.clocals().at(n)); + found &= true; } - auto k_search = interpolator_sym.interactions_matrices().at(ik); - auto p_search = xt::eval(xt::view(permutations, i, xt::all())); - apply_m2l_debug<kn, km>(source1, target_sym1, k_search, p_search, - matrix_kernel_sym_type{}.scale_factor(target_sym1.width()), - std::size_t(3), interpolator_sym.nnodes()); - for(std::size_t n{0}; n < kn; ++n) + else { - if(xt::allclose(target_ref1.clocals().at(n), target_sym1.clocals().at(n))) - { - found &= true; - } - else - { - found &= false; - } + found &= false; } - ++cmp_p; } - ++cmp_k; - } - if(found) - { - std::cout << cpp_tools::colors::cyan; - std::cout << "Symmetries for flat index " << flat_index << "should be : " << cmp_p - 1 << ".\n"; - std::cout << "k for flat index " << flat_index << "should be : " << cmp_k - 1 << ".\n"; - } - else - { - std::cout << cpp_tools::colors::red; - std::cout << "Can't find symmetry for flat index " << flat_index << ".\n"; + ++cmp_p; } + ++cmp_k; + } + if(found) + { + std::cout << cpp_tools::colors::cyan; + std::cout << "Symmetries for flat index " << flat_index << "should be : " << cmp_p - 1 << ".\n"; + std::cout << "k for flat index " << flat_index << "should be : " << cmp_k - 1 << ".\n"; } + else + { + std::cout << cpp_tools::colors::red; + std::cout << "Can't find symmetry for flat index " << flat_index << ".\n"; + } + } for(std::size_t n{0}; n < kn; ++n) { diff --git a/checks/test_morton_per.cpp b/checks/test_morton_per.cpp index 1293742582ba50b14db18533ad48dd03666213be..7e1672f55013adac6407bae48169b4bd6c70fada 100644 --- a/checks/test_morton_per.cpp +++ b/checks/test_morton_per.cpp @@ -107,7 +107,6 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int // lambda function auto getN = [&level, &box](std::size_t indexRef) { - auto pos = scalfmm::index::get_coordinate_from_morton_index<dimension>(indexRef); auto per = box.get_periodicity(); auto neig1 = scalfmm::index::get_neighbors(pos, level, per, neighbour_separation); diff --git a/checks/test_p2p_inner_non_mutual.cpp b/checks/test_p2p_inner_non_mutual.cpp index 1ffd03632d8bbc07485a4ac05dc58b7e55e7365d..d7398c5c7b42ecc57f39ec0ae0e132675d17055f 100644 --- a/checks/test_p2p_inner_non_mutual.cpp +++ b/checks/test_p2p_inner_non_mutual.cpp @@ -1,10 +1,5 @@ -#include <array> -#include <iostream> -#include <random> -#include <tuple> -#include <utility> - // scalfmm +#include "scalfmm/container/access.hpp" #include "scalfmm/container/block.hpp" #include "scalfmm/container/particle.hpp" #include "scalfmm/matrix_kernels/laplace.hpp" @@ -19,6 +14,12 @@ #include <cpp_tools/colors/colorized.hpp> #include <cpp_tools/timers/simple_timer.hpp> +#include <array> +#include <iostream> +#include <random> +#include <tuple> +#include <utility> + namespace local_args { struct nb_particles diff --git a/checks/test_p2p_interface.cpp b/checks/test_p2p_interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..15dffef389c3e56de5d56cd59d1189952dbb8b2c --- /dev/null +++ b/checks/test_p2p_interface.cpp @@ -0,0 +1,649 @@ +#include <array> +#include <iostream> +#include <random> +#include <tuple> +#include <utility> + +// scalfmm +#include "scalfmm/container/block.hpp" +#include "scalfmm/container/particle.hpp" +#include "scalfmm/matrix_kernels/laplace.hpp" +#include "scalfmm/operators/p2p.hpp" +#include "scalfmm/tree/group_of_views.hpp" +#include "scalfmm/tree/leaf_view.hpp" +#include "scalfmm/utils/accurater.hpp" + +// cpp tools +#include "cpp_tools/cl_parser/tcli.hpp" +#include <cpp_tools/cl_parser/cl_parser.hpp> +#include <cpp_tools/colors/colorized.hpp> +#include <cpp_tools/timers/simple_timer.hpp> + +#define FULL_DEBUG_MODE 1 + +namespace local_args +{ + struct nb_particles + { + cpp_tools::cl_parser::str_vec flags = {"--N", "--number-particles"}; + std::string description = "Number of particles to generate"; + using type = std::size_t; + std::string input_hint = "int"; /*!< The input hint */ + type def = 1000; + }; + + struct data_type + { + cpp_tools::cl_parser::str_vec flags = {"--use-float"}; + using type = bool; + std::string description = "To generate float distribution"; + enum + { + flagged + }; + }; + + struct dimension + { + cpp_tools::cl_parser::str_vec flags = {"--dimension", "--d"}; + std::string description = "Dimension : \n\t1, 2, 3 or 4"; + using type = std::size_t; + type def = 3; + }; + + struct kernel + { + cpp_tools::cl_parser::str_vec flags = {"--kernel", "--k"}; + std::string description = "Kernel : \n\t0 for 'one_over_r' 2, 1 for 'grad_one_over_r<dim>, 2 for " + "'val_grad_one_over_r<dim>, (default) 'one_over_r'"; + using type = std::size_t; + type def = 0; + }; + + struct nb_runs + { + cpp_tools::cl_parser::str_vec flags = {"--number-runs", "--nruns"}; + std::string description = "Number of runs to get average runtime values"; + using type = std::size_t; + type def = 1; + }; +} // namespace local_args + +/** + * @brief Prints the content of a leaf to the standard output. + * + * This function iterates over all particles within the specified leaf and prints their details to the standard output. + * + * @tparam LeafType The type of the leaf being printed. + * + * @param leaf The constant reference to the leaf whose content is to be printed. + * + */ +template<typename LeafType> +auto print_leaf_content(LeafType const& leaf) -> void +{ + using leaf_type = LeafType; + using const_proxy_type = typename leaf_type::const_proxy_type; + + // loop through all the particles of the leaf + for(auto part: leaf) + { + auto p = const_proxy_type(part); + std::cout << p << std::endl; + } +} + +/** + * @brief Initializes the content of a leaf with random values. + * + * This function iterates over all particles in the specified leaf and initializes their positions, inputs, and outputs + * with random and default values, respectively. Positions and inputs are assigned random values within a specified + * range (0.0 to 2.0), while outputs are initialized to zero. Additionally, each particle is assigned a unique global + * index starting from zero. + * + * @tparam LeafType The type of the leaf being initialized. + * + * @param leaf The leaf whose content is to be initialized. This operation modifies the leaf by setting + * particle positions, inputs, and outputs to their initial values. + */ +template<typename LeafType> +auto init_leaf_content(LeafType& leaf) -> void +{ + using leaf_type = LeafType; + using value_type = typename leaf_type::value_type; + using particle_type = typename leaf_type::particle_type; + using proxy_type = typename particle_type::proxy_type; + + using seed_generator_type = std::random_device; + using random_generator_type = std::mt19937; + using distribution_type = std::uniform_real_distribution<value_type>; + + static constexpr std::size_t dimension = particle_type::dimension; + static constexpr std::size_t inputs_size = particle_type::inputs_size; + static constexpr std::size_t outputs_size = particle_type::outputs_size; + + // set up the random generator + seed_generator_type rd; + random_generator_type gen(rd()); + distribution_type dis(0.0, 2.0); + auto random_r = [&dis, &gen]() { return dis(gen); }; + + // get the begin and end iterators of the leaf + auto part_begin = leaf.begin(); + auto part_end = leaf.end(); + + std::size_t idx{0}; + + while(part_begin != part_end) + { + auto p = proxy_type(*part_begin); + // initialize position + for(std::size_t i = 0; i < dimension; ++i) + { + p.position(i) = random_r(); + } + // initialize inputs + for(std::size_t i = 0; i < inputs_size; ++i) + { + p.inputs(i) = random_r(); + } + // initialize outputs + for(std::size_t i = 0; i < outputs_size; ++i) + { + p.outputs(i) = value_type(0.); + } + // set the global index of the particle + std::get<0>(p.variables()) = idx++; + ++part_begin; + } +} + +/** + * @brief Copies the content from one leaf to another. + * + * This function iterates over all particles in the source leaf (`src_leaf`) and copies their positions, inputs, + * outputs, and the first variable to the corresponding particles in the destination leaf (`dst_leaf`). Both leaves + * are expected to have the same type and contain the same number of particles. + * + * @tparam LeafType The type of the leaves involved in the copy operation. + * + * @param src_leaf The source leaf from which content is copied. This leaf remains unchanged. + * @param dst_leaf The destination leaf to which content is copied. This leaf is modified to reflect the content + * of `src_leaf`. + */ +template<typename LeafType> +auto copy_leaf_content(LeafType& src_leaf, LeafType& dst_leaf) -> void +{ + using leaf_type = LeafType; + // using value_type = typename leaf_type::value_type; + using particle_type = typename leaf_type::particle_type; + using proxy_type = typename particle_type::proxy_type; + // using const_proxy_type = typename particle_type::const_proxy_type; + + static constexpr std::size_t dimension = particle_type::dimension; + static constexpr std::size_t inputs_size = particle_type::inputs_size; + static constexpr std::size_t outputs_size = particle_type::outputs_size; + + // check at run-time that both leaves have the same number of particles + assert(src_leaf.size() == dst_leaf.size()); + + // get the begin and end iterators of the first leaf (= source leaf) + auto src_part_begin = src_leaf.begin(); + auto src_part_end = src_leaf.end(); + + // get the begin and end iterators of the second leaf (= destination leaf) + auto dst_part_begin = dst_leaf.begin(); + auto dst_part_end = dst_leaf.end(); + + while(src_part_begin != src_part_end) + { + // get a proxy on the current particle of the first leaf (= source leaf) + auto src_p = proxy_type(*src_part_begin); + // get a proxy on the current particle of the second leaf (= destination leaf) + auto dst_p = proxy_type(*dst_part_begin); + // copy source position to destination position + for(std::size_t i = 0; i < dimension; ++i) + { + dst_p.position(i) = src_p.position(i); + } + // copy source inputs to destination inputs + for(std::size_t i = 0; i < inputs_size; ++i) + { + dst_p.inputs(i) = src_p.inputs(i); + } + // copy source outputs to destination outputs + for(std::size_t i = 0; i < outputs_size; ++i) + { + dst_p.outputs(i) = src_p.outputs(i); + } + // copy first source variable to first destination variable + std::get<0>(dst_p.variables()) = std::get<0>(src_p.variables()); + + ++src_part_begin; + ++dst_part_begin; + } +} + +/** + * @brief Compares the output values of two leaf objects and calculates the relative L2 norm error. + * + * This function iterates over two leaf objects of the same type, comparing their output values particle by particle. + * It assumes both leaves contain the same number of particles and that each particle in the leaves has a set of + * output values. The function calculates the error for each corresponding pair of output values between the two leaves + * and accumulates these errors to compute the relative L2 norm error across all particles and their outputs. + * + * @tparam LeafType The type of the leaf objects being compared. + * + * @param lhs_leaf A constant reference to the first leaf object. + * @param rhs_leaf A constant reference to the second leaf object. + * + */ +template<typename LeafType> +auto compare_leaf_outputs(LeafType const& lhs_leaf, LeafType const& rhs_leaf) -> typename LeafType::value_type +{ + using leaf_type = LeafType; + using value_type = typename leaf_type::value_type; + using particle_type = typename leaf_type::particle_type; + // using proxy_type = typename particle_type::proxy_type; + using const_proxy_type = typename particle_type::const_proxy_type; + + using accurater_type = scalfmm::utils::accurater<value_type>; + + static constexpr std::size_t outputs_size = particle_type::outputs_size; + + // check at run-time that both leaves have the same number of particles + assert(lhs_leaf.size() == rhs_leaf.size()); + + // get the begin and end iterators of the first leaf + auto lhs_part_begin = lhs_leaf.begin(); + auto lhs_part_end = lhs_leaf.end(); + + // get the begin and end iterators of the second leaf + auto rhs_part_begin = rhs_leaf.begin(); + auto rhs_part_end = rhs_leaf.end(); + + accurater_type error; + + while(lhs_part_begin != rhs_part_end) + { + // get a proxy on the current particle of the first leaf + auto lhs_p = const_proxy_type(*lhs_part_begin); + // get a proxy on the current particle of the second leaf + auto rhs_p = const_proxy_type(*rhs_part_begin); + + // compute the error between the two outputs + for(std::size_t i = 0; i < outputs_size; ++i) + { + error.add(lhs_p.outputs(i), rhs_p.outputs(i)); + } + + ++lhs_part_begin; + ++rhs_part_begin; + } + + // return only the relative l2 norm error + return error.get_relative_l2_norm(); +} + +/** + * @brief Creates a leaf view from a given group. + * + * This function initializes a leaf view object based on the provided group. It creates a new leaf of type `LeafType` + * by extracting and utilizing the particle storage and symbolic information from the `GroupType` object. The function + * directly modifies the `leaf` parameter to represent a view onto the first set of particles contained within the + * `group`. It is designed to work with data structures where groups manage collections of particles and leaves act + * as views or references to these collections for more efficient access and manipulation. + * + * @tparam GroupType The type of the group from which the leaf view is created. + * @tparam LeafType The type of the leaf to be initialized. + * + * @param group A reference to the group object from which the leaf view is to be created. The group must contain + * particle storage and symbolic information. + * @param leaf A reference to the leaf object to be initialized. This function directly modifies `leaf` to represent + * a view on a subset of particles from `group`. + * + */ +template<typename GroupType, typename LeafType> +auto make_leaf_view_from_group(GroupType& group, LeafType& leaf) -> void +{ + using leaf_type = LeafType; + + // get storage and symbolic data of the provided group + auto& particles_storage = group.storage(); + auto& group_symbolics = group.csymbolics(); + auto leaf_sym_ptr = &particles_storage.symbolics(0); + std::size_t number_of_particles_in_group = group_symbolics.number_of_particles_in_group; + + // create a leaf with a view on the particle storage + leaf = + leaf_type(std::make_pair(particles_storage.begin(), particles_storage.begin() + number_of_particles_in_group), + leaf_sym_ptr); +} + +/** + * @brief Executes the main program to benchmark and compare the performance of P2P operators. + * + * This function benchmarks three types of P2P operations: inner non-mutual, inner mutual, and outer. It performs + * a specified number of runs for each operation, calculates the average execution time, and evaluates the L2 error + * between the results of inner mutual and non-mutual operations. The function is templated to allow for flexibility + * in the types of values and matrix kernels used in the computations, as well as the dimensionality of the problem. + * + * @tparam ValueType The data type for the values used in computations (e.g., float, double). + * @tparam MatrixKernelType The type of the matrix kernel to be used in the P2P operations. + * @tparam Dimension The spatial dimension of the particle system (e.g., 2 for 2D, 3 for 3D). + * + * @param N The number of particles in each group or leaf. + * @param number_runs The number of times each P2P operation is executed for benchmarking. + * + * The function initializes four groups of particles and corresponding leaf views. It then fills these leaves with + * particles and benchmarks the specified P2P operations, printing the average execution time and the L2 error between + * the outputs of inner mutual and non-mutual operations. The function uses high-resolution timers to measure execution + * time and outputs the results to the standard output. + * + */ +template<typename ValueType, typename MatrixKernelType, std::size_t Dimension> +auto run(std::size_t N, std::size_t number_runs) -> void +{ + using value_type = ValueType; + using matrix_kernel_type = MatrixKernelType; + static constexpr std::size_t dimension = Dimension; + + // particle type + static constexpr std::size_t nb_inputs = matrix_kernel_type::km; + static constexpr std::size_t nb_outputs = matrix_kernel_type::kn; + using particle_type = + scalfmm::container::particle<value_type, dimension, value_type, nb_inputs, value_type, nb_outputs, std::size_t>; + + // leaf and group + using leaf_type = scalfmm::component::leaf_view<particle_type>; + using group_type = scalfmm::component::group_of_particles<leaf_type, particle_type>; + + // time measurement + using duration_type = std::chrono::nanoseconds; + using timer_type = cpp_tools::timers::timer<duration_type>; + + // matrix kernel + matrix_kernel_type mk{}; + std::cout << cpp_tools::colors::blue << "[param] kernel = " << mk.name() << cpp_tools::colors::reset << std::endl; + + // creation of groups with 1 component containing each N particles + group_type group1(0, N, 1, N, 0, true); + group_type group2(0, N, 1, N, 0, true); + group_type group3(0, N, 1, N, 0, true); + group_type group4(0, N, 1, N, 0, true); + + // creation of leaves from the groups (only views on the group's storage) + leaf_type leaf1, leaf2, leaf3, leaf4; + make_leaf_view_from_group(group1, leaf1); + make_leaf_view_from_group(group2, leaf2); + make_leaf_view_from_group(group3, leaf3); + make_leaf_view_from_group(group4, leaf4); + + // fill the leaves with particles + init_leaf_content(leaf1); + copy_leaf_content(leaf1, leaf2); + init_leaf_content(leaf3); + init_leaf_content(leaf4); + + timer_type timer_p2p_inner_non_mutual{}, timer_p2p_inner_mutual{}, timer_p2p_outer{}; + value_type time_p2p_non_mutual{}, time_p2p_mutual{}, time_p2p_outer{}, l2_error{}; + std::size_t total_number_runs = (number_runs == 1) ? 1 : (number_runs - 1); + + // --- P2P inner non-mutual --- + std::cout << cpp_tools::colors::green; + std::cout << "\n --- BENCHMARK P2P inner non-mutual ---" << std::endl; + for(std::size_t i = 0; i < number_runs; ++i) + { + // reset the output of the leaf + group1.storage().reset_outputs(); + + // run P2P operator + timer_p2p_inner_non_mutual.tic(); + scalfmm::operators::p2p_inner_non_mutual(mk, leaf1); + timer_p2p_inner_non_mutual.tac(); + + // print elapsed time of the current iteration + if(i == 0 && number_runs > 1) + { + std::cout << "\t- RUN " << i << " - elapsed time = " << timer_p2p_inner_non_mutual.elapsed() / 1e9 + << " s (WARM-UP run - not taken into account for average time measurement)" << std::endl; + timer_p2p_inner_non_mutual.reset(); + } + else + { + std::cout << "\t- RUN " << i << " - elapsed time = " << timer_p2p_inner_non_mutual.elapsed() / 1e9 << " s" + << std::endl; + } + } + std::cout << cpp_tools::colors::reset; + + // --- P2P inner mutual --- + std::cout << cpp_tools::colors::green; + std::cout << "\n --- BENCHMARK P2P inner mutual ---" << std::endl; + for(std::size_t i = 0; i < number_runs; ++i) + { + // reset the output of the leaf + group2.storage().reset_outputs(); + + // run the P2P operator + timer_p2p_inner_mutual.tic(); + scalfmm::operators::p2p_inner_mutual(mk, leaf2); + timer_p2p_inner_mutual.tac(); + + // print elapsed time of the current iteration + if(i == 0 && number_runs > 1) + { + std::cout << "\t- RUN " << i << " - elapsed time = " << timer_p2p_inner_mutual.elapsed() / 1e9 + << " s (WARM-UP run - not taken into account for average time measurement)" << std::endl; + timer_p2p_inner_mutual.reset(); + } + else + { + std::cout << "\t- RUN " << i << " - elapsed time = " << timer_p2p_inner_mutual.elapsed() / 1e9 << " s" + << std::endl; + } + } + std::cout << cpp_tools::colors::reset; + + // --- P2P outer --- + std::array<std::size_t, 1> dummy{}; + std::cout << cpp_tools::colors::green; + std::cout << "\n --- BENCHMARK P2P outer ---" << std::endl; + for(std::size_t i = 0; i < number_runs; ++i) + { + // reset the output of the leaves + group3.storage().reset_outputs(); + group4.storage().reset_outputs(); + + // run the P2P operator + timer_p2p_outer.tic(); + scalfmm::operators::p2p_outer(mk, leaf3, leaf4, dummy); + timer_p2p_outer.tac(); + + // print elapsed time of the current iteration + if(i == 0 && number_runs > 1) + { + std::cout << "\t- RUN " << i << " - elapsed time = " << timer_p2p_outer.elapsed() / 1e9 + << " s (WARM-UP run - not taken into account for average time measurement)" << std::endl; + timer_p2p_outer.reset(); + } + else + { + std::cout << "\t- RUN " << i << " - elapsed time = " << timer_p2p_outer.elapsed() / 1e9 << " s" + << std::endl; + } + } + std::cout << cpp_tools::colors::reset; + + // post-processing + time_p2p_non_mutual = timer_p2p_inner_non_mutual.cumulated() / 1e9 / total_number_runs; + time_p2p_mutual = timer_p2p_inner_mutual.cumulated() / 1e9 / total_number_runs; + time_p2p_outer = timer_p2p_outer.cumulated() / 1e9 / total_number_runs; + l2_error = compare_leaf_outputs(leaf1, leaf2); + + // print results and performances + std::cout << "\n[avg time][p2p inner non-mutual] = " << time_p2p_non_mutual << std::endl; + std::cout << "[avg time][p2p inner mutual] = " << time_p2p_mutual << std::endl; + std::cout << "[avg time][p2p outer] = " << time_p2p_outer << std::endl; + std::cout << "[ratio][p2p inner non-mutual | p2p outer] = " << time_p2p_non_mutual / time_p2p_outer << std::endl; + std::cout << std::endl; + std::cout << "[error][p2p inner mutual | p2p inner non-mutual] = " << l2_error << std::endl; +} + +/** + * @brief Executes the main program for a specified dimension and kernel choice. + * + * This function selects and runs a specific kernel based on the `kernel_choice` parameter for simulations in a given + * dimension `Dimension`. + * + * @tparam Dimension The spatial dimension of the simulation. + * @tparam ValueType The data type used for computation, typically a floating-point type like `float` or `double`. + * + * @param kernel_choice An index representing the choice of kernel to run. Each index maps to a specific kernel + * implementation within the Laplace domain: + * - 0: `one_over_r` + * - 1: `grad_one_over_r` + * - 2: `val_grad_one_over_r` + * Any other value will result in an error indicating an unsupported kernel option. + * + * @param N The number of elements or particles involved in the simulation. + * @param nb_runs The number of times the simulation is executed. Multiple runs can be used for benchmarking or + * averaging performance metrics. + * + */ +template<std::size_t Dimension, typename ValueType> +auto run_dimension(std::size_t kernel_choice, std::size_t N, std::size_t nb_runs) -> void +{ + using value_type = ValueType; + static constexpr std::size_t dimension = Dimension; + + switch(kernel_choice) + { + case 0: + { + using matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; + run<value_type, matrix_kernel_type, dimension>(N, nb_runs); + } + break; + case 1: + { + using matrix_kernel_type = scalfmm::matrix_kernels::laplace::grad_one_over_r<dimension>; + run<value_type, matrix_kernel_type, dimension>(N, nb_runs); + } + break; + case 2: + { + using matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r<dimension>; + run<value_type, matrix_kernel_type, dimension>(N, nb_runs); + } + break; + default: + { + std::cerr << "Unsupported kernel option = " << kernel_choice << "!" << std::endl; + } + break; + } +} + +/** + * @brief Executes a simulation run with a specified value type, dimension, kernel choice, number of particles, and + number of runs. + + * @tparam ValueType The data type used for computation, typically a floating-point type like `float` or `double`. + * + * @param dim The dimensionality of the simulation (1D, 2D, 3D, or 4D). + * @param kernel_choice An index representing the choice of kernel to run. Each index maps to a specific kernel + * implementation within the Laplace domain: + * - 0: `one_over_r` + * - 1: `grad_one_over_r` + * - 2: `val_grad_one_over_r` + * Any other value will result in an error indicating an unsupported kernel option. + * + * @param N The number of elements or particles involved in the simulation. + * @param nb_runs The number of times the simulation is executed. Multiple runs can be used for benchmarking or + * averaging performance metrics. + */ +template<typename ValueType> +auto run_value_type(std::size_t dim, std::size_t kernel_choice, std::size_t N, std::size_t nb_runs) -> void +{ + using value_type = ValueType; + + std::cout << cpp_tools::colors::blue << "[param] dimension = " << dim << cpp_tools::colors::reset << std::endl; + + switch(dim) + { + case 1: + { + static constexpr std::size_t dimension{1}; + run_dimension<dimension, value_type>(kernel_choice, N, nb_runs); + } + break; + case 2: + { + static constexpr std::size_t dimension{2}; + run_dimension<dimension, value_type>(kernel_choice, N, nb_runs); + } + break; + case 3: + { + static constexpr std::size_t dimension{3}; + run_dimension<dimension, value_type>(kernel_choice, N, nb_runs); + } + break; + case 4: + { + static constexpr std::size_t dimension{4}; + run_dimension<dimension, value_type>(kernel_choice, N, nb_runs); + } + break; + default: + { + std::cerr << "Only the 1D, 2D, 3D and 4D cases are supported !" << std::endl; + } + break; + } +} + +auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int +{ + // Parameter handling + auto parser = + cpp_tools::cl_parser::make_parser(cpp_tools::cl_parser::help{}, local_args::nb_particles{}, local_args::nb_runs{}, + local_args::dimension{}, local_args::kernel{}, local_args::data_type{}); + parser.parse(argc, argv); + + // get the parameter values + const std::size_t N{parser.get<local_args::nb_particles>()}; + const std::size_t dim{parser.get<local_args::dimension>()}; + const std::size_t nb_runs{parser.get<local_args::nb_runs>()}; + const std::size_t kernel_choice{parser.get<local_args::kernel>()}; + const bool use_double = (parser.exists<local_args::data_type>() ? false : true); + + std::cout << cpp_tools::colors::blue; + std::cout << "[param] N = " << N << std::endl; + std::cout << "[param] nb-runs = " << nb_runs << std::endl; + std::cout << cpp_tools::colors::reset; + +#ifdef FULL_DEBUG_MODE + using value_type = float; + using matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; + static constexpr std::size_t dimension = 3; + + run<value_type, matrix_kernel_type, dimension>(N, nb_runs); +#else + if(use_double) + { + using value_type = double; + std::cout << cpp_tools::colors::blue << "[param] data-type = double" << cpp_tools::colors::reset << std::endl; + run_value_type<value_type>(dim, kernel_choice, N, nb_runs); + } + else + { + using value_type = float; + std::cout << cpp_tools::colors::blue << "[param] data-type = float" << cpp_tools::colors::reset << std::endl; + run_value_type<value_type>(dim, kernel_choice, N, nb_runs); + } +#endif + + return 0; +} diff --git a/checks/test_storage.cpp b/checks/test_storage.cpp index 9b898f42065a5744cc4dc676a28cb56036a2361a..3e1af04910e069fc199f3dd2b502279339fb9684 100644 --- a/checks/test_storage.cpp +++ b/checks/test_storage.cpp @@ -1,13 +1,12 @@ -#include <scalfmm/memory/storage.hpp> #include <iostream> -#include <xtensor/xio.hpp> -#include <scalfmm/matrix_kernels/laplace.hpp> #include <scalfmm/interpolation/interpolator.hpp> #include <scalfmm/interpolation/uniform/uniform_interpolator.hpp> +#include <scalfmm/matrix_kernels/laplace.hpp> +#include <scalfmm/memory/storage.hpp> +#include <xtensor/xio.hpp> using namespace scalfmm; - auto main() -> int { static constexpr std::size_t dimension{2}; @@ -22,16 +21,16 @@ auto main() -> int //auto const& cmref = m.transformed_multipoles(); //auto& mrefi1 = m.transformed_multipoles(1); //auto const& cmrefi0 = m.transformed_multipoles(0); - memory::aggregate_storage<memory::multipoles_storage<double, dimension, km>//, + memory::aggregate_storage<memory::multipoles_storage<double, dimension, km> //, //memory::locals_storage<double, dimension, kn>//, //memory::transformed_multipoles_storage<double, dimension, km> > a(std::size_t(8)); - memory::aggregate_storage<memory::multipoles_storage<double, dimension, km>//, + memory::aggregate_storage<memory::multipoles_storage<double, dimension, km> //, //memory::locals_storage<double, dimension, kn>//, //memory::transformed_multipoles_storage<double, dimension, km> > - a_m = std::move(a); + a_m = std::move(a); //std::cout << memory::check_dimensions<2,2,2>() << '\n'; diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 8ce602736d408406c89538eedf954a06e2e2981e..cdcd11c752fcc5fa42c603d6209a4106eb1c4985 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -7,6 +7,7 @@ include(cmake/dependencies/mpi.cmake) include(cmake/dependencies/blas_lapack.cmake) include(cmake/dependencies/fftw.cmake) include(cmake/dependencies/starpu.cmake) +include(cmake/dependencies/pybind.cmake) if(${CMAKE_PROJECT_NAME}_USE_CATALYST) find_package(catalyst REQUIRED) diff --git a/cmake/dependencies/pybind.cmake b/cmake/dependencies/pybind.cmake new file mode 100644 index 0000000000000000000000000000000000000000..e9c2833d877f5d1b0df0ba0add0a72157466a43e --- /dev/null +++ b/cmake/dependencies/pybind.cmake @@ -0,0 +1,39 @@ +# +# Python +# --------- + +if(${CMAKE_PROJECT_NAME}_BUILD_PYFMM) + # + # Python 3.10 or newer () + # -------------------- + find_package(Python 3.10 REQUIRED COMPONENTS Interpreter Development.Module NumPy) + + if(PYTHON_FOUND AND Python_NumPy_FOUND) + # Print out the discovered paths + cmake_print_variables(Python_VERSION) + + # cmake_print_variables(Python_INTERPRETER_ID) + # cmake_print_variables(Python_EXECUTABLE) + # cmake_print_variables(Python_INCLUDE_DIRS) + # cmake_print_variables(F2PY_INCLUDE_DIR) + cmake_print_variables(Python_NumPy_VERSION) + # cmake_print_variables(Python_NumPy_INCLUDE_DIRS) + # cmake_print_variables(Python_LIBRARIES) + # cmake_print_variables(Python_ROOT_DIR) + cmake_print_variables(Python_NumPy_FOUND) + + find_package(pybind11 REQUIRED) + + if(pybind11_FOUND) + MESSAGE(STATUS "pybind11 Found") + cmake_print_variables(pybind11_VERSION) + else(pybind11_FOUND) + MESSAGE(FATAL_ERROR "pybind11 NOT FOUND") + endif(pybind11_FOUND) + else() + cmake_print_variables(PYTHON_FOUND) + cmake_print_variables(Python_NumPy_FOUND) + MESSAGE(FATAL_ERROR "Python or Numpy not found") + endif() + +endif() \ No newline at end of file diff --git a/docs/user_guide.org b/docs/user_guide.org index 48c4a3b2c4807c500ca415f542a7509660c1666a..ae9a865ae0a30f5fd5ce480abbbe7037919f20d8 100644 --- a/docs/user_guide.org +++ b/docs/user_guide.org @@ -272,13 +272,13 @@ Here we initialize all partciles outputs with random values #+end_src *** A more complex example. -Imagine that the outputs are composed of a potential and a force (p, fx, fy, fz) and that we want to multiply the force by the value of the first. -input. +Imagine that the outputs are composed of a potential and a force (p, fx, fy, fz) and we want to +multiply the force by the value of the first input. First we get a pointer, q, on the first inputs #+begin_src c++ // a lazy light iterator on the inputs of particle inside the container auto inputs_begin_lazy = scalfmm::container::inputs_begin(leaf.particles()); - // we dereference to evaluate teh lazy pointer + // we dereference to evaluate the lazy pointer auto inputs_begin = *inputs_begin_lazy; // You get the first input value and you take // its address in order to increment if diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 87d291a0e117ab89fe0f14b42935bc51d13b2d2e..a4291fe51e8e9c4265ef5fef970054fb8c62e74c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -23,6 +23,9 @@ set(source_tests_files # test to move in compose/sandox project fmm_source_target.cpp tutorial.cpp + + # Test accuracy (barycentric interpolation) + test_accuracy.cpp ) if(${CMAKE_PROJECT_NAME}_USE_MPI) diff --git a/examples/catalyst_related.hpp b/examples/catalyst_related.hpp index e6e9fadd469f0445e095571b4abf85fdb2d0c304..f46e1c08e58cd445d17d341a9b791382a880c38b 100644 --- a/examples/catalyst_related.hpp +++ b/examples/catalyst_related.hpp @@ -11,8 +11,8 @@ #include <catalyst.h> #include <cmath> #include <conduit.hpp> -#include <conduit_blueprint_mesh.h> #include <conduit_blueprint_exports.h> +#include <conduit_blueprint_mesh.h> #include <conduit_cpp_to_c.hpp> #include <cstdint> #include <inria/tcli/tcli.hpp> @@ -52,7 +52,7 @@ namespace catalyst_adaptor // we set the channel's type to "mesh". box_channel["type"].set("mesh"); - const auto n_corners = scalfmm::meta::pow(2,GroupTree::dimension); + const auto n_corners = scalfmm::meta::pow(2, GroupTree::dimension); // now create the mesh. auto& box_mesh = box_channel["data"]; @@ -65,7 +65,7 @@ namespace catalyst_adaptor conduit::float64* box_sim_x = box_mesh["coordsets/coords/values/x"].value(); conduit::float64* box_sim_y = box_mesh["coordsets/coords/values/x"].value(); - for(std::size_t n{0}; n<n_corners; ++n) + for(std::size_t n{0}; n < n_corners; ++n) { box_sim_x[n] = scalfmm::meta::get<0>(box.corner(n)); box_sim_y[n] = scalfmm::meta::get<1>(box.corner(n)); @@ -76,7 +76,7 @@ namespace catalyst_adaptor box_mesh["topologies/mesh/type"].set("unstructured"); box_mesh["topologies/mesh/coordset"].set("coords"); box_mesh["topologies/mesh/elements/shape"].set("quad"); - box_mesh["topologies/mesh/elements/connectivity"].set_int32_vector({0,1,2,3}); + box_mesh["topologies/mesh/elements/connectivity"].set_int32_vector({0, 1, 2, 3}); catalyst_execute(conduit::c_node(&exec_params)); } @@ -86,6 +86,6 @@ namespace catalyst_adaptor conduit::Node node; catalyst_finalize(conduit::c_node(&node)); } -} +} // namespace catalyst_adaptor -#endif // CATALYST_RELATED_HPP +#endif // CATALYST_RELATED_HPP diff --git a/examples/fmm_source_target.cpp b/examples/fmm_source_target.cpp index 6680b5489eb2dff022fb5477d93782f79faa11b8..75ca335be1da7b6e5f5e5858c95645963c635e2a 100644 --- a/examples/fmm_source_target.cpp +++ b/examples/fmm_source_target.cpp @@ -19,8 +19,8 @@ #include "scalfmm/operators/fmm_operators.hpp" // // Tree -#include "scalfmm/tools/tree_io.hpp" #include "scalfmm/interpolation/grid_storage.hpp" +#include "scalfmm/tools/tree_io.hpp" #include "scalfmm/tree/box.hpp" #include "scalfmm/tree/cell.hpp" #include "scalfmm/tree/group_tree_view.hpp" @@ -51,7 +51,7 @@ using namespace scalfmm::io; /// \file fmm_source_target.cpp //! -//! \brief +//! \brief //! add flag -DST_USE_OMP to compile an openmp algorithm //! //! \code @@ -329,10 +329,10 @@ auto fmm_run(const std::string& input_source_file, const std::string& input_targ // scalfmm::list::sequential::build_interaction_lists(tree_source, tree_target, neighbour_separation, mutual); auto operator_to_proceed = scalfmm::algorithms::all; -#ifndef ST_USE_OMP +#ifndef ST_USE_OMP scalfmm::algorithms::omp::task_dep(tree_source, tree_target, fmm_operator, operator_to_proceed); #else - scalfmm::algorithms::sequential::sequential(tree_source, tree_target, fmm_operator, operator_to_proceed); + scalfmm::algorithms::sequential::sequential(tree_source, tree_target, fmm_operator, operator_to_proceed); #endif std::cout << "\n" << cpp_tools::colors::reset; @@ -503,8 +503,8 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int // break; default: std::cout << "Kernel not implemented. values are\n Laplace kernels: 0) 1/r, 1) grad(1/r)," - // << " 2) p + grad(1/r) 3) like_mrhs." << std::endl - // << "Scalar kernels 4) 1/r^2 5) ln in 2d" - << std::endl; + // << " 2) p + grad(1/r) 3) like_mrhs." << std::endl + // << "Scalar kernels 4) 1/r^2 5) ln in 2d" + << std::endl; } } diff --git a/examples/test_accuracy.cpp b/examples/test_accuracy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..726db72675bbf223b9e7b5bf156de3c76ef41d4c --- /dev/null +++ b/examples/test_accuracy.cpp @@ -0,0 +1,417 @@ + +#include <iostream> +#include <string> +#include <vector> + +#include <cpp_tools/cl_parser/tcli.hpp> +#include <cpp_tools/colors/colorized.hpp> +#include <cpp_tools/timers/simple_timer.hpp> + +#include "scalfmm/algorithms/common.hpp" +#include "scalfmm/algorithms/fmm.hpp" +#include "scalfmm/algorithms/full_direct.hpp" +#include "scalfmm/container/particle.hpp" +#include "scalfmm/container/particle_container.hpp" +#include "scalfmm/container/point.hpp" +#include "scalfmm/interpolation/interpolation.hpp" +#include "scalfmm/matrix_kernels/gaussian.hpp" +#include "scalfmm/matrix_kernels/laplace.hpp" +#include "scalfmm/operators/fmm_operators.hpp" +#include "scalfmm/tools/fma_loader.hpp" +#include "scalfmm/tools/laplace_tools.hpp" +#include "scalfmm/tree/box.hpp" +#include "scalfmm/tree/cell.hpp" +#include "scalfmm/tree/group_tree_view.hpp" +#include "scalfmm/tree/leaf_view.hpp" +#include "scalfmm/utils/accurater.hpp" +#include "scalfmm/utils/parameters.hpp" +#include "scalfmm/utils/sort.hpp" + +#include <cpp_tools/cl_parser/tcli.hpp> +#include <cpp_tools/colors/colorized.hpp> +#include <cpp_tools/timers/simple_timer.hpp> + +#include "scalfmm/tools/tree_io.hpp" + +#include "scalfmm/tools/tree_is_nan.hpp" + +// example: +// ./examples/Release/test_accuracy --input-file ../data/unitCubeXYZQ100_sorted.bfma +// --tree-height 4 -gs 2 --order 4 --data-sorted --kernel 0 --pt --output-file test_new.fma + +template<typename V, std::size_t D, typename MK, typename O> +using interpolator_alias = scalfmm::interpolation::interpolator<V, D, MK, O>; +using value_type = double; +static constexpr std::size_t dimension{3}; + +namespace local_args +{ + struct isSorted + { + /// Unused type, mandatory per interface specification + using type = bool; + /// The parameter is a flag, it doesn't expect a following value + enum + { + flagged + }; + cpp_tools::cl_parser::str_vec flags = {"--data-sorted", "--ds"}; + std::string description = "Precise if the data are sorted by their morton index"; + }; + + struct outputs_already_computed + { + /// Unused type, mandatory per interface specification + using type = bool; + /// The parameter is a flag, it doesn't expect a following value + enum + { + flagged + }; + cpp_tools::cl_parser::str_vec flags = {"--outputs-already-computed", "--oac"}; + std::string description = "Precise if the exact outputs (direct computation) are already computed"; + }; + + struct interpolator : cpp_tools::cl_parser::required_tag + { + cpp_tools::cl_parser::str_vec flags = {"--interpolator", "-i"}; + std::string description = "The interpolation : 0 for uniform, 1 for chebyshev, 2 for barycentric."; + using type = int; + type def = 0; + }; + + struct matrix_kernel + { + cpp_tools::cl_parser::str_vec flags = {"--kernel", "-k"}; + const char* description = "Matrix kernels: \n 0) 1/r, 1) expr(-r^2/l^2) "; + using type = int; + type def = 0; + }; + struct variance + { + cpp_tools::cl_parser::str_vec flags = {"--variance", "-var"}; + const char* description = "parameter l for kernel expr(-r^2/l^2) "; + using type = double; + type def = 1.0; + }; +} // namespace local_args +template<typename Container> +auto read_data(const std::string& filename, bool outputs_already_computed = false) +{ + using container_type = Container; + using particle_type = typename Container::value_type; + using value_type1 = typename particle_type::position_value_type; + static constexpr std::size_t dimension{particle_type::dimension}; + const bool verbose{false}; + + scalfmm::io::FFmaGenericLoader<value_type1, dimension> loader(filename, verbose); + const auto width{loader.getBoxWidth()}; + const auto center{loader.getBoxCenter()}; + const std::size_t number_of_particles{loader.getNumberOfParticles()}; + + auto nb_val_to_red_per_part = loader.getNbRecordPerline(); + // could be a problem for binary file (float double) + std::vector<value_type1> values_to_read(nb_val_to_red_per_part); + + container_type container(number_of_particles); + + for(std::size_t idx = 0; idx < number_of_particles; ++idx) + { + loader.fillParticle(values_to_read.data(), nb_val_to_red_per_part); + particle_type p; + std::size_t ii{0}; + for(auto& e: p.position()) + { + e = values_to_read[ii++]; + } + for(auto& e: p.inputs()) + { + e = values_to_read[ii++]; + } + if(outputs_already_computed) + { + for(auto& e: p.outputs()) + { + e = values_to_read[ii++]; + } + } + // p.variables(values_to_read[ii++], idx, 1); + p.variables(idx); + container[idx] = p; + } + return std::make_tuple(container, center, width); +} + +template<class FmmOperatorType, typename NearMatrixKernelType, typename FarMatrixKernelType> +auto run(const std::string& input_file, const std::string& output_file, const int& tree_height, const int& group_size, + const int& order, NearMatrixKernelType& mk_near, FarMatrixKernelType& mk_far, + const bool& outputs_already_computed = false) -> int +{ + using near_matrix_kernel_type = typename FmmOperatorType::near_field_type::matrix_kernel_type; + using interpolator_type = typename FmmOperatorType::far_field_type::approximation_type; + using far_matrix_kernel_type = typename interpolator_type::matrix_kernel_type; + + static_assert(std::is_same_v<near_matrix_kernel_type, NearMatrixKernelType>, + "Matrix kernel types do not match (near field)"); + static_assert(std::is_same_v<far_matrix_kernel_type, FarMatrixKernelType>, + "Matrix kernel types do not match (far field)"); + + static constexpr std::size_t dimension{interpolator_type::dimension}; + // + // + bool dataSorted = false; + std::cout << cpp_tools::colors::blue << "Entering tree test...\n" << cpp_tools::colors::reset; + + // The matrix kernel + // + static constexpr std::size_t nb_inputs_near{near_matrix_kernel_type::km}; + static constexpr std::size_t nb_outputs_near{near_matrix_kernel_type::kn}; + + std::cout << cpp_tools::colors::blue << "<params> Runtime order : " << order << cpp_tools::colors::reset << '\n'; + + // Open particle file + cpp_tools::timers::timer time{}; + + // --------------------------------------- + using particle_type = scalfmm::container::particle<value_type, dimension, value_type, nb_inputs_near, value_type, + nb_outputs_near, std::size_t>; + using container_type = std::vector<particle_type>; + using point_type = typename particle_type::position_type; + using cell_type = scalfmm::component::cell<typename interpolator_type::storage_type>; + using leaf_type = scalfmm::component::leaf_view<particle_type>; + using box_type = scalfmm::component::box<point_type>; + using group_tree_type = scalfmm::component::group_tree_view<cell_type, leaf_type, box_type>; + + std::cout << cpp_tools::colors::green << "Creating & Inserting particles ...\n" << cpp_tools::colors::reset; + + point_type box_center{}; + value_type box_width{}; + container_type container{}; + time.tic(); + std::tie(container, box_center, box_width) = read_data<container_type>(input_file, outputs_already_computed); + time.tac(); + + // const std::size_t number_of_particles = std::get<0>(container->size()); + const std::size_t number_of_particles = container.size(); + std::cout << cpp_tools::colors::green << "... Done.\n" << cpp_tools::colors::reset; + std::cout << cpp_tools::colors::green << "Box center = " << box_center << " box width = " << box_width + << cpp_tools::colors::reset << '\n'; + + std::cout << cpp_tools::colors::yellow << "Container loaded in " << time.elapsed() << "ms\n" + << cpp_tools::colors::reset; + + time.tic(); + box_type box(box_width, box_center); + group_tree_type tree(static_cast<std::size_t>(tree_height), order, box, static_cast<std::size_t>(group_size), + static_cast<std::size_t>(group_size), container, dataSorted); + time.tac(); + std::cout << cpp_tools::colors::yellow << "Group tree created in " << time.elapsed() << "ms\n" + << cpp_tools::colors::reset; + + time.tic(); + interpolator_type interpolator(mk_far, order, static_cast<std::size_t>(tree_height), box.width(0)); + typename FmmOperatorType::near_field_type near_field(mk_near); + typename FmmOperatorType::far_field_type far_field(interpolator); + FmmOperatorType fmm_operator(near_field, far_field); + time.tac(); + + std::cout << cpp_tools::colors::blue << "Fmm with kernels: " << std::endl + << " near " << mk_near.name() << std::endl + << " far " << mk_far.name() << std::endl + << cpp_tools::colors::reset; + std::cout << cpp_tools::colors::yellow << "Kernel and Interp created in " << time.elapsed() << "ms\n" + << cpp_tools::colors::reset; + + auto operator_to_proceed = scalfmm::algorithms::all; + scalfmm::algorithms::fmm[scalfmm::options::_s(scalfmm::options::timit)](tree, fmm_operator, operator_to_proceed); + + if(!outputs_already_computed) + { + time.tic(); + scalfmm::algorithms::full_direct(container, mk_near); + time.tac(); + std::cout << cpp_tools::colors::yellow << "Direct computation done in " << time.elapsed() << "ms\n" + << cpp_tools::colors::reset; + } + else + { + std::cout << cpp_tools::colors::yellow << "Outputs already computed...\n" << cpp_tools::colors::reset; + } + + scalfmm::utils::accurater<value_type> error; + + scalfmm::component::for_each_leaf(std::cbegin(tree), std::cend(tree), + [&container, &error](auto const& leaf) + { + // loop on the particles of the leaf + for(auto const p_ref: leaf) + { + // build a particle + const auto p = typename leaf_type::const_proxy_type(p_ref); + // + const auto& idx = std::get<0>(p.variables()); + + auto const& output_ref = container[idx].outputs(); + auto const& output = p.outputs(); + + for(std::size_t i{0}; i < nb_outputs_near; ++i) + { + error.add(output_ref.at(i), output.at(i)); + } + } + }); + + std::cout << error << '\n'; + if(!output_file.empty()) + { + std::cout << "Write outputs in " << output_file << std::endl; + scalfmm::io::FFmaGenericWriter<double> writer(output_file); + writer.writeDataFromTree(tree, number_of_particles); + } + + return 0; +} + +template<class OPTION_TYPE> +auto select_kernel(const int& matrix_type, const std::string& input_file, const std::string& output_file, + const int& tree_height, const int& group_size, const int& order, const value_type& variance, + const bool& outputs_already_computed = false) -> void +{ + switch(matrix_type) + { + // case 0: + // { + // using matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; + // using near_field_type = scalfmm::operators::near_field_operator<matrix_kernel_type>; + // // + // using interpolation_type = interpolator_alias<value_type, dimension, matrix_kernel_type, OPTION_TYPE>; + // using far_field_type = scalfmm::operators::far_field_operator<interpolation_type>; + // matrix_kernel_type mk{}; + // run<scalfmm::operators::fmm_operators<near_field_type, far_field_type>>( + // input_file, output_file, tree_height, group_size, order, mk, outputs_already_computed); + // break; + // } + // case 1: + // { + // using matrix_kernel_type = scalfmm::matrix_kernels::gaussian<value_type>; + // using near_field_type = scalfmm::operators::near_field_operator<matrix_kernel_type>; + // // + // using interpolation_type = interpolator_alias<value_type, dimension, matrix_kernel_type, OPTION_TYPE>; + // using far_field_type = scalfmm::operators::far_field_operator<interpolation_type>; + // matrix_kernel_type mk{}; + + // mk.set_coeff(variance); + // run<scalfmm::operators::fmm_operators<near_field_type, far_field_type>>( + // input_file, output_file, tree_height, group_size, order, mk, outputs_already_computed); + // break; + // } + // TEMP AG + case 0: + { + using matrix_kernel_type = scalfmm::matrix_kernels::laplace::grad_one_over_r<dimension>; + using near_field_type = scalfmm::operators::near_field_operator<matrix_kernel_type>; + // + using interpolation_type = interpolator_alias<value_type, dimension, matrix_kernel_type, OPTION_TYPE>; + using far_field_type = scalfmm::operators::far_field_operator<interpolation_type>; + // + matrix_kernel_type mk{}; + + run<scalfmm::operators::fmm_operators<near_field_type, far_field_type>>( + input_file, output_file, tree_height, group_size, order, mk, mk, outputs_already_computed); + break; + } + case 1: + { + using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::grad_one_over_r<dimension>; + using near_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; + // + using far_matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; + using interpolation_type = interpolator_alias<value_type, dimension, far_matrix_kernel_type, OPTION_TYPE>; + using far_field_type = scalfmm::operators::far_field_operator<interpolation_type, true>; + // + near_matrix_kernel_type mk_near{}; + far_matrix_kernel_type mk_far{}; + + run<scalfmm::operators::fmm_operators<near_field_type, far_field_type>>( + input_file, output_file, tree_height, group_size, order, mk_near, mk_far, outputs_already_computed); + break; + } + default: + std::cout << "Kernel not implemented. value is 0) 1/r, 1) exp(-r^2/l^2), " << std::endl; + break; + } +} + +auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int +{ + // + // Parameter handling + auto parser = cpp_tools::cl_parser::make_parser( + cpp_tools::cl_parser::help{}, args::input_file(), args::output_file(), args::tree_height{}, + args::order{}, // args::thread_count{}, + args::block_size{}, args::log_file{}, args::log_level{}, local_args::isSorted{}, local_args::matrix_kernel{}, + local_args::interpolator{}, local_args::variance{}, local_args::outputs_already_computed{}); + parser.parse(argc, argv); + // Getting command line parameters + const int tree_height{parser.get<args::tree_height>()}; + std::cout << cpp_tools::colors::blue << "<params> Tree height : " << tree_height << cpp_tools::colors::reset + << '\n'; + + const int group_size{parser.get<args::block_size>()}; + std::cout << cpp_tools::colors::blue << "<params> Group Size : " << group_size << cpp_tools::colors::reset << '\n'; + + const std::string input_file{parser.get<args::input_file>()}; + if(!input_file.empty()) + { + std::cout << cpp_tools::colors::blue << "<params> Input file : " << input_file << cpp_tools::colors::reset + << '\n'; + } + + const auto output_file{parser.get<args::output_file>()}; + if(!output_file.empty()) + { + std::cout << cpp_tools::colors::blue << "<params> Output file : " << output_file << cpp_tools::colors::reset + << '\n'; + } + const auto order{parser.get<args::order>()}; + // const bool dataSorted(parser.exists<local_args::isSorted>()); + const bool outputs_already_computed(parser.get<local_args::outputs_already_computed>()); + const int matrix_type = parser.get<local_args::matrix_kernel>(); + const int which_interp(parser.get<local_args::interpolator>()); + const value_type variance(parser.get<local_args::variance>()); + + if(which_interp == 0) + { + std::cout << cpp_tools::colors::blue << "Fmm interpolation: uniform (uniform fft)" << std::endl + << cpp_tools::colors::reset; + using options_uniform = scalfmm::options::uniform_<scalfmm::options::fft_>; + select_kernel<options_uniform>(matrix_type, input_file, output_file, tree_height, group_size, order, variance, + outputs_already_computed); + } + else if(which_interp == 1) + { + std::cout << cpp_tools::colors::blue << "Fmm interpolation: chebyshev (chebyshev low-rank)" << std::endl + << cpp_tools::colors::reset; + using options_chebyshev = scalfmm::options::chebyshev_<scalfmm::options::low_rank_>; + select_kernel<options_chebyshev>(matrix_type, input_file, output_file, tree_height, group_size, order, variance, + outputs_already_computed); + } + else if(which_interp == 2) + { + std::cout << cpp_tools::colors::blue << "Fmm interpolation: barycentric (barycentric dense)" << std::endl + << cpp_tools::colors::reset; + using options_barycentric = scalfmm::options::barycentric_<scalfmm::options::dense_>; + select_kernel<options_barycentric>(matrix_type, input_file, output_file, tree_height, group_size, order, + variance, outputs_already_computed); + } + else if(which_interp == 3) + { + std::cout << cpp_tools::colors::blue << "Fmm interpolation: barycentric (barycentric low-rank)" << std::endl + << cpp_tools::colors::reset; + using options_barycentric = scalfmm::options::barycentric_<scalfmm::options::low_rank_>; + select_kernel<options_barycentric>(matrix_type, input_file, output_file, tree_height, group_size, order, + variance, outputs_already_computed); + } + + return 0; +} diff --git a/examples/test_barycentric.cpp b/examples/test_barycentric.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ac9efef59c25fe57098a3db3cd145926c1ff68d1 --- /dev/null +++ b/examples/test_barycentric.cpp @@ -0,0 +1,162 @@ +#include <iostream> +#include <random> +#include <vector> + +#include "scalfmm/algorithms/fmm.hpp" +#include "scalfmm/algorithms/full_direct.hpp" +#include "scalfmm/container/particle.hpp" +#include "scalfmm/interpolation/interpolation.hpp" +#include "scalfmm/matrix_kernels/laplace.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/operators/fmm_operators.hpp" +#include "scalfmm/tree/box.hpp" +#include "scalfmm/tree/cell.hpp" +#include "scalfmm/tree/for_each.hpp" +#include "scalfmm/tree/group_tree_view.hpp" +#include "scalfmm/tree/leaf_view.hpp" +#include "scalfmm/utils/accurater.hpp" + +auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int +{ + // order of the approximation + const std::size_t order{5}; + // height of the fmm tree + const std::size_t tree_height{5}; + + using value_type = double; + + // choosing a matrix kernel + // the far field matrix kernel + using far_kernel_matrix_type = scalfmm::matrix_kernels::laplace::one_over_r; + // the near field matrix kernel + using near_kernel_matrix_type = far_kernel_matrix_type; + // number of inputs and outputs. + static constexpr std::size_t nb_inputs_near{near_kernel_matrix_type::km}; + static constexpr std::size_t nb_outputs_near{near_kernel_matrix_type::kn}; + + // loading data in containers + static constexpr std::size_t dimension{2}; + + using particle_type = scalfmm::container::particle< + // position + value_type, dimension, + // inputs + value_type, nb_inputs_near, + // outputs + value_type, nb_outputs_near, + // variables + std::size_t // for storing the index in the original container. + >; + // position point type + using position_type = typename particle_type::position_type; + + using container_type = std::vector<particle_type>; + // allocate 100 particles. + constexpr std::size_t nb_particles{100}; + container_type container(nb_particles); + + // box of the simulation [0,2]x[0,2] + using box_type = scalfmm::component::box<position_type>; + // width of the box + constexpr value_type box_width{2.}; + // center of the box + const position_type box_center{1., 1.}; + // the box for the tree + box_type box(box_width, box_center); + + // random generator + std::mt19937 gen(123); + std::uniform_real_distribution<value_type> dis(0.0, 2.0); + auto random_r = [&dis, &gen]() { return dis(gen); }; + + // inserting particles in the container + for(std::size_t idx = 0; idx < nb_particles; ++idx) + { + // particle_type p; + particle_type& p = container[idx]; + for(auto& e: p.position()) + { + e = random_r(); + } + for(auto& e: p.inputs()) + { + e = random_r(); + } + for(auto& e: p.outputs()) + { + e = value_type(0.); + } + p.variables(idx); + } + + // interpolation types + // we define a near_field from its matrix kernel + using near_field_type = scalfmm::operators::near_field_operator<near_kernel_matrix_type>; + // we choose an interpolator with a far matrix kernel for the approximation + using interpolator_type = + scalfmm::interpolation::interpolator<value_type, dimension, far_kernel_matrix_type, + scalfmm::options::barycentric_<scalfmm::options::low_rank_>>; + + // then, we define the far field + using far_field_type = scalfmm::operators::far_field_operator<interpolator_type>; + // the resulting fmm operator is + using fmm_operator_type = scalfmm::operators::fmm_operators<near_field_type, far_field_type>; + // construct the fmm operator + // construct the near field + near_field_type near_field; + // a reference on the matrix_kernel of the near_field + auto near_mk = near_field.matrix_kernel(); + + // build the approximation used in the near field + interpolator_type interpolator(order, tree_height, box.width(0)); + far_field_type far_field(interpolator); + // construct the fmm operator + fmm_operator_type fmm_operator(near_field, far_field); + + // tree types + // the cell type of the tree holding multipoles and locals expansions + // here, we extract the correct storage for the cells from the interpolation method. + using cell_type = scalfmm::component::cell<typename interpolator_type::storage_type>; + // the leaf type holding the particles + using leaf_type = scalfmm::component::leaf_view<particle_type>; + // the tree type + using group_tree_type = scalfmm::component::group_tree_view<cell_type, leaf_type, box_type>; + // we construct the tree + const std::size_t group_size{10}; // the number of cells and leaf grouped in the tree + group_tree_type tree(tree_height, order, box, group_size, group_size, container); + + // now we have everything to call the fmm algorithm + scalfmm::algorithms::fmm[scalfmm::options::_s(scalfmm::options::seq)](tree, fmm_operator); + + // we will compute the reference with the full direct algorithm + // from the original container + + scalfmm::algorithms::full_direct(container, near_mk); + + scalfmm::utils::accurater<value_type> error; + + scalfmm::component::for_each_leaf(std::cbegin(tree), std::cend(tree), + [&container, &error](auto const& leaf) + { + // loop on the particles of the leaf + for(auto const p_ref: leaf) + { + // build a particle + const auto p = typename leaf_type::const_proxy_type(p_ref); + // + const auto& idx = std::get<0>(p.variables()); + + auto const& output_ref = container[idx].outputs(); + auto const& output = p.outputs(); + + for(std::size_t i{0}; i < nb_outputs_near; ++i) + { + error.add(output_ref.at(i), output.at(i)); + } + } + }); + + std::cout << error << '\n'; + + return 0; +} \ No newline at end of file diff --git a/examples/test_dimension_omp.cpp b/examples/test_dimension_omp.cpp index 587d999bcb623aea9557cf8cf5b3e509fb6cedae..2a86413c3868e7a8fb33c6166205951982cdb945 100644 --- a/examples/test_dimension_omp.cpp +++ b/examples/test_dimension_omp.cpp @@ -114,7 +114,7 @@ auto inline check_results(const Tree_T& tree, const Container_T& reference, cons scalfmm::component::for_each_leaf(std::cbegin(tree), std::cend(tree), [&reference, &error](auto const& leaf) { - const auto nb_elt = leaf.size(); + const auto nb_elt = leaf.size(); for(std::size_t i = 0; i < nb_elt; ++i) { const auto& p = leaf.particle(i); diff --git a/examples/test_laplace_kernels.cpp b/examples/test_laplace_kernels.cpp index 6affbc47379d6aa50b42904b9722b4cd92f0f99c..1d29391d67b307487f97b5d4d8883ccbe1c755c3 100644 --- a/examples/test_laplace_kernels.cpp +++ b/examples/test_laplace_kernels.cpp @@ -113,7 +113,7 @@ auto run(const std::string& input_file, const std::string& output_file, const in std::cout << cpp_tools::colors::yellow << "Group tree created in " << time.elapsed() << "ms\n" << cpp_tools::colors::reset; delete container; - gtree.statistics("Stats",std::cout); + gtree.statistics("Stats", std::cout); time.tic(); far_matrix_kernel_type mk_far{}; interpolator_type interpolator(mk_far, order, static_cast<std::size_t>(tree_height), box.width(0)); @@ -123,7 +123,7 @@ auto run(const std::string& input_file, const std::string& output_file, const in FMM_OPERATOR_TYPE fmm_operator(near_field, far_field); time.tac(); std::cout << cpp_tools::colors::blue << "Fmm with kernels: " << std::endl - << " near " << mk_near.name() << " mutual " << std::boolalpha << near_field.mutual() <<std::endl + << " near " << mk_near.name() << " mutual " << std::boolalpha << near_field.mutual() << std::endl << " far " << mk_far.name() << std::endl << cpp_tools::colors::reset; std::cout << cpp_tools::colors::yellow << "Kernel and Interp created in " << time.elapsed() << "ms\n" @@ -140,9 +140,12 @@ auto run(const std::string& input_file, const std::string& output_file, const in time.tic(); scalfmm::algorithms::fmm[scalfmm::options::_s(scalfmm::options::timit)](gtree, fmm_operator, operator_to_proceed); time.tac(); - std::cout << cpp_tools::colors::yellow << "Full algorithm " << time.elapsed()/value_type(1000) << " s\n" + std::cout << cpp_tools::colors::yellow << "Full algorithm " << time.elapsed() / value_type(1000) << " s\n" + << cpp_tools::colors::reset; + std::cout << cpp_tools::colors::yellow << "Full algorithm " + << time.elapsed() * std::chrono::milliseconds::period::num / std::chrono::milliseconds::period::den + << " s\n" << cpp_tools::colors::reset; - std::cout << cpp_tools::colors::yellow << "Full algorithm " << time.elapsed()* std::chrono::milliseconds::period::num / std::chrono::milliseconds::period::den << " s\n" << cpp_tools::colors::reset; if(postreat) { // Doesn't work because gtree is not a container // laplace::post_traitement(mk_near, >ree); @@ -202,8 +205,7 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int if(which_interp == 0) { using options_uniform = scalfmm::options::uniform_<scalfmm::options::fft_>; - std::cout << cpp_tools::colors::blue << "Fmm interpolation: uniform" << std::endl - << cpp_tools::colors::reset; + std::cout << cpp_tools::colors::blue << "Fmm interpolation: uniform" << std::endl << cpp_tools::colors::reset; switch(matrix_type) { @@ -283,8 +285,7 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int } else if(which_interp == 1) { - std::cout << cpp_tools::colors::blue << "Fmm interpolation: chebyshev" << std::endl - << cpp_tools::colors::reset; + std::cout << cpp_tools::colors::blue << "Fmm interpolation: chebyshev" << std::endl << cpp_tools::colors::reset; using options_chebyshev = scalfmm::options::chebyshev_<scalfmm::options::low_rank_>; switch(matrix_type) { diff --git a/examples/test_like_mrhs.cpp b/examples/test_like_mrhs.cpp index f727f5f67f8a4b5125bc935e1723b9837c2d91c9..32a5cdd86b37420241295778ec92571a62a7435e 100644 --- a/examples/test_like_mrhs.cpp +++ b/examples/test_like_mrhs.cpp @@ -58,7 +58,7 @@ struct command_line_parameters std::string log_file = ""; ///< Log file. std::string log_level = ""; ///< Log file. int block_size = 250; ///< Group tree group size -}; // namespace args +}; // namespace args auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int { // Parameter handling @@ -69,7 +69,8 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int // Getting command line parameters const auto tree_height{params.tree_height}; - std::cout << cpp_tools::colors::blue << "<params> Tree height : " << tree_height << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Tree height : " << tree_height << cpp_tools::colors::reset + << '\n'; const auto group_size{params.block_size}; std::cout << cpp_tools::colors::blue << "<params> Group Size : " << group_size << cpp_tools::colors::reset << '\n'; @@ -77,7 +78,8 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int const auto input_file{params.input_file}; if(!input_file.empty()) { - std::cout << cpp_tools::colors::blue << "<params> Input file : " << input_file << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Input file : " << input_file << cpp_tools::colors::reset + << '\n'; } const auto output_file{params.output_file}; @@ -102,7 +104,8 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int // scalfmm 3.0 tree tests and benchmarks. // --------------------------------------- using matrix_kernel_type = scalfmm::matrix_kernels::laplace::like_mrhs; - using interpolator_type = scalfmm::interpolation::interpolator<double, dimension, matrix_kernel_type, scalfmm::options::uniform_<scalfmm::options::fft_>>; + using interpolator_type = scalfmm::interpolation::interpolator<double, dimension, matrix_kernel_type, + scalfmm::options::uniform_<scalfmm::options::fft_>>; using particle_type = scalfmm::container::particle<double, dimension, double, inputs, double, outputs, std::size_t>; using container_type = scalfmm::container::particle_container<particle_type>; using position_type = typename particle_type::position_type; @@ -202,13 +205,15 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int scalfmm::meta::repeat([](auto& e, auto o) { e += o; }, total_physical_value, std::get<1>(res)); }); scalfmm::meta::repeat( - [](auto e) { - std::cout << cpp_tools::colors::red << std::setprecision(10) << "Energy: " << e << cpp_tools::colors::reset - << '\n'; + [](auto e) + { + std::cout << cpp_tools::colors::red << std::setprecision(10) << "Energy: " << e + << cpp_tools::colors::reset << '\n'; }, energy); scalfmm::meta::repeat( - [](auto e) { + [](auto e) + { std::cout << cpp_tools::colors::blue << std::setprecision(10) << "Total Physical Value: " << e << cpp_tools::colors::reset << '\n'; }, diff --git a/examples/test_particles.cpp b/examples/test_particles.cpp index e9afd506a063b007f655b423bf985ab0d39fbd03..2d5dd18b6b0033e6cd00703059155d3d3d3192d8 100644 --- a/examples/test_particles.cpp +++ b/examples/test_particles.cpp @@ -20,25 +20,30 @@ auto main() -> int std::cout << "--- end traits ---\n"; double inc{0}; - std::for_each(std::begin(c), std::end(c), [&inc](auto p) { - p = particle_type(container::point<double>{inc, inc, inc}, inc * 2, inc * 3, std::size_t(inc)).as_tuple(); - ++inc; - }); - - std::for_each(std::begin(c), std::end(c), [](auto p) { - std::cout << "-----\n"; - particle_type p_(p); - std::cout << p_.position() << '\n'; - for(auto const& e: p_.inputs()) - { - std::cout << e << '\n'; - } - for(auto const& e: p_.outputs()) - { - std::cout << e << '\n'; - } - meta::repeat([](auto e) { std::cout << e << '\n'; }, p_.variables()); - }); + std::for_each( + std::begin(c), std::end(c), + [&inc](auto p) + { + p = particle_type(container::point<double>{inc, inc, inc}, inc * 2, inc * 3, std::size_t(inc)).as_tuple(); + ++inc; + }); + + std::for_each(std::begin(c), std::end(c), + [](auto p) + { + std::cout << "-----\n"; + particle_type p_(p); + std::cout << p_.position() << '\n'; + for(auto const& e: p_.inputs()) + { + std::cout << e << '\n'; + } + for(auto const& e: p_.outputs()) + { + std::cout << e << '\n'; + } + meta::repeat([](auto e) { std::cout << e << '\n'; }, p_.variables()); + }); for(std::size_t i = 0; i < 13; ++i) { @@ -66,28 +71,32 @@ auto main() -> int ++it; } - std::for_each(std::begin(c), std::end(c), [](auto p) { - std::cout << "-----\n"; - particle_type p_(p); - std::cout << p_.position() << '\n'; - for(auto const& e: p_.inputs()) - { - std::cout << e << '\n'; - } - for(auto const& e: p_.outputs()) - { - std::cout << e << '\n'; - } - meta::repeat([](auto e) { std::cout << e << '\n'; }, p_.variables()); - }); + std::for_each(std::begin(c), std::end(c), + [](auto p) + { + std::cout << "-----\n"; + particle_type p_(p); + std::cout << p_.position() << '\n'; + for(auto const& e: p_.inputs()) + { + std::cout << e << '\n'; + } + for(auto const& e: p_.outputs()) + { + std::cout << e << '\n'; + } + meta::repeat([](auto e) { std::cout << e << '\n'; }, p_.variables()); + }); std::fill(container::position_begin(c), container::position_end(c), meta::to_tuple(typename particle_type::position_type(4.5))); - std::for_each(std::begin(c), std::end(c), [](auto p) { - std::cout << "-----\n"; - particle_type p_(p); - std::cout << p_.position() << '\n'; - }); + std::for_each(std::begin(c), std::end(c), + [](auto p) + { + std::cout << "-----\n"; + particle_type p_(p); + std::cout << p_.position() << '\n'; + }); auto p{particle_type(container::point<double>{0.1, 0.2, 0.3}, 0., 0., 1)}; diff --git a/examples/test_tensorial_interpolator.cpp b/examples/test_tensorial_interpolator.cpp index 5771533d9ade200b1462c054fbcab959b3b55e4a..e73f939465ba7da831e5fd93373a1603785c0a57 100644 --- a/examples/test_tensorial_interpolator.cpp +++ b/examples/test_tensorial_interpolator.cpp @@ -69,7 +69,8 @@ auto main() -> int point_type center = {2. * width, 0., 0.}; - auto make_particle = [¢er, &width, &random_r]() { + auto make_particle = [¢er, &width, &random_r]() + { point_type position = {random_r() * width, random_r() * width, random_r() * width}; position += center; particle_type p(position, random_r()); //, random_r(), random_r()); @@ -103,7 +104,8 @@ auto main() -> int // }); // Lambda for output result to file - auto print_to_file = [](auto& file, auto& container, std::size_t size) { + auto print_to_file = [](auto& file, auto& container, std::size_t size) + { auto it = std::begin(container); for(std::size_t i = 0; i < size; ++i) { diff --git a/examples/test_time_loop.cpp b/examples/test_time_loop.cpp index 34a639f832a3cb3116d69d00ff6a9902e0bcc2f3..ca7c351e561998a8ddf0d6eefae1d118348b71d0 100644 --- a/examples/test_time_loop.cpp +++ b/examples/test_time_loop.cpp @@ -129,7 +129,6 @@ namespace local_args }; } // namespace local_args - template<std::size_t Dimension, typename ContainerType, typename ValueType> void read_data(const std::string& filename, ContainerType*& container, scalfmm::container::point<ValueType, Dimension>& Centre, ValueType& width) @@ -168,18 +167,19 @@ void read_data(const std::string& filename, ContainerType*& container, template<typename GroupTree, typename ValueType> auto update_particles(GroupTree& tree, ValueType delta) -> void { - scalfmm::component::for_each_leaf(tree.begin(), tree.end(), [delta](auto& leaf) - { - auto& particles{leaf.particles()}; - scalfmm::container::point<ValueType, GroupTree::dimension> force{}; - - for(std::size_t i{0}; i<particles.size(); ++i) - { - auto proxy = particles.at(i); - force = -delta*proxy.position(); - proxy.position() += force; - } - }); + scalfmm::component::for_each_leaf(tree.begin(), tree.end(), + [delta](auto& leaf) + { + auto& particles{leaf.particles()}; + scalfmm::container::point<ValueType, GroupTree::dimension> force{}; + + for(std::size_t i{0}; i < particles.size(); ++i) + { + auto proxy = particles.at(i); + force = -delta * proxy.position(); + proxy.position() += force; + } + }); } template<typename GroupTree> @@ -192,22 +192,23 @@ auto get_new_box(GroupTree const& tree) -> typename GroupTree::box_type std::vector<value_type> max(GroupTree::dimension, 0); std::vector<value_type> min(GroupTree::dimension, 0); - scalfmm::component::for_each_leaf(tree.begin(), tree.end(), [&min, &max, &old_center](auto const& leaf) - { - auto& particles{leaf.particles()}; - for(std::size_t i{0}; i<particles.size(); ++i) - { - const auto position = particles.at(i).position(); - //std::cout << particles.at(i).inputs(0) << '\n'; - for(std::size_t d{0}; d<GroupTree::dimension; ++d) - { - min.at(d) = std::min(position.at(d), min.at(d)); - max.at(d) = std::max(position.at(d), max.at(d)); - //std::cout << particles.at(i).outputs(d) << ' '; - } - //std::cout << '\n'; - } - }); + scalfmm::component::for_each_leaf(tree.begin(), tree.end(), + [&min, &max, &old_center](auto const& leaf) + { + auto& particles{leaf.particles()}; + for(std::size_t i{0}; i < particles.size(); ++i) + { + const auto position = particles.at(i).position(); + //std::cout << particles.at(i).inputs(0) << '\n'; + for(std::size_t d{0}; d < GroupTree::dimension; ++d) + { + min.at(d) = std::min(position.at(d), min.at(d)); + max.at(d) = std::max(position.at(d), max.at(d)); + //std::cout << particles.at(i).outputs(d) << ' '; + } + //std::cout << '\n'; + } + }); value_type width{0}; for(std::size_t i{0}; i < GroupTree::dimension; ++i) { @@ -261,10 +262,12 @@ auto run(cpp_tools::cl_parser::parser<Parameters...> const& parser) -> int const std::string catalyst_script(parser.template get<local_args::catalyst_script>()); const auto delta = parser.template get<local_args::delta>(); - std::cout << cpp_tools::colors::blue << "<params> Tree height : " << tree_height << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Tree height : " << tree_height << cpp_tools::colors::reset + << '\n'; std::cout << cpp_tools::colors::blue << "<params> Group Size : " << group_size << cpp_tools::colors::reset << '\n'; std::cout << cpp_tools::colors::blue << "<params> Runtime order : " << order << cpp_tools::colors::reset << '\n'; - std::cout << cpp_tools::colors::blue << "<params> Time Steps : " << n_time_steps << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Time Steps : " << n_time_steps << cpp_tools::colors::reset + << '\n'; std::cout << cpp_tools::colors::blue << "<params> Delta forces : " << delta << cpp_tools::colors::reset << '\n'; // --------------------------------------- @@ -337,19 +340,20 @@ auto run(cpp_tools::cl_parser::parser<Parameters...> const& parser) -> int // visu related post treatment if(!visu_file.empty()) { - scalfmm::tools::io::exportVTKxml(std::to_string(steps-1)+visu_file, tree, container->size()); + scalfmm::tools::io::exportVTKxml(std::to_string(steps - 1) + visu_file, tree, container->size()); } #ifdef USE_CATALYST if(catalyst_enable) { - catalyst_adaptor::execute(steps-1, tree); + catalyst_adaptor::execute(steps - 1, tree); } #endif // --- // compute the corners and set new box auto const new_box = get_new_box(tree); - std::cout << cpp_tools::colors::on_green << "New box width is " << new_box.width(0) << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::on_green << "New box width is " << new_box.width(0) << cpp_tools::colors::reset + << '\n'; // --- // compute new morton indices @@ -375,12 +379,12 @@ auto run(cpp_tools::cl_parser::parser<Parameters...> const& parser) -> int // visu related post treatment if(!visu_file.empty()) { - scalfmm::tools::io::exportVTKxml(std::to_string(n_time_steps-1) + visu_file, tree, container->size()); + scalfmm::tools::io::exportVTKxml(std::to_string(n_time_steps - 1) + visu_file, tree, container->size()); } #ifdef USE_CATALYST if(catalyst_enable) { - catalyst_adaptor::execute(n_time_steps-1, tree); + catalyst_adaptor::execute(n_time_steps - 1, tree); catalyst_adaptor::finalize(); } #endif @@ -410,10 +414,10 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int { // // Parameter handling - auto parser = cpp_tools::cl_parser::make_parser(cpp_tools::cl_parser::help{}, local_args::tree_height{}, local_args::order{}, - local_args::input_file{}, local_args::output_file{}, - local_args::block_size{}, local_args::dimension{}, local_args::time_steps{}, - local_args::visu_file{}, local_args::delta{}, local_args::catalyst{}, local_args::catalyst_script{}); + auto parser = cpp_tools::cl_parser::make_parser( + cpp_tools::cl_parser::help{}, local_args::tree_height{}, local_args::order{}, local_args::input_file{}, + local_args::output_file{}, local_args::block_size{}, local_args::dimension{}, local_args::time_steps{}, + local_args::visu_file{}, local_args::delta{}, local_args::catalyst{}, local_args::catalyst_script{}); parser.parse(argc, argv); // Getting command line parameters @@ -428,8 +432,9 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int using near_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; // using far_matrix_kernel_type = scalfmm::matrix_kernels::others::one_over_r2; - using interpolator_type = scalfmm::interpolation::interpolator<double, dim, far_matrix_kernel_type - , scalfmm::options::uniform_<scalfmm::options::fft_>>; + using interpolator_type = + scalfmm::interpolation::interpolator<double, dim, far_matrix_kernel_type, + scalfmm::options::uniform_<scalfmm::options::fft_>>; using far_field_type = scalfmm::operators::far_field_operator<interpolator_type, true>; run<dim, scalfmm::operators::fmm_operators<near_field_type, far_field_type>>(parser); diff --git a/examples/tutorial.cpp b/examples/tutorial.cpp index 7710bca4906fdd454eb694facce52f273ecd5d09..8ed1a3e91e001a522aa75c481d4ee97c85cc4659 100644 --- a/examples/tutorial.cpp +++ b/examples/tutorial.cpp @@ -103,9 +103,9 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int using fmm_operator_type = operators::fmm_operators<near_field_type, far_field_type>; // construct the fmm operator // construct the near field - near_field_type near_field; + near_field_type near_field; // a reference on the matrix_kernel of the near_field - auto near_mk = near_field.matrix_kernel(); + auto near_mk = near_field.matrix_kernel(); // build the approximation used in the near field interpolator_type interpolator(order, tree_height, box.width(0)); diff --git a/guix-tools/scalfmm-channels.scm b/guix-tools/scalfmm-channels.scm new file mode 100644 index 0000000000000000000000000000000000000000..71f2ed892ce83ebc1291c607e9106f38fce73418 --- /dev/null +++ b/guix-tools/scalfmm-channels.scm @@ -0,0 +1,67 @@ +(list (channel + (name 'guix) + (url "https://git.savannah.gnu.org/git/guix.git") + (branch "master") + (commit + "67a535351f8678969e412e8dba9197a883b524d0") + (introduction + (make-channel-introduction + "9edb3f66fd807b096b48283debdcddccfea34bad" + (openpgp-fingerprint + "BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA")))) + (channel + (name 'guix-hpc-non-free) + (url "https://gitlab.inria.fr/guix-hpc/guix-hpc-non-free.git") + (branch "master") + (commit + "de30e6fd3fe919b386b8719595d975b6118ff86e")) + (channel + (name 'guix-hpc) + (url "https://gitlab.inria.fr/guix-hpc/guix-hpc.git") + (branch "master") + (commit + "8a3a76ab06e451c2cc4b7ce00d1241ded21ad77d")) + (channel + (name 'nonguix) + (url "https://gitlab.com/nonguix/nonguix") + (branch "master") + (commit + "c075e3ae214ca6e773c69142ede754e7cf4ae799") + (introduction + (make-channel-introduction + "897c1a470da759236cc11798f4e0a5f7d4d59fbc" + (openpgp-fingerprint + "2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5")))) + (channel + (name 'guix-science-nonfree) + (url "https://codeberg.org/guix-science/guix-science-nonfree.git") + (branch "master") + (commit + "7c58ef4da438043aaf999e8d7f02ea75abfd3402") + (introduction + (make-channel-introduction + "58661b110325fd5d9b40e6f0177cc486a615817e" + (openpgp-fingerprint + "CA4F 8CF4 37D7 478F DA05 5FD4 4213 7701 1A37 8446")))) + (channel + (name 'guix-science) + (url "https://codeberg.org/guix-science/guix-science.git") + (branch "master") + (commit + "be44985a2d468ed8bcc09ab4bf320a4e3b6c09be") + (introduction + (make-channel-introduction + "b1fe5aaff3ab48e798a4cce02f0212bc91f423dc" + (openpgp-fingerprint + "CA4F 8CF4 37D7 478F DA05 5FD4 4213 7701 1A37 8446")))) + (channel + (name 'guix-past) + (url "https://codeberg.org/guix-science/guix-past.git") + (branch "master") + (commit + "2d3485b7fd7c1904bc7c1a87fc45048376ff4d3a") + (introduction + (make-channel-introduction + "0c119db2ea86a389769f4d2b9c6f5c41c027e336" + (openpgp-fingerprint + "3CE4 6455 8A84 FDC6 9DB4 0CFB 090B 1199 3D9A EBB5"))))) diff --git a/guix-tools/scalfmm-manifest-clang-mkl.scm b/guix-tools/scalfmm-manifest-clang-mkl.scm new file mode 100644 index 0000000000000000000000000000000000000000..285a3fce6e76c628f9f4ec9b6a4924a91d8d9de1 --- /dev/null +++ b/guix-tools/scalfmm-manifest-clang-mkl.scm @@ -0,0 +1,14 @@ +;; What follows is a "manifest" equivalent to the command line you gave. +;; You can store it in a file that you may then pass to any 'guix' command +;; that accepts a '--manifest' (or '-m') option. + +(specifications->manifest + (list "clang-toolchain@18" + "cmake" + "make" + "coreutils" + "ncurses" + "intel-oneapi-mkl" + "grep" + "findutils" + "sed")) diff --git a/guix-tools/scalfmm-manifest-clang-openblas.scm b/guix-tools/scalfmm-manifest-clang-openblas.scm new file mode 100644 index 0000000000000000000000000000000000000000..7dbe2c842d78332a5499a9eb2f5693717a6d34d2 --- /dev/null +++ b/guix-tools/scalfmm-manifest-clang-openblas.scm @@ -0,0 +1,17 @@ +;; What follows is a "manifest" equivalent to the command line you gave. +;; You can store it in a file that you may then pass to any 'guix' command +;; that accepts a '--manifest' (or '-m') option. + +(specifications->manifest + (list "clang-toolchain@18" + "cmake" + "make" + "coreutils" + "ncurses" + "openblas" + "fftw" + "fftwf" + "pkg-config" + "grep" + "findutils" + "sed")) diff --git a/guix-tools/scalfmm-manifest-gcc-mkl.scm b/guix-tools/scalfmm-manifest-gcc-mkl.scm new file mode 100644 index 0000000000000000000000000000000000000000..016062e39a3a9ce9ad384e5d93bbffa546fef988 --- /dev/null +++ b/guix-tools/scalfmm-manifest-gcc-mkl.scm @@ -0,0 +1,14 @@ +;; What follows is a "manifest" equivalent to the command line you gave. +;; You can store it in a file that you may then pass to any 'guix' command +;; that accepts a '--manifest' (or '-m') option. + +(specifications->manifest + (list "gcc-toolchain@11" + "cmake" + "make" + "coreutils" + "ncurses" + "intel-oneapi-mkl" + "grep" + "findutils" + "sed")) diff --git a/guix-tools/scalfmm-manifest-gcc-openblas.scm b/guix-tools/scalfmm-manifest-gcc-openblas.scm new file mode 100644 index 0000000000000000000000000000000000000000..3db24721bb79849d63f9ac7dee6e74e53c7bfe25 --- /dev/null +++ b/guix-tools/scalfmm-manifest-gcc-openblas.scm @@ -0,0 +1,17 @@ +;; What follows is a "manifest" equivalent to the command line you gave. +;; You can store it in a file that you may then pass to any 'guix' command +;; that accepts a '--manifest' (or '-m') option. + +(specifications->manifest + (list "gcc-toolchain@11" + "cmake" + "make" + "coreutils" + "ncurses" + "openblas" + "fftw" + "fftwf" + "pkg-config" + "grep" + "findutils" + "sed")) diff --git a/guix-tools/scalfmm-manifest-gcc11-openblas.scm b/guix-tools/scalfmm-manifest-gcc11-openblas.scm new file mode 100644 index 0000000000000000000000000000000000000000..3db24721bb79849d63f9ac7dee6e74e53c7bfe25 --- /dev/null +++ b/guix-tools/scalfmm-manifest-gcc11-openblas.scm @@ -0,0 +1,17 @@ +;; What follows is a "manifest" equivalent to the command line you gave. +;; You can store it in a file that you may then pass to any 'guix' command +;; that accepts a '--manifest' (or '-m') option. + +(specifications->manifest + (list "gcc-toolchain@11" + "cmake" + "make" + "coreutils" + "ncurses" + "openblas" + "fftw" + "fftwf" + "pkg-config" + "grep" + "findutils" + "sed")) diff --git a/guix-tools/scalfmm-manifest-gcc12-openblas.scm b/guix-tools/scalfmm-manifest-gcc12-openblas.scm new file mode 100644 index 0000000000000000000000000000000000000000..a7291f952ef762d63d4874a684aac2a8b2bbe00f --- /dev/null +++ b/guix-tools/scalfmm-manifest-gcc12-openblas.scm @@ -0,0 +1,17 @@ +;; What follows is a "manifest" equivalent to the command line you gave. +;; You can store it in a file that you may then pass to any 'guix' command +;; that accepts a '--manifest' (or '-m') option. + +(specifications->manifest + (list "gcc-toolchain@12" + "cmake" + "make" + "coreutils" + "ncurses" + "openblas" + "fftw" + "fftwf" + "pkg-config" + "grep" + "findutils" + "sed")) diff --git a/guix-tools/scalfmm-manifest-gcc13-openblas.scm b/guix-tools/scalfmm-manifest-gcc13-openblas.scm new file mode 100644 index 0000000000000000000000000000000000000000..a6a02f26d7ed0cd31fffe7c93eb2588d31f45eb9 --- /dev/null +++ b/guix-tools/scalfmm-manifest-gcc13-openblas.scm @@ -0,0 +1,17 @@ +;; What follows is a "manifest" equivalent to the command line you gave. +;; You can store it in a file that you may then pass to any 'guix' command +;; that accepts a '--manifest' (or '-m') option. + +(specifications->manifest + (list "gcc-toolchain@13" + "cmake" + "make" + "coreutils" + "ncurses" + "openblas" + "fftw" + "fftwf" + "pkg-config" + "grep" + "findutils" + "sed")) diff --git a/guix-tools/scalfmm-manifest-gcc14-openblas.scm b/guix-tools/scalfmm-manifest-gcc14-openblas.scm new file mode 100644 index 0000000000000000000000000000000000000000..cb5b2d1f3c2c01e0f69739ab7843e256b1ad7f1c --- /dev/null +++ b/guix-tools/scalfmm-manifest-gcc14-openblas.scm @@ -0,0 +1,17 @@ +;; What follows is a "manifest" equivalent to the command line you gave. +;; You can store it in a file that you may then pass to any 'guix' command +;; that accepts a '--manifest' (or '-m') option. + +(specifications->manifest + (list "gcc-toolchain@14" + "cmake" + "make" + "coreutils" + "ncurses" + "openblas" + "fftw" + "fftwf" + "pkg-config" + "grep" + "findutils" + "sed")) diff --git a/include/scalfmm/algorithms/common.hpp b/include/scalfmm/algorithms/common.hpp index 979c84757f87e85f6b80008de82e540f69616357..15f9e864dadcb8b618bbac340abd16ad88c2824c 100644 --- a/include/scalfmm/algorithms/common.hpp +++ b/include/scalfmm/algorithms/common.hpp @@ -1,28 +1,20 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithms/common.hpp +// File : scalfmm/algorithms/common.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_COMMON_HPP #define SCALFMM_ALGORITHMS_COMMON_HPP + #include <cmath> #include <iostream> #include <string> namespace scalfmm::algorithms { - /// \file - /// - /// \brief Specifies the operation to perform in the algorithm - /// - /// Enum to activate operators. - /// \enum scalfmm::algorithms::operators_to_proceed - /// \tparam begin an iterator on the beginning of the tree - /// \tparam end an iterator on the beginning of the tree - /// \tparam interpolator end an iterator on the beginning of the tree - /// \param op an int - /// \return void - /// s + /** + * @brief Specifies the operations to perform in the algorithm. + */ enum operators_to_proceed : unsigned int { p2p = (1 << 0), ///< Particles to Particles operator (Near field) @@ -38,6 +30,13 @@ namespace scalfmm::algorithms all = (nearfield | farfield) ///< Near and far field operators }; + /** + * @brief Converts operator code to string. + * + * @param op operator code. + * + * @return string corresponding to the operator code. + */ [[nodiscard]] inline auto to_string(operators_to_proceed op) -> std::string { std::string s(""); @@ -76,6 +75,14 @@ namespace scalfmm::algorithms } return s; }; + + /** + * @brief builds string from operator code + * + * @param op + * + * @return + */ [[nodiscard]] inline auto build_string(const unsigned int op) -> std::string { std::string s(""); @@ -98,8 +105,19 @@ namespace scalfmm::algorithms } return s; }; + + /** + * @brief prints string corresponding to the operator code. + * + * @param op + */ void print(const unsigned int op) { std::cout << build_string(op); }; + /** + * @brief prints the operator code. + * + * @param op + */ void print(const operators_to_proceed op) { switch(op) diff --git a/include/scalfmm/algorithms/fmm.hpp b/include/scalfmm/algorithms/fmm.hpp index c61f47a0ef2c52a58e1beb37b0128c62725db279..9f6395aa49ace748a9ee3115f199228fcfe10004 100644 --- a/include/scalfmm/algorithms/fmm.hpp +++ b/include/scalfmm/algorithms/fmm.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/fmm.hpp +// File : scalfmm/algorithms/fmm.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_FMM_HPP #define SCALFMM_ALGORITHMS_FMM_HPP @@ -15,43 +15,107 @@ namespace scalfmm::algorithms { namespace impl { - template<typename Tree, typename NearField, typename FarField> - auto fmm(Tree& tree, operators::fmm_operators<NearField, FarField> const& fmmoperators, unsigned int op = operators_to_proceed::all) -> void + /** + * @brief fmm wrapper with a single tree. + * + * @tparam TreeType + * @tparam NearFieldType + * @tparam FarFieldType + * + * @param tree + * @param fmmoperators + * @param op + * + * @return + */ + template<typename TreeType, typename NearFieldType, typename FarFieldType> + auto fmm(TreeType& tree, operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, + unsigned int op = operators_to_proceed::all) -> void { return sequential::sequential(tree, fmmoperators, op); } - template<typename TreeS, typename TreeT, typename FmmOperators> - auto fmm(TreeS& tree_source, TreeT& tree_target, FmmOperators const& fmmoperators, + + /** + * @brief fmm wrapper with a source tree and a traget tree. + * + * @tparam SourceTargetTreeTypeype + * @tparam TargetTreeType + * @tparam FmmOperatorsType + * + * @param source_tree + * @param target_tree + * @param fmmoperators + * @param op + * + * @return + */ + template<typename SourceTreeType, typename TargetTreeType, typename FmmOperatorsType> + auto fmm(SourceTreeType& source_tree, TargetTreeType& target_tree, FmmOperatorsType const& fmmoperators, unsigned int op = operators_to_proceed::all) -> void { - return sequential::sequential(tree_source, tree_target, fmmoperators, op); + return sequential::sequential(source_tree, target_tree, fmmoperators, op); } - template<typename... S, typename Tree, typename FmmOperators> - inline auto fmm(options::settings<S...> s, Tree& tree, FmmOperators const& fmmoperators, - unsigned int op = operators_to_proceed::all) -> void + + /** + * @brief fmm wrapper with settings and a single tree. + * + * @tparam S + * @tparam TreeType + * @tparam FmmOperatorsType + * + * @param s + * @param tree + * @param fmmoperators + * @param op + * + * @return + */ + template<typename... S, typename TreeType, typename FmmOperatorsType> + inline auto fmm(options::settings<S...> s, TreeType& tree, FmmOperatorsType const& fmmoperators, + unsigned int op = operators_to_proceed::all) -> void { - static_assert( options::support(s, options::_s(options::omp, options::omp_timit, options::seq, options::seq_timit)) - , "unsupported fmm algo options!"); - if constexpr (options::has(s, options::seq, options::seq_timit)) + static_assert( + options::support(s, options::_s(options::omp, options::omp_timit, options::seq, options::seq_timit)), + "unsupported fmm algo options!"); + if constexpr(options::has(s, options::seq, options::seq_timit)) { using inner_settings = typename decltype(s)::inner_settings; return sequential::sequential[inner_settings{}](tree, fmmoperators, op); } #ifdef _OPENMP - else if constexpr (options::has(s, options::omp, options::omp_timit)) + else if constexpr(options::has(s, options::omp, options::omp_timit)) { using inner_settings = typename decltype(s)::inner_settings; return omp::task_dep[inner_settings{}](tree, fmmoperators, op); } #endif - else if constexpr (options::has(s, options::timit)) + else if constexpr(options::has(s, options::timit)) { return sequential::sequential[s](tree, fmmoperators, op); } } - template<typename... S, typename TreeS, typename TreeT, typename NearField, typename FarField> - inline auto fmm(options::settings<S...> s, TreeS& tree_source, TreeT& tree_target, - operators::fmm_operators<NearField, FarField> const& fmmoperators, unsigned int op = operators_to_proceed::all) -> void + + /** + * @brief fmm wrapper with a source tree, a target tree and with settings. + * + * @tparam S + * @tparam SourceTree + * @tparam TargetTree + * @tparam NearFieldType + * @tparam FarFieldType + * + * @param s + * @param tree_source + * @param tree_target + * @param fmmoperators + * @param op + * + * @return + */ + template<typename... S, typename SourceTree, typename TargetTree, typename NearFieldType, typename FarFieldType> + inline auto fmm(options::settings<S...> s, SourceTree& tree_source, TargetTree& tree_target, + operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, + unsigned int op = operators_to_proceed::all) -> void { static_assert( options::support(s, options::_s(options::omp, options::omp_timit, options::seq, options::seq_timit)), @@ -73,9 +137,9 @@ namespace scalfmm::algorithms return sequential::sequential[s](tree_source, tree_target, fmmoperators, op); } } - } + } // namespace impl DECLARE_OPTIONED_CALLEE(fmm); -} +} // namespace scalfmm::algorithms -#endif // SCALFMM_ALGORITHMS_FMM_HPP +#endif // SCALFMM_ALGORITHMS_FMM_HPP diff --git a/include/scalfmm/algorithms/full_direct.hpp b/include/scalfmm/algorithms/full_direct.hpp index 469149c2bbd020a0cffbec72f436dd47af0e7309..22e8569b788612a32f3a556f41d0caa0f003bab9 100644 --- a/include/scalfmm/algorithms/full_direct.hpp +++ b/include/scalfmm/algorithms/full_direct.hpp @@ -1,16 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/full_direct.hpp +// File : scalfmm/algorithms/full_direct.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_FULL_DIRECT_HPP #define SCALFMM_ALGORITHMS_FULL_DIRECT_HPP -#include <array> -#include <cstddef> -#include <iostream> -#include <iterator> -// - #include "scalfmm/meta/traits.hpp" #include "scalfmm/meta/utils.hpp" #include <scalfmm/container/iterator.hpp> @@ -19,36 +13,45 @@ #include <scalfmm/utils/io_helpers.hpp> #include <scalfmm/utils/static_assert_as_exception.hpp> +#include <array> +#include <cstddef> +#include <iostream> +#include <iterator> + using namespace scalfmm::io; namespace scalfmm::algorithms { /** - * @brief compute the direct particle interactions - * - * Compute the direct interactions between the particles inside the containers. - * - * \f$ out(pt) = \sum_{ps \ne pt }{ matrix\_kernel(pt, ps) input(ps) } \f$ - * - * @param[inout] particles the container of particles - * @param[in] matrix_kernel the matrix kernel - * - */ - template<typename ContainerParticles, typename MatrixKernel> - inline auto full_direct(ContainerParticles& particles, const MatrixKernel& matrix_kernel) -> void + * @brief compute the direct particle interactions for the built-in particle container. + * + * Compute the direct interactions between the particles inside the containers. + * + * \f$ out(pt) = \sum_{ps \ne pt }{ matrix\_kernel(pt, ps) input(ps) } \f$ + * + * @tparam ParticleContainerType + * @tparam MatrixKernelType + * + * @param[inout] particles the container of particles + * @param[in] matrix_kernel the matrix kernel + */ + template<typename ParticleContainerType, typename MatrixKernelType> + inline auto full_direct(ParticleContainerType& particles, const MatrixKernelType& matrix_kernel) -> void { - // using particle_type = Particle; - using particle_type = typename ContainerParticles::value_type; + using particle_container_type = ParticleContainerType; + using matrix_kernel_type = MatrixKernelType; + using particle_type = typename particle_container_type::value_type; using value_type = typename particle_type::position_type::value_type; - // using range_outputs_type = typename particle_type::range_outputs_type; - using matrix_type = typename MatrixKernel::template matrix_type<value_type>; + using matrix_type = typename matrix_kernel_type::template matrix_type<value_type>; - static_assert(MatrixKernel::km == particle_type::inputs_size, + static_assert(matrix_kernel_type::km == particle_type::inputs_size, "Different input size between Matrix kernel and container!"); - static_assert(MatrixKernel::kn == particle_type::outputs_size, + static_assert(matrix_kernel_type::kn == particle_type::outputs_size, "Different output size between Matrix kernel and container!"); - // #pragma omp parallel for shared(matrix_kernel) + static constexpr std::size_t inputs_size = matrix_kernel_type::km; + static constexpr std::size_t outputs_size = matrix_kernel_type::kn; + for(std::size_t idx = 0; idx < particles.size(); ++idx) { // Get proxy particle position @@ -65,11 +68,11 @@ namespace scalfmm::algorithms auto q = particles.at(idx_2).inputs(); auto pt_y = particles.at(idx_2).position(); val_mat = matrix_kernel.evaluate(pt_x, pt_y); - for(std::size_t j = 0; j < MatrixKernel::kn; ++j) + for(std::size_t j = 0; j < outputs_size; ++j) { - for(std::size_t i = 0; i < MatrixKernel::km; ++i) + for(std::size_t i = 0; i < inputs_size; ++i) { - val[j] += val_mat.at(j * MatrixKernel::km + i) * q[i]; + val[j] += val_mat.at(j * inputs_size + i) * q[i]; } } } @@ -79,22 +82,32 @@ namespace scalfmm::algorithms compute(idx + 1, particles.size()); } } - template<typename Particles, typename MatrixKernel> - inline auto full_direct(std::vector<Particles>& particles, const MatrixKernel& matrix_kernel) -> void + + /** + * @brief compute the direct particle interactions (when the particle container is a std::vector). + * + * @tparam ParticleType + * @tparam MatrixKernelType + * + * @param particles + * @param matrix_kernel + */ + template<typename ParticleType, typename MatrixKernelType> + inline auto full_direct(std::vector<ParticleType>& particles, const MatrixKernelType& matrix_kernel) -> void { - // using particle_type = Particle; - using particle_type = Particles; + using particle_type = ParticleType; + using matrix_kernel_type = MatrixKernelType; using value_type = typename particle_type::position_type::value_type; - // using range_outputs_type = typename particle_type::range_outputs_type; - using matrix_type = typename MatrixKernel::template matrix_type<value_type>; + using matrix_type = typename matrix_kernel_type::template matrix_type<value_type>; - static_assert(MatrixKernel::km == particle_type::inputs_size, + static_assert(matrix_kernel_type::km == particle_type::inputs_size, "Different input size between Matrix kernel and container!"); - static_assert(MatrixKernel::kn == particle_type::outputs_size, + static_assert(matrix_kernel_type::kn == particle_type::outputs_size, "Different output size between Matrix kernel and container!"); - // int idx{}; - // #pragma omp parallel for shared(matrix_kernel) + static constexpr std::size_t inputs_size = matrix_kernel_type::km; + static constexpr std::size_t outputs_size = matrix_kernel_type::kn; + for(std::size_t idx = 0; idx < particles.size(); ++idx) { // Get proxy particle position @@ -111,11 +124,11 @@ namespace scalfmm::algorithms auto q = particles.at(idx_2).inputs(); auto pt_y = particles.at(idx_2).position(); val_mat = matrix_kernel.evaluate(pt_x, pt_y); - for(std::size_t j = 0; j < MatrixKernel::kn; ++j) + for(std::size_t j = 0; j < outputs_size; ++j) { - for(std::size_t i = 0; i < MatrixKernel::km; ++i) + for(std::size_t i = 0; i < inputs_size; ++i) { - val[j] += val_mat.at(j * MatrixKernel::km + i) * q[i]; + val[j] += val_mat.at(j * inputs_size + i) * q[i]; } } } @@ -125,143 +138,156 @@ namespace scalfmm::algorithms compute(idx + 1, particles.size()); } } + /** - * @brief compute the field due to the particles the source container on the target particles - * - * compute the field due to the particles the source container on the target particles by - * - * \f$ out(pt) = \sum_{ps\in source} { matrix\_kernel(pt, ps) input(ps) } \f$ - * - * We don't check if | pt - ps| =0 - * - * @param[in] particles_source container of the source particles - * @param[inout] particles_target container of the target particles - * @param[in] matrix_kernel matrix kernel - * - */ - template<typename SourceParticles_type, typename TargetParticles_type, typename MatrixKernel> - inline auto full_direct(std::vector<SourceParticles_type> const & particles_source, - std::vector<TargetParticles_type>& particles_target, MatrixKernel const& matrix_kernel) + * @brief compute the direct interactions with a built-in source container and a built-in target container. + * + * @tparam SourceContainerType + * @tparam TargetContainerType + * @tparam MatrixKernelType + * + * @param source_particles + * @param target_particles + * @param matrix_kernel + */ + template<typename SourceContainerType, typename TargetContainerType, typename MatrixKernelType> + inline auto full_direct(SourceContainerType const& source_particles, TargetContainerType& target_particles, + const MatrixKernelType& matrix_kernel) { - bool same_tree{false}; - if constexpr(std::is_same_v<SourceParticles_type, TargetParticles_type>) + using source_container_type = SourceContainerType; + using target_container_type = TargetContainerType; + using matrix_kernel_type = MatrixKernelType; + using source_particle_type = typename source_container_type::value_type; + using target_particle_type = typename target_container_type::value_type; + using value_type = typename source_particle_type::position_type::value_type; + using matrix_type = typename matrix_kernel_type::template matrix_type<value_type>; + + static_assert(matrix_kernel_type::km == source_particle_type::inputs_size, + "Different input size between Matrix kernel and container!"); + static_assert(matrix_kernel_type::kn == target_particle_type::outputs_size, + "Different output size between Matrix kernel and container!"); + + static constexpr std::size_t inputs_size = matrix_kernel_type::km; + static constexpr std::size_t outputs_size = matrix_kernel_type::kn; + + bool same_container{false}; + if constexpr(std::is_same_v<source_container_type, target_container_type>) { - same_tree = (&particles_source == &particles_target); + same_container = (&source_particles == &target_particles); } - if(same_tree) + if(same_container) { - throw std::runtime_error("In p2p_outer the two containers must be different."); + throw std::runtime_error("full_direct: the two containers must be different!"); } - // using particle_type = Particle; - using particle_source_type = SourceParticles_type; - using particle_target_type = TargetParticles_type; - using value_type = typename particle_source_type::position_type::value_type; - // using range_outputs_type = typename particle_target_type::range_outputs_type; - // using position_type = typename particle_source_type::position_type; - using matrix_type = typename MatrixKernel::template matrix_type<value_type>; - - static_assert(MatrixKernel::km == particle_source_type::inputs_size, - "Different input size between Matrix kernel and container!"); - static_assert(MatrixKernel::kn == particle_target_type::outputs_size, - "Different output size between Matrix kernel and container!"); - // int idx{}; - // #pragma omp parallel for simd shared(matrix_kernel) - for(std::size_t idx = 0; idx < particles_target.size(); ++idx) + for(std::size_t idx = 0; idx < target_particles.size(); ++idx) { - // const auto p = particles_target.particle(idx); - auto& p = particles_target[idx]; - - // // Get particle position - auto const& pt_x = p.position(); - // // val is an alias on the array of outputs - auto& val = p.outputs(); + // Get proxy particle position + auto pt_x = target_particles.at(idx).position(); + // Get proxy particle outputs + auto val = target_particles.at(idx).outputs(); matrix_type val_mat{}; auto compute = - [&pt_x, &val, &matrix_kernel, &val_mat, &particles_source](std::size_t start, std::size_t end) + [&pt_x, &val, &matrix_kernel, &val_mat, &source_particles](std::size_t start, std::size_t end) { - // #pragma omp for simd for(std::size_t idx_2 = start; idx_2 < end; ++idx_2) { - // auto ps = particles_source.particle(idx_2); - auto const& q = particles_source.at(idx_2).inputs(); + auto q = source_particles.at(idx_2).inputs(); - auto const& pt_y = particles_source.at(idx_2).position(); + auto pt_y = source_particles.at(idx_2).position(); val_mat = matrix_kernel.evaluate(pt_x, pt_y); - for(std::size_t j = 0; j < MatrixKernel::kn; ++j) + for(std::size_t j = 0; j < outputs_size; ++j) { - for(std::size_t i = 0; i < MatrixKernel::km; ++i) + for(std::size_t i = 0; i < inputs_size; ++i) { - val[j] += val_mat.at(j * MatrixKernel::km + i) * q[i]; + val[j] += val_mat.at(j * inputs_size + i) * q[i]; } } } }; - compute(0, particles_source.size()); + compute(0, source_particles.size()); } } - template<typename ContainerSourceParticles_type, typename ContainerTargetParticles_type, typename MatrixKernel> - inline auto full_direct(ContainerSourceParticles_type const & particles_source, - ContainerTargetParticles_type& particles_target, const MatrixKernel& matrix_kernel) + + /** + * @brief compute the field due to the particles the source container on the target particles + * + * compute the field due to the particles the source container on the target particles by + * + * \f$ out(pt) = \sum_{ps\in source} { matrix\_kernel(pt, ps) input(ps) } \f$ + * + * We don't check if | pt - ps| =0 + * + * @tparam SourceParticleType + * @tparam TargetParticleType + * @tparam MatrixKernelType + * + * @param[in] source_particles container of the source particles + * @param[inout] target_particles container of the target particles + * @param[in] matrix_kernel matrix kernel + */ + template<typename SourceParticleType, typename TargetParticleType, typename MatrixKernelType> + inline auto full_direct(std::vector<SourceParticleType> const& source_particles, + std::vector<TargetParticleType>& target_particles, MatrixKernelType const& matrix_kernel) { - bool same_tree{false}; - if constexpr(std::is_same_v<ContainerSourceParticles_type, ContainerTargetParticles_type>) + using source_particle_type = SourceParticleType; + using target_particle_type = TargetParticleType; + using matrix_kernel_type = MatrixKernelType; + using value_type = typename source_particle_type::position_type::value_type; + using matrix_type = typename matrix_kernel_type::template matrix_type<value_type>; + + static_assert(matrix_kernel_type::km == source_particle_type::inputs_size, + "Different input size between Matrix kernel and container!"); + static_assert(matrix_kernel_type::kn == target_particle_type::outputs_size, + "Different output size between Matrix kernel and container!"); + + static constexpr std::size_t inputs_size = matrix_kernel_type::km; + static constexpr std::size_t outputs_size = matrix_kernel_type::kn; + + bool same_container{false}; + if constexpr(std::is_same_v<source_particle_type, target_particle_type>) { - same_tree = (&particles_source == &particles_target); + same_container = (&source_particles == &target_particles); } - if(same_tree) + if(same_container) { - throw std::runtime_error("In p2p_outer the two containers must be different."); + throw std::runtime_error("full_direct: the two containers must be different !"); } - // using particle_type = Particle; - using particle_source_type = typename ContainerSourceParticles_type::value_type; - using particle_target_type = typename ContainerTargetParticles_type::value_type; - using value_type = typename particle_source_type::position_type::value_type; - // using range_outputs_type = typename particle_target_type::range_outputs_type; - // using position_type = typename particle_source_type::position_type; - using matrix_type = typename MatrixKernel::template matrix_type<value_type>; - - static_assert(MatrixKernel::km == particle_source_type::inputs_size, - "Different input size between Matrix kernel and container!"); - static_assert(MatrixKernel::kn == particle_target_type::outputs_size, - "Different output size between Matrix kernel and container!"); - // int idx{}; - // #pragma omp parallel for simd shared(matrix_kernel) - for(std::size_t idx = 0; idx < particles_target.size(); ++idx) + for(std::size_t idx = 0; idx < target_particles.size(); ++idx) { - // Get proxy particle position - auto pt_x = particles_target.at(idx).position(); - // Get proxy particle outputs - auto val = particles_target.at(idx).outputs(); + auto& p = target_particles[idx]; + + // // Get particle position + auto const& pt_x = p.position(); + // // val is an alias on the array of outputs + auto& val = p.outputs(); matrix_type val_mat{}; auto compute = - [&pt_x, &val, &matrix_kernel, &val_mat, &particles_source](std::size_t start, std::size_t end) + [&pt_x, &val, &matrix_kernel, &val_mat, &source_particles](std::size_t start, std::size_t end) { - // #pragma omp for simd for(std::size_t idx_2 = start; idx_2 < end; ++idx_2) { - // auto ps = particles_source.particle(idx_2); - auto q = particles_source.at(idx_2).inputs(); + auto const& q = source_particles.at(idx_2).inputs(); - auto pt_y = particles_source.at(idx_2).position(); + auto const& pt_y = source_particles.at(idx_2).position(); val_mat = matrix_kernel.evaluate(pt_x, pt_y); - for(std::size_t j = 0; j < MatrixKernel::kn; ++j) + for(std::size_t j = 0; j < outputs_size; ++j) { - for(std::size_t i = 0; i < MatrixKernel::km; ++i) + for(std::size_t i = 0; i < inputs_size; ++i) { - val[j] += val_mat.at(j * MatrixKernel::km + i) * q[i]; + val[j] += val_mat.at(j * inputs_size + i) * q[i]; } } } }; - compute(0, particles_source.size()); + compute(0, source_particles.size()); } } diff --git a/include/scalfmm/algorithms/mpi/direct.hpp b/include/scalfmm/algorithms/mpi/direct.hpp index 3b03bd9c3c886c52e516ab6d56bfbc7a40fba675..d08d8fe4df9a7f5f177d7e5dfb14c4f00c0a702e 100644 --- a/include/scalfmm/algorithms/mpi/direct.hpp +++ b/include/scalfmm/algorithms/mpi/direct.hpp @@ -4,9 +4,10 @@ // -------------------------------- #ifndef SCALFMM_ALGORITHMS_MPI_DIRECT_HPP #define SCALFMM_ALGORITHMS_MPI_DIRECT_HPP -#include "scalfmm/algorithms/omp/direct.hpp" +#include "scalfmm/algorithms/omp/direct.hpp" #include "scalfmm/parallel/mpi/comm.hpp" + #include <mpi.h> namespace scalfmm::algorithms::mpi::pass @@ -14,18 +15,17 @@ namespace scalfmm::algorithms::mpi::pass namespace comm { /** - * @brief Perform the communications between the tree_source and the tree_target for the current level - * - * THE algorithm is done in three steps - * step 1 find the number of data to exchange - * step 2 construct the morton index to send - * step 3 send/receive the particles - * - * @tparam TreeS - * @tparam TreeT - * @param tree_source source tree (contains the particles) - * @param tree_target target tree - */ + * @brief Perform the communications between the tree_source and the tree_target for the current level + * + * THE algorithm is done in three steps + * step 1 find the number of data to exchange + * step 2 construct the morton index to send + * step 3 send/receive the particles + * + * @tparam TreeS + * + * @param tree_source source tree (contains the particles) + */ template<typename TreeS> inline auto start_communications(TreeS& tree_source) -> void { @@ -159,12 +159,17 @@ namespace scalfmm::algorithms::mpi::pass // (*ptr_group)->cstorage().print_block_data(std::cout); // } } // en d start_communications - } // namespace comm + } // namespace comm + /** * @brief Compute direct interaction between particles * * When tree_source = tree_target we call the direct(tree_source, nearfield) function * + * @tparam TreeSource + * @tparam TreeTarget + * @tparam NearField + * * @param tree_source the source tree * @param tree_target tke target tree where we compute the field * @param nearfield the near-field operator diff --git a/include/scalfmm/algorithms/mpi/downward.hpp b/include/scalfmm/algorithms/mpi/downward.hpp index 24e53024b5b4a43c1143758de8d28d4bfbad5770..71d648a16148e64158b05bb6906ce800cf96aa2a 100644 --- a/include/scalfmm/algorithms/mpi/downward.hpp +++ b/include/scalfmm/algorithms/mpi/downward.hpp @@ -20,9 +20,8 @@ namespace scalfmm::algorithms::mpi::pass /** * @brief perform the l2l communications for the father level * - * * @tparam Tree - * @tparam Approximation + * * @param level current father level * @param tree the tree */ @@ -65,8 +64,6 @@ namespace scalfmm::algorithms::mpi::pass // temporary buffer auto nb_m = m.size(); - // std::cout << "cell index: " << cell.index() << " = parent " << (last_child_index >> dimension) << - // "\n"; loop to serialize the multipoles auto it = std::begin(buffer); for(std::size_t i{0}; i < nb_m; ++i) { @@ -100,14 +97,8 @@ namespace scalfmm::algorithms::mpi::pass auto pos = it_group->get()->size() - 1; // index of the last cell in the group auto& cell = it_group->get()->component(pos); auto& m = cell.multipoles(); - // auto tm = cell.ctransformed_multipoles(); - // std::cout << "cell index: " << cell.index() << " = parent " << (last_child_index_before_me >> - // dimension) - // << "\n"; auto nb_m = m.size(); - // std::cout << "cell index: " << it_cell->index() << " level " << it_cell->csymbolics().level << - // "\n"; auto it = std::begin(buffer); for(std::size_t i{0}; i < nb_m; ++i) { @@ -118,32 +109,29 @@ namespace scalfmm::algorithms::mpi::pass } } } - /// @brief This function constructs the local approximation for all the cells of the tree by applying the - /// operator l2l - /// - /// @param tree the tree target - /// @param approximation the approximation to construct the local approximation - /// + + /** + * @brief This function constructs the local approximation for all the cells of the tree by applying the operator l2l + * + * @tparam Tree + * @tparam Approximation + * + * @param tree the target tree. + * @param approximation the approximation to construct the local approximation. + */ template<typename Tree, typename Approximation> inline auto downward(Tree& tree, Approximation const& approximation) -> void { // upper working level is const auto top_height = tree.box().is_periodic() ? 0 : 2; const auto leaf_level = tree.leaf_level(); - // std::cout << "start downward pass top_height " << top_height << " leaf_level " << leaf_level << std::endl - // << std::flush; for(std::size_t level = top_height; level < leaf_level; ++level) { - // std::cout << "downward : " << level << std::endl << std::flush; - // std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; downward_communications_level(level, tree); - // std::cout << " end downward comm " << level << std::endl << std::flush; - // std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; omp::pass::downward_level(level, tree, approximation); } - // std::cout << "end downward pass" << std::endl << std::flush; } } // namespace scalfmm::algorithms::mpi::pass diff --git a/include/scalfmm/algorithms/mpi/proc_task.hpp b/include/scalfmm/algorithms/mpi/proc_task.hpp index fb4bbaf9f4ca32e116534e3e2f4d3b1da361ba49..bea9aef9bc125696b0479c474b996dba68e02f1f 100644 --- a/include/scalfmm/algorithms/mpi/proc_task.hpp +++ b/include/scalfmm/algorithms/mpi/proc_task.hpp @@ -1,16 +1,12 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/task_dep.hpp +// File : scalfmm/algorithms/mpi/proc_task.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_MPI_PROC_TASK_HPP #define SCALFMM_ALGORITHMS_MPI_PROC_TASK_HPP #ifdef _OPENMP -#include <chrono> -#include <iostream> -#include <omp.h> - #include "scalfmm/algorithms/common.hpp" #include "scalfmm/algorithms/omp/cell_to_leaf.hpp" #include "scalfmm/algorithms/omp/leaf_to_cell.hpp" @@ -18,15 +14,18 @@ #include "scalfmm/algorithms/omp/utils.hpp" #include "scalfmm/algorithms/mpi/direct.hpp" -#include "scalfmm/lists/lists.hpp" -// #include "scalfmm/algorithms/omp/periodic.hpp" #include "scalfmm/algorithms/mpi/downward.hpp" #include "scalfmm/algorithms/mpi/transfer.hpp" #include "scalfmm/algorithms/mpi/upward.hpp" +#include "scalfmm/lists/lists.hpp" #include <cpp_tools/parallel_manager/parallel_manager.hpp> #include <cpp_tools/timers/simple_timer.hpp> +#include <chrono> +#include <iostream> +#include <omp.h> + namespace scalfmm::algorithms::mpi { namespace impl @@ -40,6 +39,7 @@ namespace scalfmm::algorithms::mpi * @tparam TreeT type of the tree target * @tparam NearField Near-field type * @tparam FarField Far-field type + * * @param s the option (omp, seq, ...) * @param tree_source the tree of particle sources * @param tree_target the tree of particle sources @@ -84,7 +84,7 @@ namespace scalfmm::algorithms::mpi auto buffers{scalfmm::algorithms::omp::impl::init_buffers(far_field.approximation())}; // #pragma omp parallel default(none) shared(tree_source, tree_target, far_field, near_field, buffers, op, \ - separation_criterion, mutual, same_tree, std::cout) + separation_criterion, mutual, same_tree, std::cout) { #pragma omp single nowait { @@ -108,12 +108,12 @@ namespace scalfmm::algorithms::mpi { pass::upward(tree_source, far_field.approximation()); } - // if(same_tree && tree_target.box().is_periodic()) - // { - // // timers["field0"].tic(); - // pass::build_field_level0(tree_target, far_field.approximation()); - // // timers["field0"].tac(); - // } + // if(same_tree && tree_target.box().is_periodic()) + // { + // // timers["field0"].tic(); + // pass::build_field_level0(tree_target, far_field.approximation()); + // // timers["field0"].tac(); + // } if((op & operators_to_proceed::m2l) == operators_to_proceed::m2l) { pass::transfer(tree_source, tree_target, far_field, buffers, @@ -129,10 +129,10 @@ namespace scalfmm::algorithms::mpi pass::transfer(tree_source, tree_target, far_field, buffers, scalfmm::algorithms::omp::pass::split_m2l::leaf_level); } - if((op & operators_to_proceed::l2p) == operators_to_proceed::l2p) - { - scalfmm::algorithms::omp::pass::cell_to_leaf(tree_target, far_field); - } + if((op & operators_to_proceed::l2p) == operators_to_proceed::l2p) + { + scalfmm::algorithms::omp::pass::cell_to_leaf(tree_target, far_field); + } } } // end parallel diff --git a/include/scalfmm/algorithms/mpi/transfer.hpp b/include/scalfmm/algorithms/mpi/transfer.hpp index d1e352127a06e3f208c8483f84f3486042886e82..665fbc036cba80ea6d72a9003a5de55c8ccd1846 100644 --- a/include/scalfmm/algorithms/mpi/transfer.hpp +++ b/include/scalfmm/algorithms/mpi/transfer.hpp @@ -1,16 +1,12 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/upward.hpp +// File : scalfmm/algorithms/mpi/transfer.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_MPI_TRANSFER_HPP #define SCALFMM_ALGORITHMS_MPI_TRANSFER_HPP #ifdef _OPENMP -#include <map> -#include <omp.h> -#include <utility> - #include "scalfmm/algorithms/omp/transfer.hpp" #include "scalfmm/meta/traits.hpp" #include "scalfmm/operators/tags.hpp" @@ -23,6 +19,10 @@ #include <cpp_tools/parallel_manager/parallel_manager.hpp> +#include <map> +#include <omp.h> +#include <utility> + namespace scalfmm::algorithms::mpi::pass { @@ -36,6 +36,7 @@ namespace scalfmm::algorithms::mpi::pass * * @tparam TreeS * @tparam TreeT + * * @param level level in the tree * @param tree_source source tree (contains the multipoles) * @param tree_target target tree @@ -53,9 +54,6 @@ namespace scalfmm::algorithms::mpi::pass meta::has_value_type_t<value_type_ori>, value_type_ori>; int nb_values = meta::is_complex_v<value_type_ori> ? 2 : 1; - // std::cout << " Complex " << std::boolalpha << meta::is_complex_v<value_type_ori> << " nb val=" << nb_values - // << " value_type " << typeid(value_type).name() << " value_type_ori " - // << typeid(value_type_ori).name() << std::endl; auto& para = tree_target.get_parallel_manager(); auto comm = para.get_communicator(); @@ -99,12 +97,6 @@ namespace scalfmm::algorithms::mpi::pass end_right_ghost, distrib, nb_messages_to_receive, nb_messages_to_send, to_receive, morton_to_receive); } - // for(auto p = 0; p < nb_proc; ++p) - // { - // io::print(" morton to receive[" + std::to_string(p) + "] ", morton_to_receive[p]); - // } - // io::print(" nb_messages_to_receive ", nb_messages_to_receive); - // io::print(" nb_messages_to_send ", nb_messages_to_send); /////////////////////////////////////////////////////////////////////////////////// /// STEP 2 @@ -116,18 +108,7 @@ namespace scalfmm::algorithms::mpi::pass scalfmm::parallel::comm::start_step2(nb_proc, rank, comm, nb_messages_to_receive, nb_messages_to_send, morton_to_receive, morton_to_send); - - - /* - for(auto p = 0; p < nb_proc; ++p) - { - io::print(" morton_to_receive[" + std::to_string(p) + "] ", morton_to_receive[p]); - } - for(auto p = 0; p < nb_proc; ++p) - { - io::print(" morton_to_send[" + std::to_string(p) + "] ", morton_to_send[p]); - } - */ + static constexpr auto prio{omp::priorities::max}; ///////////////////////////////////////////////////////////////////////////////// /// STEP 3 @@ -148,7 +129,7 @@ namespace scalfmm::algorithms::mpi::pass using dep_type = typename TreeS::group_of_cell_type::symbolics_type::ptr_multi_dependency_type; auto mpi_multipole_type = cpp_tools::parallel_manager::mpi::get_datatype<value_type>(); - // std::cout << "\n Start step 3\n\n"; + // std::cout << "\n Start step 3\n\n"; auto nb_cells{morton_to_send[0].size()}; for(auto const& p: morton_to_send) { @@ -167,56 +148,50 @@ namespace scalfmm::algorithms::mpi::pass std::vector<mortonIdx_type> const& index_to_send, std::vector<value_type_ori>& buffer) { - try - { - // std::cout << " ----- build_buffer ----\n" <<std::flush; - // std::cout << " ----- buffer size " <<buffer.size() <<std::endl<<std::flush; - // std::cout << " ----- index_to_send size " <<index_to_send.size() <<std::endl<<std::flush; - int idx{0}; - int max_idx = index_to_send.size(); // loop on the groups - auto it = std::begin(buffer); - for(auto grp_ptr = first_group_ghost; grp_ptr != last_group_ghost; ++grp_ptr) - { - int start_grp{0}; - - auto const& csymb = (*grp_ptr)->csymbolics(); - // iterate on the cells - while(idx < max_idx and math::between(index_to_send[idx], csymb.starting_index, csymb.ending_index)) - { // find cell inside the group - int pos{-1}; - for(int i = start_grp; i < (*grp_ptr)->size(); ++i) - { - auto morton = (*grp_ptr)->component(i).csymbolics().morton_index; - if(index_to_send[idx] == morton) - { - pos = i; - start_grp = i + 1; - // std::cout << " pos = " << pos << std::endl; - break; - } - } - // std::cout << " morton to find " << index_to_send[idx] << " cell found " - // << (*grp_ptr)->component(pos).csymbolics().morton_index << '\n'; - auto const& cell = (*grp_ptr)->component(pos); - auto const& m = cell.transfer_multipoles(); - auto nb_m = m.size(); - // std::cout << " nb_m" << m.size() <<std::endl; - for(std::size_t i{0}; i < nb_m; ++i) - { - auto const& ten = m.at(i); - std::copy(std::begin(ten), std::end(ten), it); - it += ten.size(); - } - ++idx; - } - } - // std::cout << " ----- build_buffer ----\n" <<std::flush; - } - catch(std::out_of_range& e) - { - std::cout <<" error in buffer building !!!!!!!!!\n"; - std::cout << e.what() << '\n'<<std::flush; - } + try + { + int idx{0}; + int max_idx = index_to_send.size(); // loop on the groups + auto it = std::begin(buffer); + for(auto grp_ptr = first_group_ghost; grp_ptr != last_group_ghost; ++grp_ptr) + { + int start_grp{0}; + + auto const& csymb = (*grp_ptr)->csymbolics(); + // iterate on the cells + while(idx < max_idx and + math::between(index_to_send[idx], csymb.starting_index, csymb.ending_index)) + { // find cell inside the group + int pos{-1}; + for(int i = start_grp; i < (*grp_ptr)->size(); ++i) + { + auto morton = (*grp_ptr)->component(i).csymbolics().morton_index; + if(index_to_send[idx] == morton) + { + pos = i; + start_grp = i + 1; + // std::cout << " pos = " << pos << std::endl; + break; + } + } + auto const& cell = (*grp_ptr)->component(pos); + auto const& m = cell.transfer_multipoles(); + auto nb_m = m.size(); + for(std::size_t i{0}; i < nb_m; ++i) + { + auto const& ten = m.at(i); + std::copy(std::begin(ten), std::end(ten), it); + it += ten.size(); + } + ++idx; + } + } + } + catch(std::out_of_range& e) + { + std::cout << " error in buffer building !!!!!!!!!\n"; + std::cout << e.what() << '\n' << std::flush; + } }; // vector of dependencies on the group for each MPI process @@ -225,7 +200,6 @@ namespace scalfmm::algorithms::mpi::pass // loop on the processors to construct the buffer and to send it for(auto p = 0; p < nb_proc; ++p) { - // std::cout << " loop to send " << p <<" "<< morton_to_send[p].size() <<std::endl; /// check if I have something to send if(p != rank and morton_to_send[p].size() > 0) { @@ -235,29 +209,18 @@ namespace scalfmm::algorithms::mpi::pass parallel::utils::build_dependencies_from_morton_vector(tree_source.begin_mine_cells(level), tree_source.end_mine_cells(level), morton_to_send[p], deps_send[p]); -/// spawn a task for sending communication to process p - - #pragma omp task shared(tree_source, morton_to_send, buffer, rank, nb_proc, deps_send, nb_messages_to_receive, \ - size_mult, mpi_multipole_type) firstprivate(p, level) depend(iterator(std::size_t it = 0 \ - : deps_send[p].size()), \ - in \ - : ((deps_send[p])[it])[0]) \ - priority(prio) + /// spawn a task for sending communication to process p + +#pragma omp task shared(tree_source, morton_to_send, buffer, rank, nb_proc, deps_send, nb_messages_to_receive, \ + size_mult, mpi_multipole_type) firstprivate(p, level) \ + depend(iterator(std::size_t it = 0 : deps_send[p].size()), in : ((deps_send[p])[it])[0]) priority(prio) #endif { buffer[p].resize(morton_to_send[p].size() * size_mult); - // std::cout << " prepare to processor " << p << '\n'; - // io::print(" --> morton_to_send[" + std::to_string(p) + "] ", morton_to_send[p]); - // std::cout << '\n'; - // std::cout << " nb cells " << std::distance(tree_source.begin_mine_cells(level), tree_source.end_mine_cells(level)) <<std::endl<<std::flush; build_buffer(tree_source.begin_mine_cells(level), tree_source.end_mine_cells(level), morton_to_send[p], buffer[p]); - // std::cout << " end build buffer "<< p << std::endl << std::flush; // send buffer to processor p - // std::cout << " post send to proc " << p << " size= " << buffer[p].size() << std::endl - // << std::flush; - // io::print(" buffer send ",buffer[p]); comm.isend(reinterpret_cast<value_type*>(buffer[p].data()), buffer[p].size(), mpi_multipole_type, p, 611); } @@ -288,13 +251,10 @@ namespace scalfmm::algorithms::mpi::pass deps_recv[idx] = &(it_grp->get()->ccomponent(0).cmultipoles(0)); } } - // std::cout << "size_dep= " << size_dep << " " << deps_recv.size() << std::endl; + // post the task on the reception -#pragma omp task shared(rank, nb_proc, nb_messages_to_receive, size_mult, mpi_multipole_type) \ - depend(iterator(std::size_t it = 0 \ - : deps_recv.size()), \ - out \ - : (deps_recv[it])[0]) priority(prio) +#pragma omp task shared(rank, nb_proc, nb_messages_to_receive, size_mult, mpi_multipole_type) \ + depend(iterator(std::size_t it = 0 : deps_recv.size()), out : (deps_recv[it])[0]) priority(prio) #endif { // post the receives @@ -307,15 +267,12 @@ namespace scalfmm::algorithms::mpi::pass if(p != rank and nb_messages_to_receive[p] != 0) { buffer_rep[p].resize(nb_messages_to_receive[p] * size_mult); - // std::cout << " post comm to proc " << p << "size= " << nb_messages_to_receive[p] * size_mult - // << std::endl << std::flush; recept_mpi_status.push_back( comm.irecv(buffer_rep[p].data(), buffer_rep[p].size(), mpi_multipole_type, p, 611)); ++cc; } } - // std::cout << " Post reception nb=" << cc << " " << recept_mpi_status.size() << '\n' << std::flush; // wait we receive all the communication @@ -324,7 +281,6 @@ namespace scalfmm::algorithms::mpi::pass cpp_tools::parallel_manager::mpi::request::waitall(recept_mpi_status.size(), recept_mpi_status.data()); } - // std::cout << " end wait all\n" << std::flush; // put the multipoles inside the ghosts for(auto p = 0; p < nb_proc; ++p) @@ -332,8 +288,6 @@ namespace scalfmm::algorithms::mpi::pass if(p != rank and to_receive[p].size() > 0) { auto const& buffer = buffer_rep[p]; - // std::cout << " proc p= " << p << std::endl << std::flush; - // io::print(" buffer ",buffer) ; // ONLY WORKS IF SOURCE == TARGET auto const& pairs = to_receive[p]; auto it = std::begin(buffer); @@ -354,29 +308,29 @@ namespace scalfmm::algorithms::mpi::pass } } } // end task - // std::cout << "end step 3 \n"<<std::flush; - } // end step3 - para.get_communicator().barrier(); - } // end function start_communications + } // end step3 + para.get_communicator().barrier(); + } // end function start_communications /////////////////////////////////////////////////////////////////////////////////// - /// @brief apply the transfer operator to construct the local approximation in tree_target - /// - /// @tparam TreeS template for the Tree source type - /// @tparam TreeT template for the Tree target type - /// @tparam FarField template for the far field type - /// @tparam BufferPtr template for the type of pointer of the buffer - /// @param tree_source the tree containing the source cells/leaves - /// @param tree_target the tree containing the target cells/leaves - /// @param far_field The far field operator - /// @param buffers vector of buffers used by the far_field in the transfer pass (if needed) - /// @param split the enum (@see split_m2l) tp specify on which level we apply the transfer - /// operator - /// + /** + * @brief apply the transfer operator to construct the local approximation in tree_target + * + * @tparam TreeS template for the Tree source type + * @tparam TreeT template for the Tree target type + * @tparam FarField template for the far field type + * @tparam BufferPtr template for the type of pointer of the buffer + * + * @param tree_source the tree containing the source cells/leaves + * @param tree_target the tree containing the target cells/leaves + * @param far_field The far field operator + * @param buffers vector of buffers used by the far_field in the transfer pass (if needed) + * @param split the enum (@see split_m2l) tp specify on which level we apply the transfer operator + */ template<typename TreeS, typename TreeT, typename FarField, typename BufferPtr> inline auto transfer(TreeS& tree_source, TreeT& tree_target, FarField const& far_field, - std::vector<BufferPtr> const& buffers, omp::pass::split_m2l split = omp::pass::split_m2l::full) - -> void + std::vector<BufferPtr> const& buffers, + omp::pass::split_m2l split = omp::pass::split_m2l::full) -> void { // auto tree_height = tree_target.height(); @@ -400,15 +354,9 @@ namespace scalfmm::algorithms::mpi::pass } for(std::size_t level = top_height; level < last_level; ++level) { - // std::cout << "transfer : " << level << std::endl << std::flush; start_communications(level, tree_source, tree_target); - // std::cout << " end comm " << level << std::endl << std::flush; - // std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; - omp::pass::transfer_level(level, tree_source, tree_target, far_field, buffers); - // std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; } - // std::cout << "end transfer pass" << std::endl << std::flush; } } // namespace scalfmm::algorithms::mpi::pass diff --git a/include/scalfmm/algorithms/mpi/upward.hpp b/include/scalfmm/algorithms/mpi/upward.hpp index eb3e2d64158e08c8c7da44834c8c8cbd724de567..463e5814c85a2db8e0faba004aae68422bd08a17 100644 --- a/include/scalfmm/algorithms/mpi/upward.hpp +++ b/include/scalfmm/algorithms/mpi/upward.hpp @@ -1,29 +1,25 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/upward.hpp +// File : scalfmm/algorithms/mpi/upward.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_MPI_UPWARD_HPP #define SCALFMM_ALGORITHMS_MPI_UPWARD_HPP #ifdef _OPENMP -#include <omp.h> - #include "scalfmm/algorithms/omp/upward.hpp" -// #include "scalfmm/operators/m2m.hpp" -// #include "scalfmm/operators/tags.hpp" -// #include "scalfmm/tree/utils.hpp" -// #include "scalfmm/utils/massert.hpp" -// #include "scalfmm/utils/math.hpp" #include <cpp_tools/parallel_manager/parallel_manager.hpp> +#include <omp.h> + namespace scalfmm::algorithms::mpi::pass { /** * @brief Perform the communications for the children level * * @tparam Tree + * * @tparam Approximation */ template<typename Tree> @@ -70,9 +66,6 @@ namespace scalfmm::algorithms::mpi::pass auto first_group = tree.begin_mine_cells(level)->get(); auto index = first_group->component(0).index(); // Check if I have the parent - // std::cout << " upward " << index << " parent of last child index" << (last_index_child >> dimension) - // << "comm " << std::boolalpha << (index > last_index_child >> dimension) << std::endl; - // std::cout << " send comm to " << rank - 1 << std::endl; // Should be in a task ! auto it_group = tree.begin_mine_cells(level_child); @@ -86,11 +79,8 @@ namespace scalfmm::algorithms::mpi::pass deps.push_back(&(it_grp->get()->ccomponent(0).cmultipoles(0))); } #pragma omp task shared(rank, mpi_int_type, size, tag_data, tag_nb, dimension) firstprivate(it_group) \ - depend(iterator(std::size_t it = 0 \ - : deps.size()), \ - out \ - : (deps[it])[0]) priority(prio) - #endif + depend(iterator(std::size_t it = 0 : deps.size()), out : (deps[it])[0]) priority(prio) +#endif { if(index > last_index_child >> dimension) { @@ -105,13 +95,12 @@ namespace scalfmm::algorithms::mpi::pass // auto it_group = tree.begin_mine_cells(level_child); auto it_cell = it_group->get()->begin(); auto next = scalfmm::component::generate_linear_iterator(1, it_group, it_cell); - // std::vector<> cells_to_send(); + // We construct an MPI DATA_TYPE // MPI_Datatype mult_data_type; auto it = std::begin(buffer); for(int i = 0; i < nb_children - 1; ++i, next()) { - // std::cout << " Check children P " << index << " C " << it_cell->index() << std::endl; if(index < (it_cell->index() >> dimension)) { break; @@ -120,9 +109,7 @@ namespace scalfmm::algorithms::mpi::pass auto const& m = it_cell->cmultipoles(); auto nb_m = m.size(); - // std::cout << "cell index: " << it_cell->index() << " level " << it_cell->csymbolics().level - // << - // "\n"; + for(std::size_t i{0}; i < nb_m; ++i) { auto const& ten = m.at(i); @@ -135,19 +122,12 @@ namespace scalfmm::algorithms::mpi::pass comm.isend(&count, 1, mpi_int_type, rank - 1, tag_nb); - // std::cout << "nb_send = " << count << std::endl; - if(count != 0) { // loop to serialize the multipoles auto mpi_type = cpp_tools::parallel_manager::mpi::get_datatype<value_type>(); comm.isend(buffer.data(), size, mpi_type, rank - 1, tag_data); - // std::cout << "buffer:"; - // for(int i = 0; i < 10; ++i) - // { - // std::cout << " " << buffer[i]; - // } } } } @@ -175,27 +155,19 @@ namespace scalfmm::algorithms::mpi::pass auto group_parent_dep = it_group_parent->get()->ccomponent(0).cmultipoles(0); #ifdef M2M_COMM_TASKS -#pragma omp task shared(rank, mpi_int_type, size, tag_data, tag_nb) depend(iterator(std::size_t it = 0 \ - : deps.size()), \ - out \ - : (deps[it])[0]) priority(prio) - #endif +#pragma omp task shared(rank, mpi_int_type, size, tag_data, tag_nb) \ + depend(iterator(std::size_t it = 0 : deps.size()), out : (deps[it])[0]) priority(prio) +#endif { int count{-1}; comm.recv(&count, 1, mpi_int_type, rank + 1, tag_nb); - // std::cout << " We receive " << count << " cell(s)\n"; // use a recv if(count > 0) { std::vector<value_type> buffer(size); auto mpi_type = cpp_tools::parallel_manager::mpi::get_datatype<value_type>(); comm.recv(buffer.data(), size, mpi_type, rank + 1, tag_data); - // - // std::cout << "buffer:"; - // for(int i = 0; i < 10; ++i) - // { - // std::cout << " " << buffer[i]; - // } + std::cout << std::endl; auto it_group = tree.end_mine_cells(level_child); auto it_cell = it_group->get()->begin(); @@ -206,11 +178,8 @@ namespace scalfmm::algorithms::mpi::pass { // copy the multipoles in the buffer auto& m = it_cell->multipoles(); - // auto tm = cell.ctransformed_multipoles(); auto nb_m = m.size(); - // std::cout << "cell index: " << it_cell->index() << " level " << it_cell->csymbolics().level << - // "\n"; for(std::size_t i{0}; i < nb_m; ++i) { auto& ten = m.at(i); @@ -220,16 +189,17 @@ namespace scalfmm::algorithms::mpi::pass } } } - // std::cout << " END start_communications" << std::endl << std::flush; - // } - /// @brief This function constructs the local approximation for all the cells of the tree by applying the operator - /// m2m - /// - /// @param tree the tree target - /// @param approximation the approximation to construct the local approximation - /// + /** + * @brief This function constructs the local approximation for all the cells of the tree by applying the operator m2m. + * + * @tparam Tree + * @tparam Approximation + * + * @param tree the tree target + * @param approximation the approximation to construct the local approximation + */ template<typename Tree, typename Approximation> inline auto upward(Tree& tree, Approximation const& approximation) -> void { @@ -237,29 +207,14 @@ namespace scalfmm::algorithms::mpi::pass // upper working level is const int top_height = tree.box().is_periodic() ? 0 : 2; - // const int start_duplicated_level = tree.start_duplicated_level(); - // - // int top = start_duplicated_level < 0 ? top_height : start_duplicated_level - 1; + int top = top_height; for(int level = leaf_level - 1; level >= top /*top_height*/; --level) // int because top_height could be 0 { - // std::cout << "M2M : " << level + 1 << " -> " << level << std::endl << std::flush; - // start_communications(level, tree); - // std::cout << " end comm " << std::endl << std::flush; omp::pass::upward_level(level, tree, approximation); - // std::cout << " end upward_level " << level << std::endl << std::flush; } - // std::cout << "end upward " << std::endl << std::flush; - - // - - // for(int level = start_duplicated_level; level >= top_height; --level) // int because top_height could be 0 - // { - // std::cout << "Level duplicated (seq): " << level << std::endl; - // upward_level(level, tree, approximation); - // } } } // namespace scalfmm::algorithms::mpi::pass diff --git a/include/scalfmm/algorithms/omp/cell_to_leaf.hpp b/include/scalfmm/algorithms/omp/cell_to_leaf.hpp index 9fd5d3bb732b96084d0c19e0952967bdec0d104e..f7f0ef2a3f645785e0e1bf5c613cb61f4cc773c6 100644 --- a/include/scalfmm/algorithms/omp/cell_to_leaf.hpp +++ b/include/scalfmm/algorithms/omp/cell_to_leaf.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/cell_to_leaf.hpp +// File : scalfmm/algorithms/omp/cell_to_leaf.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_OMP_CELL_TO_LEAF_HPP #define SCALFMM_ALGORITHMS_OMP_CELL_TO_LEAF_HPP @@ -14,27 +14,30 @@ namespace scalfmm::algorithms::omp::pass { - /// @brief This function applies the l2p operator from the lower cell level to the leaf level. - /// - /// It applies the far field on the particles. - /// We pass here the entire far-field operator, this allows - /// us to catch combination of matrix kernels and trigger - /// optimizations. - /// - /// @tparam Tree the tree type - /// @tparam FarField the far-field operator type - /// @param tree reference to the tree - /// @param FarField const reference to the far-field operator of the fmm_operator - /// - template<typename Tree, typename FarField> - inline auto cell_to_leaf(Tree& tree, FarField const& far_field) - -> void + /** + * @brief This function applies the l2p operator from the lower cell level to the leaf level. + * + * It applies the far field on the particles. + * We pass here the entire far-field operator, this allows + * us to catch combination of matrix kernels and trigger + * optimizations. + * + * @tparam TreeType the tree type + * @tparam FarFieldType the far-field operator type + * + * @param tree reference to the tree + * @param FarFieldType const reference to the far-field operator of the fmm_operator + */ + template<typename TreeType, typename FarFieldType> + inline auto cell_to_leaf(TreeType& tree, FarFieldType const& far_field) -> void { using operators::l2p; static constexpr auto prio{priorities::l2p}; + // The leaves auto group_of_leaf_begin = tree.begin_mine_leaves(); auto group_of_leaf_end = tree.end_mine_leaves(); + // the cells auto leaf_level = tree.height() - 1; auto group_of_cell_begin = tree.begin_mine_cells(leaf_level); @@ -70,5 +73,5 @@ namespace scalfmm::algorithms::omp::pass } } // namespace scalfmm::algorithms::omp::pass -#endif // _OPENMP +#endif // _OPENMP #endif diff --git a/include/scalfmm/algorithms/omp/direct.hpp b/include/scalfmm/algorithms/omp/direct.hpp index 013069470c146be6ff59c2937b29f47fab2c798d..e11a58abf57208db8453b15af27bbe5d32c45dd3 100644 --- a/include/scalfmm/algorithms/omp/direct.hpp +++ b/include/scalfmm/algorithms/omp/direct.hpp @@ -1,14 +1,12 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/direct.hpp +// File : scalfmm/algorithms/omp/direct.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_OMP_DIRECT_HPP #define SCALFMM_ALGORITHMS_OMP_DIRECT_HPP #ifdef _OPENMP -#include <tuple> - #include "scalfmm/algorithms/common.hpp" #include "scalfmm/algorithms/omp/macro.hpp" #include "scalfmm/algorithms/omp/priorities.hpp" @@ -17,34 +15,31 @@ #include "scalfmm/operators/p2p.hpp" #include "scalfmm/operators/tags.hpp" +#include <tuple> + namespace scalfmm::algorithms::omp::pass { - /// @brief compute the near field interaction when source=target. - /// - /// Compute the near field interaction when source=target, the p2p interactions lists - /// should be construct before this call - /// - /// @tparam Tree - /// @tparam NearField - /// @param tree the octree - /// @param nearfield the nearfield operator - /// /** - * @brief Compute direct interaction between particles when the source tree and the target tree are the same - * - - * @param tree the source/target tree - * @param nearfield the near-field operator - */ - template<typename Tree, typename NearField> - inline auto direct(Tree const& tree, NearField const& nearfield) -> void + * @brief Compute direct interaction between particles when the source tree and the target tree are the same. + * + * Compute the near field interaction when source=target, the p2p interactions lists + * should be construct before this call + * + * @tparam TreeType + * @tparam NearFieldType + * + * @param tree the octree + * @param nearfield the nearfield operator + */ + template<typename TreeType, typename NearFieldType> + inline auto direct(TreeType const& tree, NearFieldType const& nearfield) -> void { using operators::p2p_full_mutual; using operators::p2p_inner; using operators::p2p_outer; - // static constexpr int limit = (Tree::dimension - 1) * 6 + (Tree::dimension - 1); + // static constexpr int limit = (TreeType::dimension - 1) * 6 + (TreeType::dimension - 1); const auto separation_criterion = nearfield.separation_criterion(); const auto mutual = nearfield.mutual(); const auto period = tree.box().get_periodicity(); @@ -105,8 +100,8 @@ namespace scalfmm::algorithms::omp::pass const auto current_group_ptr_particles = (*begin_groups).get()->depends_update(); // clang does not manage to pass the separation_criterion correctly if it is not firstprivate #pragma omp task untied default(none) shared(period, box_width, matrix_kernel) \ - firstprivate(begin_groups, separation_criterion, mutual) depend(inout \ - : current_group_ptr_particles[0]) priority(prio_big) + firstprivate(begin_groups, separation_criterion, mutual) depend(inout : current_group_ptr_particles[0]) \ + priority(prio_big) { // loop on the leaves of the current group for(std::size_t leaf_index = 0; leaf_index < (*begin_groups)->size(); ++leaf_index) @@ -138,7 +133,7 @@ namespace scalfmm::algorithms::omp::pass } p2p_inner(matrix_kernel, leaf, mutual); } // end of for_each on leaves - } // end task + } // end task ++begin_groups; } } @@ -146,25 +141,29 @@ namespace scalfmm::algorithms::omp::pass /** * @brief Compute direct interaction between particles * - * When tree_source = tree_target we call the direct(tree_source, nearfield) function + * When source_tree = target_tree we call the direct(source_tree, nearfield) function + * + * @tparam SourceTreeType + * @tparam TargetTreeType + * @tparam NearFieldType * - * @param tree_source the source tree - * @param tree_target tke target tree where we compute the field + * @param source_tree the source tree + * @param target_tree tke target tree where we compute the field * @param nearfield the near-field operator */ - template<typename TreeSource, typename TreeTarget, typename NearField> - inline auto direct(TreeSource& tree_source, TreeTarget& tree_target, NearField const& nearfield) -> void + template<typename SourceTreeType, typename TargetTreeType, typename NearFieldType> + inline auto direct(SourceTreeType& source_tree, TargetTreeType& target_tree, NearFieldType const& nearfield) -> void { bool source_target{false}; - if constexpr(std::is_same_v<TreeSource, TreeTarget>) + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { - source_target = (&tree_source == &tree_target); + source_target = (&source_tree == &target_tree); } if(source_target) { - if constexpr(std::is_same_v<TreeSource, TreeTarget>) + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { - direct(tree_source, nearfield); + direct(source_tree, nearfield); } } else @@ -172,8 +171,8 @@ namespace scalfmm::algorithms::omp::pass // source != target using operators::p2p_outer; - const auto period = tree_source.box().get_periodicity(); - const auto box_width = tree_source.box_width(); + const auto period = source_tree.box().get_periodicity(); + const auto box_width = source_tree.box_width(); // move test to execute // p2p stage @@ -181,8 +180,8 @@ namespace scalfmm::algorithms::omp::pass static constexpr auto prio_big{priorities::p2p_big}; // iterators on target leaves - auto begin_groups{tree_target.begin_mine_leaves()}; - auto end_groups{tree_target.end_mine_leaves()}; + auto begin_groups{target_tree.begin_mine_leaves()}; + auto end_groups{target_tree.end_mine_leaves()}; while(begin_groups != end_groups) { @@ -190,8 +189,7 @@ namespace scalfmm::algorithms::omp::pass const auto current_group_ptr_particles = (*begin_groups).get()->depends_update(); #pragma omp task untied default(none) shared(period, box_width, matrix_kernel) firstprivate(begin_groups) \ - depend(inout \ - : current_group_ptr_particles[0]) priority(prio_big) + depend(inout : current_group_ptr_particles[0]) priority(prio_big) { // mutexinoutset // loop on the leaves of the current group for(std::size_t leaf_index = 0; leaf_index < (*begin_groups)->size(); ++leaf_index) diff --git a/include/scalfmm/algorithms/omp/downward.hpp b/include/scalfmm/algorithms/omp/downward.hpp index e279dadba9e8a5ac7fd6be16f7fece9c779c1ce7..759a0c9b9f2527f07cadf1cdb753060614bda472 100644 --- a/include/scalfmm/algorithms/omp/downward.hpp +++ b/include/scalfmm/algorithms/omp/downward.hpp @@ -6,29 +6,34 @@ #define SCALFMM_ALGORITHMS_OMP_DOWNWARD_HPP #ifdef _OPENMP -#include <limits> #include "scalfmm/operators/l2l.hpp" #include "scalfmm/tree/utils.hpp" #include "scalfmm/utils/massert.hpp" #include "scalfmm/utils/math.hpp" +#include <limits> + namespace scalfmm::algorithms::omp::pass { /** * @brief perform the l2l operator for a given level * - * @tparam Tree - * @tparam Approximation + * @tparam TreeType + * @tparam ApproximationType + * * @param level current level to construct the m2m * @param tree the tree * @param approximation the approximation */ - template<typename Tree, typename Approximation> - inline auto downward_level(const int& level, Tree& tree, Approximation const& approximation) -> void + template<typename TreeType, typename ApproximationType> + inline auto downward_level(const int& level, TreeType& tree, ApproximationType const& approximation) -> void { using scalfmm::operators::l2l; - using value_type = typename Approximation::value_type; + using interpolator_type = typename std::decay_t<ApproximationType>; + using value_type = typename interpolator_type::value_type; + + static constexpr auto dimension = interpolator_type::dimension; // Get the index of the corresponding child-parent interpolator std::size_t level_interpolator_index = @@ -48,9 +53,6 @@ namespace scalfmm::algorithms::omp::pass while(group_of_child_cell_begin != group_of_child_cell_end) { - using interpolator_type = typename std::decay_t<Approximation>; - static constexpr auto dimension{interpolator_type::dimension}; - using ptr_parent_groups_type = std::decay_t<decltype(group_of_cell_begin->get())>; auto group_child = group_of_child_cell_begin->get(); @@ -113,17 +115,22 @@ namespace scalfmm::algorithms::omp::pass } } // end pragma task ++group_of_child_cell_begin; - } // end while + } // end while assert(group_of_child_cell_begin == group_of_child_cell_end); } - /// @brief This function constructs the local approximation for all the cells of the tree by applying the - /// operator l2l - /// - /// @param tree the tree target - /// @param approximation the approximation to construct the local approximation - /// - template<typename Tree, typename Approximation> - inline auto downward(Tree& tree, Approximation const& approximation) -> void + + /** + * @brief This function constructs the local approximation for all the cells of the tree by applying the + * operator l2l + * + * @tparam TreeType + * @tparam ApproximationType + * + * @param tree the tree target + * @param approximation the approximation to construct the local approximation + */ + template<typename TreeType, typename ApproximationType> + inline auto downward(TreeType& tree, ApproximationType const& approximation) -> void { // upper working level is const auto top_height = tree.box().is_periodic() ? 0 : 2; diff --git a/include/scalfmm/algorithms/omp/leaf_to_cell.hpp b/include/scalfmm/algorithms/omp/leaf_to_cell.hpp index b228f2981a1e9eb738e06d16114ed43c9fb898b0..f1b53a405d3c2a89c3d8e23602941292ea5bc584 100644 --- a/include/scalfmm/algorithms/omp/leaf_to_cell.hpp +++ b/include/scalfmm/algorithms/omp/leaf_to_cell.hpp @@ -1,28 +1,35 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/leaf_to_cell.hpp +// File : scalfmm/algorithms/omp/leaf_to_cell.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_OMP_LEAF_TO_CELL_HPP #define SCALFMM_ALGORITHMS_OMP_LEAF_TO_CELL_HPP #ifdef _OPENMP -#include <omp.h> - #include "scalfmm/algorithms/omp/priorities.hpp" #include "scalfmm/operators/p2m.hpp" #include "scalfmm/utils/massert.hpp" +#include <omp.h> + namespace scalfmm::algorithms::omp::pass { - /// @brief This function applies the p2m operator from the leaf level of the tree - /// - /// This function applies the p2m operator from the leaf level to - /// the first cell level. The inputs of the particles contained in the leaves - /// will be approximated by the technique used in the far_field - /// - template<typename Tree, typename FarField> - inline auto leaf_to_cell(Tree& tree, FarField const& far_field) -> void + /** + * @brief This function applies the p2m operator from the leaf level of the tree. + * + * This function applies the p2m operator from the leaf level to + * the first cell level. The inputs of the particles contained in the leaves + * will be approximated by the technique used in the far_field + * + * @tparam TreeType + * @tparam FarFieldType + * + * @param tree + * @param far_field + */ + template<typename TreeType, typename FarFieldType> + inline auto leaf_to_cell(TreeType& tree, FarFieldType const& far_field) -> void { using operators::p2m; static constexpr auto prio{priorities::p2m}; @@ -35,7 +42,8 @@ namespace scalfmm::algorithms::omp::pass auto group_of_cell_begin = tree.begin_mine_cells(leaf_level); auto group_of_cell_end = tree.end_mine_cells(leaf_level); - assertm( std::distance(group_of_leaf_begin, group_of_leaf_end) == std::distance(group_of_cell_begin, group_of_cell_end), + assertm(std::distance(group_of_leaf_begin, group_of_leaf_end) == + std::distance(group_of_cell_begin, group_of_cell_end), "Bottom pass : nb group of leaves and nb first level of group cells differs !"); // Loop over the group @@ -77,6 +85,6 @@ namespace scalfmm::algorithms::omp::pass } } // namespace scalfmm::algorithms::omp::pass -#endif // _OPENMP +#endif // _OPENMP #endif // SCALFMM_ALGORITHMS_LEAF_TO_CELL_BOTTOM_HPP diff --git a/include/scalfmm/algorithms/omp/macro.hpp b/include/scalfmm/algorithms/omp/macro.hpp index d73db158c1117213d3a14232ae27aa4fbb1f109d..049a3191213841e5a34dd7c7317b2ddd22e1f59a 100644 --- a/include/scalfmm/algorithms/omp/macro.hpp +++ b/include/scalfmm/algorithms/omp/macro.hpp @@ -1,14 +1,14 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/macro.hpp +// File : scalfmm/algorithms/omp/macro.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_OMP_MACRO_HPP #define SCALFMM_ALGORITHMS_OMP_MACRO_HPP #if _OPENMP >= 201811 - #define commute mutexinoutset +#define commute mutexinoutset #else - #define commute inout +#define commute inout #endif -#endif // SCALFMM_ALGORITHMS_OMP_MACRO_HPP +#endif // SCALFMM_ALGORITHMS_OMP_MACRO_HPP diff --git a/include/scalfmm/algorithms/omp/periodic.hpp b/include/scalfmm/algorithms/omp/periodic.hpp index 31893a7102b966aa3a02b638b5d16a8da6bba6f5..e087cf56371f97eb24d2cb2518e2b84204ce08cf 100644 --- a/include/scalfmm/algorithms/omp/periodic.hpp +++ b/include/scalfmm/algorithms/omp/periodic.hpp @@ -11,33 +11,35 @@ */ // -------------------------------- // See LICENCE file at project root +// File : scalfmm/algorithms/omp/periodic.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_OMP_PERIODIC_HPP #define SCALFMM_ALGORITHMS_OMP_PERIODIC_HPP -#include <iostream> -#include <numeric> - -#include <cpp_tools/colors/colorized.hpp> - #include "scalfmm/operators/l2l.hpp" #include "scalfmm/operators/m2l.hpp" #include "scalfmm/operators/m2m.hpp" #include <scalfmm/algorithms/sequential/periodic.hpp> #include <scalfmm/meta/utils.hpp> +#include <cpp_tools/colors/colorized.hpp> + +#include <iostream> +#include <numeric> + namespace scalfmm::algorithms::omp::pass { - /// @brief update the level (move to the fake level) for the FMM operators - /// - /// @tparam[inout] Tree - /// - /// - template<typename Tree> - inline auto preprocessing_execute(Tree& tree) -> void + /** + * @brief Updates the level (move to the fake level) for the FMM operators. + * + * @tparam[inout] TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto preprocessing_execute(TreeType& tree) -> void { - // auto offset_real_tree = 2 + tree.levels_above_root(); if(offset_real_tree > 2) { @@ -57,8 +59,7 @@ namespace scalfmm::algorithms::omp::pass auto length = std::size(*group); ///////////////////////////////////////////////////////////////////////////////////////// /// loop on the leaves of the current group -#pragma omp task untied default(none) firstprivate(length, group_index) shared(offset_real_tree) \ - priority(prio) +#pragma omp task untied default(none) firstprivate(length, group_index) shared(offset_real_tree) priority(prio) { for(std::size_t index_in_group{0}; index_in_group < length; ++index_in_group) { @@ -72,15 +73,17 @@ namespace scalfmm::algorithms::omp::pass #pragma omp taskwait } } - /// @brief remove the fake level (fake level) for the FMM operators - /// - /// @tparam[inout] Tree - /// - /// - template<typename Tree> - inline auto postprocessing_execute(Tree& tree) -> void + + /** + * @brief Removes the fake level (fake level) for the FMM operators. + * + * @tparam[inout] TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto postprocessing_execute(TreeType& tree) -> void { - // auto offset_real_tree = 2 + tree.levels_above_root(); if(offset_real_tree > 2) { @@ -114,9 +117,17 @@ namespace scalfmm::algorithms::omp::pass #pragma omp taskwait } } + #ifdef SCALFMMM_UPDATE_LEVEL_OLD - template<typename Tree> - auto preprocessing_execute(Tree& tree) -> void + /** + * @brief Updates the level (move to the fake level) for the FMM operators. + * + * @tparam[inout] TreeType + * + * @param tree + */ + template<typename TreeType> + auto preprocessing_execute(TreeType& tree) -> void { auto offset_real_tree = 2 + tree.levels_above_root(); if(offset_real_tree > 2) @@ -124,14 +135,21 @@ namespace scalfmm::algorithms::omp::pass // Add parallelism (loop on the group then cells) for(std::size_t level{0}; level < tree.height(); ++level) { - for_each_cell(std::begin(tree), std::end(tree), level, - [&offset_real_tree](auto& cell) + for_each_cell(std::begin(tree), std::end(tree), level, [&offset_real_tree](auto& cell) { cell.symbolics().level = cell.symbolics().level + offset_real_tree; }); } } } - template<typename Tree> - auto postprocessing_execute(Tree& tree) -> void + + /** + * @brief Removes the fake level (fake level) for the FMM operators. + * + * @tparam[inout] TreeType + * + * @param tree + */ + template<typename TreeType> + auto postprocessing_execute(TreeType& tree) -> void { auto offset_real_tree = 2 + tree.levels_above_root(); if(offset_real_tree > 2) @@ -140,21 +158,24 @@ namespace scalfmm::algorithms::omp::pass { // Add parallelism (loop on the group then cells) - for_each_cell(std::begin(tree), std::end(tree), level, - [&offset_real_tree](auto& cell) + for_each_cell(std::begin(tree), std::end(tree), level, [&offset_real_tree](auto& cell) { cell.symbolics().level = cell.symbolics().level - offset_real_tree; }); } } } - #endif +#endif + /** - * @brief + * @brief Builds the field at level 0. + * + * @tparam Tree + * @tparam InterpolatorType * * @param tree * @param approximation */ - template<typename Tree, typename Interpolator> - inline auto build_field_level0(Tree& tree, Interpolator const& approximation) -> void + template<typename TreeType, typename InterpolatorType> + inline auto build_field_level0(TreeType& tree, InterpolatorType const& approximation) -> void { // // if kernel is absolutely convergent @@ -177,9 +198,8 @@ namespace scalfmm::algorithms::omp::pass auto& root_cell = *cell_begin; auto* ptr_dep_in = &(root_cell.multipoles(0)); auto* ptr_dep_out = &(root_cell.locals(0)); -#pragma omp task untied default(none) shared(tree, approximation) depend(in \ - : ptr_dep_in[0]) depend(out \ - : ptr_dep_out[0]) +#pragma omp task untied default(none) shared(tree, approximation) depend(in : ptr_dep_in[0]) \ + depend(out : ptr_dep_out[0]) { scalfmm::algorithms::sequential::pass::full_periodic_classical_summation(tree, approximation); } diff --git a/include/scalfmm/algorithms/omp/priorities.hpp b/include/scalfmm/algorithms/omp/priorities.hpp index bed701d46260b5c4d43700ef76e5ce3a8f819d77..99406f9731c94299ecdd20ae4f4da2372b37b430 100644 --- a/include/scalfmm/algorithms/omp/priorities.hpp +++ b/include/scalfmm/algorithms/omp/priorities.hpp @@ -1,88 +1,40 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/priorities.hpp +// File : scalfmm/algorithms/omp/priorities.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_OMP_PRIORITIES_HPP #define SCALFMM_ALGORITHMS_OMP_PRIORITIES_HPP #ifdef __clang__ -#define ACCESS_DEP(V,i) V[i] +#define ACCESS_DEP(V, i) V[i] #elif defined(__GNUC__) -#define ACCESS_DEP(V,i) V.at(i) +#define ACCESS_DEP(V, i) V.at(i) #else #warning "MACRO FOR DEPENDENCIES : OTHER COMPILER TO CHECK (NOT GCC & CLANG) !" -#define ACCESS_DEP(V,i) V.at(i) +#define ACCESS_DEP(V, i) V.at(i) #endif namespace scalfmm::algorithms::omp { + /** + * @brief Priorities of the different passes for the task-based algorithms. + */ struct priorities { static constexpr int max = 10; static constexpr int p2m = 9; static constexpr int m2m = 8; - //static constexpr int m2l_high = 7, static constexpr int m2l = 7; static constexpr int l2l = 6; static constexpr int p2p_big = 5; static constexpr int l2p = 3; static constexpr int p2p_small = 2; }; - - //struct priorities - //{ - // constexpr priorities(std::size_t tree_height) - // : _th(tree_height) - // { - // if(tree_height > 2) - // { - // int inc_prio{0}; - // _p2m_send = inc_prio++; - // _p2m = inc_prio++; - // _m2m_send = inc_prio++; - // _m2m = inc_prio++; - // _p2p = inc_prio++; - // _m2l = inc_prio++; - // _l2l = inc_prio++; - // inc_prio += (tree_height-3)-1; - // inc_prio += (tree_height-3)-1; - // inc_prio += (tree_height-3)-1; - // _p2p_out = inc_prio++; - // _m2l_last_level = inc_prio++; - // _l2p = inc_prio++; - // _max_prio = inc_prio; - // } - // } - // int p2m() const noexcept { return _p2m; } - // int p2m_send() const noexcept { return _p2m_send; } - // int m2m_send() const noexcept { return _m2m_send; } - // int m2l() const noexcept { return _m2l; } - // int m2l_last_level() const noexcept { return _m2l_last_level; } - // int l2l() const noexcept { return _l2l; } - // int p2p() const noexcept { return _p2p; } - // int p2p_out() const noexcept { return _p2p_out; } - // int l2p() const noexcept { return _l2p; } - // int max_prio() const noexcept { return _max_prio; } + /** + * @brief Scales the priority values according to the level. + */ + int scale_prio(int prio, std::size_t level) { return prio + (level - 2) * 3; } +} // namespace scalfmm::algorithms::omp - // std::size_t _th{0}; - // int _p2m{0}; - // int _p2m_send{0}; - // int _m2m_send{0}; - // int _m2m{0}; - // int _m2l{0}; - // int _m2l_last_level{0}; - // int _l2l{0}; - // int _p2p{0}; - // int _p2p_out{0}; - // int _l2p{0}; - // int _max_prio{0}; - //}; - - int scale_prio(int prio, std::size_t level) - { - return prio+(level-2)*3; - } -} - -#endif // SCALFMM_ALGORITHMS_OMP_PRIORITIES_HPP +#endif // SCALFMM_ALGORITHMS_OMP_PRIORITIES_HPP diff --git a/include/scalfmm/algorithms/omp/task_dep.hpp b/include/scalfmm/algorithms/omp/task_dep.hpp index d3ce61b89e174f788a86c08637a1cfdfec53042d..b5933240aeed59245bf1a1a93b2c03a0247d3735 100644 --- a/include/scalfmm/algorithms/omp/task_dep.hpp +++ b/include/scalfmm/algorithms/omp/task_dep.hpp @@ -1,69 +1,77 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/task_dep.hpp +// File : scalfmm/algorithms/omp/task_dep.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_OMP_TASK_DEP_HPP #define SCALFMM_ALGORITHMS_OMP_TASK_DEP_HPP #ifdef _OPENMP -#include <chrono> -#include <iomanip> -#include <iostream> -#include <omp.h> -#include <vector> - -#include <cpp_tools/timers/simple_timer.hpp> - #include "scalfmm/algorithms/common.hpp" #include "scalfmm/algorithms/omp/cell_to_leaf.hpp" #include "scalfmm/algorithms/omp/direct.hpp" #include "scalfmm/algorithms/omp/downward.hpp" #include "scalfmm/algorithms/omp/leaf_to_cell.hpp" #include "scalfmm/algorithms/omp/periodic.hpp" -#include "scalfmm/algorithms/omp/utils.hpp" -#include "scalfmm/lists/omp.hpp" -//#include "scalfmm/algorithms/omp/priorities.hpp" #include "scalfmm/algorithms/omp/transfer.hpp" #include "scalfmm/algorithms/omp/upward.hpp" +#include "scalfmm/algorithms/omp/utils.hpp" +#include "scalfmm/lists/omp.hpp" + +#include <cpp_tools/timers/simple_timer.hpp> + +#include <chrono> +#include <iomanip> +#include <iostream> +#include <omp.h> +#include <vector> namespace scalfmm::algorithms::omp { namespace impl { /** - * @brief Openmp task algorithm to compute the interactions + * @brief OpenMP task-based algorithm. + * + * This function launches all the passes of the task-based algorithm. * * @tparam S type of the options - * @tparam TreeS type of the tree source - * @tparam TreeT type of the tree target - * @tparam NearField Near-field type - * @tparam FarField Far-field type + * @tparam SourceTreeType type of the tree source + * @tparam TargetTreeType type of the tree target + * @tparam NearFieldType Near-field type + * @tparam FarFieldType Far-field type + * * @param s the option (omp, seq, ...) - * @param tree_source the tree of particle sources - * @param tree_target the tree of particle sources + * @param source_tree the tree of particle sources + * @param target_tree the tree of particle sources * @param fmmoperators the fmm operator * @param op_in the type of computation (nearfield, farfield, all) see @see operators_to_proceed */ - template<typename... S, typename TreeS, typename TreeT, typename NearField, typename FarField> - inline auto task_dep(options::settings<S...> s, TreeS& tree_source, TreeT& tree_target, - operators::fmm_operators<NearField, FarField> const& fmmoperators, + template<typename... S, typename SourceTreeType, typename TargetTreeType, typename NearFieldType, + typename FarFieldType> + inline auto task_dep(options::settings<S...> s, SourceTreeType& source_tree, TargetTreeType& target_tree, + operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, unsigned int op_in = operators_to_proceed::all) -> void { static_assert(options::support(s, options::_s(options::timit)), "task_dep algo unsupported options!"); using timer = cpp_tools::timers::timer<std::chrono::milliseconds>; timer time{}; bool same_tree{false}; - if constexpr(std::is_same_v<TreeS, TreeT>) + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { - same_tree = (&tree_source == &tree_target); + same_tree = (&source_tree == &target_tree); } - // - auto const& far_field = fmmoperators.far_field(); + auto const& near_field = fmmoperators.near_field(); - auto const& separation_criterion = fmmoperators.near_field().separation_criterion(); - auto const& mutual = fmmoperators.near_field().mutual(); - // const priorities prios(tree.height()); + auto const& far_field = fmmoperators.far_field(); + auto const& approximation = far_field.approximation(); + auto const& near_field_separation_criterion = near_field.separation_criterion(); + auto const& far_field_separation_criterion = far_field.separation_criterion(); + auto const& mutual = near_field.mutual(); + + assertm(near_field_separation_criterion == far_field_separation_criterion, + "Far field kernel and near field kernel must have the same separation criterion."); + // Buffer for m2l optimization : we calculate the inverse fft on the fly and free memory. // get the shape of the buffer used in the m2l pass (get it from the far field) // and allocate it @@ -74,7 +82,7 @@ namespace scalfmm::algorithms::omp << priorities::max + 1 << cpp_tools::colors::reset << std::endl; } - const auto op = tree_target.height() == 2 ? operators_to_proceed::p2p : op_in; + const auto op = target_tree.height() == 2 ? operators_to_proceed::p2p : op_in; if constexpr(options::has(s, options::timit)) { @@ -83,68 +91,54 @@ namespace scalfmm::algorithms::omp auto buffers{init_buffers(far_field.approximation())}; // #pragma omp parallel default(none) \ - shared(tree_source, tree_target, far_field, near_field, buffers, op, separation_criterion, mutual, same_tree) + shared(source_tree, target_tree, far_field, approximation, near_field, buffers, op, near_field_separation_criterion, \ + far_field_separation_criterion, mutual, same_tree) { #pragma omp single nowait { - if(tree_target.is_interaction_p2p_lists_built() == false) + if(target_tree.is_interaction_p2p_lists_built() == false) { - list::omp::build_p2p_interaction_list(tree_source, tree_target, separation_criterion, mutual); + list::omp::build_p2p_interaction_list(source_tree, target_tree, near_field_separation_criterion, + mutual); } if((op & operators_to_proceed::p2p) == operators_to_proceed::p2p) { - pass::direct(tree_source, tree_target, near_field); + pass::direct(source_tree, target_tree, near_field); } - if(tree_target.is_interaction_m2l_lists_built() == false) + if(target_tree.is_interaction_m2l_lists_built() == false) { - list::omp::build_m2l_interaction_list(tree_source, tree_target, separation_criterion); + list::omp::build_m2l_interaction_list(source_tree, target_tree, far_field_separation_criterion); } if((op & operators_to_proceed::p2m) == operators_to_proceed::p2m) { - // std::cout << "\n\n leaf_to_cell pass \n" << std::flush; - - pass::leaf_to_cell(tree_source, far_field); - + pass::leaf_to_cell(source_tree, far_field); } if((op & operators_to_proceed::m2m) == operators_to_proceed::m2m) { - // std::cout << "\n\n upward pass\n " << std::flush; - - pass::upward(tree_source, far_field.approximation()); + pass::upward(source_tree, approximation); } - if(same_tree && tree_target.box().is_periodic()) + if(same_tree && target_tree.box().is_periodic()) { - // timers["field0"].tic(); - pass::build_field_level0(tree_target, far_field.approximation()); - // timers["field0"].tac(); + pass::build_field_level0(target_tree, far_field.approximation()); } if((op & operators_to_proceed::m2l) == operators_to_proceed::m2l) { - // std::cout << "\n\n transfer(pass::split_m2l::remove_leaf_level) pass\n " << std::flush; - - pass::transfer(tree_source, tree_target, far_field, buffers, + pass::transfer(source_tree, target_tree, far_field, buffers, pass::split_m2l::remove_leaf_level); - } if((op & operators_to_proceed::l2l) == operators_to_proceed::l2l) { - // std::cout << "\n\n downward pass\n " << std::endl << std::flush; - - pass::downward(tree_target, far_field.approximation()); + pass::downward(target_tree, approximation); } if((op & operators_to_proceed::m2l) == operators_to_proceed::m2l) { - // std::cout << "\n\n transfer(pass::split_m2l::leaf_level) pass\n " << std::flush; - - pass::transfer(tree_source, tree_target, far_field, buffers, pass::split_m2l::leaf_level); + pass::transfer(source_tree, target_tree, far_field, buffers, pass::split_m2l::leaf_level); } if((op & operators_to_proceed::l2p) == operators_to_proceed::l2p) { - // std::cout << " cell_to_leaf pass\n " << std::flush; - - pass::cell_to_leaf(tree_target, far_field); + pass::cell_to_leaf(target_tree, far_field); } } // end single } // end parallel @@ -162,25 +156,72 @@ namespace scalfmm::algorithms::omp } } - template<typename Tree, typename NearField, typename FarField> - inline auto task_dep(Tree& tree, operators::fmm_operators<NearField, FarField> const& fmmoperators, + /** + * @brief Wrapper to run the OpenMP task-based algorithm both with a source tree and a target tree but + * without settings. + * + * @tparam SourceTreeType + * @tparam TargetTreeType + * @tparam NearFieldType + * @tparam FarFieldType + * + * @param source_tree + * @param target_tree + * @param fmmoperators + * @param op + * + * @return the task-based algorithm. + */ + template<typename SourceTreeType, typename TargetTreeType, typename NearFieldType, typename FarFieldType> + inline auto task_dep(SourceTreeType& source_tree, TargetTreeType& target_tree, + operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, unsigned int op = operators_to_proceed::all) -> void { - return task_dep(options::settings<>{}, tree, tree, fmmoperators, op); + return task_dep(options::settings<>{}, source_tree, target_tree, fmmoperators, op); } - template<typename... S, typename Tree, typename NearField, typename FarField> - inline auto task_dep(options::settings<S...> s, Tree& tree, - operators::fmm_operators<NearField, FarField> const& fmmoperators, + + /** + * @brief Wrapper to run the OpenMP task-based algorithm both with a source tree and a target tree but + * without settings. + * + * @tparam TreeType + * @tparam NearFieldType + * @tparam FarFieldType + * + * @param tree + * @param fmmoperators + * @param op + * + * @return the task-based algorithm. + */ + template<typename TreeType, typename NearFieldType, typename FarFieldType> + inline auto task_dep(TreeType& tree, operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, unsigned int op = operators_to_proceed::all) -> void { - return task_dep(s, tree, tree, fmmoperators, op); + return task_dep(options::settings<>{}, tree, tree, fmmoperators, op); } - template<typename TreeS, typename TreeT, typename NearField, typename FarField> - inline auto task_dep(TreeS& tree_source, TreeT& tree_target, - operators::fmm_operators<NearField, FarField> const& fmmoperators, + + /** + * @brief Wrapper to run the OpenMP task-based algorithm with a single tree and with settings. + * + * @tparam S + * @tparam TreeType + * @tparam NearFieldType + * @tparam FarFieldType + * + * @param s + * @param tree + * @param fmmoperators + * @param op + * + * @return the task-based algorithm. + */ + template<typename... S, typename TreeType, typename NearFieldType, typename FarFieldType> + inline auto task_dep(options::settings<S...> s, TreeType& tree, + operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, unsigned int op = operators_to_proceed::all) -> void { - return task_dep(options::settings<>{}, tree_source, tree_target, fmmoperators, op); + return task_dep(s, tree, tree, fmmoperators, op); } } // namespace impl diff --git a/include/scalfmm/algorithms/omp/transfer.hpp b/include/scalfmm/algorithms/omp/transfer.hpp index 9940d5d5991c35400b0fef59a82968540ec50c69..d24931582f7230fe6cfceb6a84fd41c74e76f903 100644 --- a/include/scalfmm/algorithms/omp/transfer.hpp +++ b/include/scalfmm/algorithms/omp/transfer.hpp @@ -1,15 +1,12 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/transfert.hpp +// File : scalfmm/algorithms/omp/transfer.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_OMP_TRANSFER_HPP #define SCALFMM_ALGORITHMS_OMP_TRANSFER_HPP #ifdef _OPENMP -#include <omp.h> -#include <vector> - #include "scalfmm/meta/utils.hpp" #include "scalfmm/operators/m2l.hpp" #include "scalfmm/operators/mutual_apply.hpp" @@ -21,8 +18,14 @@ #include "scalfmm/utils/math.hpp" #include "scalfmm/utils/tensor.hpp" +#include <omp.h> +#include <vector> + namespace scalfmm::algorithms::omp::pass { + /** + * @brief + */ enum class split_m2l { full, ///< apply the transfer on all level of the tree @@ -30,28 +33,44 @@ namespace scalfmm::algorithms::omp::pass leaf_level ///< apply the transfer only on leaf level }; - template<typename TreeS, typename TreeT, typename FarField, typename BufferPtr> - inline auto transfer_level(const int level, TreeS& tree_source, TreeT& tree_target, FarField const& far_field, - std::vector<BufferPtr> const& buffers) -> void + /** + * @brief + * + * @tparam SourceTreeType + * @tparam TargetTreeType + * @tparam FarFieldType + * @tparam BufferPtrType + * + * @param level + * @param source_tree + * @param target_tree + * @param far_field + * @param buffers + * + * @return + */ + template<typename SourceTreeType, typename TargetTreeType, typename FarFieldType, typename BufferPtrType> + inline auto transfer_level(const int level, SourceTreeType& source_tree, TargetTreeType& target_tree, + FarFieldType const& far_field, std::vector<BufferPtrType> const& buffers) -> void { using operators::m2l_loop; // Useful to overload m2l(...) function auto const& approximation = far_field.approximation(); - // - auto begin_cell_target_level_it = tree_target.begin_cells(level); - auto end_cell_target_level_it = tree_target.end_cells(level); + auto begin_cell_target_level_it = target_tree.begin_cells(level); + auto end_cell_target_level_it = target_tree.end_cells(level); auto num{0}; + ///////////////////////////////////////////////////////////////////////////////////////// /// loop on the groups at level level - const auto num_threads{omp_get_num_threads()}; - for(auto cell_target_level_it = begin_cell_target_level_it; cell_target_level_it != end_cell_target_level_it; - ++cell_target_level_it,++num) + const auto num_threads{omp_get_num_threads()}; + for(auto cell_target_level_it = begin_cell_target_level_it; cell_target_level_it != end_cell_target_level_it; + ++cell_target_level_it, ++num) { // get() because cell_target_level_it is a shared pointer auto group_ptr = cell_target_level_it->get(); // dependence on the first local of the group auto ptr_local_raw = &(group_ptr->ccomponent(0).clocals(0)); static constexpr auto prio{priorities::m2l}; - // + // clang-format off #pragma omp task untied default(none) firstprivate(group_ptr, ptr_local_raw) shared(approximation, buffers) \ depend(iterator(it = 0 : std::size(group_ptr->csymbolics().group_dependencies_m2l)), \ @@ -76,29 +95,31 @@ namespace scalfmm::algorithms::omp::pass /// post-processing the group if necessary } } - /// @brief apply the transfer operator to construct the local approximation in tree_target - /// - /// @tparam TreeS template for the Tree source type - /// @tparam TreeT template for the Tree target type - /// @tparam FarField template for the far field type - /// @tparam BufferPtr template for the type of pointer of the buffer - /// @param tree_source the tree containing the source cells/leaves - /// @param tree_target the tree containing the target cells/leaves - /// @param far_field The far field operator - /// @param buffers vector of buffers used by the far_field in the transfer pass (if needed) - /// @param split the enum (@see split_m2l) tp specify on which level we apply the transfer - /// operator - /// - template<typename TreeS, typename TreeT, typename FarField, typename BufferPtr> - inline auto transfer(TreeS& tree_source, TreeT& tree_target, FarField const& far_field, - std::vector<BufferPtr> const& buffers, split_m2l split = split_m2l::full) -> void + + /** + * @brief Applies the transfer operator to construct the local approximation in target_tree. + * + * @tparam SourceTreeType template for the Tree source type + * @tparam TargetTreeType template for the Tree target type + * @tparam FarFieldType template for the far field type + * @tparam BufferPtrType template for the type of pointer of the buffer + * + * @param source_tree the tree containing the source cells/leaves + * @param target_tree the tree containing the target cells/leaves + * @param far_field The far field operator + * @param buffers vector of buffers used by the far_field in the transfer pass (if needed) + * @param split the enum (@see split_m2l) tp specify on which level we apply the transfer + * operator + */ + template<typename SourceTreeType, typename TargetTreeType, typename FarFieldType, typename BufferPtrType> + inline auto transfer(SourceTreeType& source_tree, TargetTreeType& target_tree, FarFieldType const& far_field, + std::vector<BufferPtrType> const& buffers, split_m2l split = split_m2l::full) -> void { - // - auto tree_height = tree_target.height(); + auto tree_height = target_tree.height(); ///////////////////////////////////////////////////////////////////////////////////////// /// Loop on the level from the level top_height to the leaf level (Top to Bottom) - auto top_height = tree_target.box().is_periodic() ? 1 : 2; + auto top_height = target_tree.box().is_periodic() ? 1 : 2; auto last_level = tree_height; switch(split) @@ -112,13 +133,10 @@ namespace scalfmm::algorithms::omp::pass case split_m2l::full: break; } - // std::cout << "transfer " - // << " last_level " << last_level << " top " << top_height << std::endl; + for(std::size_t level = top_height; level < last_level; ++level) { - // std::cout << " current level " << level << " last_level " << last_level << " top " << top_height - // << std::endl << std::flush; - transfer_level(level, tree_source, tree_target, far_field, buffers); + transfer_level(level, source_tree, target_tree, far_field, buffers); } } } // namespace scalfmm::algorithms::omp::pass diff --git a/include/scalfmm/algorithms/omp/upward.hpp b/include/scalfmm/algorithms/omp/upward.hpp index 7dfe05476e50fd297f34f2e5c1d9f58021e54062..7738ae44c0dacf661120e07f4352807ac42408e7 100644 --- a/include/scalfmm/algorithms/omp/upward.hpp +++ b/include/scalfmm/algorithms/omp/upward.hpp @@ -1,40 +1,40 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/upward.hpp +// File : scalfmm/algorithms/omp/upward.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_OMP_UPWARD_HPP #define SCALFMM_ALGORITHMS_OMP_UPWARD_HPP #ifdef _OPENMP -#include <limits> -#include <omp.h> - #include "scalfmm/operators/m2m.hpp" #include "scalfmm/operators/tags.hpp" #include "scalfmm/tree/utils.hpp" #include "scalfmm/utils/massert.hpp" #include "scalfmm/utils/math.hpp" +#include <limits> +#include <omp.h> + namespace scalfmm::algorithms::omp::pass { - /** * @brief perform the m2m operator for a given level * - * @tparam Tree - * @tparam Approximation + * @tparam TreeType + * @tparam ApproximationType + * * @param level current level to construct the m2m * @param tree the tree * @param approximation the approximation */ - template<typename Tree, typename Approximation> - inline auto upward_level(const int& level, Tree& tree, Approximation const& approximation) -> void + template<typename TreeType, typename ApproximationType> + inline auto upward_level(const int& level, TreeType& tree, ApproximationType const& approximation) -> void { using operators::m2m; // - using interpolator_type = typename std::decay_t<Approximation>; - using value_type = typename Approximation::value_type; + using interpolator_type = typename std::decay_t<ApproximationType>; + using value_type = typename interpolator_type::value_type; static constexpr auto dimension{interpolator_type::dimension}; static constexpr auto prio{priorities::m2m}; @@ -89,7 +89,6 @@ namespace scalfmm::algorithms::omp::pass child_groups.push_back(start_range_dependencies_tmp->get()); ++start_range_dependencies_tmp; } - // std::cout << "- child_groups size " << child_groups.size() << std::endl << std::flush; for(std::size_t cell_index = 0; cell_index < group_parent->size(); ++cell_index) { @@ -115,14 +114,19 @@ namespace scalfmm::algorithms::omp::pass } assert(group_of_cell_begin == group_of_cell_end); } - /// @brief This function constructs the local approximation for all the cells of the tree by applying the - /// operator m2m - /// - /// @param tree the tree target - /// @param approximation the approximation to construct the local approximation - /// - template<typename Tree, typename Approximation> - inline auto upward(Tree& tree, Approximation const& approximation) -> void + + /** + * @brief This function constructs the local approximation for all the cells of the tree by applying the + * operator m2m + * + * @tparam TreeType + * @tparam ApproximationType + * + * @param tree the tree target + * @param approximation the approximation to construct the local approximation + */ + template<typename TreeType, typename ApproximationType> + inline auto upward(TreeType& tree, ApproximationType const& approximation) -> void { auto leaf_level = tree.height() - 1; // diff --git a/include/scalfmm/algorithms/omp/utils.hpp b/include/scalfmm/algorithms/omp/utils.hpp index 1b0f7d21f4c054b46b2f2e0be88b72cc52cccddd..25e2830855b1b11f5163aea99cd242783b0d2da4 100644 --- a/include/scalfmm/algorithms/omp/utils.hpp +++ b/include/scalfmm/algorithms/omp/utils.hpp @@ -1,3 +1,7 @@ +// -------------------------------- +// See LICENCE file at project root +// File : algorithms/omp/utils.hpp +// -------------------------------- #ifndef SCALFMM_ALGORITHMS_OPENMP_UTILS_HPP #define SCALFMM_ALGORITHMS_OPENMP_UTILS_HPP @@ -5,17 +9,21 @@ namespace scalfmm::algorithms::omp { namespace impl { - /// @brief - /// - /// @tparam Approximation - /// @param approximation - /// - /// @return - template<typename Approximation> - auto init_buffers(Approximation const& approximation) - -> std::vector<std::decay_t<decltype(std::declval<Approximation>().buffer_initialization())>*> + /** + * @brief + * + * @tparam ApproximationType + * + * @param approximation + * + * @return + */ + template<typename ApproximationType> + auto init_buffers(ApproximationType const& approximation) + -> std::vector<std::decay_t<decltype(std::declval<ApproximationType>().buffer_initialization())>*> { - using buffer_type = typename Approximation::buffer_type; + using approximation_type = ApproximationType; + using buffer_type = typename approximation_type::buffer_type; using ptr_buffer_type = buffer_type*; std::vector<ptr_buffer_type> buffers{}; @@ -32,8 +40,17 @@ namespace scalfmm::algorithms::omp return buffers; } - template<typename BuffersPtr> - auto delete_buffers(std::vector<BuffersPtr> const& buffers) -> void + /** + * @brief + * + * @tparam BuffersPtrType + * + * @param buffers + * + * @return + */ + template<typename BuffersPtrType> + auto delete_buffers(std::vector<BuffersPtrType> const& buffers) -> void { #pragma omp parallel { @@ -42,54 +59,151 @@ namespace scalfmm::algorithms::omp } } // namespace impl - /// @brief reset to zero the particles in the tree - /// - template<typename Tree> - inline auto reset_particles(Tree& tree) + + /** + * @brief Resets to zero the particles in the tree (task-based version). + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_particles(TreeType& tree) { -#pragma omp parallel shared(tree) +#pragma omp parallel default(none) shared(tree) + { #pragma omp single nowait + { + for(auto pg: tree.vector_of_leaf_groups()) + { +#pragma omp task default(none) firstprivate(pg) + { + // reset the particles in the block + pg->storage().reset_particles(); + } + } + } +#pragma omp taskwait + } + } + + /** + * @brief Resets to zero the positions of the particles in the tree (task-based version). + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_positions(TreeType& tree) + { +#pragma omp parallel shared(tree) { - for(auto pg: tree.group_of_leaves()) +#pragma omp single nowait { + for(auto pg: tree.vector_of_leaf_groups()) + { #pragma omp task firstprivate(pg) - // loop on leaves - for(auto& leaf: pg->block()) + { + // reset the inputs in the block + pg->storage().reset_positions(); + } + } + } +#pragma omp taskwait + } + } + + /** + * @brief Resets to zero the inputs of the particles in the tree (task-based version). + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_inputs(TreeType& tree) + { +#pragma omp parallel shared(tree) + { +#pragma omp single nowait + { + for(auto pg: tree.vector_of_leaf_groups()) { - leaf.particles().clear(); +#pragma omp task firstprivate(pg) + { + // reset the inputs in the block + pg->storage().reset_inputs(); + } } } +#pragma omp taskwait } + } + + /** + * @brief Resets to zero the outputs of the particles in the tree (task-based version). + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_outputs(TreeType& tree) + { +#pragma omp parallel shared(tree) + { +#pragma omp single nowait + { + for(auto pg: tree.vector_of_leaf_groups()) + { +#pragma omp task firstprivate(pg) + { + // reset the outputs in the block + pg->storage().reset_outputs(); + } + } + } #pragma omp taskwait + } } - /// - /// @brief reset to zero the output particles in the tree - /// - template<typename Tree> - inline auto reset_outputs(Tree& tree) + + /** + * @brief Resets to zero the variables of the particles in the tree (task-based version). + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_variables(TreeType& tree) { - // loop on group of leaves - // No openmp - // #pragma omp parallel shared(tree) - // #pragma omp single nowait +#pragma omp parallel shared(tree) { - // #pragma omp parallel for shared(tree) - for(auto pg: tree.group_of_leaves()) +#pragma omp single nowait { - // #pragma omp task firstprivate(pg) + for(auto pg: tree.vector_of_leaf_groups()) { - // reset the output in the block - pg->storage().reset_outputs(); +#pragma omp task firstprivate(pg) + { + // reset the inputs in the block + pg->storage().reset_variables(); + } } } - // #pragma omp taskwait +#pragma omp taskwait } } - /// - /// @brief reset to zero the multipole and the local values in the cells - /// - template<typename Tree> - inline void reset_far_field(Tree& tree) + + /** + * @brief Resets to zero the multipole and the local values in the cells (task-based version). + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_far_field(TreeType& tree) -> void { #pragma omp parallel shared(tree) { @@ -120,14 +234,18 @@ namespace scalfmm::algorithms::omp } } #pragma omp taskwait - } // namespace scalfmm::algorithms::omp + } } - /// - /// @brief reset to zero the multipole values in the cells - /// - template<typename Tree> - inline void reset_multipoles(Tree& tree) + /** + * @brief Resets to zero the multipole values in the cells (task-based version). + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_multipoles(TreeType& tree) -> void { #pragma omp parallel shared(tree) { @@ -145,7 +263,6 @@ namespace scalfmm::algorithms::omp auto const& ptr_group = *it; #pragma omp task firstprivate(ptr_group) { - // auto const& current_group_symbolics = ptr_group->csymbolics(); component::for_each(std::begin(*ptr_group), std::end(*ptr_group), [](auto& cell) { cell.reset_multipoles(); }); } @@ -156,11 +273,16 @@ namespace scalfmm::algorithms::omp #pragma omp taskwait } } - /// - /// @brief reset to zero the local values in the cells - /// - template<typename Tree> - inline void reset_locals(Tree& tree) + + /** + * @brief Resets to zero the local values in the cells (task-based version). + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_locals(TreeType& tree) -> void { #pragma omp parallel shared(tree) { @@ -178,7 +300,6 @@ namespace scalfmm::algorithms::omp auto const& ptr_group = *it; #pragma omp task firstprivate(ptr_group) { - // auto const& current_group_symbolics = ptr_group->csymbolics(); component::for_each(std::begin(*ptr_group), std::end(*ptr_group), [](auto& cell) { cell.reset_locals(); }); } diff --git a/include/scalfmm/algorithms/sequential/cell_to_leaf.hpp b/include/scalfmm/algorithms/sequential/cell_to_leaf.hpp index 691b4f57dfe6f9c7090dffed92dd004b411833ea..518b493d240997644389209f2a57192289136ff5 100644 --- a/include/scalfmm/algorithms/sequential/cell_to_leaf.hpp +++ b/include/scalfmm/algorithms/sequential/cell_to_leaf.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/sequential/cell_to_leaf.hpp +// File : scalfmm/algorithms/sequential/cell_to_leaf.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_SEQUENTIAL_CELL_TO_LEAF_HPP #define SCALFMM_ALGORITHMS_SEQUENTIAL_CELL_TO_LEAF_HPP @@ -11,20 +11,22 @@ namespace scalfmm::algorithms::sequential::pass { - /// @brief This function applies the l2p operator from the lower cell level to the leaf level. - /// - /// It applies the far field on the particles. - /// We pass here the entire FmmOperator, this allows - /// us to catch combination of matrix kernels and trigger - /// optimizations. - /// - /// @tparam Tree the tree type - /// @tparam FmmOperator the FmmOperator type - /// @param tree reference to the tree - /// @param FmmOperator const reference to the fmm_operator - /// - template<typename Tree, typename FmmOperator> - inline auto cell_to_leaf(Tree& tree, FmmOperator const& fmm_operator) -> void + /** + * @brief This function applies the l2p operator from the lower cell level to the leaf level. + * + * It applies the far field on the particles. + * We pass here the entire FmmOperator, this allows + * us to catch combination of matrix kernels and trigger + * optimizations. + * + * @tparam TreeType the tree type + * @tparam FarFieldType + * + * @param tree reference to the tree + * @param far_field const reference to the far-field operator of the fmm_operator + */ + template<typename TreeType, typename FarFieldType> + inline auto cell_to_leaf(TreeType& tree, FarFieldType const& far_field) -> void { using operators::l2p; auto begin = std::begin(tree); @@ -36,14 +38,12 @@ namespace scalfmm::algorithms::sequential::pass auto group_of_cell_begin = std::begin(cells_at_leaf_level); auto group_of_cell_end = std::end(cells_at_leaf_level); - auto const& far_field = fmm_operator.far_field(); - assertm((*group_of_leaf_begin)->size() == (*group_of_cell_begin)->size(), "cell_to_leaf : nb group of leaves and nb first level of group cells differs !"); while(group_of_leaf_begin != group_of_leaf_end && group_of_cell_begin != group_of_cell_end) { - { // Can be a task(in:iterCells, inout:iterParticles) + { for(std::size_t leaf_index = 0; leaf_index < (*group_of_leaf_begin)->size(); ++leaf_index) { auto const& source_cell = (*group_of_cell_begin)->ccomponent(leaf_index); diff --git a/include/scalfmm/algorithms/sequential/direct.hpp b/include/scalfmm/algorithms/sequential/direct.hpp index dfe006139767bace2d496b7bb5ca41bde74c35d7..b000ce4438a859272ffec5036c2a73d1dfa029a1 100644 --- a/include/scalfmm/algorithms/sequential/direct.hpp +++ b/include/scalfmm/algorithms/sequential/direct.hpp @@ -1,19 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/sequential/direct.hpp +// File : scalfmm/algorithms/sequential/direct.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_SEQUENTIAL_DIRECT_HPP #define SCALFMM_ALGORITHMS_SEQUENTIAL_DIRECT_HPP -#include <algorithm> -#include <array> -#include <cstddef> -#include <iterator> -#include <tuple> -#include <type_traits> -#include <utility> -#include <vector> - #include "scalfmm/algorithms/common.hpp" #include "scalfmm/operators/l2p.hpp" #include "scalfmm/operators/mutual_apply.hpp" @@ -24,59 +15,68 @@ #include "scalfmm/utils/massert.hpp" #include "scalfmm/utils/math.hpp" +#include <algorithm> +#include <array> +#include <cstddef> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + namespace scalfmm::algorithms::sequential::pass { - /// @brief Applies the near-field on the leaves of the tree. - /// The pass will update the leaves' interaction lists, retrieve the - /// neighbors for each leaf and apply the p2p operator on the list. - /// - /// @tparam TreeSource source tree type - /// @tparam TreeTarget target tree type - /// @tparam NearField near_field type - /// @param tree_source a reference to the tree containing the sources - /// @param tree_target a reference to the tree containing the target - /// @param nearfield a const reference of the near-field - /// - /// @return void - - template<typename TreeSource, typename TreeTarget, typename NearField> - inline auto direct(TreeSource& tree_source, TreeTarget& tree_target, NearField const& nearfield) -> void + /** + * @brief Applies the near-field on the leaves of the tree. + * The pass will update the leaves' interaction lists, retrieve the + * neighbors for each leaf and apply the p2p operator on the list. + * + * @tparam SourceTreeType source tree type + * @tparam TargetTreeType target tree type + * @tparam NearFieldType near_field type + * + * @param source_tree a reference to the tree containing the sources + * @param target_tree a reference to the tree containing the target + * @param near_field a const reference of the near-field + */ + template<typename SourceTreeType, typename TargetTreeType, typename NearFieldType> + inline auto direct(SourceTreeType& source_tree, TargetTreeType& target_tree, + NearFieldType const& near_field) -> void { bool source_target{false}; - if constexpr(std::is_same_v<TreeSource, TreeTarget>) + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { - source_target = (&tree_source == &tree_target); + source_target = (&source_tree == &target_tree); } { using operators::p2p_full_mutual; using operators::p2p_inner; using operators::p2p_outer; - // static constexpr int limit = (Tree::dimension - 1) * 6 + (Tree::dimension - 1); - const auto separation_criterion = nearfield.separation_criterion(); - // get parameter to check if we are using mutual interactions - const auto mutual = nearfield.mutual(); - const auto period = tree_source.box().get_periodicity(); - const auto box_width = tree_source.box_width(); + const auto separation_criterion = near_field.separation_criterion(); + const auto mutual = near_field.mutual(); // get parameter to check if we are using mutual interactions + const auto period = source_tree.box().get_periodicity(); + const auto box_width = source_tree.box_width(); // move test to execute // p2p stage - const auto level_leaves = tree_target.height() - 1; - // iterators on target leaves - auto begin = std::begin(tree_target); - auto end = std::end(tree_target); + const auto level_leaves = target_tree.height() - 1; + auto begin = std::begin(target_tree); // iterators on target leaves (begin) + auto end = std::end(target_tree); // iterators on target leaves (end) std::size_t number_of_groups_processed{0}; - const auto& matrix_kernel = nearfield.matrix_kernel(); + const auto& matrix_kernel = near_field.matrix_kernel(); + + auto group_of_source_leaf_begin = std::get<0>(std::begin(source_tree)); + auto group_of_source_leaf_end = std::get<0>(std::end(source_tree)); - auto group_of_source_leaf_begin = std::get<0>(std::begin(tree_source)); - auto group_of_source_leaf_end = std::get<0>(std::end(tree_source)); // loop on the groups auto l_group_p2p = [&matrix_kernel, begin, &level_leaves, group_of_source_leaf_begin, group_of_source_leaf_end, &period, &box_width, &number_of_groups_processed, separation_criterion, source_target, mutual](auto& group) { std::size_t index_in_group{0}; + // loop on the leaves of the current group auto l_leaf_p2p = [&matrix_kernel, &group, &index_in_group, &level_leaves, group_of_source_leaf_begin, group_of_source_leaf_end, &period, &box_width, separation_criterion, source_target, @@ -85,61 +85,16 @@ namespace scalfmm::algorithms::sequential::pass // Get interation infos auto const& leaf_symbolics = leaf.csymbolics(); auto const& interaction_iterators = leaf_symbolics.interaction_iterators; - // + // No test on separation_criterion -#ifdef OLD_DIRECT - if(leaf_symbolics.number_of_neighbors > 0) - { - if constexpr(std::is_same_v<TreeSource, TreeTarget>) - { - // Here we need the same type. - if(source_target) // sources == targets - { - // if(separation_criterion == 1) - { - if(mutual) - { - // Optimization for mutual interactions - // The p2p interaction list contains only indexes bellow mine. - // Mutual with neighbors + inner of current component - p2p_full_mutual(matrix_kernel, leaf, interaction_iterators, - leaf_symbolics.existing_neighbors_in_group, period, box_width); - } - else // non mutual - { - p2p_outer(matrix_kernel, leaf, interaction_iterators, period, box_width); - // Wrong call p2p_inner - the function uses mutual approach. - } - p2p_inner(matrix_kernel, leaf, mutual); - } - } - else // sources != targets - { - p2p_outer(matrix_kernel, leaf, interaction_iterators, period, box_width); - } - } - else - { - p2p_outer(matrix_kernel, leaf, interaction_iterators, period, box_width); - } - } - else // no neighbors, only interaction with the cell + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { if(source_target) // sources == targets { p2p_inner(matrix_kernel, leaf, mutual); - } - } -#error("OLD DIRECT") -#else - if constexpr(std::is_same_v<TreeSource, TreeTarget>) - { - if(source_target) // sources == targets - { - p2p_inner(matrix_kernel, leaf,mutual); - if(leaf_symbolics.number_of_neighbors > 0) + if(leaf_symbolics.number_of_neighbors > 0) { - if(mutual) + if(mutual) { // Optimization for mutual interactions // The p2p interaction list contains only indexes bellow mine. @@ -147,30 +102,30 @@ namespace scalfmm::algorithms::sequential::pass p2p_full_mutual(matrix_kernel, leaf, interaction_iterators, leaf_symbolics.existing_neighbors_in_group, period, box_width); } - else // non mutual + else // non mutual { - p2p_outer(matrix_kernel, leaf, interaction_iterators, period, box_width); - // + p2p_outer(matrix_kernel, leaf, interaction_iterators, period, box_width); } } } - else // source != tree + else // source != tree { - p2p_outer(matrix_kernel, leaf, interaction_iterators, period, box_width); + p2p_outer(matrix_kernel, leaf, interaction_iterators, period, box_width); } } else - { + { p2p_outer(matrix_kernel, leaf, interaction_iterators, period, box_width); - } -#endif + } }; + // Evaluate inside the group component::for_each(std::begin(*group), std::end(*group), l_leaf_p2p); + // if the sources and the target are identical and we are using the mutual // algorithm then we have to proceed teh interactions with leaves inside groups // bellow mine - if constexpr(std::is_same_v<TreeSource, TreeTarget>) + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { if(mutual && source_target) { @@ -178,7 +133,7 @@ namespace scalfmm::algorithms::sequential::pass } } }; - // + // Evaluate P2P for all group group component::for_each(std::get<0>(begin), std::get<0>(end), l_group_p2p); } diff --git a/include/scalfmm/algorithms/sequential/downward.hpp b/include/scalfmm/algorithms/sequential/downward.hpp index 4869dc18f8671077be161d4cfe24f687b7edc63e..add05576709c393efa7e55db6faa92d7558bba0a 100644 --- a/include/scalfmm/algorithms/sequential/downward.hpp +++ b/include/scalfmm/algorithms/sequential/downward.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/sequential/downward.hpp +// File : scalfmm/algorithms/sequential/downward.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_SEQUENTIAL_DOWNWARD_HPP #define SCALFMM_ALGORITHMS_SEQUENTIAL_DOWNWARD_HPP @@ -17,26 +17,34 @@ namespace scalfmm::algorithms::sequential::pass { - - /// @brief This function constructs the local approximation for all the cells of the tree by applying the operator - /// l2l - /// - /// @param tree the tree target - /// @param approximation the approximation to construct the local approximation - /// - template<typename Tree, typename Interpolator> - inline auto downward(Tree& tree, Interpolator const& interpolator) -> void + /** + * @brief This function constructs the local approximation for all the cells of the tree by + * applying the operator l2l. + * + * @tparam TreeType + * @tparam InterpolatorType + * + * @param tree the tree target + * @param interpolator + */ + template<typename TreeType, typename InterpolatorType> + inline auto downward(TreeType& tree, InterpolatorType const& interpolator) -> void { using scalfmm::operators::l2l; - using value_type = typename Interpolator::value_type; + + using interpolator_type = InterpolatorType; + using value_type = typename interpolator_type::value_type; + auto begin = std::begin(tree); auto tree_height = tree.height(); // upper working level is const auto top_height = tree.box().is_periodic() ? 0 : 2; const auto leaf_level = tree_height - 1; + // First cell level + 1 auto cell_level_it = std::get<1>(begin) + top_height; + for(std::size_t level = top_height; level < leaf_level; ++level) { auto group_of_cell_begin = std::begin(*cell_level_it); @@ -51,7 +59,7 @@ namespace scalfmm::algorithms::sequential::pass // while(group_of_cell_begin != group_of_cell_end && group_of_child_cell_begin != group_of_child_cell_end) { - { // Can be a task(in:iterParticles, out:iterChildCells ...) + { auto cell_begin = std::begin(**group_of_cell_begin); for(std::size_t cell_index = 0; @@ -89,7 +97,7 @@ namespace scalfmm::algorithms::sequential::pass ++group_of_child_cell_begin; } } - + std::for_each(std::begin(indexes_of_childs), std::end(indexes_of_childs), [&parent_cell, &interpolator, level_interpolator_index](auto indexes) { l2l(interpolator, parent_cell, std::get<0>(indexes), *std::get<1>(indexes), diff --git a/include/scalfmm/algorithms/sequential/leaf_to_cell.hpp b/include/scalfmm/algorithms/sequential/leaf_to_cell.hpp index 2b36b695015854714f23589f69d721dc9bcbaa2d..a20b3707647c6314613ee7a0d2164f7b8c30943b 100644 --- a/include/scalfmm/algorithms/sequential/leaf_to_cell.hpp +++ b/include/scalfmm/algorithms/sequential/leaf_to_cell.hpp @@ -1,34 +1,37 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/sequential/leaf_to_cell.hpp +// File : scalfmm/algorithms/sequential/leaf_to_cell.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_SEQUENTIAL_LEAF_TO_CELL_HPP #define SCALFMM_ALGORITHMS_SEQUENTIAL_LEAF_TO_CELL_HPP +#include "scalfmm/operators/p2m.hpp" +#include "scalfmm/utils/massert.hpp" + #include <iterator> #include <tuple> #include <utility> -#include "scalfmm/operators/p2m.hpp" -#include "scalfmm/utils/massert.hpp" - namespace scalfmm::algorithms::sequential::pass { - /// @brief This function applies the p2m operator from the leaf level of the tree - /// - /// This function applies the p2m operator from the leaf level to - /// the first cell level. The inputs of the particles contained in the leaves - /// will be approximated by the technique used in the far_field - /// - /// @tparam Tree the tree type - /// @tparam FarField the far-field type - /// @param tree reference to the tree source - /// @param far_field reference to the far field. - /// - template<typename Tree, typename FarField> - inline auto leaf_to_cell(Tree& tree, FarField const& far_field) -> void + /** + * @brief This function applies the p2m operator from the leaf level of the tree. + * + * This function applies the p2m operator from the leaf level to + * the first cell level. The inputs of the particles contained in the leaves + * will be approximated by the technique used in the far_field + * + * @tparam TreeType the tree type + * @tparam FarFieldType the far-field type + * + * @param tree reference to the tree source + * @param far_field reference to the far field. + */ + template<typename TreeType, typename FarFieldType> + inline auto leaf_to_cell(TreeType& tree, FarFieldType const& far_field) -> void { using operators::p2m; + auto begin = std::begin(tree); auto end = std::end(tree); auto leaf_level = tree.height() - 1; diff --git a/include/scalfmm/algorithms/sequential/periodic.hpp b/include/scalfmm/algorithms/sequential/periodic.hpp index 4ba80995faaf964117474a288413a7b388e79ce2..5b47932600dac5ab348e3b476d5867d0abcc7899 100644 --- a/include/scalfmm/algorithms/sequential/periodic.hpp +++ b/include/scalfmm/algorithms/sequential/periodic.hpp @@ -11,13 +11,11 @@ */ // -------------------------------- // See LICENCE file at project root +// File : scalfmm/algorithms/sequential/periodic.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_SEQUENTIAL_PERIODIC_HPP #define SCALFMM_ALGORITHMS_SEQUENTIAL_PERIODIC_HPP -#include <iostream> -#include <numeric> - #include <cpp_tools/colors/colorized.hpp> #include "scalfmm/operators/l2l.hpp" @@ -26,75 +24,97 @@ #include <scalfmm/meta/utils.hpp> #include <scalfmm/tree/utils.hpp> +#include <iostream> +#include <numeric> + namespace scalfmm::algorithms::sequential::pass { - template<typename Tree> - auto preprocessing_execute(Tree& tree) -> void + + /** + * @brief + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto preprocessing_execute(TreeType& tree) -> void { auto offset_real_tree = 2 + tree.levels_above_root(); if(offset_real_tree > 2) { for(std::size_t level{0}; level < tree.height(); ++level) { - for_each_cell(std::begin(tree), std::end(tree), level, - [&offset_real_tree](auto& cell) + for_each_cell(std::begin(tree), std::end(tree), level, [&offset_real_tree](auto& cell) { cell.symbolics().level = cell.symbolics().level + offset_real_tree; }); } } } - template<typename Tree> - auto postprocessing_execute(Tree& tree) -> void + + /** + * @brief + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto postprocessing_execute(TreeType& tree) -> void { auto offset_real_tree = 2 + tree.levels_above_root(); if(offset_real_tree > 2) { for(std::size_t level{0}; level < tree.height(); ++level) { - for_each_cell(std::begin(tree), std::end(tree), level, - [&offset_real_tree](auto& cell) + for_each_cell(std::begin(tree), std::end(tree), level, [&offset_real_tree](auto& cell) { cell.symbolics().level = cell.symbolics().level - offset_real_tree; }); } } } + /** - * @brief + * @brief Performs the full summation in the periodic case. + * * The kernel is absolutely convergent, so we perform the FMM with negative level in order to * increase the domain taken into account in the far field. * + * @tparam TreeType + * @tparam InterpolatorType + * * @param tree - * @param farfield + * @param interpolator */ - template<typename Tree, typename Interpolator> - inline auto full_periodic_classical_summation(Tree& tree, Interpolator const& interpolator) -> void + template<typename TreeType, typename InterpolatorType> + inline auto full_periodic_classical_summation(TreeType& tree, InterpolatorType const& interpolator) -> void { // // if kernel is absolutely convergent // Perform MM2 / M2L / L2L on negative levels // need height on negative level (algo in scalfmm2) - using cell_type = typename Tree::cell_type; - using value_type = typename Tree::position_value_type; - using interpolator_type = Interpolator; - static constexpr std::size_t dimension = Tree::dimension; + using tree_type = TreeType; + using interpolator_type = InterpolatorType; + using cell_type = typename tree_type::cell_type; + using value_type = typename tree_type::position_value_type; + static constexpr std::size_t dimension = tree_type::dimension; + auto levels_above_root = tree.levels_above_root(); + if(levels_above_root < 0) { return; } + // all cells at a same level have the same multipole - // const auto order = tree.order(); const auto center = tree.box().center(); const auto nb_upper_cells = levels_above_root + 2; const auto width_ext = tree.box().extended_width(levels_above_root); - // - // - // M2M pass - // + + // number of child (for the M2M pass) constexpr int nb_child{1 << dimension}; - // - int root_level = 0; // level 0 - // check + + int root_level = 0; auto group_level_it = std::get<1>(std::begin(tree)) + root_level; // The number of group @@ -102,13 +122,13 @@ namespace scalfmm::algorithms::sequential::pass auto group_of_cell_begin = std::cbegin(*(group_level_it)); // The unique cell at level 0 auto cell_begin = std::begin(**group_of_cell_begin); - auto nb_cells = (**group_of_cell_begin).size(); // should be one ! + auto nb_cells = (**group_of_cell_begin).size(); if(nb_cells != 1) { std::cerr << " Bad news number of cells at root level should be 1 and not " << nb_cells << " !!!!\n "; std::exit(-1); } - // + auto& root_cell = *cell_begin; // std::vector<cell_type> upper_cells; @@ -242,14 +262,19 @@ namespace scalfmm::algorithms::sequential::pass } /** - * @brief + * @brief Builds the field at level 0. * - * @param tree + * @tparam SourceTreeType + * @tparam TargetTreeType + * @tparam InterpolatorType + * + * @param source_tree + * @param target_tree * @param approximation */ - template<typename TreeSource, typename TreeTarget, typename Interpolator> - inline auto build_field_level0(TreeSource& tree_source, TreeTarget& tree_target, Interpolator const& approximation) - -> void + template<typename SourceTreeType, typename TargetTreeType, typename InterpolatorType> + inline auto build_field_level0(SourceTreeType& source_tree, TargetTreeType& target_tree, + InterpolatorType const& approximation) -> void { // // if kernel is absolutely convergent @@ -258,17 +283,17 @@ namespace scalfmm::algorithms::sequential::pass // if kernel is conditionally convergent // new algorithm to develop bool same_tree{false}; - if constexpr(std::is_same_v<TreeSource, TreeTarget>) + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { - same_tree = (&tree_source == &tree_target); + same_tree = (&source_tree == &target_tree); } if(!same_tree) { throw std::runtime_error("In periodic algorithm works only when source==target."); } - if(tree_source.levels_above_root() > 0) + if(source_tree.levels_above_root() > 0) { - full_periodic_classical_summation(tree_source, approximation); + full_periodic_classical_summation(source_tree, approximation); } } } // namespace scalfmm::algorithms::sequential::pass diff --git a/include/scalfmm/algorithms/sequential/sequential.hpp b/include/scalfmm/algorithms/sequential/sequential.hpp index 393a2b4b0ebebf510c2359f7b73d9448a5d8355e..cb4b53651e3e9f9ad9ef8ccbffa11fcb5ccc6c1e 100644 --- a/include/scalfmm/algorithms/sequential/sequential.hpp +++ b/include/scalfmm/algorithms/sequential/sequential.hpp @@ -4,12 +4,6 @@ // -------------------------------- #ifndef SCALFMM_ALGORITHMS_SEQUENTIAL_SEQUENTIAL_HPP #define SCALFMM_ALGORITHMS_SEQUENTIAL_SEQUENTIAL_HPP -#include <chrono> -#include <iostream> -#include <string> -#include <unordered_map> - -#include <cpp_tools/timers/simple_timer.hpp> #include "scalfmm/algorithms/common.hpp" #include "scalfmm/algorithms/sequential/cell_to_leaf.hpp" @@ -21,161 +15,179 @@ #include "scalfmm/algorithms/sequential/upward.hpp" #include "scalfmm/lists/sequential.hpp" #include "scalfmm/tools/bench.hpp" +#include "scalfmm/utils/massert.hpp" + +#include <cpp_tools/timers/simple_timer.hpp> + +#include <chrono> +#include <iostream> +#include <string> +#include <unordered_map> namespace scalfmm::algorithms::sequential { namespace impl { - /** \brief Sequential algorithm + /** + * @brief Sequential algorithm. * * This function launches all the passes of the algorithm. * - * \tparam TreeIterator an iterator on the beginning of the tree - * \tparam Interpolator an iterator on the beginning of the tree - * \param begin - * \param end - * \param op an int - * \return void - */ - - /** \brief Sequential algorithm - * - * This function launches all the passes of the algorithm. + * @tparam SourceTreeType + * @tparam TargetTreeType + * @tparam NearFieldType + * @tparam FarFieldType + * @tparam S * - * \tparam Tree the template og the tree - * \tparam FmmOperators a fmm operator containing near-field and far-field operators - * \param tree - * \param fmmoperators - * \param op an int - * \return void + * @param s + * @param source_tree + * @param target_tree + * @param fmmoperators + * @param op_in */ - template<typename TreeSource, typename TreeTarget, typename NearField, typename FarField, typename... S> - inline auto sequential(options::settings<S...> s, TreeSource& tree_source, TreeTarget& tree_target, - operators::fmm_operators<NearField, FarField> const& fmmoperators, + template<typename SourceTreeType, typename TargetTreeType, typename NearFieldType, typename FarFieldType, + typename... S> + inline auto sequential(options::settings<S...> s, SourceTreeType& source_tree, TargetTreeType& target_tree, + operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, unsigned int op_in = operators_to_proceed::all) -> void { static_assert(options::support(s, options::_s(options::timit)), "sequential algo unsupported options!"); + bool same_tree{false}; - if constexpr(std::is_same_v<TreeSource, TreeTarget>) + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { - same_tree = (&tree_source == &tree_target); + same_tree = (&source_tree == &target_tree); } + // timer for the different passes using timer_type = cpp_tools::timers::timer<std::chrono::nanoseconds>; - std::unordered_map<std::string, timer_type> timers = {{"p2m", timer_type()}, - {"m2m", timer_type()}, - {"m2l", timer_type()}, - {"l2l", timer_type()}, - {"l2p", timer_type()}, - {"p2p", timer_type()}, - {"m2l-list", timer_type()}, - {"p2p-list", timer_type()}, - {"field0", timer_type()}}; - auto const& approximation = fmmoperators.far_field().approximation(); - auto const& neighbour_separation = fmmoperators.near_field().separation_criterion(); - auto const& mutual = fmmoperators.near_field().mutual(); - - if(tree_target.is_interaction_m2l_lists_built() == false) + std::unordered_map<std::string, timer_type> timers = { + {"p2m", timer_type()}, {"m2m", timer_type()}, {"m2l", timer_type()}, + {"l2l", timer_type()}, {"l2p", timer_type()}, {"p2p", timer_type()}, + {"m2l-list", timer_type()}, {"p2p-list", timer_type()}, {"field0", timer_type()}}; + + auto const& near_field = fmmoperators.near_field(); + auto const& far_field = fmmoperators.far_field(); + auto const& approximation = far_field.approximation(); + auto const& near_field_separation_criterion = near_field.separation_criterion(); + auto const& far_field_separation_criterion = far_field.separation_criterion(); + auto const& mutual = near_field.mutual(); + + assertm(near_field_separation_criterion == far_field_separation_criterion, + "Far field kernel and near field kernel must have the same separation criterion."); + + // Build M2L interaction list (if needed) + if(target_tree.is_interaction_m2l_lists_built() == false) { if constexpr(options::has(s, options::timit)) { timers["m2l-list"].tic(); } - scalfmm::list::sequential::build_m2l_interaction_list(tree_source, tree_target, neighbour_separation); + scalfmm::list::sequential::build_m2l_interaction_list(source_tree, target_tree, + far_field_separation_criterion); if constexpr(options::has(s, options::timit)) { timers["m2l-list"].tac(); } } - const auto op = tree_target.height() == 2 ? operators_to_proceed::p2p : op_in; + const auto op = target_tree.height() == 2 ? operators_to_proceed::p2p : op_in; + + // P2M pass if((op & operators_to_proceed::p2m) == operators_to_proceed::p2m) { if constexpr(options::has(s, options::timit)) { timers["p2m"].tic(); } - pass::leaf_to_cell(tree_source, fmmoperators.far_field()); + pass::leaf_to_cell(source_tree, far_field); if constexpr(options::has(s, options::timit)) { timers["p2m"].tac(); } } + // M2M pass if((op & operators_to_proceed::m2m) == operators_to_proceed::m2m) { if constexpr(options::has(s, options::timit)) { timers["m2m"].tic(); } - pass::upward(tree_source, approximation); + pass::upward(source_tree, approximation); if constexpr(options::has(s, options::timit)) { timers["m2m"].tac(); } } - if(same_tree && tree_source.box().is_periodic()) + // Upper levels (for the periodic case) + if(same_tree && source_tree.box().is_periodic()) { if constexpr(options::has(s, options::timit)) { timers["field0"].tic(); } - pass::build_field_level0(tree_source, tree_target, approximation); + pass::build_field_level0(source_tree, target_tree, approximation); if constexpr(options::has(s, options::timit)) { timers["field0"].tac(); } } + + // M2L pass if((op & operators_to_proceed::m2l) == operators_to_proceed::m2l) { if constexpr(options::has(s, options::timit)) { timers["m2l"].tic(); } - pass::transfer(tree_source, tree_target, fmmoperators.far_field()); + pass::transfer(source_tree, target_tree, far_field); if constexpr(options::has(s, options::timit)) { timers["m2l"].tac(); } } + // L2L pass if((op & operators_to_proceed::l2l) == operators_to_proceed::l2l) { if constexpr(options::has(s, options::timit)) { timers["l2l"].tic(); } - pass::downward(tree_target, approximation); + pass::downward(target_tree, approximation); if constexpr(options::has(s, options::timit)) { timers["l2l"].tac(); } } + // L2P pass if((op & operators_to_proceed::l2p) == operators_to_proceed::l2p) { if constexpr(options::has(s, options::timit)) { timers["l2p"].tic(); } - pass::cell_to_leaf(tree_target, fmmoperators); + pass::cell_to_leaf(target_tree, far_field); if constexpr(options::has(s, options::timit)) { timers["l2p"].tac(); } } + // P2P pass if((op & operators_to_proceed::p2p) == operators_to_proceed::p2p) { - if(tree_target.is_interaction_p2p_lists_built() == false) + if(target_tree.is_interaction_p2p_lists_built() == false) { if constexpr(options::has(s, options::timit)) { timers["p2p-list"].tic(); } - scalfmm::list::sequential::build_p2p_interaction_list(tree_source, tree_target, - neighbour_separation, mutual); + scalfmm::list::sequential::build_p2p_interaction_list(source_tree, target_tree, + near_field_separation_criterion, mutual); if constexpr(options::has(s, options::timit)) { timers["p2p-list"].tac(); @@ -186,13 +198,14 @@ namespace scalfmm::algorithms::sequential { timers["p2p"].tic(); } - pass::direct(tree_source, tree_target, fmmoperators.near_field()); + pass::direct(source_tree, target_tree, near_field); if constexpr(options::has(s, options::timit)) { timers["p2p"].tac(); } } + // print time of each pass if constexpr(options::has(s, options::timit)) { auto [fartime, neartime, overall, ratio] = bench::print(timers); @@ -207,22 +220,69 @@ namespace scalfmm::algorithms::sequential } } - template<typename TreeS, typename TreeT, typename NearField, typename FarField> - inline auto sequential(TreeS& tree_source, TreeT& tree_target, - operators::fmm_operators<NearField, FarField> const& fmmoperators, + /** + * @brief Wrapper to run the sequential algorithm both with a source tree and a target tree + * but without settings. + * + * @tparam SourceTreeType + * @tparam TargetTreeType + * @tparam NearFieldType + * @tparam FarFieldType + * + * @param source_tree + * @param target_tree + * @param fmmoperators + * @param op + * + * @return the sequential algorithm. + */ + template<typename SourceTreeType, typename TargetTreeType, typename NearFieldType, typename FarFieldType> + inline auto sequential(SourceTreeType& source_tree, TargetTreeType& target_tree, + operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, unsigned int op = operators_to_proceed::all) -> void { - return sequential(options::settings<>{}, tree_source, tree_target, fmmoperators, op); + return sequential(options::settings<>{}, source_tree, target_tree, fmmoperators, op); } - template<typename Tree, typename NearField, typename FarField> - inline auto sequential(Tree& tree, operators::fmm_operators<NearField, FarField> const& fmmoperators, + + /** + * @brief Wrapper to run the sequential algorithm with a single tree and without settings. + * + * @tparam TreeType + * @tparam NearFieldType + * @tparam FarFieldType + * + * @param tree + * @param fmmoperators + * @param op + * + * @return the sequential algorithm. + */ + template<typename TreeType, typename NearFieldType, typename FarFieldType> + inline auto sequential(TreeType& tree, + operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, unsigned int op = operators_to_proceed::all) -> void { return sequential(options::settings<>{}, tree, tree, fmmoperators, op); } - template<typename... S, typename Tree, typename NearField, typename FarField> - inline auto sequential(options::settings<S...> s, Tree& tree, - operators::fmm_operators<NearField, FarField> const& fmmoperators, + + /** + * @brief Wrapper to run the sequential algorithm with a single tree and with settings. + * + * @tparam S + * @tparam TreeType + * @tparam NearFieldType + * @tparam FarFieldType + * + * @param s + * @param tree + * @param fmmoperators + * @param op + * + * @return the sequential algorithm. + */ + template<typename... S, typename TreeType, typename NearFieldType, typename FarFieldType> + inline auto sequential(options::settings<S...> s, TreeType& tree, + operators::fmm_operators<NearFieldType, FarFieldType> const& fmmoperators, unsigned int op = operators_to_proceed::all) -> void { return sequential(s, tree, tree, fmmoperators, op); diff --git a/include/scalfmm/algorithms/sequential/transfer.hpp b/include/scalfmm/algorithms/sequential/transfer.hpp index 63252cb1e4e924419e51377ab21fd24192e6dbd9..fc101148be13ab1d6de292fae36f2dc7f62ce1ab 100644 --- a/include/scalfmm/algorithms/sequential/transfer.hpp +++ b/include/scalfmm/algorithms/sequential/transfer.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/sequential/transfert.hpp +// File : scalfmm/algorithms/sequential/transfer.hpp // -------------------------------- #ifndef SCALFMM_ALGORITHMS_SEQUENTIAL_TRANSFER_HPP #define SCALFMM_ALGORITHMS_SEQUENTIAL_TRANSFER_HPP @@ -28,36 +28,32 @@ namespace scalfmm::algorithms::sequential::pass { - /// @brief This function transfer the multipoles to the local - /// expansions by applying the matrix kernel. This is - /// performed by the m2l operator. - /// - /// @tparam Tree tree type - /// @tparam Approximation interpolation type - /// @param tree reference on the tree - /// @param approximation const reference on the approximation - /// - // template<typename Tree, typename FarField> - // inline auto transfer(Tree& tree, FarField const& far_field) -> void - template<typename TreeSource, typename TreeTarget, typename FarField> - inline auto transfer(TreeSource& tree_source, TreeTarget& tree_target, FarField const& far_field) -> void + /** + * @brief This function transfer the multipoles to the local expansions by applying the matrix kernel. + * This is performed by the m2l operator. + * + * @tparam SourceTreeType + * @tparam TargetTreeType + * @tparam FarFieldType + * + * @param source_tree + * @param target_tree + * @param far_field + */ + template<typename SourceTreeType, typename TargetTreeType, typename FarFieldType> + inline auto transfer(SourceTreeType& source_tree, TargetTreeType& target_tree, + FarFieldType const& far_field) -> void { - // bool source_eq_target{true}; - // if(&tree_source != &tree_target) - // { - // source_eq_target = false; - // } bool source_eq_target{false}; - if constexpr(std::is_same_v<TreeSource, TreeTarget>) + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { - source_eq_target = (&tree_source == &tree_target); + source_eq_target = (&source_tree == &target_tree); } - // auto& tree = tree_source; + using operators::m2l_loop; // Useful to overload m2l(...) function - /// - auto tree_height = tree_target.height(); - // Should be setted by the kernel ! + auto tree_height = target_tree.height(); + // Should be set by the kernel ! auto const& approximation = far_field.approximation(); // Buffer for m2l optimization : we calculate the inverse fft on the fly and free memory. @@ -68,18 +64,13 @@ namespace scalfmm::algorithms::sequential::pass ///////////////////////////////////////////////////////////////////////////////////////// /// Loop on the level from the level 1/2 to the leaf level (Top to Bottom) // - const auto top_height = tree_target.box().is_periodic() ? 1 : 2; - // - // auto begin_target = std::begin(tree_target); - // auto end_target = std::end(tree_target); - // // auto cell_level_it = std::get<1>(begin_target) + top_height; // - // auto cell_target_level_it = std::get<1>(begin_target) + top_height; - // auto cell_source_level_it = std::get<1>(std::begin(tree_source)) + top_height; + const auto top_height = target_tree.box().is_periodic() ? 1 : 2; + // Iterate on the target tree for(std::size_t level = top_height; level < tree_height; ++level) { - auto begin_cell_target_level_it = tree_target.begin_cells(level); - auto end_cell_target_level_it = tree_target.end_cells(level); + auto begin_cell_target_level_it = target_tree.begin_cells(level); + auto end_cell_target_level_it = target_tree.end_cells(level); ///////////////////////////////////////////////////////////////////////////////////////// /// loop on the target groups at level level @@ -87,7 +78,7 @@ namespace scalfmm::algorithms::sequential::pass cell_target_level_it != end_cell_target_level_it; ++cell_target_level_it) { auto group_target = cell_target_level_it->get(); - // + ///////////////////////////////////////////////////////////////////////////////////////// /// loop on the target leaves of the current group for(std::size_t index_in_group{0}; index_in_group < group_target->size() /*std::size(*group_target)*/; diff --git a/include/scalfmm/algorithms/sequential/upward.hpp b/include/scalfmm/algorithms/sequential/upward.hpp index 811ea91ce5de5c7ce7eaf08e8be3cf56e5922a84..1f951954dc650c86618e2d4c7fd7b8c28726fbbd 100644 --- a/include/scalfmm/algorithms/sequential/upward.hpp +++ b/include/scalfmm/algorithms/sequential/upward.hpp @@ -17,23 +17,28 @@ namespace scalfmm::algorithms::sequential::pass { - /// @brief This function builds the multipole for all the cells of the tree by applying the operator m2m - /// - /// @param tree the tree - /// @param interpolator the approximation to construct the multipole - /// - /// @return - template<typename Tree, typename Interpolator> - inline auto upward(Tree& tree, Interpolator const& interpolator) -> void + /** + * @brief This function builds the multipole for all the cells of the tree by applying the operator m2m. + * + * @tparam TreeType + * @tparam InterpolatorType + * + * @param tree + * @param interpolator + */ + template<typename TreeType, typename InterpolatorType> + inline auto upward(TreeType& tree, InterpolatorType const& interpolator) -> void { using scalfmm::operators::m2m; - using cell_type = typename Tree::cell_type; - using value_type = typename Interpolator::value_type; - // using cell_iterator_type = typename Tree::const_cell_iterator_type; + using tree_type = TreeType; + using interpolator_type = InterpolatorType; + using cell_type = typename tree_type::cell_type; + using value_type = typename interpolator_type::value_type; + static constexpr auto dimension = cell_type::dimension; static constexpr auto number_of_child = math::pow(2, dimension); - // + auto begin = std::begin(tree); auto leaf_level = tree.height() - 1; @@ -52,66 +57,63 @@ namespace scalfmm::algorithms::sequential::pass auto group_of_cell_end = std::end(*cell_level_it); std::size_t index_child_cell{0}; // Get the index of the corresponding child-parent interpolator - //std::size_t level_interpolator_index = (interpolator.cell_width_extension() == 0.) ? 2 : level; - std::size_t level_interpolator_index = 2; - if(interpolator.cell_width_extension() > std::numeric_limits<value_type>::epsilon()) - level_interpolator_index = level; - // - while(group_of_cell_begin != group_of_cell_end && group_of_child_cell_begin != group_of_child_cell_end) - { - { // Can be a task(in:iterParticles, out:iterChildCells ...) - auto cell_begin = std::begin(**group_of_cell_begin); - for(std::size_t cell_index = 0; - cell_index < (*group_of_cell_begin)->csymbolics().number_of_component_in_group; ++cell_index) - { - auto& parent_cell = *cell_begin; - auto const& parent_symbolics = parent_cell.csymbolics(); - - // using cell_type = std::decay_t<decltype(parent_cell)>; - using cell_iterator_type = - typename std::decay_t<decltype(*group_of_child_cell_begin)>::element_type::const_iterator_type; - // static constexpr auto dimension = cell_type::dimension; - // static constexpr auto number_of_child = math::pow(2, dimension); - std::vector<std::tuple<std::size_t, cell_iterator_type>> indexes_of_childs{}; - - assertm(group_of_child_cell_begin != group_of_child_cell_end, - "Upward pass : reach the end of child's cells."); - - while(group_of_child_cell_begin != group_of_child_cell_end && - (((*group_of_child_cell_begin)->ccomponent(index_child_cell).csymbolics().morton_index >> - dimension) == parent_symbolics.morton_index)) - { - auto const& child_cell_group_symbolics = (*group_of_child_cell_begin)->csymbolics(); - const std::size_t child_index = - (*group_of_child_cell_begin)->ccomponent(index_child_cell).csymbolics().morton_index & - (number_of_child - 1); - - indexes_of_childs.emplace_back(std::make_tuple( - child_index, (*group_of_child_cell_begin)->ccomponent_iterator(index_child_cell))); - - ++index_child_cell; - if(index_child_cell == child_cell_group_symbolics.number_of_component_in_group) - { - index_child_cell = 0; - ++group_of_child_cell_begin; - } - } - - std::for_each(std::cbegin(indexes_of_childs), std::cend(indexes_of_childs), - [&parent_cell, &interpolator, level_interpolator_index](auto indexes) { - m2m(interpolator, *std::get<1>(indexes), std::get<0>(indexes), parent_cell, - level_interpolator_index); - }); - - if(!indexes_of_childs.empty()) - { - interpolator.apply_multipoles_preprocessing(parent_cell); - } - ++cell_begin; - } - } - ++group_of_cell_begin; - } + std::size_t level_interpolator_index = 2; + if(interpolator.cell_width_extension() > std::numeric_limits<value_type>::epsilon()) + level_interpolator_index = level; + // + while(group_of_cell_begin != group_of_cell_end && group_of_child_cell_begin != group_of_child_cell_end) + { + { // Can be a task(in:iterParticles, out:iterChildCells ...) + auto cell_begin = std::begin(**group_of_cell_begin); + for(std::size_t cell_index = 0; + cell_index < (*group_of_cell_begin)->csymbolics().number_of_component_in_group; ++cell_index) + { + auto& parent_cell = *cell_begin; + auto const& parent_symbolics = parent_cell.csymbolics(); + + // using cell_type = std::decay_t<decltype(parent_cell)>; + using cell_iterator_type = typename std::decay_t< + decltype(*group_of_child_cell_begin)>::element_type::const_iterator_type; + std::vector<std::tuple<std::size_t, cell_iterator_type>> indexes_of_childs{}; + + assertm(group_of_child_cell_begin != group_of_child_cell_end, + "Upward pass : reach the end of child's cells."); + + while(group_of_child_cell_begin != group_of_child_cell_end && + (((*group_of_child_cell_begin)->ccomponent(index_child_cell).csymbolics().morton_index >> + dimension) == parent_symbolics.morton_index)) + { + auto const& child_cell_group_symbolics = (*group_of_child_cell_begin)->csymbolics(); + const std::size_t child_index = + (*group_of_child_cell_begin)->ccomponent(index_child_cell).csymbolics().morton_index & + (number_of_child - 1); + + indexes_of_childs.emplace_back(std::make_tuple( + child_index, (*group_of_child_cell_begin)->ccomponent_iterator(index_child_cell))); + + ++index_child_cell; + if(index_child_cell == child_cell_group_symbolics.number_of_component_in_group) + { + index_child_cell = 0; + ++group_of_child_cell_begin; + } + } + + std::for_each(std::cbegin(indexes_of_childs), std::cend(indexes_of_childs), + [&parent_cell, &interpolator, level_interpolator_index](auto indexes) { + m2m(interpolator, *std::get<1>(indexes), std::get<0>(indexes), parent_cell, + level_interpolator_index); + }); + + if(!indexes_of_childs.empty()) + { + interpolator.apply_multipoles_preprocessing(parent_cell); + } + ++cell_begin; + } + } + ++group_of_cell_begin; + } assert(group_of_cell_begin == group_of_cell_end); assert(group_of_child_cell_begin == group_of_child_cell_end); diff --git a/include/scalfmm/algorithms/sequential/utils.hpp b/include/scalfmm/algorithms/sequential/utils.hpp index 866afa515e739dfb8fb13cb2ffb9c6b735c8a954..0f1c05fe9a1a1952d7e61e478c526ac213b601d5 100644 --- a/include/scalfmm/algorithms/sequential/utils.hpp +++ b/include/scalfmm/algorithms/sequential/utils.hpp @@ -1,3 +1,7 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/algorithms/sequential/utils.hpp +// -------------------------------- #ifndef SCALFMM_ALGORITHMS_SEQUENTIAL_UTILS_HPP #define SCALFMM_ALGORITHMS_SEQUENTIAL_UTILS_HPP @@ -5,55 +9,106 @@ namespace scalfmm::algorithms::sequential { + /** + * @brief Resets to zero the particles in the tree. + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_particles(TreeType& tree) -> void + { + tree.reset_particles(); + } + + /** + * @brief Resets to zero the positions of the particles in the tree. + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_positions(TreeType& tree) -> void + { + tree.reset_positions(); + } + + /** + * @brief Resets to zero the inputs of the particles in the tree. + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_inputs(TreeType& tree) -> void + { + tree.reset_inputs(); + } - /// @brief reset to zero the particles in the tree - /// - template<typename Tree> - inline auto reset_particles(Tree& tree) - { - for(auto pg: tree.group_of_leaves()) - { - // loop on leaves - for(auto& leaf: pg->block()) - { - leaf.particles().clear(); - } - } - } - /// - /// @brief reset to zero the output particles in the tree - /// - template<typename Tree> - inline auto reset_outputs(Tree& tree) - { - // loop on group of leaves - for(auto pg: tree.vector_of_leaf_groups()) - { - // reset the output in the block - pg->storage().reset_outputs(); - } - } - /// - /// @brief reset to zero the multipole and the local values in the cells - /// - template<typename Tree> - inline void reset_far_field(Tree& tree) + /** + * @brief Resets to zero the outputs of the particles in the tree. + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_outputs(TreeType& tree) -> void + { + tree.reset_outputs(); + } + + /** + * @brief Resets to zero the variables of the particles in the tree. + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_variables(TreeType& tree) -> void + { + tree.reset_variables(); + } + + /** + * @brief Reset to zero the multipole and the local values in the cells. + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_far_field(TreeType& tree) -> void { tree.reset_far_field(); } - /// - /// @brief reset to zero the multipole values in the cells - /// - template<typename Tree> - inline void reset_multipoles(Tree& tree) + + /** + * @brief Reset to zero the multipole values in the cells. + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_multipoles(TreeType& tree) -> void { tree.reset_multipoles(); } - /// - /// @brief reset to zero the local values in the cells - /// - template<typename Tree> - inline void reset_locals(Tree& tree) + + /** + * @brief Reset to zero the local values in the cells. + * + * @tparam TreeType + * + * @param tree + */ + template<typename TreeType> + inline auto reset_locals(TreeType& tree) -> void { tree.reset_locals(); } diff --git a/include/scalfmm/concepts/library.hpp b/include/scalfmm/concepts/library.hpp deleted file mode 100644 index e19a36352c73fa742a718fcf27bedfa6843928d3..0000000000000000000000000000000000000000 --- a/include/scalfmm/concepts/library.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// -------------------------------- -// See LICENCE file at project root -// File : concepts/library.hpp -// -------------------------------- -#ifndef SCALFMM_CONCEPTS_LIBRARY_HPP -#define SCALFMM_CONCEPTS_LIBRARY_HPP - -#include <type_traits> - -namespace scalfmm::concepts -{ - // template<typename R, typename Enabler> - // struct require_impl; - - // template<typename R> - // struct require_impl<R, void> - //{ - // using type = R; - //}; - - // template<typename Return, typename... Ts> - // struct require_check : require_impl<Return, std::void_t<Ts...>> - //{ - //}; - - // template<typename From, typename To> - // using Convertible = std::enable_if_t<std::is_convertible_v<From, To>>; - - // template<typename T> - // using Arithmetic = std::enable_if_t<std::is_arithmetic_v<T>>; - - // template<typename T> - // using Integral = std::enable_if_t<std::is_integral_v<T>>; - - // template<bool Condition> - // using If = std::enable_if_t<Condition>; - -} // end namespace scalfmm::concepts - -//// Pseudo require macro -//#define requires(...)->typename ::concept ::require_check < __VA_ARGS__> ::type - -#endif // SCALFMM_CONCEPTS_LIBRARY_HPP diff --git a/include/scalfmm/container/access.hpp b/include/scalfmm/container/access.hpp index 03113c9dcc6644b85b374afd0863580cc946d73b..21405380cdb4be592e03a69dfbe4f33f1d62f3bc 100644 --- a/include/scalfmm/container/access.hpp +++ b/include/scalfmm/container/access.hpp @@ -1,12 +1,13 @@ // -------------------------------- // See LICENCE file at project root -// File : container/access.hpp +// File : scalfmm/container/access.hpp // -------------------------------- #pragma once -#include <utility> -#include <scalfmm/meta/utils.hpp> +#include "scalfmm/meta/utils.hpp" + #include <cpp_tools/colors/colorized.hpp> +#include <utility> namespace scalfmm::container { @@ -29,26 +30,87 @@ namespace scalfmm::container using tuple_ptr_type = meta::replace_inner_tuple_type_t<std::decay_t, TuplePtr>; using tuple_ptr_const_type = meta::replace_inner_tuple_type_t<std::add_const_t, tuple_ptr_type>; using tuple_ref_type = meta::replace_inner_tuple_type_t<std::add_lvalue_reference_t, tuple_type>; - using tuple_const_ref_type = meta::replace_inner_tuple_type_t<std::add_const_t, tuple_ref_type>; + using tuple_const_ref_type = meta::replace_inner_tuple_type_t<std::add_const_t, tuple_ref_type>; + + // Need to adapt iterator category !!! + using iterator_category = std::random_access_iterator_tag; + using value_type = tuple_type; + using reference = std::conditional_t<IsConst, tuple_const_ref_type, tuple_ref_type>; + using pointer = void; + using difference_type = std::size_t; + + static constexpr bool is_const_qualified{IsConst}; private: + /** + * @brief + * + */ tuple_ptr_const_type vec_{}; + + /** + * @brief + * + */ int index_{0}; public: + /** + * @brief Construct a new light tuple iterator object + * + */ light_tuple_iterator() = default; + + /** + * @brief Construct a new light tuple iterator object + * + */ light_tuple_iterator(light_tuple_iterator const&) = default; + + /** + * @brief Construct a new light tuple iterator object + * + */ light_tuple_iterator(light_tuple_iterator&&) noexcept = default; + + /** + * @brief + * + * @return light_tuple_iterator& + */ inline auto operator=(light_tuple_iterator const&) -> light_tuple_iterator& = default; + + /** + * @brief + * + * @return light_tuple_iterator& + */ inline auto operator=(light_tuple_iterator&&) noexcept -> light_tuple_iterator& = default; + + /** + * @brief Destroy the light tuple iterator object + * + */ ~light_tuple_iterator() = default; + /** + * @brief Construct a new light tuple iterator object + * + * @param vec + * @param index + */ light_tuple_iterator(tuple_ptr_const_type vec, int index) noexcept : vec_{vec} , index_{index} { } + /** + * @brief Construct a new light tuple iterator object + * + * @tparam T + * @param other + */ template<typename T> light_tuple_iterator(light_tuple_iterator<T, not(IsConst)> const& other) noexcept : vec_{other.data()} @@ -56,6 +118,13 @@ namespace scalfmm::container { } + /** + * @brief + * + * @param os + * @param iter + * @return std::ostream& + */ inline friend auto operator<<(std::ostream& os, light_tuple_iterator iter) -> std::ostream& { auto tup = iter.data(); @@ -82,88 +151,202 @@ namespace scalfmm::container os << cpp_tools::colors::reset; return os; } - // Need to adapt iterator category !!! - using iterator_category = std::random_access_iterator_tag; - using value_type = tuple_type; - using reference = std::conditional_t<IsConst, tuple_const_ref_type, tuple_ref_type>; - using pointer = void; - using difference_type = std::size_t; - static constexpr bool is_const_qualified{IsConst}; private: + /** + * @brief + * + * @tparam Is + * @param s + * @return auto + */ template<size_t... Is> [[nodiscard]] inline auto make_proxy(std::index_sequence<Is...> s) const noexcept { - // return std::forward_as_tuple(*(std::get<Is>(vec_) + index_)...); return std::forward_as_tuple(std::get<Is>(vec_)[index_]...); } public: + /** + * @brief + * + * @return auto + */ [[nodiscard]] inline auto operator*() const noexcept { return make_proxy(std::make_index_sequence<std::tuple_size_v<tuple_type>>{}); } + /** + * @brief + * + * @return tuple_ptr_type + */ [[nodiscard]] inline auto data() const noexcept -> tuple_ptr_type { return this->vec_; } + /** + * @brief + * + * @return int + */ [[nodiscard]] inline auto index() const noexcept -> int { return this->index_; } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator==(light_tuple_iterator const& rhs) const noexcept -> bool { return index_ == rhs.index_; } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator!=(light_tuple_iterator const& rhs) const noexcept -> bool { return !(*this == rhs); } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator<(light_tuple_iterator const& rhs) const noexcept -> bool { return index_ < rhs.index_; } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator>(light_tuple_iterator const& rhs) const noexcept -> bool { return rhs < *this; } + + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator<=(light_tuple_iterator const& rhs) const noexcept -> bool { return !(rhs < *this); } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator>=(light_tuple_iterator const& rhs) const noexcept -> bool { return !(*this < rhs); } + /** + * @brief + * + * @return light_tuple_iterator& + */ inline auto operator++() noexcept -> light_tuple_iterator& { return ++index_, *this; } + + /** + * @brief + * + * @return light_tuple_iterator& + */ inline auto operator--() noexcept -> light_tuple_iterator& { return --index_, *this; } + + /** + * @brief + * + * @return light_tuple_iterator + */ inline auto operator++(int) noexcept -> light_tuple_iterator { const auto old = *this; return ++index_, old; } + + /** + * @brief + * + * @return light_tuple_iterator + */ inline auto operator--(int) noexcept -> light_tuple_iterator { const auto old = *this; return --index_, old; } + /** + * @brief + * + * @param shift + * @return light_tuple_iterator& + */ inline auto operator+=(int shift) noexcept -> light_tuple_iterator& { return index_ += shift, *this; } + + /** + * @brief + * + * @param shift + * @return light_tuple_iterator& + */ inline auto operator-=(int shift) noexcept -> light_tuple_iterator& { return index_ -= shift, *this; } + /** + * @brief + * + * @param shift + * @return light_tuple_iterator + */ inline auto operator+(int shift) const noexcept -> light_tuple_iterator { return light_tuple_iterator(vec_, index_ + shift); } + + /** + * @brief + * + * @param shift + * @return light_tuple_iterator + */ inline auto operator-(int shift) const noexcept -> light_tuple_iterator { return light_tuple_iterator(vec_, index_ - shift); } + + /** + * @brief + * + * @param rhs + * @return int + */ inline auto operator-(light_tuple_iterator const& rhs) const noexcept -> int { return index_ - rhs.index_; } }; + /** * @brief Return an interator on the particle * @@ -174,22 +357,29 @@ namespace scalfmm::container template<typename ProxyParticleIterator> constexpr inline auto begin(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { - // using range_type = typename ProxyParticleIterator::range_part_type; - // using sub_tuple_type = std::decay_t<decltype(meta::sub_tuple(p.first.data(), range_type{}))>; - // using iterator_type = light_tuple_iterator<sub_tuple_type, ProxyParticleIterator::is_const_qualified>; - // return iterator_type(p.first.data(), p.first.index()); return p.first; } + /** + * @brief + * + * @tparam ProxyParticleIterator + * @param p + * @return constexpr auto + */ template<typename ProxyParticleIterator> constexpr inline auto end(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { - // using range_type = typename ProxyParticleIterator::range_part_type; - // using sub_tuple_type = std::decay_t<decltype(meta::sub_tuple(p.second.data(), range_type{}))>; - // using iterator_type = light_tuple_iterator<sub_tuple_type, ProxyParticleIterator::is_const_qualified>; - // return iterator_type(p.second.data(), p.second.index()); return p.second; } + + /** + * @brief + * + * @tparam ProxyParticleIterator + * @param p + * @return constexpr auto + */ template<typename ProxyParticleIterator> constexpr inline auto position_begin(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { @@ -199,6 +389,13 @@ namespace scalfmm::container return iterator_type(meta::make_sub_tuple(p.first.data(), range_type{}), p.first.index()); } + /** + * @brief + * + * @tparam ProxyParticleIterator + * @param p + * @return constexpr auto + */ template<typename ProxyParticleIterator> constexpr inline auto position_end(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { @@ -208,6 +405,13 @@ namespace scalfmm::container return iterator_type(meta::make_sub_tuple(p.second.data(), range_type{}), p.second.index()); } + /** + * @brief + * + * @tparam ProxyParticleIterator + * @param p + * @return constexpr auto + */ template<typename ProxyParticleIterator> constexpr inline auto inputs_begin(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { @@ -217,6 +421,13 @@ namespace scalfmm::container return iterator_type(meta::make_sub_tuple(p.first.data(), range_type{}), p.first.index()); } + /** + * @brief + * + * @tparam ProxyParticleIterator + * @param p + * @return constexpr auto + */ template<typename ProxyParticleIterator> constexpr inline auto inputs_end(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { @@ -226,6 +437,13 @@ namespace scalfmm::container return iterator_type(meta::make_sub_tuple(p.second.data(), range_type{}), p.second.index()); } + /** + * @brief + * + * @tparam ProxyParticleIterator + * @param p + * @return constexpr auto + */ template<typename ProxyParticleIterator> constexpr inline auto outputs_begin(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { @@ -235,6 +453,13 @@ namespace scalfmm::container return iterator_type(meta::make_sub_tuple(p.first.data(), range_type{}), p.first.index()); } + /** + * @brief + * + * @tparam ProxyParticleIterator + * @param p + * @return constexpr auto + */ template<typename ProxyParticleIterator> constexpr inline auto outputs_end(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { @@ -244,6 +469,13 @@ namespace scalfmm::container return iterator_type(meta::make_sub_tuple(p.second.data(), range_type{}), p.second.index()); } + /** + * @brief + * + * @tparam ProxyParticleIterator + * @param p + * @return constexpr auto + */ template<typename ProxyParticleIterator> constexpr inline auto variables_begin(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { @@ -253,6 +485,13 @@ namespace scalfmm::container return iterator_type(meta::make_sub_tuple(p.first.data(), range_type{}), p.first.index()); } + /** + * @brief + * + * @tparam ProxyParticleIterator + * @param p + * @return constexpr auto + */ template<typename ProxyParticleIterator> constexpr inline auto variables_end(std::pair<ProxyParticleIterator, ProxyParticleIterator> const& p) { @@ -261,4 +500,4 @@ namespace scalfmm::container using iterator_type = light_tuple_iterator<sub_tuple_type, ProxyParticleIterator::is_const_qualified>; return iterator_type(meta::make_sub_tuple(p.second.data(), range_type{}), p.second.index()); } -} +} // namespace scalfmm::container diff --git a/include/scalfmm/container/block.hpp b/include/scalfmm/container/block.hpp index a0bf8efb29c8df72b222d04b7334f8b0baa869db..225dfefc0b69771c9a29eed5742ed615e58410db 100644 --- a/include/scalfmm/container/block.hpp +++ b/include/scalfmm/container/block.hpp @@ -1,9 +1,15 @@ // -------------------------------- // See LICENCE file at project root -// File : container/block.hpp +// File : scalfmm/container/block.hpp // -------------------------------- #pragma once +#include "scalfmm/container/particle.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/tree/header.hpp" + +#include <cpp_tools/colors/colorized.hpp> + #include <iterator> #include <ostream> #include <string> @@ -12,17 +18,11 @@ #include <utility> #include <vector> -#include "scalfmm/container/particle.hpp" -#include "scalfmm/meta/utils.hpp" -#include "scalfmm/tree/header.hpp" -#include "scalfmm/tree/leaf_view.hpp" - -#include <cpp_tools/colors/colorized.hpp> - namespace scalfmm::container { template<typename Particle, bool IsConst> class proxy_particle_iterator; + /** * @brief the container to store all the data inside the group * @@ -30,17 +30,19 @@ namespace scalfmm::container * |header| the particles inside the group | the symbolic data of the leaves| * The particles are stored in a SOA format * + * @tparam Header + * @tparam Particle + * @tparam Component */ template<typename Header, typename Particle, typename Component> struct particles_block { private: /** - * @brief Get the true particle raw size object + * @brief Get the particle raw size object * * @return constexpr std::size_t */ - // constexpr std::size_t get_particle_raw_size() const static constexpr std::size_t get_particle_raw_size() { constexpr auto values_size = sizeof(position_value_type) * dimension_size + @@ -95,11 +97,36 @@ namespace scalfmm::container using block_type = std::vector<raw_type, XTENSOR_DEFAULT_ALLOCATOR(raw_type)>; static constexpr std::size_t number_of_elements = particle_traits<particle_type>::number_of_elements; + /** + * @brief Construct a new particles block object + * + */ particles_block() = default; + + /** + * @brief Construct a new particles block object + * + */ particles_block(particles_block const&) = default; + + /** + * @brief Construct a new particles block object + * + */ particles_block(particles_block&&) = default; + + /** + * @brief Destroy the particles block object + * + */ ~particles_block() = default; + /** + * @brief Construct a new particles block object + * + * @param nb_particles + * @param nb_leaves + */ particles_block(std::size_t nb_particles, std::size_t nb_leaves) : m_block(sizeof(header_type) + (get_particle_raw_size() * nb_particles) + (sizeof(symbolics_type) * nb_leaves), @@ -116,6 +143,24 @@ namespace scalfmm::container , m_tuple_start(init_tuple_start(m_particles_start, m_nb_particles)) { } + + /** + * @brief Construct a new particles block object + * + * @param particles + * @param nb_leaves + */ + particles_block(std::vector<particle_type> particles, std::size_t nb_leaves) + : particles_block(particles.size(), nb_leaves) + { + auto it = this->begin(); + for(std::size_t i = 0; i < m_nb_particles; ++i) + { + *it = particles.at(i).as_tuple(); + ++it; + } + } + /** * @brief print the data inside the block * @@ -161,6 +206,7 @@ namespace scalfmm::container }); } } + /** * @brief Display the block information and the data inside the block * @@ -188,42 +234,122 @@ namespace scalfmm::container return os; } + /** + * @brief + * + * @return std::size_t + */ inline auto raw_size() const noexcept -> std::size_t { return m_raw_size; } + + /** + * @brief + * + * @return std::size_t + */ inline auto size() const noexcept -> std::size_t { return m_nb_particles; } + + /** + * @brief + * + * @return std::size_t + */ inline auto nb_leaves() const noexcept -> std::size_t { return m_nb_leaves; } + + /** + * @brief + * + * @return const std::byte* + */ inline auto data() const noexcept -> const std::byte* { return m_block.data(); } + /** + * @brief + * + * @return block_type const& + */ inline auto block() const noexcept -> block_type const& { return m_block; } + + /** + * @brief + * + * @return block_type const& + */ inline auto cblock() const noexcept -> block_type const& { return m_block; } + + /** + * @brief + * + * @return block_type& + */ inline auto block() noexcept -> block_type& { return m_block; } + /** + * @brief + * + * @return header_type const& + */ inline auto header() const noexcept -> header_type const& { return *m_header; } + + /** + * @brief + * + * @return header_type const& + */ inline auto cheader() const noexcept -> header_type const& { return *m_header; } + + /** + * @brief + * + * @return header_type& + */ inline auto header() noexcept -> header_type& { return *m_header; } + /** + * @brief + * + * @return raw_type* const + */ inline auto particles() const noexcept -> raw_type* const { return m_particles_start; } + + /** + * @brief + * + * @return const raw_type* const + */ inline auto cparticles() const noexcept -> const raw_type* const { return m_particles_start; } + + /** + * @brief + * + * @return raw_type* const + */ inline auto particles() noexcept -> raw_type* const { return m_particles_start; } /** - * @brief Get the pointer of the first position inside the block - * - * This pointer allows to access the different positions as arrays. - * \code - * auto * pos_x = ptr_on_position() ; - * auto * pos_y = pos_x + nb_particles; - - * \endcode - * @return particle_type::inputs_value_type* const - */ + * @brief Get the pointer of the first position inside the block + * + * This pointer allows to access the different positions as arrays. + * \code + * auto * pos_x = ptr_on_position() ; + * auto * pos_y = pos_x + nb_particles; + * \endcode + * @return particle_type::inputs_value_type* const + */ inline auto ptr_on_position() const noexcept -> typename particle_type::position_value_type* const { return reinterpret_cast<typename particle_type::inputs_value_type*>(m_particles_start); } + /** + * @brief + * + * @return particle_type::position_value_type const* const + */ inline auto cptr_on_position() const noexcept -> typename particle_type::position_value_type const* const { return reinterpret_cast<typename particle_type::inputs_value_type*>(m_particles_start); } + /** * @brief Get the pointer of the first input inside the block * @@ -231,7 +357,7 @@ namespace scalfmm::container * \code * auto * first_input = ptr_on_input() ; * auto *second_input = first_input + nb_particles; -- * \endcode + * \endcode * @return particle_type::inputs_value_type* const */ inline auto ptr_on_input() const noexcept -> typename particle_type::inputs_value_type* const @@ -239,6 +365,7 @@ namespace scalfmm::container return reinterpret_cast<typename particle_type::inputs_value_type*>( std::get<dimension_size>(m_tuple_start)); } + /** * @brief Get the pointer of the first input inside the block * @@ -255,6 +382,7 @@ namespace scalfmm::container return reinterpret_cast<typename particle_type::inputs_value_type*>( std::get<dimension_size>(m_tuple_start)); } + /** * @brief Get the pointer of the first output inside the block * @@ -269,6 +397,7 @@ namespace scalfmm::container { return reinterpret_cast<outputs_value_type*>(std::get<dimension_size + inputs_size>(m_tuple_start)); } + /** * @brief Get the pointer of the first output inside the block * @@ -280,39 +409,139 @@ namespace scalfmm::container * * @return outputs_value_type* const **/ - /// inline auto cptr_on_output() const noexcept -> const outputs_value_type* const { return reinterpret_cast<outputs_value_type*>(std::get<dimension_size + inputs_size>(m_tuple_start)); } + /** - * @brief Sets all outputs inside the block to zero. - * + * @brief Sets all positions inside the block to zero. */ - inline auto reset_outputs() const noexcept -> void + inline auto reset_positions() noexcept -> void { - auto output = reinterpret_cast<outputs_value_type*>(std::get<dimension_size + inputs_size>(m_tuple_start)); + auto position_ptr = this->ptr_on_position(); + + for(std::size_t i = 0; i < m_nb_particles * dimension_size; ++i) + { + position_ptr[i] = position_value_type(0.0); + } + } - // memset(output, 0, m_nb_particles * outputs_size * sizeof(outputs_value_type)); + /** + * @brief Sets all inputs inside the block to zero. + */ + inline auto reset_inputs() noexcept -> void + { + auto input_ptr = this->ptr_on_input(); + + for(std::size_t i = 0; i < m_nb_particles * inputs_size; ++i) + { + input_ptr[i] = inputs_value_type(0.0); + } + } + + /** + * @brief Sets all outputs inside the block to zero. + */ + inline auto reset_outputs() noexcept -> void + { + auto output_ptr = this->ptr_on_output(); for(std::size_t i = 0; i < m_nb_particles * outputs_size; ++i) { - output[i] = outputs_value_type(0.0); + output_ptr[i] = outputs_value_type(0.0); } } + /** + * @brief Sets all variables inside the block to zero (or use default constructor). + */ + inline auto reset_variables() noexcept -> void + { + if constexpr(particle_type::variables_size != 0) + { + auto variable_ptr = + reinterpret_cast<raw_type*>(std::get<dimension_size + inputs_size + outputs_size>(m_tuple_start)); + + variables_type tt{}; + std::size_t offset = 0; + + meta::for_each(tt, + [&offset, &variable_ptr, this](auto elem) + { + using current_variable_type = std::decay_t<decltype(elem)>; + auto current_ptr = reinterpret_cast<current_variable_type*>(variable_ptr + offset); + + for(std::size_t i = 0; i < m_nb_particles; ++i) + { + current_ptr[i] = current_variable_type{}; + } + + offset += m_nb_particles * sizeof(current_variable_type); + }); + } + } + + /** + * @brief Sets all positions, inputs, outputs, and variables inside the block to zero (or use default constructor). + */ + inline auto reset_particles() noexcept -> void + { + reset_positions(); + reset_inputs(); + reset_outputs(); + reset_variables(); + } + + /** + * @brief + * + * @return const symbolics_type* const + */ inline auto symbolics() const noexcept -> const symbolics_type* const { return m_symbolics_start; } + + /** + * @brief + * + * @return const symbolics_type* const + */ inline auto csymbolics() const noexcept -> const symbolics_type* const { return m_symbolics_start; } + + /** + * @brief + * + * @return symbolics_type* const + */ inline auto symbolics() noexcept -> symbolics_type* const { return m_symbolics_start; } + /** + * @brief + * + * @param i + * @return symbolics_type const& + */ inline auto symbolics(std::size_t i) const noexcept -> symbolics_type const& { return *(m_symbolics_start + i); } + + /** + * @brief + * + * @param i + * @return symbolics_type const& + */ inline auto csymbolics(std::size_t i) const noexcept -> symbolics_type const& { return *(m_symbolics_start + i); } + + /** + * @brief + * + * @param i + * @return symbolics_type& + */ inline auto symbolics(std::size_t i) noexcept -> symbolics_type& { return *(m_symbolics_start + i); } public: @@ -323,24 +552,63 @@ namespace scalfmm::container * @return iterator */ inline auto begin() noexcept -> iterator { return {m_tuple_start, 0}; } + /** * @brief to get a tuple of iterators on the last particle (position, inputs, outputs) - * + * * @return iterator */ - inline auto end() noexcept -> iterator { return {m_tuple_start, m_nb_particles}; } + inline auto end() noexcept -> iterator { return {m_tuple_start, static_cast<int>(m_nb_particles)}; } + /** + * @brief + * + * @return const_iterator + */ inline auto begin() const noexcept -> const_iterator { return {m_tuple_start, 0}; } + + /** + * @brief + * + * @return const_iterator + */ inline auto cbegin() const noexcept -> const_iterator { return {m_tuple_start, 0}; } - inline auto end() const noexcept -> const_iterator { return {m_tuple_start, m_nb_particles}; } - inline auto cend() const noexcept -> const_iterator { return {m_tuple_start, m_nb_particles}; } + /** + * @brief + * + * @return const_iterator + */ + inline auto end() const noexcept -> const_iterator { return {m_tuple_start, static_cast<int>(m_nb_particles)}; } + /** + * @brief + * + * @return const_iterator + */ + inline auto cend() const noexcept -> const_iterator + { + return {m_tuple_start, static_cast<int>(m_nb_particles)}; + } + + /** + * @brief + * + * @param i + * @return auto + */ [[nodiscard]] inline auto operator[](std::size_t i) { auto it = this->begin() + i; return *it; } + + /** + * @brief + * + * @param i + * @return auto + */ [[nodiscard]] inline auto operator[](std::size_t i) const { auto it = this->cbegin() + i; @@ -370,6 +638,12 @@ namespace scalfmm::container return tuple_start; } + + /** + * @brief + * + * @tparam Is + */ template<std::size_t... Is> inline constexpr auto generate_ptr_tuple(std::index_sequence<Is...> /*unused*/) const noexcept { @@ -377,6 +651,14 @@ namespace scalfmm::container (Is * m_nb_particles))...); } + /** + * @brief + * + * @tparam Iterator_type + * @param os + * @param iter + * @param string + */ template<typename Iterator_type> inline auto print_one_data(std::ostream& os, Iterator_type& iter, std::string&& string) const noexcept -> void { @@ -396,6 +678,15 @@ namespace scalfmm::container ++iter; } } + + /** + * @brief + * + * @tparam Iterator_type + * @tparam Value_type + * @param iter + * @param value + */ template<typename Iterator_type, typename Value_type> inline auto init_data(Iterator_type& iter, Value_type&& value) const noexcept -> void { @@ -426,13 +717,15 @@ namespace scalfmm::container ///< elements of the particles }; - // the iterator on the container /** * @brief the iterator on the block container * * the iterator is a pair (iterators , index). The true iterator is iterators + index * iterators is an iterator tuple pointing to the first particle of the block * index is an int to advance to the position of the good particle in the block + * + * @tparam Particle + * @tparam IsConst */ template<typename Particle, bool IsConst> struct proxy_particle_iterator @@ -487,30 +780,86 @@ namespace scalfmm::container using range_part_type = meta::make_range_sequence<0, dimension_size + inputs_size + outputs_size + variables_size>; + // Need to adapt iterator category !!! + using iterator_category = std::random_access_iterator_tag; + using value_type = particle_type; + using reference = std::conditional_t<IsConst, tuple_const_ref_type, tuple_ref_type>; + using pointer = void; + using difference_type = std::size_t; + static constexpr bool is_const_qualified{IsConst}; + private: tuple_ptr_type vec_{}; ///< a tuple of iterators pointing to the first element of the block int index_{0}; ///< the index to move the iterator to the element public: + /** + * @brief Construct a new proxy particle iterator object + * + */ proxy_particle_iterator() = default; + + /** + * @brief Construct a new proxy particle iterator object + * + */ proxy_particle_iterator(proxy_particle_iterator const&) = default; + + /** + * @brief Construct a new proxy particle iterator object + * + */ proxy_particle_iterator(proxy_particle_iterator&&) noexcept = default; + + /** + * @brief + * + * @return proxy_particle_iterator& + */ inline auto operator=(proxy_particle_iterator const&) -> proxy_particle_iterator& = default; + + /** + * @brief + * + * @return proxy_particle_iterator& + */ inline auto operator=(proxy_particle_iterator&&) noexcept -> proxy_particle_iterator& = default; + + /** + * @brief Destroy the proxy particle iterator object + * + */ ~proxy_particle_iterator() = default; + /** + * @brief Construct a new proxy particle iterator object + * + * @param vec + * @param index + */ proxy_particle_iterator(tuple_ptr_type vec, int index) noexcept : vec_{vec} , index_{index} { } + /** + * @brief + * + */ proxy_particle_iterator(proxy_particle_iterator<Particle, not(IsConst)> const& other) noexcept : vec_{other.data()} , index_{other.index()} { } + /** + * @brief + * + * @param os + * @param iter + * @return std::ostream& + */ inline friend auto operator<<(std::ostream& os, proxy_particle_iterator iter) -> std::ostream& { auto tup = iter.data(); @@ -537,87 +886,199 @@ namespace scalfmm::container os << cpp_tools::colors::reset; return os; } - // Need to adapt iterator category !!! - using iterator_category = std::random_access_iterator_tag; - using value_type = particle_type; - using reference = std::conditional_t<IsConst, tuple_const_ref_type, tuple_ref_type>; - using pointer = void; - using difference_type = std::size_t; - static constexpr bool is_const_qualified{IsConst}; private: + /** + * @brief + * + * @tparam Is + * @param s + * @return auto + */ template<size_t... Is> [[nodiscard]] inline auto make_proxy(std::index_sequence<Is...> s) const noexcept { - // return std::forward_as_tuple(*(std::get<Is>(vec_) + index_)...); return std::forward_as_tuple(std::get<Is>(vec_)[index_]...); } public: + /** + * @brief + * + * @return auto + */ [[nodiscard]] inline auto operator*() const noexcept { - // std::cout << " ---****---- index_ " << index_<<std::endl; return make_proxy(std::make_index_sequence<number_of_elements>{}); } + /** + * @brief + * + * @return tuple_ptr_type + */ [[nodiscard]] inline auto data() const noexcept -> tuple_ptr_type { return this->vec_; } + /** + * @brief + * + * @return int + */ [[nodiscard]] inline auto index() const noexcept -> int { return this->index_; } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator==(proxy_particle_iterator const& rhs) const noexcept -> bool { return index_ == rhs.index_; } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator!=(proxy_particle_iterator const& rhs) const noexcept -> bool { return !(*this == rhs); } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator<(proxy_particle_iterator const& rhs) const noexcept -> bool { return index_ < rhs.index_; } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator>(proxy_particle_iterator const& rhs) const noexcept -> bool { return rhs < *this; } + + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator<=(proxy_particle_iterator const& rhs) const noexcept -> bool { return !(rhs < *this); } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] inline auto operator>=(proxy_particle_iterator const& rhs) const noexcept -> bool { return !(*this < rhs); } + /** + * @brief + * + * @return proxy_particle_iterator& + */ inline auto operator++() noexcept -> proxy_particle_iterator& { return ++index_, *this; } + + /** + * @brief + * + * @return proxy_particle_iterator& + */ inline auto operator--() noexcept -> proxy_particle_iterator& { return --index_, *this; } + + /** + * @brief + * + * @return proxy_particle_iterator + */ inline auto operator++(int) noexcept -> proxy_particle_iterator { const auto old = *this; return ++index_, old; } + + /** + * @brief + * + * @return proxy_particle_iterator + */ inline auto operator--(int) noexcept -> proxy_particle_iterator { const auto old = *this; return --index_, old; } + /** + * @brief + * + * @param shift + * @return proxy_particle_iterator& + */ inline auto operator+=(int shift) noexcept -> proxy_particle_iterator& { return index_ += shift, *this; } + + /** + * @brief + * + * @param shift + * @return proxy_particle_iterator& + */ inline auto operator-=(int shift) noexcept -> proxy_particle_iterator& { return index_ -= shift, *this; } + /** + * @brief + * + * @param shift + * @return proxy_particle_iterator + */ inline auto operator+(int shift) const noexcept -> proxy_particle_iterator { return proxy_particle_iterator(vec_, index_ + shift); } + + /** + * @brief + * + * @param shift + * @return proxy_particle_iterator + */ inline auto operator-(int shift) const noexcept -> proxy_particle_iterator { return proxy_particle_iterator(vec_, index_ - shift); } + + /** + * @brief + * + * @param rhs + * @return int + */ inline auto operator-(proxy_particle_iterator const& rhs) const noexcept -> int { return index_ - rhs.index_; } }; diff --git a/include/scalfmm/container/iterator.hpp b/include/scalfmm/container/iterator.hpp index a6e1c30d9dfa0d85032ced258c857e62e169fe78..ca8faa35d848fa739d8a07010374792ab4569a7e 100644 --- a/include/scalfmm/container/iterator.hpp +++ b/include/scalfmm/container/iterator.hpp @@ -1,25 +1,59 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/container/iterator.hpp +// -------------------------------- #ifndef SCALFMM_CONTAINER_ITERATOR_HPP #define SCALFMM_CONTAINER_ITERATOR_HPP -#include <iterator> #include <cstddef> +#include <iterator> namespace scalfmm::container { - // Forward declaration for traits support + /** + * @brief Forward declaration for traits support (proxy_iterator). + * + * @tparam VariadicContainer + * @tparam DerivedVariadic + * @tparam Seq + * @tparam IsConst + */ template<class VariadicContainer, class DerivedVariadic, typename Seq, bool IsConst> class proxy_iterator; + + /** + * @brief Forward declaration for traits support (variadic_adaptor). + * + * @tparam Derived + * @tparam Containers + */ template<typename Derived, typename... Containers> struct variadic_adaptor; + + /** + * @brief Forward declaration for traits support (particle_container). + * + * @tparam Particle + */ template<typename Particle> class particle_container; - /// @brief - /// - /// @tparam Iterator + + /** + * @brief Forward declaration for traits support (iterator_traits). + * + * @tparam Iterator + */ template<typename Iterator> struct iterator_traits; - + /** + * @brief + * + * @tparam Seq + * @tparam IsConst + * @tparam Derived + * @tparam Containers + */ template<typename Seq, bool IsConst, typename Derived, typename... Containers> struct iterator_traits< proxy_iterator<variadic_adaptor<Derived, Containers...>, variadic_adaptor<Derived, Containers...>, Seq, IsConst>> @@ -29,6 +63,15 @@ namespace scalfmm::container using container_type = variadic_adaptor<Derived, Containers...>; }; + /** + * @brief + * + * @tparam Seq + * @tparam IsConst + * @tparam Particle + * @tparam Derived + * @tparam Containers + */ template<typename Seq, bool IsConst, typename Particle, typename Derived, typename... Containers> struct iterator_traits< proxy_iterator<variadic_adaptor<Derived, Containers...>, particle_container<Particle>, Seq, IsConst>> diff --git a/include/scalfmm/container/morton_curve.hpp b/include/scalfmm/container/morton_curve.hpp index 12b3b5effb0cef34d1c0678490a4d36ed71c115b..872d13fa04d98b20f74a29bcc53c21aac9324a91 100644 --- a/include/scalfmm/container/morton_curve.hpp +++ b/include/scalfmm/container/morton_curve.hpp @@ -1,13 +1,16 @@ +// -------------------------------- // See LICENCE file at project root -// +// File : scalfmm/container/morton_curve.hpp +// -------------------------------- #ifndef SCALFMM_CONTAINER_MORTON_CURVE_HPP #define SCALFMM_CONTAINER_MORTON_CURVE_HPP #include <scalfmm/container/point.hpp> #include <scalfmm/meta/const_functions.hpp> -#include <math.h> + #include <array> #include <cstddef> +#include <math.h> /** Provides the corner traversal order of an N dimension hypercube * @@ -30,15 +33,15 @@ */ namespace scalfmm::container { - - /// @brief - /// - /// @tparam _Dim + /** + * @brief + * + * @tparam _Dim + */ template<std::size_t _Dim> class z_curve { public: - /// Space dimension count static const std::size_t Dim = _Dim; /// Position type used @@ -52,7 +55,11 @@ namespace scalfmm::container /// Array to cache the positions corresponding to indexes static const position_array_t _positions; - /** Creates an array of positions to initialize #_positions */ + /** + * @brief Creates an array of positions to initialize #_positions + * + * @return position_array_t + */ static position_array_t create_array() noexcept { position_array_t positions; @@ -67,17 +74,19 @@ namespace scalfmm::container } public: - /** The position corresponding to an index + /** + * @brief The position corresponding to an index * - * \param idx The index of the point in the space filling curve - * \return The position corresponding to the space filling curve index + * @param idx The index of the point in the space filling curve + * @return The position corresponding to the space filling curve index */ static position_t position(std::size_t idx) noexcept { return _positions[idx]; } - /** Index in the space filling curve of a boolean position + /** + * @brief Index in the space filling curve of a boolean position * - * \param p The position - * \return The space filling curve index corresponding to the position + * @param p The position + * @return The space filling curve index corresponding to the position */ static std::size_t index(const position_t& p) noexcept { @@ -90,11 +99,13 @@ namespace scalfmm::container return idx; } - /** Index in the space filling curve of a real position relative to the center of the hypercube + /** + * @brief Index in the space filling curve of a real position relative to the center of the hypercube * - * \param p The position - * \param center The center of the hypercube - * \return The space filling curve index corresponding to the position + * @tparam T + * @param p The position + * @param center The center of the hypercube + * @return The space filling curve index corresponding to the position */ template<typename T> static std::size_t index(const point<T, Dim>& p, const point<T, Dim>& center) noexcept @@ -109,7 +120,12 @@ namespace scalfmm::container } }; - // Initialization of static variable + /** + * @brief Initialization of static variable + * + * @tparam _Dim + * @return const z_curve<_Dim>::position_array_t + */ template<std::size_t _Dim> const typename z_curve<_Dim>::position_array_t z_curve<_Dim>::_positions(z_curve<_Dim>::create_array()); diff --git a/include/scalfmm/container/particle.hpp b/include/scalfmm/container/particle.hpp index ad9fbebda93f8ed97514b8ef55cca854cdacf197..1d9dd9d2152a097da6f4a3489b35e99fffdb9958 100644 --- a/include/scalfmm/container/particle.hpp +++ b/include/scalfmm/container/particle.hpp @@ -1,69 +1,72 @@ // -------------------------------- // See LICENCE file at project root -// File : particle.hpp +// File : scalfmm/container/particle.hpp // -------------------------------- #ifndef SCALFMM_CONTAINER_PARTICLE_HPP #define SCALFMM_CONTAINER_PARTICLE_HPP +#include "scalfmm/container/particle_impl.hpp" +#include "scalfmm/container/particle_proxy.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/utils/io_helpers.hpp" + #include <cstddef> #include <ostream> -#include <scalfmm/container/particle_impl.hpp> -#include <scalfmm/container/particle_proxy.hpp> #include <type_traits> -#include "scalfmm/meta/utils.hpp" -#include "scalfmm/utils/io_helpers.hpp" - -///** -// * \brief Multi-purpose particle implementation -// * -// * This template implementation of a particle allows simple reuse for several -// * use cases. The aim it to provide an interface that is compatible with the -// * rest of ScalFMM. It is mainly intended to be used as an interface for the -// * particle containers. -// * -// * The Types parameter pack can accept any type that is to be considered as a -// * particle attribute. You can also specify scalfmm::pack type to factorize -// * several types. -// * -// * In the following example, the two specializations of the class will give the -// * same final structure. -// * -// * ``` -// * using FReal = double; -// * static constexpr std::size_t dimension = 3; -// * -// * particle<FReal, dimension, int, float, float, float, float>; -// * particle<FReal, dimension, int, scalfmm::meta::pack<4, float> >; -// * ``` -// * -// * The base of these two classes is -// * ``` -// * std::tuple<double, double, double, int, float, float, float, float>; -// * ``` -// * -// * \warning Although the classes will have the same final layout, C++ considers -// * these two classes to be different ! -// * -// * ##### Example -// * -// * ``` -// * // Define a 3D particle with an int attribute -// * using Particle = particle<double, 3, int>; -// * -// * Particle p; -// * p.get<> -// * ``` -// * -// * -// * \tparam FReal Floating point type -// * \tparam dimension Space dimension count -// * \tparam Types Attributes type list -// * -// */ +/** + * @brief Multi-purpose particle implementation + * + * This template implementation of a particle allows simple reuse for several + * use cases. The aim it to provide an interface that is compatible with the + * rest of ScalFMM. It is mainly intended to be used as an interface for the + * particle containers. + * + * The Types parameter pack can accept any type that is to be considered as a + * particle attribute. You can also specify scalfmm::pack type to factorize + * several types. + * + * In the following example, the two specializations of the class will give the + * same final structure. + * + * ``` + * using FReal = double; + * static constexpr std::size_t dimension = 3; + * + * particle<FReal, dimension, int, float, float, float, float>; + * particle<FReal, dimension, int, scalfmm::meta::pack<4, float> >; + * ``` + * + * The base of these two classes is + * ``` + * std::tuple<double, double, double, int, float, float, float, float>; + * ``` + * + * @warning Although the classes will have the same final layout, C++ considers + * these two classes to be different ! + * + * ##### Example + * + * ``` + * // Define a 3D particle with an int attribute + * using Particle = particle<double, 3, int>; + * + * Particle p; + * p.get<> + * ``` + * + * + * @tparam FReal Floating point type + * @tparam dimension Space dimension count + * @tparam Types Attributes type list + */ namespace scalfmm::container { - // particle traits to extract info about the particle. + /** + * @brief Particle traits to extract info about the particle. + * + * @tparam Particle + */ template<typename Particle> struct particle_traits { @@ -92,10 +95,22 @@ namespace scalfmm::container static constexpr std::size_t number_of_elements = dimension_size + inputs_size + outputs_size + variables_size; }; - // This is the particle container. It holds a nd position, N number of inputs and M number of outputs. - // If you pass references as PositionType, InputsType and OutputsType, the particle becomes a - // proxy meaning you can wrap a tuple and modify the the tuple through the proxy. - // This is useful the directly modify the variadic container particle_container. + /** + * @brief + * + * This is the particle container. It holds a nd position, N number of inputs and M number of outputs. + * If you pass references as PositionType, InputsType and OutputsType, the particle becomes a + * proxy meaning you can wrap a tuple and modify the the tuple through the proxy. + * This is useful the directly modify the variadic container particle_container. + * + * @tparam PositionType + * @tparam PositionDim + * @tparam InputsType + * @tparam NInputs + * @tparam OutputsType + * @tparam MOutputs + * @tparam Variables + */ template<typename PositionType, std::size_t PositionDim, typename InputsType, std::size_t NInputs, typename OutputsType, std::size_t MOutputs, typename... Variables> struct particle @@ -141,7 +156,7 @@ namespace scalfmm::container // Common formatted output operator inline friend auto operator<<(std::ostream& os, const particle& part) -> std::ostream& { - os << part.position() ; + os << part.position(); if constexpr(base_type::inputs_size > 0) { io::print(os, part.inputs()); @@ -152,103 +167,227 @@ namespace scalfmm::container } if constexpr(base_type::variables_size > 0) { - io::print(os, part.variables()); + io::print(os, part.variables()); } return os; } // Common size functions. - // Size of dimensions + + /** + * @brief Size of dimensions. + * + * @return std::size_t + */ [[nodiscard]] constexpr inline auto sizeof_dimension() const noexcept -> std::size_t { return PositionDim; } - // Number of inputs + + /** + * @brief Number of inputs. + * + * @return std::size_t + */ [[nodiscard]] constexpr inline auto sizeof_inputs() const noexcept -> std::size_t { return NInputs; } - // Nulber of outputs + + /** + * @brief Number of outputs. + * + * @return std::size_t + */ [[nodiscard]] constexpr inline auto sizeof_outputs() const noexcept -> std::size_t { return MOutputs; } - // Number of variables + + /** + * @brief Number of variables. + * + * @return std::size_t + */ [[nodiscard]] constexpr inline auto sizeof_variables() const noexcept -> std::size_t { return sizeof...(Variables); } }; - // Return the underlying reference to the position array + /** + * @brief Returns the underlying reference to the position array (const version). + * + * @tparam Particle + * @param part + * @return Particle::position_type const& + */ template<typename Particle> constexpr inline auto position(Particle const& part) -> typename Particle::position_type const& { return part.position(); } - // Return the i'est component of the position + + /** + * @brief Returns the ith component of the positions (const version). + * + * @tparam Particle + * @param part + * @param i + * @return Particle::position_value_type const& + */ template<typename Particle> constexpr inline auto position(Particle const& part, std::size_t i) -> typename Particle::position_value_type const& { return part.position(i); } - // Same here but non const versions + + /** + * @brief Returns the underlying reference to the position array. + * + * @tparam Particle + * @param part + * @return Particle::position_type& + */ template<typename Particle> constexpr inline auto position(Particle& part) -> typename Particle::position_type& { return part.position(); } + + /** + * @brief Returns the ith component of the positions. + * + * @tparam Particle + * @param part + * @param i + * @return Particle::position_value_type& + */ template<typename Particle> constexpr inline auto position(Particle const& part, std::size_t i) -> typename Particle::position_value_type& { return part.position(i); } - // Return the underlying reference to the inputs array + /** + * @brief Returns the underlying reference to the inputs array (const version). + * + * @tparam Particle + * @param part + * @return Particle::inputs_type const& + */ template<typename Particle> constexpr inline auto inputs(Particle const& part) -> typename Particle::inputs_type const& { return part.inputs(); } - // Return the i'est input + + /** + * @brief Returns the ith component of the inputs (const version). + * + * @tparam Particle + * @param part + * @param i + * @return Particle::inputs_value_type const& + */ template<typename Particle> constexpr inline auto inputs(Particle const& part, std::size_t i) -> typename Particle::inputs_value_type const& { return part.inputs(i); } - // Same here but non const versions + + /** + * @brief Returns the underlying reference to the inputs array. + * + * @tparam Particle + * @param part + * @return Particle::inputs_type& + */ template<typename Particle> constexpr inline auto inputs(Particle& part) -> typename Particle::inputs_type& { return part.inputs(); } + + /** + * @brief Returns the ith component of the inputs. + * + * @tparam Particle + * @param part + * @param i + * @return Particle::inputs_value_type& + */ template<typename Particle> constexpr inline auto inputs(Particle const& part, std::size_t i) -> typename Particle::inputs_value_type& { return part.inputs(i); } - // Return the underlying reference to the outputs array + /** + * @brief Returns the underlying reference to the outputs array (const version). + * + * @tparam Particle + * @param part + * @return Particle::outputs_type const& + */ template<typename Particle> constexpr inline auto outputs(Particle const& part) -> typename Particle::outputs_type const& { return part.outputs(); } - // Return the i'est output + + /** + * @brief Returns the ith component of the outputs (const version). + * + * @tparam Particle + * @param part + * @param i + * @return Particle::outputs_value_type const& + */ template<typename Particle> constexpr inline auto outputs(Particle const& part, std::size_t i) -> typename Particle::outputs_value_type const& { return part.outputs(i); } - // Same here but non const versions + + /** + * @brief Returns the underlying reference to the outputs array. + * + * @tparam Particle + * @param part + * @return Particle::outputs_type& + */ template<typename Particle> constexpr inline auto outputs(Particle& part) -> typename Particle::outputs_type& { return part.outputs(); } + + /** + * @brief Returns the ith component of the outputs. + * + * @tparam Particle + * @param part + * @param i + * @return Particle::outputs_value_type& + */ template<typename Particle> constexpr inline auto outputs(Particle const& part, std::size_t i) -> typename Particle::outputs_value_type& { return part.outputs(i); } - // Return the underlying tuple of variables (std::tuple<Ts...> or std::tuple<Ts&...>) + /** + * @brief Returns the underlying tuple of variables in a tuple (const version). + * + * @tparam Particle + * @param part + * @return Particle::variables_type const& + */ template<typename Particle> constexpr inline auto variables(Particle const& part) -> typename Particle::variables_type const& { return part.variables(); } + + /** + * @brief Returns the underlying tuple of variables in a tuple. + * + * @tparam Particle + * @param part + * @return Particle::variables_type const& + */ template<typename Particle> constexpr inline auto variables(Particle& part) -> typename Particle::variables_type& { diff --git a/include/scalfmm/container/particle_container.hpp b/include/scalfmm/container/particle_container.hpp index ab64e443b9b66a0ac8a34535a78f9a15c1cc0786..0a079182bb06a45985793b97c9ed59eb3c7bcd70 100644 --- a/include/scalfmm/container/particle_container.hpp +++ b/include/scalfmm/container/particle_container.hpp @@ -1,27 +1,22 @@ // -------------------------------- // See LICENCE file at project root -// File : container/particle_container.hpp +// File : scalfmm/container/particle_container.hpp // -------------------------------- #ifndef SCALFMM_CONTAINER_PARTICLE_CONTAINER_HPP #define SCALFMM_CONTAINER_PARTICLE_CONTAINER_HPP -#include <cstddef> -#include <iterator> -#include <numeric> -#include <tuple> -#include <type_traits> -#include <utility> -#include <vector> - #include "scalfmm/container/particle.hpp" #include "scalfmm/container/variadic_adaptor.hpp" -#include "scalfmm/meta/traits.hpp" #include "scalfmm/meta/type_pack.hpp" #include "scalfmm/meta/utils.hpp" +#include <cstddef> +#include <iterator> +#include <utility> +#include <vector> + namespace scalfmm::container { - template<typename Particle> /** * @brief This class stores the particles in the leaf of the tree. * @@ -37,37 +32,49 @@ namespace scalfmm::container * * The container is seen as a tuple structured in blocks. * - * \image html particles_container.svg "Particle container class" - * \image latex particles_container.pdf "Particle container class" width=0.5*\textwidth - * + * @tparam Particle * + * @image html particles_container.svg "Particle container class" + * @image latex particles_container.pdf "Particle container class" width=0.5*\textwidth */ + template<typename Particle> class particle_container : public variadic_container_tuple<particle_container<Particle>, typename Particle::tuple_type> { public: + using particle_type = Particle; + + using position_value_type = typename particle_type::position_value_type; + using inputs_value_type = typename particle_type::inputs_value_type; + using outputs_value_type = typename particle_type::outputs_value_type; + using variables_type = typename particle_type::variables_type; + + static constexpr std::size_t dimension = particle_type::dimension_size; + static constexpr std::size_t inputs_size = particle_type::inputs_size; + static constexpr std::size_t outputs_size = particle_type::outputs_size; + // base type : concatenating the particle tuple with the indexes needed // in order to allocate the vector - using tuple_type = typename meta::cat< - typename meta::pack_expand_tuple<meta::pack<Particle::dimension_size, typename Particle::position_value_type>, - meta::pack<Particle::inputs_size, typename Particle::inputs_value_type>, - meta::pack<Particle::outputs_size, typename Particle::outputs_value_type>>, - typename Particle::variables_type>::type; - using self_type = particle_container<Particle>; + using tuple_type = + typename meta::cat<typename meta::pack_expand_tuple<meta::pack<dimension, position_value_type>, + meta::pack<inputs_size, inputs_value_type>, + meta::pack<outputs_size, outputs_value_type>>, + variables_type>::type; + using self_type = particle_container<particle_type>; using base_type = variadic_container_tuple<self_type, tuple_type>; - using particle_type = Particle; - using value_type = Particle; - using proxy_type = typename Particle::proxy_type; - using const_proxy_type = typename Particle::const_proxy_type; + using value_type = particle_type; + using proxy_type = typename particle_type::proxy_type; + using const_proxy_type = typename particle_type::const_proxy_type; // Forwarding constructors using base_type::base_type; - /// @brief - /// - /// @param i - /// - /// @return + /** + * @brief + * + * @param i + * @return particle_type + */ [[nodiscard]] inline auto particle(std::size_t i) const -> particle_type { auto it{std::begin(*this)}; @@ -75,24 +82,51 @@ namespace scalfmm::container return particle_type(*it); } + /** + * @brief + * + * @param i + * @return proxy_type + */ [[nodiscard]] inline auto at(std::size_t i) -> proxy_type { auto it{std::begin(*this)}; std::advance(it, i); return proxy_type(*it); } + + /** + * @brief + * + * @param i + * @return const_proxy_type + */ [[nodiscard]] inline auto at(std::size_t i) const -> const_proxy_type { auto it{std::begin(*this)}; std::advance(it, i); return const_proxy_type(*it); } + + /** + * @brief + * + * @param i + * @return proxy_type + */ [[nodiscard]] inline auto operator[](std::size_t i) noexcept -> proxy_type { auto it{std::begin(*this)}; std::advance(it, i); return proxy_type(*it); } + + /** + * @brief + * + * @param i + * @return const_proxy_type + */ [[nodiscard]] inline auto operator[](std::size_t i) const noexcept -> const_proxy_type { auto it{std::begin(*this)}; @@ -100,18 +134,19 @@ namespace scalfmm::container return const_proxy_type(*it); } - /// - /// \brief size - /// \return the number of particles inside the container - /// + /** + * @brief + * + * @return the number of particles inside the container + */ [[nodiscard]] inline auto size() const -> std::size_t { return std::get<0>(base_type::all_size()); } - /// @brief - /// - /// @param i - /// @param p - /// - /// @return + /** + * @brief + * + * @param i + * @param p + */ inline auto insert_particle(std::size_t i, particle_type p) -> void { auto it{std::begin(*this)}; @@ -119,11 +154,12 @@ namespace scalfmm::container *it = p.as_tuple(); } - /// @brief - /// - /// @param i - /// - /// @return + /** + * @brief + * + * @param i + * @return particle_type::position_type + */ [[nodiscard]] inline auto position(std::size_t i) const -> typename particle_type::position_type { auto it{std::begin(*this)}; @@ -131,11 +167,12 @@ namespace scalfmm::container return meta::to_array(meta::sub_tuple(*it, typename particle_type::range_position_type{})); } - /// @brief - /// - /// @param i - /// - /// @return + /** + * @brief + * + * @param i + * @return auto + */ [[nodiscard]] inline auto position_as_tuple(std::size_t i) const { auto it{std::begin(*this)}; @@ -143,12 +180,12 @@ namespace scalfmm::container return meta::sub_tuple(*it, typename particle_type::range_position_type{}); } - /// @brief - /// - /// @param i - /// @param p - /// - /// @return + /** + * @brief + * + * @param i + * @param p + */ inline auto insert_position(std::size_t i, typename particle_type::position_type p) -> void { auto it{std::begin(*this)}; @@ -156,12 +193,12 @@ namespace scalfmm::container meta::sub_tuple(*it, typename particle_type::range_position_type{}) = meta::to_tuple(p); } - /// @brief - /// - /// @param i - /// @param p - /// - /// @return + /** + * @brief + * + * @param i + * @param p + */ inline auto insert_position(std::size_t i, typename particle_type::position_tuple_type p) -> void { auto it{std::begin(*this)}; @@ -169,11 +206,12 @@ namespace scalfmm::container meta::sub_tuple(*it, typename particle_type::range_position_type{}) = meta::to_tuple(p); } - /// @brief - /// - /// @param i - /// - /// @return + /** + * @brief + * + * @param i + * @return particle_type::inputs_type + */ [[nodiscard]] inline auto inputs(std::size_t i) const -> typename particle_type::inputs_type { auto it{std::begin(*this)}; @@ -181,11 +219,12 @@ namespace scalfmm::container return meta::to_array(meta::sub_tuple(*it, typename particle_type::range_inputs_type{})); } - /// @brief - /// - /// @param i - /// - /// @return + /** + * @brief + * + * @param i + * @return auto + */ [[nodiscard]] inline auto inputs_as_tuple(std::size_t i) const { auto it{std::begin(*this)}; @@ -193,12 +232,12 @@ namespace scalfmm::container return meta::sub_tuple(*it, typename particle_type::range_inputs_type{}); } - /// @brief insert an input to particle i - /// - /// @param i - /// @param in_input the input to insert - /// - /// @return + /** + * @brief + * + * @param i + * @param in_input + */ inline auto insert_inputs(std::size_t i, typename particle_type::inputs_type in_input) -> void { auto it{std::begin(*this)}; @@ -206,12 +245,12 @@ namespace scalfmm::container meta::sub_tuple(*it, typename particle_type::range_inputs_type{}) = meta::to_tuple(in_input); } - /// @brief - /// - /// @param i - /// @param p - /// - /// @return + /** + * @brief + * + * @param i + * @param p + */ inline auto insert_inputs(std::size_t i, typename particle_type::inputs_tuple_type p) -> void { auto it{std::begin(*this)}; @@ -219,11 +258,12 @@ namespace scalfmm::container meta::sub_tuple(*it, typename particle_type::range_inputs_type{}) = p; } - /// @brief - /// - /// @param i - /// - /// @return + /** + * @brief + * + * @param i + * @return particle_type::outputs_type + */ [[nodiscard]] inline auto outputs(std::size_t i) const -> typename particle_type::outputs_type { auto it{std::begin(*this)}; @@ -231,11 +271,12 @@ namespace scalfmm::container return meta::to_array(meta::sub_tuple(*it, typename particle_type::range_outputs_type{})); } - /// @brief - /// - /// @param i - /// - /// @return + /** + * @brief + * + * @param i + * @return auto + */ [[nodiscard]] inline auto outputs_as_tuple(std::size_t i) const { auto it{std::begin(*this)}; @@ -243,12 +284,12 @@ namespace scalfmm::container return meta::sub_tuple(*it, typename particle_type::range_outputs_type{}); } - /// @brief - /// - /// @param i - /// @param p - /// - /// @return + /** + * @brief + * + * @param i + * @param p + */ inline auto insert_outputs(std::size_t i, typename particle_type::outputs_type p) -> void { auto it{std::begin(*this)}; @@ -256,12 +297,12 @@ namespace scalfmm::container meta::sub_tuple(*it, typename particle_type::range_outputs_type{}) = meta::to_tuple(p); } - /// @brief - /// - /// @param i - /// @param p - /// - /// @return + /** + * @brief + * + * @param i + * @param p + */ inline auto insert_outputs(std::size_t i, typename particle_type::outputs_tuple_type p) -> void { auto it{std::begin(*this)}; @@ -269,11 +310,12 @@ namespace scalfmm::container meta::sub_tuple(*it, typename particle_type::range_outputs_type{}) = p; } - /// @brief - /// - /// @param i - /// - /// @return + /** + * @brief + * + * @param i + * @return particle_type::variables_type + */ [[nodiscard]] inline auto variables(std::size_t i) const -> typename particle_type::variables_type { auto it{std::begin(*this)}; @@ -281,12 +323,12 @@ namespace scalfmm::container return meta::sub_tuple(*it, typename particle_type::range_variables_type{}); } - /// @brief - /// - /// @param i - /// @param p - /// - /// + /** + * @brief + * + * @param i + * @param p + */ inline auto insert_variables(std::size_t i, typename particle_type::variables_type p) -> void { auto it{std::begin(*this)}; @@ -294,45 +336,145 @@ namespace scalfmm::container meta::sub_tuple(*it, typename particle_type::range_variables_type{}) = p; } - /// - /// \brief reset the outputs in the container - /// - inline auto reset_outputs() -> void + /** + * @brief + * + */ + inline auto reset_positions() noexcept -> void { - using value_type = typename Particle::outputs_value_type; - auto it = std::begin(*this); - for(std::size_t i{0}; i < this->size(); ++i) + auto it_begin{std::begin(*this)}; + auto it_end{std::end(*this)}; + + while(it_begin != it_end) { - auto proxy = proxy_type(*it); + auto proxy = proxy_type(*it_begin); + for(std::size_t ii{0}; ii < dimension; ++ii) + { + proxy.position(ii) = position_value_type(0.0); + } + ++it_begin; + } + } + + /** + * @brief + * + */ + inline auto reset_inputs() noexcept -> void + { + auto it_begin{std::begin(*this)}; + auto it_end{std::end(*this)}; - for(std::size_t ii{0}; ii < particle_type::outputs_size; ++ii) + while(it_begin != it_end) + { + auto proxy = proxy_type(*it_begin); + for(std::size_t ii{0}; ii < inputs_size; ++ii) { - proxy.outputs(ii) = value_type(0.0); + proxy.inputs(ii) = inputs_value_type(0.0); } - ++it; + ++it_begin; } } + + /** + * @brief + * + */ + inline auto reset_outputs() noexcept -> void + { + auto it_begin{std::begin(*this)}; + auto it_end{std::end(*this)}; + + while(it_begin != it_end) + { + auto proxy = proxy_type(*it_begin); + for(std::size_t ii{0}; ii < outputs_size; ++ii) + { + proxy.outputs(ii) = outputs_value_type(0.0); + } + ++it_begin; + } + } + + /** + * @brief + * + */ + inline auto reset_variables() noexcept -> void + { + auto it_begin{std::begin(*this)}; + auto it_end{std::end(*this)}; + + while(it_begin != it_end) + { + auto proxy = proxy_type(*it_begin); + proxy.variables() = variables_type{}; + ++it_begin; + } + } + + /** + * @brief + * + */ + inline auto reset_particles() noexcept -> void + { + auto it_begin{std::begin(*this)}; + auto it_end{std::end(*this)}; + + while(it_begin != it_end) + { + auto proxy = proxy_type(*it_begin); + for(std::size_t ii{0}; ii < dimension; ++ii) + { + proxy.position(ii) = position_value_type(0.0); + } + for(std::size_t ii{0}; ii < inputs_size; ++ii) + { + proxy.inputs(ii) = inputs_value_type(0.0); + } + for(std::size_t ii{0}; ii < outputs_size; ++ii) + { + proxy.outputs(ii) = outputs_value_type(0.0); + } + proxy.variables() = variables_type{}; + ++it_begin; + } + } + + /** + * @brief + * + * @param os + * @param container + * @return std::ostream& + */ inline friend auto operator<<(std::ostream& os, const particle_container& container) -> std::ostream& { for(std::size_t i{0}; i < container.size(); ++i) { auto const& p = container.at(i); - std::cout << i << " " << p << std::endl; + std::cout << i << " " << p << std::endl; } - return os ; + return os; } - /// @brief - /// - /// @return + + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto position_begin() noexcept { using position_range = typename container::particle_traits<particle_type>::range_position_type; return this->template sbegin<position_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto position_begin() const noexcept { using position_range = typename container::particle_traits<particle_type>::range_position_type; @@ -348,117 +490,143 @@ namespace scalfmm::container return this->template send<position_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto position_end() const noexcept { using position_range = typename container::particle_traits<particle_type>::range_position_type; return this->template send<position_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto inputs_begin() noexcept { using inputs_range = typename container::particle_traits<particle_type>::range_inputs_type; return this->template sbegin<inputs_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto inputs_begin() const noexcept { using inputs_range = typename container::particle_traits<particle_type>::range_inputs_type; return this->template sbegin<inputs_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto inputs_end() noexcept { using inputs_range = typename container::particle_traits<particle_type>::range_inputs_type; return this->template send<inputs_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto inputs_end() const noexcept { using inputs_range = typename container::particle_traits<particle_type>::range_inputs_type; return this->template send<inputs_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto outputs_begin() noexcept { using outputs_range = typename container::particle_traits<particle_type>::range_outputs_type; return this->template sbegin<outputs_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto outputs_begin() const noexcept { using outputs_range = typename container::particle_traits<particle_type>::range_outputs_type; return this->template sbegin<outputs_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto outputs_end() noexcept { using outputs_range = typename container::particle_traits<particle_type>::range_outputs_type; return this->template send<outputs_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto outputs_end() const noexcept { using outputs_range = typename container::particle_traits<particle_type>::range_outputs_type; return this->template send<outputs_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto variables_begin() noexcept { using variables_range = typename container::particle_traits<particle_type>::range_variables_type; return this->template sbegin<variables_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto variables_begin() const noexcept { using variables_range = typename container::particle_traits<particle_type>::range_variables_type; return this->template sbegin<variables_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto variables_end() noexcept { using variables_range = typename container::particle_traits<particle_type>::range_variables_type; return this->template send<variables_range>(); } - /// @brief - /// - /// @return + /** + * @brief + * + * @return constexpr auto + */ [[nodiscard]] constexpr inline auto variables_end() const noexcept { using variables_range = typename container::particle_traits<particle_type>::range_variables_type; @@ -466,96 +634,104 @@ namespace scalfmm::container } }; - /// @brief - /// - /// @tparam C - /// @param c - /// - /// @return + /** + * @brief + * + * @tparam C + * @param c + * @return constexpr auto + */ template<typename C> constexpr inline auto position_begin(C&& c) { return std::forward<C>(c).position_begin(); } - /// @brief - /// - /// @tparam C - /// @param c - /// - /// @return + /** + * @brief + * + * @tparam C + * @param c + * @return constexpr auto + */ template<typename C> constexpr inline auto position_end(C&& c) { return std::forward<C>(c).position_end(); } - /// @brief - /// - /// @tparam C - /// @param c - /// - /// @return + /** + * @brief + * + * @tparam C + * @param c + * @return constexpr auto + */ template<typename C> constexpr inline auto inputs_begin(C&& c) { return std::forward<C>(c).inputs_begin(); } - /// @brief - /// - /// @tparam C - /// @param c - /// - /// @return + /** + * @brief + * + * @tparam C + * @param c + * @return constexpr auto + */ template<typename C> constexpr inline auto inputs_end(C&& c) { return std::forward<C>(c).inputs_end(); } - /// @brief - /// - /// @tparam C - /// @param c - /// - /// @return + /** + * @brief + * + * @tparam C + * @param c + * @return constexpr auto + */ template<typename C> constexpr inline auto outputs_begin(C&& c) { return std::forward<C>(c).outputs_begin(); } - /// @brief - /// - /// @tparam C - /// @param c - /// - /// @return + /** + * @brief + * + * @tparam C + * @param c + * @return constexpr auto + */ template<typename C> constexpr inline auto outputs_end(C&& c) { return std::forward<C>(c).outputs_end(); } - /// @brief - /// - /// @tparam C - /// @param c - /// - /// @return + /** + * @brief + * + * @tparam C + * @param c + * @return constexpr auto + */ template<typename C> constexpr inline auto variables_begin(C&& c) { return std::forward<C>(c).variables_begin(); } - /// @brief - /// - /// @tparam C - /// @param c - /// - /// @return + /** + * @brief + * + * @tparam C + * @param c + * @return constexpr auto + */ template<typename C> constexpr inline auto variables_end(C&& c) { diff --git a/include/scalfmm/container/particle_impl.hpp b/include/scalfmm/container/particle_impl.hpp index 9e521fcb4d6e47d9eb9baa13f76ad0d3f6946dea..eb9ce3779a5b4d435f4b26d90ec4a701fcf9a38d 100644 --- a/include/scalfmm/container/particle_impl.hpp +++ b/include/scalfmm/container/particle_impl.hpp @@ -1,80 +1,80 @@ - // -------------------------------- // See LICENCE file at project root -// File : particle.hpp +// File : scalfmm/container/particle_impl.hpp // -------------------------------- #ifndef SCALFMM_CONTAINER_PARTICLE_IMPL_HPP #define SCALFMM_CONTAINER_PARTICLE_IMPL_HPP +#include "scalfmm/container/point.hpp" +#include "scalfmm/meta/type_pack.hpp" +#include "scalfmm/meta/utils.hpp" #include <inria/integer_sequence.hpp> -#include <scalfmm/container/point.hpp> -#include <scalfmm/meta/type_pack.hpp> + +#include <array> +#include <cstddef> #include <iterator> #include <tuple> #include <type_traits> -#include <array> -#include <cstddef> - -#include "scalfmm/meta/utils.hpp" -///** -// * \brief Multi-purpose particle implementation -// * -// * This template implementation of a particle allows simple reuse for several -// * use cases. The aim it to provide an interface that is compatible with the -// * rest of ScalFMM. It is mainly intended to be used as an interface for the -// * particle containers. -// * -// * The Types parameter pack can accept any type that is to be considered as a -// * particle attribute. You can also specify scalfmm::pack type to factorize -// * several types. -// * -// * In the following example, the two specialisations of the class will give the -// * same final structure. -// * -// * ``` -// * using FReal = double; -// * static constexpr std::size_t dimension = 3; -// * -// * particle<FReal, dimension, int, float, float, float, float>; -// * particle<FReal, dimension, int, scalfmm::meta::pack<4, float> >; -// * ``` -// * -// * The base of these two classes is -// * ``` -// * std::tuple<double, double, double, int, float, float, float, float>; -// * ``` -// * -// * \warning Although the classes will have the same final layout, C++ considers -// * these two classes to be different ! -// * -// * ##### Example -// * -// * ``` -// * // Define a 3D particle with an int attribute -// * using Particle = particle<double, 3, int>; -// * -// * Particle p; -// * p.get<> -// * ``` -// * -// * -// * \tparam FReal Floating point type -// * \tparam dimension Space dimension count -// * \tparam Types Attributes type list -// * -// */ +/** + * @brief Multi-purpose particle implementation + * + * This template implementation of a particle allows simple reuse for several + * use cases. The aim it to provide an interface that is compatible with the + * rest of ScalFMM. It is mainly intended to be used as an interface for the + * particle containers. + * + * The Types parameter pack can accept any type that is to be considered as a + * particle attribute. You can also specify scalfmm::pack type to factorize + * several types. + * + * In the following example, the two specialisations of the class will give the + * same final structure. + * + * ``` + * using FReal = double; + * static constexpr std::size_t dimension = 3; + * + * particle<FReal, dimension, int, float, float, float, float>; + * particle<FReal, dimension, int, scalfmm::meta::pack<4, float> >; + * ``` + * + * The base of these two classes is + * ``` + * std::tuple<double, double, double, int, float, float, float, float>; + * ``` + * + * \warning Although the classes will have the same final layout, C++ considers + * these two classes to be different ! + * + * ##### Example + * + * ``` + * // Define a 3D particle with an int attribute + * using Particle = particle<double, 3, int>; + * + * Particle p; + * p.get<> + * ``` + * + * + * @tparam FReal Floating point type + * @tparam dimension Space dimension count + * @tparam Types Attributes type list + */ namespace scalfmm::container { - /// @brief - /// - /// @tparam PositionType - /// @tparam PositionDim - /// @tparam InputsType - /// @tparam NInputs - /// @tparam OutputsType - /// @tparam MOutputs - /// @tparam Variables + /** + * @brief + * + * @tparam PositionType + * @tparam PositionDim + * @tparam InputsType + * @tparam NInputs + * @tparam OutputsType + * @tparam MOutputs + * @tparam Variables + */ template<typename PositionType, std::size_t PositionDim, typename InputsType, std::size_t NInputs, typename OutputsType, std::size_t MOutputs, typename... Variables> struct particle_impl @@ -112,21 +112,54 @@ namespace scalfmm::container meta::pack<outputs_size, outputs_value_type>>, variables_type>::type; + /** + * @brief + * + */ constexpr particle_impl() = default; + + /** + * @brief + * + */ constexpr particle_impl(particle_impl const&) = default; + + /** + * @brief + * + */ constexpr particle_impl(particle_impl&&) noexcept = default; + + /** + * @brief + * + * @return particle_impl& + */ constexpr inline auto operator=(particle_impl const&) -> particle_impl& = default; + + /** + * @brief + * + * @return particle_impl& + */ constexpr inline auto operator=(particle_impl&&) noexcept -> particle_impl& = default; + + /** + * @brief Destroy the particle impl object + * + */ ~particle_impl() = default; - /// @brief - /// - /// @param p position of the particle - /// @param i input associated to the particle - /// @param o output associated to the particle - /// @param vs variables - /// - /// @return + /** + * @brief + * + * @param p position of the particle + * @param i input associated to the particle + * @param o output associated to the particle + * @param vs variables + * + * @return + */ constexpr particle_impl(position_type p, inputs_value_type i, outputs_value_type o, Variables... vs) : m_position(p) , m_variables(vs...) @@ -135,14 +168,17 @@ namespace scalfmm::container std::fill(std::begin(m_outputs), std::end(m_outputs), o); } - /// @brief - /// - /// @param p - /// @param i - /// @param o - /// @param vs - /// - /// @return + /** + * @brief + * + * @param p + * @param i + * @param o + * @param vs + * + * @return + * + */ constexpr particle_impl(position_type p, inputs_type i, outputs_type o, Variables... vs) : m_position(p) , m_inputs(i) @@ -151,9 +187,11 @@ namespace scalfmm::container { } - /// @brief - /// - /// @param t + /** + * @brief Construct a new particle impl object + * + * @param t + */ particle_impl(tuple_type t) : m_position(meta::to_array(meta::sub_tuple(t, range_position_type{}))) , m_inputs{meta::to_array(meta::sub_tuple(t, range_inputs_type{}))} @@ -162,60 +200,180 @@ namespace scalfmm::container { } + /** + * @brief + * + * @return tuple_type + */ [[nodiscard]] constexpr inline auto as_tuple() const noexcept -> tuple_type { return std::tuple_cat(meta::to_tuple(m_position), meta::to_tuple(m_inputs), meta::to_tuple(m_outputs), m_variables); } + /** + * @brief + * + * @param p + */ constexpr inline auto position(position_type p) noexcept -> void { m_position = p; } + + /** + * @brief + * + * @return position_type const& + */ [[nodiscard]] constexpr inline auto position() const noexcept -> position_type const& { return m_position; } + + /** + * @brief + * + * @return position_type& + */ [[nodiscard]] constexpr inline auto position() noexcept -> position_type& { return m_position; } + /** + * @brief + * + * @param i + * @return position_value_type + */ [[nodiscard]] constexpr inline auto position(std::size_t i) const noexcept -> position_value_type { return m_position.at(i); } + + /** + * @brief + * + * @param i + * @return position_value_type& + */ [[nodiscard]] constexpr inline auto position(std::size_t i) noexcept -> position_value_type& { return m_position.at(i); } + /** + * @brief + * + * @param i + */ constexpr inline auto inputs(inputs_type i) noexcept -> void { m_inputs = i; } + + /** + * @brief + * + * @return inputs_type const& + */ [[nodiscard]] constexpr inline auto inputs() const noexcept -> inputs_type const& { return m_inputs; } + + /** + * @brief + * + * @return inputs_type& + */ [[nodiscard]] constexpr inline auto inputs() noexcept -> inputs_type& { return m_inputs; } + /** + * @brief + * + * @param i + * @return inputs_value_type + */ [[nodiscard]] constexpr inline auto inputs(std::size_t i) const noexcept -> inputs_value_type { return m_inputs.at(i); } + + /** + * @brief + * + * @param i + * @return inputs_value_type& + */ [[nodiscard]] constexpr inline auto inputs(std::size_t i) noexcept -> inputs_value_type& { return m_inputs.at(i); } + /** + * @brief + * + * @param i + */ constexpr inline auto outputs(outputs_type i) noexcept -> void { m_outputs = i; } + + /** + * @brief + * + * @return outputs_type const& + */ [[nodiscard]] constexpr inline auto outputs() const noexcept -> outputs_type const& { return m_outputs; } + + /** + * @brief + * + * @return outputs_type& + */ [[nodiscard]] constexpr inline auto outputs() noexcept -> outputs_type& { return m_outputs; } + /** + * @brief + * + * @param i + * @return outputs_value_type + */ [[nodiscard]] constexpr inline auto outputs(std::size_t i) const noexcept -> outputs_value_type { return m_outputs.at(i); } + + /** + * @brief + * + * @param i + * @return outputs_value_type& + */ [[nodiscard]] constexpr inline auto outputs(std::size_t i) noexcept -> outputs_value_type& { return m_outputs.at(i); } + /** + * @brief + * + * @return variables_type const& + */ [[nodiscard]] constexpr inline auto variables() const noexcept -> variables_type const& { return m_variables; } + + /** + * @brief + * + * @return variables_type& + */ [[nodiscard]] constexpr inline auto variables() noexcept -> variables_type& { return m_variables; } + /** + * @brief + * + * @tparam T + * @param v + * @return std::enable_if_t<std::is_same_v<T, variables_type>, void> + */ template<typename T> constexpr inline auto variables(T v) noexcept -> std::enable_if_t<std::is_same_v<T, variables_type>, void> { m_variables = v; } + /** + * @brief + * + * @tparam Vs + * @param vs + * @return std::enable_if_t<(sizeof...(Vs) != 0), void> + */ template<typename... Vs> constexpr inline auto variables(Vs... vs) noexcept -> std::enable_if_t<(sizeof...(Vs) != 0), void> { diff --git a/include/scalfmm/container/particle_proxy.hpp b/include/scalfmm/container/particle_proxy.hpp index 4d69613783308694e4b0162a3fda2a6b41b3349a..1e482b99d0090f6fc7ace6bea258d342c02fb6ec 100644 --- a/include/scalfmm/container/particle_proxy.hpp +++ b/include/scalfmm/container/particle_proxy.hpp @@ -1,27 +1,37 @@ // -------------------------------- // See LICENCE file at project root -// File : particle_proxy.hpp +// File : scalfmm/container/particle_proxy.hpp // -------------------------------- #ifndef SCALFMM_CONTAINER_PARTICLE_PROXY_HPP #define SCALFMM_CONTAINER_PARTICLE_PROXY_HPP +#include "scalfmm/container/point.hpp" +#include "scalfmm/container/reference_sequence.hpp" +#include "scalfmm/meta/type_pack.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/utils/io_helpers.hpp" + #include <array> #include <cstddef> #include <functional> -#include <scalfmm/container/point.hpp> -#include <scalfmm/meta/type_pack.hpp> #include <tuple> #include <type_traits> -#include "scalfmm/container/reference_sequence.hpp" -#include "scalfmm/meta/utils.hpp" -#include "scalfmm/utils/io_helpers.hpp" - namespace scalfmm::container { - /// Proxy for particle + /** + * @brief + * + * @tparam PositionType + * @tparam PositionDim + * @tparam InputsType + * @tparam NInputs + * @tparam OutputsType + * @tparam NOutputs + * @tparam Variables + */ template<typename PositionType, std::size_t PositionDim, typename InputsType, std::size_t NInputs, - typename OutputsType, std::size_t MOutputs, typename... Variables> + typename OutputsType, std::size_t NOutputs, typename... Variables> struct particle_proxy { public: @@ -41,7 +51,7 @@ namespace scalfmm::container using range_inputs_type = meta::make_range_sequence<dimension_size, dimension_size + inputs_size>; using outputs_value_type = OutputsType; - static constexpr std::size_t outputs_size = MOutputs; + static constexpr std::size_t outputs_size = NOutputs; using outputs_type = std::array<std::reference_wrapper<outputs_value_type>, outputs_size>; using const_outputs_type = std::array<std::reference_wrapper<std::add_const_t<outputs_value_type>>, outputs_size>; @@ -66,15 +76,49 @@ namespace scalfmm::container meta::pack<outputs_size, std::add_lvalue_reference_t<outputs_value_type>>>, variables_type>::type; + /** + * @brief + * + */ constexpr particle_proxy() = delete; + + /** + * @brief + * + */ constexpr particle_proxy(particle_proxy const&) = default; + + /** + * @brief + * + */ constexpr particle_proxy(particle_proxy&&) noexcept = default; + /** + * @brief + * + * @return particle_proxy& + */ constexpr inline auto operator=(particle_proxy const&) -> particle_proxy& = default; + + /** + * @brief + * + * @return particle_proxy& + */ constexpr inline auto operator=(particle_proxy&&) noexcept -> particle_proxy& = default; + /** + * @brief Destroy the particle proxy object + * + */ ~particle_proxy() = default; + /** + * @brief Construct a new particle proxy object + * + * @param t + */ particle_proxy(tuple_type t) : m_position(container::get_reference_sequence(meta::sub_tuple(t, range_position_type{}))) , m_inputs(container::get_reference_sequence(meta::sub_tuple(t, range_inputs_type{}))) @@ -83,85 +127,167 @@ namespace scalfmm::container { } + /** + * @brief + * + * @return tuple_type + */ [[nodiscard]] constexpr inline auto as_tuple() const noexcept -> tuple_type { return std::tuple_cat(meta::to_tuple(m_position), meta::to_tuple(m_inputs), meta::to_tuple(m_outputs), m_variables); } + /** + * @brief + * + * @return const_position_type const& + */ [[nodiscard]] constexpr inline auto position() const noexcept -> const_position_type const& { return m_position; } + + /** + * @brief + * + * @return position_type& + */ [[nodiscard]] constexpr inline auto position() noexcept -> position_type& { return m_position; } + /** + * @brief + * + * @param i + * @return position_value_type const& + */ [[nodiscard]] constexpr inline auto position(std::size_t i) const noexcept -> position_value_type const& { return m_position.at(i); } + + /** + * @brief + * + * @param i + * @return position_value_type& + */ [[nodiscard]] constexpr inline auto position(std::size_t i) noexcept -> position_value_type& { return m_position.at(i); } + /** + * @brief + * + * @return const_inputs_type const& + */ [[nodiscard]] constexpr inline auto inputs() const noexcept -> const_inputs_type const& { return m_inputs; } + + /** + * @brief + * + * @return inputs_type& + */ [[nodiscard]] constexpr inline auto inputs() noexcept -> inputs_type& { return m_inputs; } + /** + * @brief + * + * @param i + * @return inputs_value_type const& + */ [[nodiscard]] constexpr inline auto inputs(std::size_t i) const noexcept -> inputs_value_type const& { return m_inputs.at(i).get(); } + + /** + * @brief + * + * @param i + * @return inputs_value_type& + */ [[nodiscard]] constexpr inline auto inputs(std::size_t i) noexcept -> inputs_value_type& { return m_inputs.at(i).get(); } + /** + * @brief + * + * @return const_outputs_type const& + */ [[nodiscard]] constexpr inline auto outputs() const noexcept -> const_outputs_type const& { return m_outputs; } + + /** + * @brief + * + * @return outputs_type& + */ [[nodiscard]] constexpr inline auto outputs() noexcept -> outputs_type& { return m_outputs; } + /** + * @brief + * + * @param i + * @return outputs_value_type const& + */ [[nodiscard]] constexpr inline auto outputs(std::size_t i) const noexcept -> outputs_value_type const& { return m_outputs.at(i).get(); } + + /** + * @brief + * + * @param i + * @return outputs_value_type& + */ [[nodiscard]] constexpr inline auto outputs(std::size_t i) noexcept -> outputs_value_type& { return m_outputs.at(i).get(); } - [[nodiscard]] constexpr inline auto variables() const noexcept -> const_variables_type - { - return m_variables; - } + /** + * @brief + * + * @return const_variables_type + */ + [[nodiscard]] constexpr inline auto variables() const noexcept -> const_variables_type { return m_variables; } + + /** + * @brief + * + * @return variables_type& + */ [[nodiscard]] constexpr inline auto variables() noexcept -> variables_type& { return m_variables; } + /** + * @brief + * + * @tparam T + * @param v + * @return std::enable_if_t<std::is_same_v<T, variables_type>, void> + */ template<typename T> constexpr inline auto variables(T v) noexcept -> std::enable_if_t<std::is_same_v<T, variables_type>, void> { m_variables = v; } + /** + * @brief + * + * @tparam Vs + * @param vs + * @return std::enable_if_t<(sizeof...(Vs) != 0), void> + */ template<typename... Vs> constexpr inline auto variables(Vs... vs) noexcept -> std::enable_if_t<(sizeof...(Vs) != 0), void> { m_variables = std::make_tuple(vs...); } - // inline friend auto operator<<(std::ostream& os, particle_proxy const& proxy) -> std::ostream& - // { - // os << proxy.position(); - // if constexpr(inputs_size > 0) - // { - // io::print(os, proxy.inputs()); - // } - // if constexpr(outputs_size > 0) - // { - // io::print(os, proxy.outputs()); - // } - // if constexpr(variables_size > 0) - // { - // io::print(os, proxy.variables()); - // } - // return os; - // } private: std::conditional_t<is_const_proxy, const_position_type, position_type> m_position; diff --git a/include/scalfmm/container/point.hpp b/include/scalfmm/container/point.hpp index 490c07bbf4036302e6692f1940b663747fabf542..2f8cddd3daf940df7ae248124c1402b7af15bc3f 100644 --- a/include/scalfmm/container/point.hpp +++ b/include/scalfmm/container/point.hpp @@ -1,50 +1,103 @@ -// See LICENCE file at project root -// +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/container/point.hpp +// -------------------------------- #ifndef SCALFMM_CONTAINER_POINT_HPP #define SCALFMM_CONTAINER_POINT_HPP -#include <scalfmm/container/reference_sequence.hpp> -#include <scalfmm/meta/utils.hpp> +#include "scalfmm/container/reference_sequence.hpp" +#include "scalfmm/meta/traits.hpp" +#include "scalfmm/meta/utils.hpp" + #include <array> +#include <cstddef> +#include <functional> #include <initializer_list> +#include <limits> #include <ostream> #include <tuple> #include <type_traits> -#include <functional> -#include <cstddef> -#include <limits> - -#include "scalfmm/meta/traits.hpp" namespace scalfmm::container { - // classic point implementation - template<typename Arithmetic, std::size_t Dim> - struct point_impl : public std::array<Arithmetic, Dim> + /** + * @brief classic point implementation + * + * @tparam Arithmetic + * @tparam Dimension + */ + template<typename Arithmetic, std::size_t Dimension> + struct point_impl : public std::array<Arithmetic, Dimension> { public: - using base_type = std::array<Arithmetic, Dim>; + using base_type = std::array<Arithmetic, Dimension>; /// Floating number type using value_type = Arithmetic; /// Dimension type using dimension_type = std::size_t; /// Space dimension count - constexpr static const std::size_t dimension = Dim; + constexpr static const std::size_t dimension = Dimension; + /** + * @brief Construct a new point impl object + * + * @param l + */ point_impl(std::initializer_list<value_type> l) { std::copy(l.begin(), l.end(), this->begin()); } + /** + * @brief Construct a new point impl object + * + */ point_impl() = default; + + /** + * @brief Construct a new point impl object + * + */ point_impl(point_impl const&) = default; + + /** + * @brief Construct a new point impl object + * + */ point_impl(point_impl&&) noexcept = default; + + /** + * @brief + * + * @return point_impl& + */ auto operator=(point_impl const&) -> point_impl& = default; + + /** + * @brief + * + * @return point_impl& + */ auto operator=(point_impl&&) noexcept -> point_impl& = default; + + /** + * @brief Destroy the point impl object + * + */ ~point_impl() = default; + /** + * @brief Construct a new point impl object + * + * @param a + */ point_impl(std::array<value_type, dimension> a) : base_type(a) { } + /** + * @brief Construct a new point impl object + * + * @param to_splat + */ explicit point_impl(value_type to_splat) { for(std::size_t i = 0; i < dimension; ++i) @@ -53,6 +106,12 @@ namespace scalfmm::container } } + /** + * @brief Construct a new point impl object + * + * @tparam U + * @param other + */ template<typename U> point_impl(point_impl<U, dimension> const& other) { @@ -62,6 +121,12 @@ namespace scalfmm::container } } + /** + * @brief Construct a new point impl object + * + * @tparam U + * @param other + */ template<typename U> explicit point_impl(U const* other) { @@ -72,12 +137,17 @@ namespace scalfmm::container } }; - // proxy for point - template<typename Arithmetic, std::size_t Dim> - struct point_proxy : public std::array<std::reference_wrapper<Arithmetic>, Dim> + /** + * @brief proxy for point. + * + * @tparam Arithmetic + * @tparam Dimension + */ + template<typename Arithmetic, std::size_t Dimension> + struct point_proxy : public std::array<std::reference_wrapper<Arithmetic>, Dimension> { public: - using base_type = std::array<std::reference_wrapper<Arithmetic>, Dim>; + using base_type = std::array<std::reference_wrapper<Arithmetic>, Dimension>; /// Floating number type using value_type = std::decay_t<Arithmetic>; /// Reference wrapper type @@ -85,48 +155,123 @@ namespace scalfmm::container using const_reference_wrapper_type = std::reference_wrapper<std::add_const_t<value_type>>; /// Dimension type using dimension_type = std::size_t; - /// Space dimension count - constexpr static const std::size_t dimension = Dim; + /** + * @brief Space dimension (static) + * + */ + constexpr static const std::size_t dimension = Dimension; + + /** + * @brief Construct a new point proxy object + * + */ point_proxy() = delete; + + /** + * @brief Construct a new point proxy object + * + */ point_proxy(point_proxy const&) = default; + + /** + * @brief Construct a new point proxy object + * + */ point_proxy(point_proxy&&) noexcept = default; + + /** + * @brief + * + * @return point_proxy& + */ [[nodiscard]] auto operator=(point_proxy const&) -> point_proxy& = default; + + /** + * @brief + * + * @return point_proxy& + */ [[nodiscard]] auto operator=(point_proxy&&) noexcept -> point_proxy& = default; + + /** + * @brief Destroy the point proxy object + * + */ ~point_proxy() = default; + /** + * @brief Construct a new point proxy object + * + * @param a + */ explicit point_proxy(std::array<value_type, dimension>& a) : base_type(get_reference_sequence(a)) { } + /** + * @brief Construct a new point proxy object + * + * @param a + */ explicit point_proxy(std::array<value_type, dimension> const& a) : base_type(get_reference_sequence(a)) { } + /** + * @brief Construct a new point proxy object + * + * @param a + */ explicit point_proxy(std::array<reference_wrapper_type, dimension> const& a) : base_type(a) { } + + /** + * @brief Construct a new point proxy object + * + * @param a + */ explicit point_proxy(std::array<const_reference_wrapper_type, dimension> const& a) : base_type(a) { } + /** + * @brief Construct a new point proxy object + * + * @tparam Ts + * @param a + */ template<typename... Ts, std::enable_if_t<meta::all(std::is_same_v<value_type, Ts>...), int> = 0> explicit point_proxy(std::tuple<Ts...>& a) : base_type(get_reference_sequence(a)) { } + /** + * @brief Construct a new point proxy object + * + * @tparam Ts + * @param a + */ template<typename... Ts, std::enable_if_t<meta::all(std::is_same_v<value_type, Ts>...), int> = 0> explicit point_proxy(std::tuple<Ts...> const& a) : base_type(get_reference_sequence(a)) { } - // constructor from tuples of references + /** + * @brief Construct a new point proxy object + * + * constructor from tuples of references + * + * @tparam Ts + * @param a + */ template<typename... Ts, std::enable_if_t< meta::all(std::is_same_v<std::add_lvalue_reference_t<value_type>, Ts>...) || @@ -137,39 +282,90 @@ namespace scalfmm::container { } - // element access function returning the underlying reference + /** + * @brief element access function returning the underlying reference. + * + * @param pos + * @return value_type& + */ [[nodiscard]] constexpr inline auto at(std::size_t pos) -> value_type& { return base_type::at(pos).get(); } - [[nodiscard]] constexpr inline auto at(std::size_t pos) const -> value_type const& { return base_type::at(pos).get(); } + /** + * @brief element access function returning the underlying reference. + * + * @param pos + * @return value_type const& + */ + [[nodiscard]] constexpr inline auto at(std::size_t pos) const -> value_type const& + { + return base_type::at(pos).get(); + } + + /** + * @brief + * + * @param pos + * @return value_type& + */ [[nodiscard]] constexpr inline auto operator[](std::size_t pos) -> value_type& { return at(pos); } + + /** + * @brief + * + * @param pos + * @return value_type const& + */ [[nodiscard]] constexpr inline auto operator[](std::size_t pos) const -> value_type const& { return at(pos); } }; - // entry point and test if the type is arithmetic - template<typename Arithmetic, std::size_t Dim = 3, typename Enable = void> + /** + * @brief entry point and test if the type is arithmetic + * + * @tparam Arithmetic + * @tparam Dimension + * @tparam Enable + */ + template<typename Arithmetic, std::size_t Dimension = 3, typename Enable = void> struct point { static_assert(meta::is_arithmetic<std::decay_t<Arithmetic>>::value, "Point's inner type should be arithmetic!"); }; - // selection on the template parameter - // if Arithmetic is a ref with get a proxy, if not a classic point. - template<typename Arithmetic, std::size_t Dim> - struct point<Arithmetic, Dim, typename std::enable_if<meta::is_arithmetic<std::decay_t<Arithmetic>>::value>::type> - : std::conditional_t<std::is_reference_v<Arithmetic>, point_proxy<std::remove_reference_t<Arithmetic>, Dim>, - point_impl<Arithmetic, Dim>> + /** + * @brief + * + * selection on the template parameter + * if Arithmetic is a ref with get a proxy, if not a classic point. + * + * @tparam Arithmetic + * @tparam Dimension + */ + template<typename Arithmetic, std::size_t Dimension> + struct point<Arithmetic, Dimension, + typename std::enable_if<meta::is_arithmetic<std::decay_t<Arithmetic>>::value>::type> + : std::conditional_t<std::is_reference_v<Arithmetic>, point_proxy<std::remove_reference_t<Arithmetic>, Dimension>, + point_impl<Arithmetic, Dimension>> { - static constexpr std::size_t dimension{Dim}; + /** + * @brief Space dimension (static). + * + */ + static constexpr std::size_t dimension{Dimension}; + using arithmetic_type = Arithmetic; using value_type = std::decay_t<Arithmetic>; using point_proxy_type = point_proxy<std::remove_reference_t<arithmetic_type>, dimension>; using point_impl_type = point_impl<std::remove_reference_t<arithmetic_type>, dimension>; - using base_type = - std::conditional_t<std::is_reference_v<Arithmetic>, point_proxy_type, - point_impl_type>; + using base_type = std::conditional_t<std::is_reference_v<Arithmetic>, point_proxy_type, point_impl_type>; using base_type::base_type; + /** + * @brief + * + * @param p + * @return auto + */ auto operator=(point<value_type, dimension> p) noexcept { for(std::size_t i = 0; i < dimension; ++i) @@ -179,7 +375,13 @@ namespace scalfmm::container return *this; } - // Addition assignment operator + /** + * @brief Addition assignment operator. + * + * @tparam PointOrProxyOrArray + * @param other + * @return point& + */ template<typename PointOrProxyOrArray> inline auto operator+=(PointOrProxyOrArray const& other) -> point& { @@ -190,7 +392,13 @@ namespace scalfmm::container return *this; } - // Soustraction assignment operator + /** + * @brief Soustraction assignment operator. + * + * @tparam PointOrProxyOrArray + * @param other + * @return point& + */ template<typename PointOrProxyOrArray> inline auto operator-=(PointOrProxyOrArray const& other) -> point& { @@ -201,7 +409,13 @@ namespace scalfmm::container return *this; } - // Data to data multiplication assignment + /** + * @brief Data to data multiplication assignment. + * + * @tparam PointOrProxyOrArray + * @param other + * @return point& + */ template<typename PointOrProxyOrArray> inline auto operator*=(PointOrProxyOrArray const& other) -> point& { @@ -212,7 +426,13 @@ namespace scalfmm::container return *this; } - // Data to data division assignment + /** + * @brief Data to data division assignment. + * + * @tparam PointOrProxyOrArray + * @param other + * @return point& + */ template<typename PointOrProxyOrArray> inline auto operator/=(PointOrProxyOrArray const& other) -> point& { @@ -223,7 +443,12 @@ namespace scalfmm::container return *this; } - // Addition assignment operator + /** + * @brief Addition assignment operator. + * + * @param other + * @return point& + */ inline auto operator+=(value_type other) -> point& { for(std::size_t i = 0; i < dimension; ++i) @@ -233,7 +458,12 @@ namespace scalfmm::container return *this; } - // Soustraction assignment operator + /** + * @brief Soustraction assignment operator + * + * @param other + * @return point& + */ inline auto operator-=(value_type other) -> point& { for(std::size_t i = 0; i < dimension; ++i) @@ -243,7 +473,12 @@ namespace scalfmm::container return *this; } - // Data to data multiplication assignment + /** + * @brief Data to data multiplication assignment. + * + * @param other + * @return point& + */ inline auto operator*=(value_type other) -> point& { for(std::size_t i = 0; i < dimension; ++i) @@ -253,7 +488,12 @@ namespace scalfmm::container return *this; } - // Data to data division assignment + /** + * @brief Data to data division assignment. + * + * @param other + * @return point& + */ inline auto operator/=(value_type other) -> point& { for(std::size_t i = 0; i < dimension; ++i) @@ -263,73 +503,173 @@ namespace scalfmm::container return *this; } + /** + * @brief + * + * @tparam A + * @tparam D + * @param os + * @param pos + * @return std::ostream& + */ template<typename A, std::size_t D> inline friend auto operator<<(std::ostream& os, const point<A, D>& pos) -> std::ostream&; + /** + * @brief + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator+(point other, point const& another) - -> point<std::decay_t<A>, D>; + inline friend auto operator+(point other, point const& another) -> point<std::decay_t<A>, D>; + + /** + * @brief + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator-(point other, point const& another) - -> point<std::decay_t<A>, D>; + inline friend auto operator-(point other, point const& another) -> point<std::decay_t<A>, D>; + + /** + * @brief + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator*(point other, point const& another) - -> point<std::decay_t<A>, D>; + inline friend auto operator*(point other, point const& another) -> point<std::decay_t<A>, D>; + + /** + * @brief + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator/(point other, point const& another) - -> point<std::decay_t<A>, D>; - + inline friend auto operator/(point other, point const& another) -> point<std::decay_t<A>, D>; + + /** + * @brief + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator+(point other, value_type another) - -> point<std::decay_t<A>, D>; + inline friend auto operator+(point other, value_type another) -> point<std::decay_t<A>, D>; + + /** + * @brief + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator-(point other, value_type another) - -> point<std::decay_t<A>, D>; + inline friend auto operator-(point other, value_type another) -> point<std::decay_t<A>, D>; + + /** + * @brief + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator*(point other, value_type another) - -> point<std::decay_t<A>, D>; + inline friend auto operator*(point other, value_type another) -> point<std::decay_t<A>, D>; + + /** + * @brief + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator/(point other, value_type another) - -> point<std::decay_t<A>, D>; - + inline friend auto operator/(point other, value_type another) -> point<std::decay_t<A>, D>; + + /** + * @brief + * + * @tparam A + * @tparam D + * @param another + * @param other + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator+(value_type another, point other) - -> point<std::decay_t<A>, D>; + inline friend auto operator+(value_type another, point other) -> point<std::decay_t<A>, D>; + + /** + * @brief + * + * @tparam A + * @tparam D + * @param another + * @param other + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline friend auto operator*(value_type another, point other) - -> point<std::decay_t<A>, D>; + inline friend auto operator*(value_type another, point other) -> point<std::decay_t<A>, D>; - /// - /// \brief compute the minimum of the coordinates of point_impl. - /// \return the minimum of the coordinates. - /// + /** + * @brief compute the minimum of the coordinates of point_impl. + * + * @return the minimum of the coordinates. + */ inline auto min() const -> value_type { value_type min{std::numeric_limits<value_type>::max()}; for(auto a: *this) { - min =std::min(min, a); + min = std::min(min, a); } return min; } - /// - /// \brief compute the minimum of the coordinates of point_impl. - /// \return the minimum of the coordinates. - /// + + /** + * @brief compute the minimum of the coordinates of point_impl. + * + * @return the minimum of the coordinates. + */ inline auto max() const -> value_type { value_type max{-std::numeric_limits<value_type>::max()}; for(auto a: *this) { - max =std::max(max, a); + max = std::max(max, a); } return max; } - /// - /// \brief norm compute the L2 norm of the point_impl. - /// \return the L2 norm of the point_impl. - /// + /** + * @brief norm compute the L2 norm of the point_impl. + * + * @return the L2 norm of the point_impl. + */ inline auto norm() const -> value_type { value_type square_sum{0}; @@ -340,10 +680,11 @@ namespace scalfmm::container return std::sqrt(square_sum); } - /// - /// \brief norm2 compute the L2 norm squared of the point_impl. - /// \return the L2 norm squared of the point_impl. - /// + /** + * @brief norm2 compute the L2 norm squared of the point_impl. + * + * @return the L2 norm squared of the point_impl. + */ inline auto norm2() const -> value_type { value_type square_sum{0}; @@ -353,35 +694,59 @@ namespace scalfmm::container } return square_sum; } + + /** + * @brief + * + * @tparam PointOrProxyOrArray + * @param p + * @return value_type + */ template<typename PointOrProxyOrArray> inline auto distance(PointOrProxyOrArray const& p) const -> value_type { value_type square_sum{0}; - for(std::size_t i{0}; i<dimension; ++i) + for(std::size_t i{0}; i < dimension; ++i) { auto tmp = (*this)[i] - p.at(i); - square_sum += tmp*tmp; + square_sum += tmp * tmp; } return std::sqrt(square_sum); } }; - template<typename Arithmetic, std::size_t Dim> - inline auto operator<<(std::ostream& os, const point<Arithmetic, Dim>& pos) -> std::ostream& + /** + * @brief + * + * @tparam Arithmetic + * @tparam Dimension + * @param os + * @param pos + * @return std::ostream& + */ + template<typename Arithmetic, std::size_t Dimension> + inline auto operator<<(std::ostream& os, const point<Arithmetic, Dimension>& pos) -> std::ostream& { os << "["; - for(std::size_t i{0}; i < Dim - 1; ++i) + for(std::size_t i{0}; i < Dimension - 1; ++i) { os << pos.at(i) << ", "; } - os << pos.at(Dim - 1) << "]"; + os << pos.at(Dimension - 1) << "]"; return os; } - // Addition operator + /** + * @brief Addition operator. + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator+(point<A, D> other, point<A, D> const& another) - -> point<std::decay_t<A>, D> + inline auto operator+(point<A, D> other, point<A, D> const& another) -> point<std::decay_t<A>, D> { point<std::decay_t<A>, D> res{}; for(std::size_t i = 0; i < D; ++i) @@ -391,10 +756,17 @@ namespace scalfmm::container return res; } - // Soustraction operator + /** + * @brief Substraction operator. + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator-(point<A, D> other, point<A, D> const& another) - -> point<std::decay_t<A>, D> + inline auto operator-(point<A, D> other, point<A, D> const& another) -> point<std::decay_t<A>, D> { point<std::decay_t<A>, D> res{}; for(std::size_t i = 0; i < D; ++i) @@ -404,10 +776,17 @@ namespace scalfmm::container return res; } - // Multiply operator + /** + * @brief Multiply operator. + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator*(point<A, D> other, point<A, D> const& another) - -> point<std::decay_t<A>, D> + inline auto operator*(point<A, D> other, point<A, D> const& another) -> point<std::decay_t<A>, D> { point<std::decay_t<A>, D> res{}; for(std::size_t i = 0; i < D; ++i) @@ -417,10 +796,17 @@ namespace scalfmm::container return res; } - // Divide operator + /** + * @brief Divide operator. + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator/(point<A, D> other, point<A, D> const& another) - -> point<std::decay_t<A>, D> + inline auto operator/(point<A, D> other, point<A, D> const& another) -> point<std::decay_t<A>, D> { point<std::decay_t<A>, D> res{}; for(std::size_t i = 0; i < D; ++i) @@ -430,10 +816,17 @@ namespace scalfmm::container return res; } - // Addition operator + /** + * @brief Addition operator. + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator+(point<A, D> other, std::decay_t<A> another) - -> point<std::decay_t<A>, D> + inline auto operator+(point<A, D> other, std::decay_t<A> another) -> point<std::decay_t<A>, D> { point<std::decay_t<A>, D> res{}; for(std::size_t i = 0; i < D; ++i) @@ -443,10 +836,17 @@ namespace scalfmm::container return res; } - // Soustraction operator + /** + * @brief Substraction operator. + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator-(point<A, D> other, std::decay_t<A> another) - -> point<std::decay_t<A>, D> + inline auto operator-(point<A, D> other, std::decay_t<A> another) -> point<std::decay_t<A>, D> { point<std::decay_t<A>, D> res{}; for(std::size_t i = 0; i < D; ++i) @@ -456,10 +856,17 @@ namespace scalfmm::container return res; } - // Multiply operator + /** + * @brief Multiply operator. + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator*(point<A, D> other, std::decay_t<A> another) - -> point<std::decay_t<A>, D> + inline auto operator*(point<A, D> other, std::decay_t<A> another) -> point<std::decay_t<A>, D> { point<std::decay_t<A>, D> res{}; for(std::size_t i = 0; i < D; ++i) @@ -469,10 +876,17 @@ namespace scalfmm::container return res; } - // Divide operator + /** + * @brief Divide operator. + * + * @tparam A + * @tparam D + * @param other + * @param another + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator/(point<A, D> other, std::decay_t<A> another) - -> point<std::decay_t<A>, D> + inline auto operator/(point<A, D> other, std::decay_t<A> another) -> point<std::decay_t<A>, D> { point<std::decay_t<A>, D> res{}; for(std::size_t i = 0; i < D; ++i) @@ -482,110 +896,77 @@ namespace scalfmm::container return res; } - // Addition operator + /** + * @brief Addition operator. + * + * @tparam A + * @tparam D + * @param another + * @param other + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator+(std::decay_t<A> another, point<A, D> other) - -> point<std::decay_t<A>, D> + inline auto operator+(std::decay_t<A> another, point<A, D> other) -> point<std::decay_t<A>, D> { return other + another; } - // Multiply operator + /** + * @brief Multiply operator. + * + * @tparam A + * @tparam D + * @param another + * @param other + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator*(std::decay_t<A> another, point<A, D> other) - -> point<std::decay_t<A>, D> + inline auto operator*(std::decay_t<A> another, point<A, D> other) -> point<std::decay_t<A>, D> { return other * another; } - // Divide operator + /** + * @brief Divide operator. + * + * @tparam A + * @tparam D + * @param another + * @param other + * @return point<std::decay_t<A>, D> + */ template<typename A, std::size_t D> - inline auto operator/(std::decay_t<A> another, point<A, D> other) - -> point<std::decay_t<A>, D> + inline auto operator/(std::decay_t<A> another, point<A, D> other) -> point<std::decay_t<A>, D> { - point<A,D> val(another); + point<A, D> val(another); return val / other; } + /** + * @brief Equality test operator (for SIMD case) + * + * @tparam Arithmetic xsimd type + * @tparam Dimension + * @param lhs + * @param rhs + * @return std::enable_if_t<meta::is_simd<Arithmetic>::value, xsimd::batch_bool<typename Arithmetic:value_type>> + */ + template<typename Arithmetic, std::size_t Dimension> + inline auto operator==(const point_impl<Arithmetic, Dimension>& lhs, const point_impl<Arithmetic, Dimension>& rhs) + -> std::enable_if_t<meta::is_simd<Arithmetic>::value, xsimd::batch_bool<typename Arithmetic::value_type>> + { + auto lhs_it = lhs.begin(); + auto rhs_it = rhs.begin(); + + auto equal = *lhs_it++ == *rhs_it++; -// - //___________________ TODO : ________________________ - ///** Equality test operator */ - // template<class T> - // inline friend auto operator==(const point_impl<T, Dim>& lhs, const point_impl<T, Dim>& rhs) - // -> std::enable_if_t<meta::is_float<T>::value, bool> - //{ - // auto lhs_it = lhs.begin(); - // auto rhs_it = rhs.begin(); - // const T p{1e-7}; - // for(std::size_t i = 0; i < Dim; ++i, ++lhs_it, ++rhs_it) - // { - // if(!meta::feq(*lhs_it, *rhs_it, p)) - // { - // return false; - // } - // } - // return true; - //} - - ///** Equality test operator */ - // template<class T> - // inline friend auto operator==(const point_impl<T, Dim>& lhs, const point_impl<T, Dim>& rhs) - // -> std::enable_if_t<meta::is_double<T>::value, bool> - //{ - // auto lhs_it = lhs.begin(); - // auto rhs_it = rhs.begin(); - // const T p{1e-13}; - // for(std::size_t i = 0; i < Dim; ++i, ++lhs_it, ++rhs_it) - // { - // if(!meta::feq(*lhs_it, *rhs_it, p)) - // { - // return false; - // } - // } - // return true; - //} - - ///** Equality test operator */ - // template<class T> - // inline friend auto operator==(const point_impl<T, Dim>& lhs, const point_impl<T, Dim>& rhs) - // -> std::enable_if_t<std::is_integral<T>::value, bool> - //{ - // auto lhs_it = lhs.begin(); - // auto rhs_it = rhs.begin(); - - // for(std::size_t i = 0; i < Dim; i++, ++lhs_it, ++rhs_it) - // { - // if(*lhs_it != *rhs_it) - // { - // return false; - // } - // } - // return true; - //} - - ///** Equality test operator */ - // template<class T> - // inline friend auto operator==(const point_impl<T, Dim>& lhs, const point_impl<T, Dim>& rhs) - // -> std::enable_if_t<meta::is_simd<T>::value, bool> - //{ - // auto lhs_it = lhs.begin(); - // auto rhs_it = rhs.begin(); - - // for(std::size_t i = 0; i < Dim; i++, ++lhs_it, ++rhs_it) - // { - // auto different = *lhs_it != *rhs_it; - - // if(!xsimd::any(different)) - // { - // return false; - // } - // } - // return true; - //} - - /** Non equality test operator */ - //inline friend auto operator!=(const point_impl& lhs, const point_impl& rhs) { return !(lhs == rhs); } + for(std::size_t i = 1; i < Dimension; i++) + { + equal = equal && (*lhs_it++ == *rhs_it++); + } + + return equal; + } } // end of namespace scalfmm::container #endif diff --git a/include/scalfmm/container/reference_sequence.hpp b/include/scalfmm/container/reference_sequence.hpp index 3037b4eefbfd035528184c40c650c673bf3137b1..d84aa2f7c859d87b6de2745ddd6126ac9bd7109f 100644 --- a/include/scalfmm/container/reference_sequence.hpp +++ b/include/scalfmm/container/reference_sequence.hpp @@ -1,32 +1,55 @@ +// -------------------------------- // See LICENCE file at project root -// +// File : scalfmm/container/reference_sequence.hpp +// -------------------------------- #ifndef SCALFMM_CONTAINER_REFERENCE_SEQUENCE_HPP #define SCALFMM_CONTAINER_REFERENCE_SEQUENCE_HPP -#include <functional> +#include "scalfmm/meta/traits.hpp" +#include "scalfmm/meta/utils.hpp" + #include <array> #include <cstddef> +#include <functional> #include <type_traits> #include <utility> -#include "scalfmm/meta/traits.hpp" -#include "scalfmm/meta/utils.hpp" - namespace scalfmm::container { - // Overloads to get e generic function of returning a reference + /** + * @brief Overloads to get a generic function of returning reference. + * + * @tparam T + * @param t + * @return constexpr auto + */ template<typename T> constexpr inline auto generic_ref(T& t) { return std::ref(t); } + /** + * @brief Overloads to get a generic function of returning reference. + * + * @tparam T + * @param t + * @return constexpr auto + */ template<typename T> constexpr inline auto generic_ref(T const& t) { return std::cref(t); } + /** + * @brief Get the reference sequence impl object + * + * @tparam Seq + * @tparam Is + * @param s + * @return constexpr auto + */ template<typename Seq, std::size_t... Is> constexpr inline auto get_reference_sequence_impl(Seq&& s, std::index_sequence<Is...>) { @@ -62,14 +85,19 @@ namespace scalfmm::container } } - - // Get a sequence of std::reference_wrapper + /** + * @brief Get the reference sequence object + * + * @tparam Seq + * @param s + * @return constexpr auto + */ template<typename Seq> constexpr inline auto get_reference_sequence(Seq&& s) { return get_reference_sequence_impl(std::forward<Seq>(s), std::make_index_sequence<meta::tuple_size_v<std::decay_t<Seq>>>{}); } -} +} // namespace scalfmm::container -#endif // SCALFMM_CONTAINER_REFERENCE_SEQUENCE_HPP +#endif // SCALFMM_CONTAINER_REFERENCE_SEQUENCE_HPP diff --git a/include/scalfmm/container/simple_variadic.hpp b/include/scalfmm/container/simple_variadic.hpp index 00059040f9a649261fbc073b7ef72f72ec2ed348..2b63b320a434bfbd97c6da4e9ae249355da43bae 100644 --- a/include/scalfmm/container/simple_variadic.hpp +++ b/include/scalfmm/container/simple_variadic.hpp @@ -1,29 +1,43 @@ // -------------------------------- // See LICENCE file at project root -// File : simple_variadic.cpp +// File : scalfmm/container/simple_variadic.hpp // -------------------------------- #ifndef SCALFMM_CONTAINER_SIMPLE_VARIADIC_CONTAINER_HPP #define SCALFMM_CONTAINER_SIMPLE_VARIADIC_CONTAINER_HPP +#include "xsimd/config/xsimd_config.hpp" + #include <tuple> #include <type_traits> #include <utility> #include <vector> -#include <xsimd/config/xsimd_config.hpp> - namespace scalfmm::container { + /** + * @brief Forward declaration + * + * @tparam Types + * @tparam Indices + */ template<typename Types, typename Indices> class variad_impl; - // template<typename Allocator = memory::aligned_allocator<XSIMD_DEFAULT_ALIGNMENT, Ts>> + /** + * @brief + * + * @tparam Ts + * @tparam Indices + */ template<typename... Ts, std::size_t... Indices> class variad_impl<std::tuple<Ts...>, std::integer_sequence<std::size_t, Indices...>> : public std::tuple<std::vector<Ts, XSIMD_DEFAULT_ALLOCATOR(Ts)>...> { private: - // Discard fold expression results + /** + * @brief Discard fold expression results. + * + */ struct noop_t { template<typename... Types> @@ -44,19 +58,61 @@ namespace scalfmm::container using const_pointer = const std::tuple<const Ts*...>; private: + /** + * @brief + * + */ allocator_type m_allocator{}; public: + /** + * @brief Construct a new variad impl object + * + */ variad_impl() = default; + + /** + * @brief Construct a new variad impl object + * + */ variad_impl(const variad_impl&) = default; + + /** + * @brief Construct a new variad impl object + * + */ variad_impl(variad_impl&&) noexcept = default; + + /** + * @brief + * + * @return variad_impl& + */ variad_impl& operator=(const variad_impl&) = default; + + /** + * @brief + * + * @return variad_impl& + */ variad_impl& operator=(variad_impl&&) noexcept = default; + + /** + * @brief Destroy the variad impl object + * + */ ~variad_impl() = default; // TODO // explicit variad( const allocator_type& alloc){} + /** + * @brief Construct a new variad impl object + * + * @param count + * @param value + * @param alloc + */ explicit variad_impl(size_type count, const value_type& value, const allocator_type& alloc = allocator_type()) : m_allocator(alloc) { @@ -67,6 +123,11 @@ namespace scalfmm::container } } + /** + * @brief Construct a new variad impl object + * + * @param count + */ explicit variad_impl(size_type count) { reserve(count); @@ -76,14 +137,29 @@ namespace scalfmm::container } } + /** + * @brief + * + * @param size + */ inline void reserve(size_type size) { noop_t{(std::get<Indices>(*this).reserve(size), 0)...}; } + /** + * @brief + * + * @param value + */ inline void push_back(const value_type& value) { noop_t{(std::get<Indices>(*this).push_back(std::get<Indices>(value)), 0)...}; } }; + /** + * @brief + * + * @tparam Ts + */ template<typename... Ts> struct variad : public variad_impl<std::tuple<Ts...>, std::make_index_sequence<sizeof...(Ts)>> { diff --git a/include/scalfmm/container/variadic_adaptor.hpp b/include/scalfmm/container/variadic_adaptor.hpp index 59c79b47b6236cf3b9384566f14e690227051946..60703d945a4657d4040f798fe6306faa968e6eff 100644 --- a/include/scalfmm/container/variadic_adaptor.hpp +++ b/include/scalfmm/container/variadic_adaptor.hpp @@ -1,30 +1,38 @@ // -------------------------------- // See LICENCE file at project root -// File : container/variadic_adaptor.hpp +// File : scalfmm/container/variadic_adaptor.hpp // -------------------------------- #ifndef SCALFMM_VARIADIC_ADAPTOR_HPP #define SCALFMM_VARIADIC_ADAPTOR_HPP +#include "scalfmm/container/reference_sequence.hpp" +#include "scalfmm/meta/is_valid.hpp" +#include "scalfmm/meta/traits.hpp" +#include "scalfmm/meta/utils.hpp" + +#include "xsimd/config/xsimd_config.hpp" +#include "xtensor/xexpression.hpp" + #include <algorithm> #include <cstddef> #include <functional> #include <iterator> #include <memory> -#include <scalfmm/container/reference_sequence.hpp> -#include <scalfmm/meta/is_valid.hpp> -#include <scalfmm/meta/traits.hpp> -#include <scalfmm/meta/utils.hpp> #include <tuple> #include <type_traits> #include <utility> #include <vector> -#include <xsimd/config/xsimd_config.hpp> -#include <xtensor/xexpression.hpp> namespace scalfmm { namespace container { + /** + * @brief + * + * @tparam Derived + * @tparam Containers + */ template<typename Derived, typename... Containers> struct variadic_adaptor; } // namespace container @@ -32,75 +40,86 @@ namespace scalfmm namespace scalfmm::container { - /// @brief - /// - /// @tparam Container - /// @param C - /// @param i - /// - /// @return + /** + * @brief + * + * @tparam Container + * @param C + * @param i + * @return constexpr auto + */ template<typename Container> [[nodiscard]] inline constexpr auto id(Container C, std::size_t i) { return Container{}; } - /// @brief - /// - /// @tparam Containers - /// @param Cs - /// - /// @return + /** + * @brief Get the variadic adaptor object + * + * @tparam Containers + * @param Cs + * @return constexpr auto + */ template<typename... Containers> [[nodiscard]] inline constexpr auto get_variadic_adaptor(Containers... Cs) { return variadic_adaptor<void, Containers...>{}; } - /// @brief - /// - /// @tparam Container - /// @tparam Is - /// @param s - /// - /// @return + /** + * @brief Get the variadic adaptor object + * + * @tparam Container + * @tparam Is + * @param s + * @return constexpr auto + */ template<typename Container, std::size_t... Is> [[nodiscard]] inline constexpr auto get_variadic_adaptor(std::index_sequence<Is...> s) { return get_variadic_adaptor(id(Container{}, Is)...); } - /// @brief - /// - /// @tparam Container - /// @tparam Size - /// - /// @return + /** + * @brief Get the variadic adaptor object + * + * @tparam Container + * @tparam Size + * @return constexpr auto + */ template<typename Container, std::size_t Size> [[nodiscard]] inline constexpr auto get_variadic_adaptor() { return get_variadic_adaptor<Container>(std::make_index_sequence<Size>{}); } - /// @brief - /// - /// @tparam Container - /// @tparam Size - /// @param - /// - /// @return + /** + * @brief + * + * @tparam Container + * @tparam Size + */ template<typename Container, std::size_t Size> using get_variadic_adaptor_t = decltype(get_variadic_adaptor<Container, Size>()); + /** + * @brief + * + * @tparam Derived + * @tparam Containers + */ template<typename Derived, typename... Containers> struct variadic_adaptor; - /// @brief - /// - /// @tparam VariadicAdaptor - /// @tparam DerivedVariadic - /// @tparam Seq - /// @tparam IsConst + /** + * @brief + * + * @tparam VariadicAdaptor + * @tparam DerivedVariadic + * @tparam Seq + * @tparam IsConst + */ template<class VariadicAdaptor, typename DerivedVariadic, typename Seq, bool IsConst> class proxy_iterator { @@ -109,10 +128,30 @@ namespace scalfmm::container using vector_pointer_type = std::conditional_t<IsConst, container_private_type const*, container_private_type*>; using derived_value_type = typename DerivedVariadic::value_type; + /** + * @brief + * + */ friend container_private_type; + + /** + * @brief + * + */ vector_pointer_type vec_; + + /** + * @brief + * + */ int index_; + /** + * @brief Construct a new proxy iterator object + * + * @param vec + * @param index + */ proxy_iterator(vector_pointer_type vec, int index) noexcept : vec_{vec} , index_{index} @@ -129,9 +168,19 @@ namespace scalfmm::container using reference = tuple_of_ref; using pointer = void; using difference_type = std::size_t; + + /** + * @brief + * + */ static constexpr bool is_const_qualified{IsConst}; private: + /** + * @brief + * + * @tparam Is + */ template<size_t... Is> [[nodiscard]] [[nodiscard]] auto make_proxy(std::index_sequence<Is...> /*unused*/) const noexcept { @@ -140,48 +189,182 @@ namespace scalfmm::container } public: + /** + * @brief Construct a new proxy iterator object + * + */ proxy_iterator() = default; + + /** + * @brief Construct a new proxy iterator object + * + */ proxy_iterator(proxy_iterator const&) = default; + + /** + * @brief Construct a new proxy iterator object + * + */ proxy_iterator(proxy_iterator&&) = default; + + /** + * @brief + * + * @return proxy_iterator& + */ auto operator=(proxy_iterator const&) -> proxy_iterator& = default; + + /** + * @brief + * + * @return proxy_iterator& + */ auto operator=(proxy_iterator&&) -> proxy_iterator& = default; + /** + * @brief + * + * @return auto + */ [[nodiscard]] auto operator*() const noexcept { return make_proxy(Seq{}); } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] auto operator==(proxy_iterator const& rhs) const noexcept -> bool { return index_ == rhs.index_; } + + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] auto operator!=(proxy_iterator const& rhs) const noexcept -> bool { return !(*this == rhs); } + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] auto operator<(proxy_iterator const& rhs) const noexcept -> bool { return index_ < rhs.index_; } + + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] auto operator>(proxy_iterator const& rhs) const noexcept -> bool { return rhs < *this; } + + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] auto operator<=(proxy_iterator const& rhs) const noexcept -> bool { return !(rhs < *this); } + + /** + * @brief + * + * @param rhs + * @return true + * @return false + */ [[nodiscard]] auto operator>=(proxy_iterator const& rhs) const noexcept -> bool { return !(*this < rhs); } + /** + * @brief + * + * @return proxy_iterator& + */ auto operator++() noexcept -> proxy_iterator& { return ++index_, *this; } + + /** + * @brief + * + * @return proxy_iterator& + */ auto operator--() noexcept -> proxy_iterator& { return --index_, *this; } + + /** + * @brief + * + * @return proxy_iterator + */ auto operator++(int) noexcept -> proxy_iterator { const auto old = *this; return ++index_, old; } + + /** + * @brief + * + * @return proxy_iterator + */ auto operator--(int) noexcept -> proxy_iterator { const auto old = *this; return --index_, old; } + /** + * @brief + * + * @param shift + * @return proxy_iterator& + */ auto operator+=(int shift) noexcept -> proxy_iterator& { return index_ += shift, *this; } + + /** + * @brief + * + * @param shift + * @return proxy_iterator& + */ auto operator-=(int shift) noexcept -> proxy_iterator& { return index_ -= shift, *this; } + /** + * @brief + * + * @param shift + * @return proxy_iterator + */ auto operator+(int shift) const noexcept -> proxy_iterator { return {vec_, index_ + shift}; } + + /** + * @brief + * + * @param shift + * @return proxy_iterator + */ auto operator-(int shift) const noexcept -> proxy_iterator { return {vec_, index_ - shift}; } + /** + * @brief + * + * @param rhs + * @return int + */ auto operator-(proxy_iterator const& rhs) const noexcept -> int { return index_ - rhs.index_; } }; - /// @brief - /// - /// @tparam Derived - /// @tparam Containers + /** + * @brief + * + * @tparam Derived + * @tparam Containers + */ template<typename Derived, typename... Containers> struct variadic_adaptor : public std::tuple<Containers...> { @@ -208,15 +391,52 @@ namespace scalfmm::container friend const_iterator; public: + /** + * @brief Construct a new variadic adaptor object + * + */ variadic_adaptor() = default; + + /** + * @brief Construct a new variadic adaptor object + * + */ variadic_adaptor(variadic_adaptor const&) = default; + + /** + * @brief Construct a new variadic adaptor object + * + */ variadic_adaptor(variadic_adaptor&&) noexcept = default; + + /** + * @brief + * + * @return variadic_adaptor& + */ auto operator=(variadic_adaptor const&) -> variadic_adaptor& = default; + + /** + * @brief + * + * @return variadic_adaptor& + */ auto operator=(variadic_adaptor&&) noexcept -> variadic_adaptor& = default; + + /** + * @brief Destroy the variadic adaptor object + * + */ ~variadic_adaptor() = default; - // =========================================================== - // Constructors - // =========================================================== + + /** + * @brief Construct a new variadic adaptor object + * + * @tparam Allocators + * @tparam std::enable_if_t<meta::all( + * std::is_same_v<typename Containers::allocator_type, Allocators>...)> + * @param alloc + */ template<typename... Allocators, typename = std::enable_if_t<meta::all( std::is_same_v<typename Containers::allocator_type, Allocators>...)>> explicit variadic_adaptor(Allocators const&... alloc) @@ -231,6 +451,18 @@ namespace scalfmm::container //{ //} + /** + * @brief Construct a new variadic adaptor object + * + * @tparam T + * @tparam Allocator + * @tparam Allocator, + * typename + * @tparam )> + * @param count + * @param value + * @param allocator + */ template< typename T, typename Allocator = std::allocator<T>, typename = std::enable_if_t<meta::all((std::is_same_v<T, typename Containers::value_type> && @@ -240,11 +472,28 @@ namespace scalfmm::container { } + /** + * @brief Construct a new variadic adaptor object + * + * @param counts + * @param allocators + */ explicit variadic_adaptor(size_type counts, allocator_type const& allocators = allocator_type()) : variadic_adaptor(counts, allocators, std::index_sequence_for<Containers...>{}) { } + /** + * @brief Construct a new variadic adaptor object + * + * @tparam T + * @tparam Allocator + * @tparam Allocator, + * typename + * @tparam )> + * @param count + * @param allocator + */ template< typename T, typename Allocator = std::allocator<T>, typename = std::enable_if_t<meta::all(std::is_same_v<Allocator, typename Containers::allocator_type>...)>> @@ -253,22 +502,44 @@ namespace scalfmm::container { } + /** + * @brief Construct a new variadic adaptor object + * + * @param counts + */ explicit variadic_adaptor(size_type counts) : variadic_adaptor(counts, std::index_sequence_for<Containers...>{}) { } + /** + * @brief Construct a new variadic adaptor object + * + * @param count + */ explicit variadic_adaptor(std::size_t count) : base_type(Containers(typename Containers::size_type(count))...) { } - // template<typename... Containers> + /** + * @brief Construct a new variadic adaptor object + * + * @param cs + */ explicit variadic_adaptor(Containers&&... cs) : base_type(std::forward<Containers>(cs)...) { } + /** + * @brief Construct a new variadic adaptor object + * + * @tparam Expressions + * @tparam Expressions, + * typename + * @param Es + */ template<typename... Expressions, typename = std::enable_if_t<meta::all(xt::is_xexpression<std::decay_t<Expressions>>::value...)>> explicit variadic_adaptor(Expressions&&... Es) @@ -276,6 +547,14 @@ namespace scalfmm::container { } + /** + * @brief Construct a new variadic adaptor object + * + * @tparam Expressions + * @tparam Expressions, + * typename + * @param tp_expr + */ template<typename... Expressions, typename = std::enable_if_t<meta::all(xt::is_xexpression<std::decay_t<Expressions>>::value...)>> explicit variadic_adaptor(std::tuple<Expressions...>&& tp_expr) @@ -283,9 +562,13 @@ namespace scalfmm::container { } - // =========================================================== - // Capacity - // =========================================================== + /** + * @brief + * + * @tparam DelayedReturnType + * @tparam > + * @return std::enable_if_t<meta::all(meta::has_size_func_v<Containers>...), DelayedReturnType> + */ template<typename DelayedReturnType = std::tuple<decltype(meta::delayed_trait<Containers, meta::has_size_func>( meta::has_size_func_f(Containers{})))...>> [[nodiscard]] constexpr auto all_size() const noexcept @@ -296,16 +579,30 @@ namespace scalfmm::container return sizes; } + /** + * @brief + * + * @tparam DelayedReturnType + * @tparam > + * @return std::enable_if_t<meta::all(meta::has_empty_func_v<Containers>...), DelayedReturnType> + */ template<typename DelayedReturnType = std::tuple<decltype(meta::delayed_trait<Containers, meta::has_empty_func>( meta::has_empty_func_f(Containers{})))...>> - [[nodiscard]] constexpr auto empty() const noexcept - -> std::enable_if_t<meta::all(meta::has_empty_func_v<Containers>...), DelayedReturnType> + [[nodiscard]] constexpr auto + empty() const noexcept -> std::enable_if_t<meta::all(meta::has_empty_func_v<Containers>...), DelayedReturnType> { std::tuple<meta::has_empty_func_t<Containers>...> bools; meta::for_each(bools, *this, [](auto& container) { return container.empty(); }); return bools; } + /** + * @brief + * + * @tparam DelayedReturnType + * @tparam > + * @return std::enable_if_t<meta::all(meta::has_max_size_func_v<Containers>...), DelayedReturnType> + */ template< typename DelayedReturnType = std::tuple<decltype(meta::delayed_trait<Containers, meta::has_max_size_func>( meta::has_max_size_func_f(Containers{})))...>> @@ -317,6 +614,13 @@ namespace scalfmm::container return bools; } + /** + * @brief + * + * @tparam DelayedReturnType + * @param count + * @return std::enable_if_t<meta::all(meta::has_resize_func_v<Containers>...), DelayedReturnType> + */ template<typename DelayedReturnType = void> [[nodiscard]] constexpr auto resize(std::size_t count) -> std::enable_if_t<meta::all(meta::has_resize_func_v<Containers>...), DelayedReturnType> @@ -324,59 +628,110 @@ namespace scalfmm::container meta::repeat([count](auto& container) { container.resize(count); }, *this); } + /** + * @brief + * + * @tparam T + * @tparam DelayedReturnType + * @param count + * @param value + * @return std::enable_if_t<meta::all(meta::has_resize_valued_func_v<Containers>&& + * std::is_same<T, typename Containers::value_type>::value...), + * DelayedReturnType> + */ template<typename T, typename DelayedReturnType = void> constexpr auto resize(std::size_t count, T const& value) - -> std::enable_if_t<meta::all(meta::has_resize_valued_func_v<Containers> && + -> std::enable_if_t<meta::all(meta::has_resize_valued_func_v<Containers>&& std::is_same<T, typename Containers::value_type>::value...), DelayedReturnType> { meta::repeat([count, &value](auto& container) { container.resize(count, value); }, *this); } + /** + * @brief + * + * @tparam DelayedReturnType + * @return std::enable_if_t<meta::all(meta::has_clear_func_v<Containers>...), DelayedReturnType> + */ template<typename DelayedReturnType = void> constexpr auto clear() -> std::enable_if_t<meta::all(meta::has_clear_func_v<Containers>...), DelayedReturnType> { meta::repeat([](auto& container) { container.clear(); }, *this); } - // =========================================================== - // Iterators - // =========================================================== public: + /** + * @brief + * + * @return std::enable_if_t<meta::all(meta::has_begin_v<Containers>...), iterator> + */ [[nodiscard]] inline auto begin() -> std::enable_if_t<meta::all(meta::has_begin_v<Containers>...), iterator> { return {this, 0}; } - [[nodiscard]] inline auto begin() const - -> std::enable_if_t<meta::all(meta::has_cbegin_v<Containers>...), const_iterator> + /** + * @brief + * + * @return std::enable_if_t<meta::all(meta::has_cbegin_v<Containers>...), const_iterator> + */ + [[nodiscard]] inline auto + begin() const -> std::enable_if_t<meta::all(meta::has_cbegin_v<Containers>...), const_iterator> { return {this, 0}; } - [[nodiscard]] inline auto cbegin() const - -> std::enable_if_t<meta::all(meta::has_cbegin_v<Containers>...), const_iterator> + /** + * @brief + * + * @return std::enable_if_t<meta::all(meta::has_cbegin_v<Containers>...), const_iterator> + */ + [[nodiscard]] inline auto + cbegin() const -> std::enable_if_t<meta::all(meta::has_cbegin_v<Containers>...), const_iterator> { return {this, 0}; } + /** + * @brief + * + * @return std::enable_if_t<meta::all(meta::has_end_v<Containers>...), iterator> + */ [[nodiscard]] inline auto end() -> std::enable_if_t<meta::all(meta::has_end_v<Containers>...), iterator> { return {this, static_cast<int>(std::get<0>(*this).size())}; } - [[nodiscard]] inline auto end() const - -> std::enable_if_t<meta::all(meta::has_cend_v<Containers>...), const_iterator> + /** + * @brief + * + * @return std::enable_if_t<meta::all(meta::has_cend_v<Containers>...), const_iterator> + */ + [[nodiscard]] inline auto + end() const -> std::enable_if_t<meta::all(meta::has_cend_v<Containers>...), const_iterator> { return {this, static_cast<int>(std::get<0>(*this).size())}; } - [[nodiscard]] inline auto cend() const - -> std::enable_if_t<meta::all(meta::has_cend_v<Containers>...), const_iterator> + /** + * @brief + * + * @return std::enable_if_t<meta::all(meta::has_cend_v<Containers>...), const_iterator> + */ + [[nodiscard]] inline auto + cend() const -> std::enable_if_t<meta::all(meta::has_cend_v<Containers>...), const_iterator> { return {this, static_cast<int>(std::get<0>(*this).size())}; } + /** + * @brief + * + * @tparam Seq + * @return std::enable_if_t<meta::all(meta::has_begin_v<Containers>...), + * proxy_iterator<self_type, derived_type, Seq, false>> + */ template<typename Seq> [[nodiscard]] inline auto sbegin() -> std::enable_if_t<meta::all(meta::has_begin_v<Containers>...), proxy_iterator<self_type, derived_type, Seq, false>> @@ -384,6 +739,13 @@ namespace scalfmm::container return {this, 0}; } + /** + * @brief + * + * @tparam Seq + * @return std::enable_if_t<meta::all(meta::has_begin_v<Containers>...), + * proxy_iterator<self_type, derived_type, Seq, true>> + */ template<typename Seq> [[nodiscard]] inline auto sbegin() const -> std::enable_if_t<meta::all(meta::has_begin_v<Containers>...), proxy_iterator<self_type, derived_type, Seq, true>> @@ -391,6 +753,13 @@ namespace scalfmm::container return {this, 0}; } + /** + * @brief + * + * @tparam Seq + * @return std::enable_if_t<meta::all(meta::has_end_v<Containers>...), + * proxy_iterator<self_type, derived_type, Seq, false>> + */ template<typename Seq> [[nodiscard]] inline auto send() -> std::enable_if_t<meta::all(meta::has_end_v<Containers>...), proxy_iterator<self_type, derived_type, Seq, false>> @@ -398,45 +767,82 @@ namespace scalfmm::container return {this, static_cast<int>(std::get<0>(*this).size())}; } + /** + * @brief + * + * @tparam Seq + * @return std::enable_if_t<meta::all(meta::has_end_v<Containers>...), + * proxy_iterator<self_type, derived_type, Seq, true>> + */ template<typename Seq> [[nodiscard]] inline auto send() const -> std::enable_if_t<meta::all(meta::has_end_v<Containers>...), proxy_iterator<self_type, derived_type, Seq, true>> { return {this, static_cast<int>(std::get<0>(*this).size())}; } + // at, WARNING changing of behavior, here at was return the vector in the sequence // now it returns the refs from the tuple sequence like the subscript // operator. TODO: this should throw to be standart compliant. + + /** + * @brief + * + * @param i + * @return auto + */ [[nodiscard]] inline auto at(std::size_t i) { - auto it = this->begin()+i; + auto it = this->begin() + i; return *it; } + + /** + * @brief + * + * @param i + * @return auto + */ [[nodiscard]] inline auto at(std::size_t i) const { - auto it = this->cbegin()+i; + auto it = this->cbegin() + i; return *it; } + /** + * @brief + * + * @param i + * @return auto + */ [[nodiscard]] inline auto operator[](std::size_t i) { - auto it = this->begin()+i; + auto it = this->begin() + i; return *it; } + + /** + * @brief + * + * @param i + * @return auto + */ [[nodiscard]] inline auto operator[](std::size_t i) const { - auto it = this->cbegin()+i; + auto it = this->cbegin() + i; return *it; } }; // Use the same container in a variadic_adaptor - /// @brief - /// - /// @tparam Derived - /// @tparam U - /// @tparam Allocator + /** + * @brief + * + * @tparam Derived + * @tparam Container + * @tparam Types + */ template<typename Derived, template<typename U, typename Allocator = XTENSOR_DEFAULT_ALLOCATOR(U)> class Container, typename... Types> struct unique_variadic_container : public variadic_adaptor<Derived, Container<Types>...> @@ -445,10 +851,12 @@ namespace scalfmm::container using base_type::base_type; }; - /// @brief - /// - /// @tparam Derived - /// @tparam Types + /** + * @brief + * + * @tparam Derived + * @tparam Types + */ template<typename Derived, typename... Types> struct variadic_container : public unique_variadic_container<Derived, std::vector, Types...> { @@ -456,13 +864,21 @@ namespace scalfmm::container using base_type::base_type; }; - /// @brief - /// - /// @tparam Derived - /// @tparam Tuple + /** + * @brief + * + * @tparam Derived + * @tparam Tuple + */ template<typename Derived, typename Tuple> struct variadic_container_tuple; + /** + * @brief + * + * @tparam Derived + * @tparam Types + */ template<typename Derived, typename... Types> struct variadic_container_tuple<Derived, std::tuple<Types...>> : public variadic_container<Derived, Types...> { diff --git a/include/scalfmm/functional/utils.hpp b/include/scalfmm/functional/utils.hpp index 6e98abaaa7f662852527a3ffbf788ed6eb0ee95c..dcec2b3b470fdaef6a4775f54c9015a0563f0208 100644 --- a/include/scalfmm/functional/utils.hpp +++ b/include/scalfmm/functional/utils.hpp @@ -1,15 +1,24 @@ +// -------------------------------- // See LICENCE file at project root -// +// File : scalfmm/functional/utils.hpp +// -------------------------------- #ifndef SCALFMM_FUNCTIONAL_UTILS_HPP #define SCALFMM_FUNCTIONAL_UTILS_HPP -#include <scalfmm/meta/traits.hpp> +#include "scalfmm/meta/traits.hpp" + #include <cmath> #include <type_traits> namespace scalfmm::utils { - // Norm2 on a range + /** + * @brief Squared norm of a range. + * + * @tparam T + * @param range + * @return std::enable_if_t<meta::has_value_type<T>::value && meta::has_range_interface<T>::value, typename T::value_type> + */ template<typename T> inline auto norm2(const T& range) -> std::enable_if_t<meta::has_value_type<T>::value && meta::has_range_interface<T>::value, typename T::value_type> @@ -22,7 +31,13 @@ namespace scalfmm::utils return square_sum; } - // Norm + /** + * @brief Norm of a range. + * + * @tparam T + * @param range + * @return std::enable_if_t<meta::has_value_type<T>::value && meta::has_range_interface<T>::value, typename T::value_type> + */ template<typename T> inline auto norm(const T& range) -> std::enable_if_t<meta::has_value_type<T>::value && meta::has_range_interface<T>::value, typename T::value_type> diff --git a/include/scalfmm/interpolation/barycentric/barycentric_interpolator.hpp b/include/scalfmm/interpolation/barycentric/barycentric_interpolator.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f6786301390edfcc676a062acb2654c65e6bbbe9 --- /dev/null +++ b/include/scalfmm/interpolation/barycentric/barycentric_interpolator.hpp @@ -0,0 +1,518 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/interpolation/barycentric/barycentric_interpolator.hpp +// -------------------------------- +#ifndef SCALFMM_INTERPOLATION_BARYCENTRIC_BARYCENTRIC_STORAGE_HPP +#define SCALFMM_INTERPOLATION_BARYCENTRIC_BARYCENTRIC_STORAGE_HPP + +#include "scalfmm/container/point.hpp" +#include "scalfmm/container/variadic_adaptor.hpp" +#include "scalfmm/interpolation/grid_storage.hpp" +#include "scalfmm/interpolation/interpolator.hpp" +#include "scalfmm/interpolation/mapping.hpp" +#include "scalfmm/interpolation/permutations.hpp" +#include "scalfmm/interpolation/traits.hpp" +#include "scalfmm/matrix_kernels/mk_common.hpp" +#include "scalfmm/meta/traits.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/simd/memory.hpp" +#include "scalfmm/simd/utils.hpp" +#include "scalfmm/utils/math.hpp" +#include "scalfmm/utils/tensor.hpp" +#include "xflens/cxxblas/typedefs.h" +#include "xtensor-blas/xblas_utils.hpp" +#include "xtensor/xlayout.hpp" +#include "xtensor/xoperation.hpp" + +#include <cpp_tools/colors/colorized.hpp> + +#include "xsimd/xsimd.hpp" +#include "xtensor-blas/xblas.hpp" +#include "xtensor-blas/xblas_config.hpp" +#include "xtensor/xarray.hpp" +#include "xtensor/xbuilder.hpp" +#include "xtensor/xcomplex.hpp" +#include "xtensor/xio.hpp" +#include "xtensor/xmanipulation.hpp" +#include "xtensor/xmath.hpp" +#include "xtensor/xnoalias.hpp" +#include "xtensor/xslice.hpp" +#include "xtensor/xtensor.hpp" +#include "xtensor/xtensor_forward.hpp" +#include "xtensor/xtensor_simd.hpp" +#include "xtensor/xvectorize.hpp" +#include "xtl/xclosure.hpp" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cmath> +#include <cstddef> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + +// This class implements barycentric Lagrange interpolation. By default, we use chebyshev points of the second kind, but +// chebyshev points of the first kind can be used instead simply by uncommenting the following macro. #define +// ENABLE_CHEBYSHEV_FIRST_KIND + +// #define WEIGHTS_FIRST_KIND +#define WEIGHTS_UNIFORM + +namespace scalfmm::interpolation +{ + template<typename ValueType, std::size_t Dimension, typename MatrixKernel, typename Settings> + struct barycentric_interpolator + : public impl::interpolator<barycentric_interpolator<ValueType, Dimension, MatrixKernel, Settings>> + , public impl::m2l_handler<barycentric_interpolator<ValueType, Dimension, MatrixKernel, Settings>> + { + static_assert(options::support(options::_s(Settings{}), + options::_s(options::barycentric_dense, options::barycentric_low_rank)), + "unsupported barycentric interpolator options!"); + + public: + using value_type = ValueType; + static constexpr std::size_t dimension = Dimension; + using size_type = std::size_t; + + using settings = Settings; + using matrix_kernel_type = MatrixKernel; + using self_type = barycentric_interpolator<value_type, dimension, matrix_kernel_type, settings>; + using base_interpolator_type = impl::interpolator<self_type>; + using base_m2l_handler_type = impl::m2l_handler<self_type>; + + using base_interpolator_type::base_interpolator_type; + using base_m2l_handler_type::base_m2l_handler_type; + + /** + * @brief Construct a new barycentric interpolator object + * + */ + barycentric_interpolator() = delete; + + /** + * @brief Construct a new barycentric interpolator object + * + */ + barycentric_interpolator(barycentric_interpolator const&) = delete; + + /** + * @brief Construct a new barycentric interpolator object + * + */ + barycentric_interpolator(barycentric_interpolator&&) noexcept = delete; + + /** + * @brief + * + * @return barycentric_interpolator& + */ + auto operator=(barycentric_interpolator const&) -> barycentric_interpolator& = delete; + + /** + * @brief + * + * @return barycentric_interpolator& + */ + auto operator=(barycentric_interpolator&&) noexcept -> barycentric_interpolator& = delete; + + /** + * @brief Destroy the barycentric interpolator object + * + */ + ~barycentric_interpolator() = default; + + /** + * @brief Constructor of the interpolator based on barycentric Lagrange interpolation. + * It initializes the interpolation and transfer tools (M2L). Weights and roots (Chebyshev points of the first + * or second kind) are precalculated. In addition, we also precompute d/dx S_i(x_j) for i,j in + * {1,...,order-1}. + * + * @param far_field : matrix kernel used in the far field computation + * @param order : order of the simulation + * @param tree_height : height of the tree (= width of the simulation box) + * @param root_cell_width : width of the root cell + * @param cell_width_extension : parameter to extend the width of each cell (by default = 0) + */ + barycentric_interpolator(matrix_kernel_type const& far_field, size_type order, size_type tree_height, + value_type root_cell_width, value_type cell_width_extension = value_type(0.)) + + : base_interpolator_type(order, tree_height, root_cell_width, cell_width_extension, true) + , base_m2l_handler_type(far_field, roots_impl(), tree_height, root_cell_width, cell_width_extension, true) + , m_roots_for_polynomials(roots_impl()) + , m_weights_for_polynomials(weights_impl()) + , m_derivative_of_roots(set_m_derivative_of_roots(order)) + , m_tolerance(std::numeric_limits<value_type>::min()) + { + base_interpolator_type::initialize(); + base_m2l_handler_type::initialize(order, root_cell_width, tree_height); + } + + /** + * @brief Constructor of the interpolator based on barycentric Lagrange interpolation. + * This constructor does not take a matrix kernel object as a parameter (it is initialized by default) and + * calls the previous constructor. + * + * @param order : order of the simulation + * @param tree_height : height of the tree (= width of the simulation box) + * @param root_cell_width : width of the root cell + * @param cell_width_extension : parameter to extend the width of each cell (by default = 0) + */ + barycentric_interpolator(size_type order, size_type tree_height, value_type root_cell_width, + value_type cell_width_extension = value_type(0.)) + : barycentric_interpolator(matrix_kernel_type{}, order, tree_height, root_cell_width, cell_width_extension) + { + } + + /** + * @brief Roots in [-1,1] + * If the Chebyshev roots of the first kind are used, the roots are computed as (with \f$\ell = order\f$) + * \f$\bar x_n = \cos\left(\frac{\pi}{2}\frac{2n-1}{\ell}\right)\f$ for \f$n=1,\dots,\ell\f$ + * If the Chebyshev roots of the second kind are used, the roots are computed as (with \f$\ell = order\f$) + * \f$\bar x_n = \cos\left(\pi\frac{n-1}{\ell-1}\right)\f$ for \f$n=1,\dots,\ell\f$ + * + * @return the roots + */ + [[nodiscard]] inline auto roots_impl() const -> xt::xarray<value_type> + { +#ifdef ENABLE_CHEBYSHEV_FIRST_KIND + const auto order{this->order()}; + const value_type coeff = value_type(3.14159265358979323846264338327950) / (value_type(order)); + return xt::cos(coeff * (xt::arange(int(order - 1), int(-1), -1) + 0.5)); +#else + const auto order{this->order()}; + const value_type coeff = value_type(3.14159265358979323846264338327950) / (value_type(order - 1.)); + return xt::cos(coeff * (xt::arange(int(order - 1), int(-1), -1))); +#endif + } + + /** + * @brief Weights of the barycentric formula + * If the Chebyshev roots of the first kind are used, the roots are computed as (with \f$\ell = order\f$) + * \f$\bar \omega_n = (-1)^{n+1}\sin\left(\frac{pi}{2}\frac{2n-1}{\ell}\right)\f$ for \f$n=1,\dots,\ell\f$ + * If the Chebyshev roots of the second kind are used, the roots are computed as (with \f$\ell = order\f$) + * \f$\bar \omega_n = (-1)^{n+1}\delta_n\f$ for \f$n=1,\dots,\ell\f$ with \f$\delta_n = 1/2\f$ if + * \f$n\in\{1,\ell\}\f$ and $\f\delta_n = 1\f$ otherwise + * + * @return the weights + */ + [[nodiscard]] inline auto weights_impl() const -> xt::xarray<value_type> + { +#ifdef ENABLE_CHEBYSHEV_FIRST_KIND + const auto order{this->order()}; + const value_type coeff = value_type(3.14159265358979323846264338327950) / (value_type(order)); + return xt::pow(-1, xt::arange(order)) * xt::sin(coeff * (xt::arange(int(order - 1), int(-1), -1) + 0.5)); +#else + const auto order{this->order()}; + xt::xarray<value_type> w = xt::pow(-1, xt::arange(order)); + w[0] *= value_type(0.5); + w[order - 1] *= value_type(0.5); + return w; +#endif + } + + /** + * @brief S_n(x) Lagrange function based on barycentric formula + * Computed as \f$S_n(x) = \frac{\frac{w_i}{x - x_i}}{\sum_{k = 0}^{n}{\frac{w_k}{x - x_k}}} \f$ for + * \f$n=1,\dots,\ell\f$ (with \f$\ell = order\f$) + * + * @tparam ComputationType + * @param x : 1D evaluation point in [-1,1] + * @param n : index of the Lagrange function + * @return function value S_n(x) + */ + template<typename ComputationType> + [[nodiscard]] inline auto polynomials_impl(ComputationType x, std::size_t n) const -> ComputationType + { + simd::compare(x); + const auto order{this->order()}; + + ComputationType flag{-1}, sum{0.}; + ComputationType root_at_o, weights_at_o, diff, final_result; + + for(unsigned int o = 0; o < order; ++o) + { + root_at_o = m_roots_for_polynomials[o]; + weights_at_o = m_weights_for_polynomials[o]; + + diff = x - root_at_o; + flag = xsimd::select((xsimd::abs(diff) < m_tolerance), ComputationType(o), flag); + + sum += weights_at_o / diff; + } + + auto logical_mask = flag > ComputationType(-1); + auto kronecker_delta = xsimd::select(flag == ComputationType(n), ComputationType(1.), ComputationType(0.)); + final_result = + ComputationType(m_weights_for_polynomials[n]) / ((x - ComputationType(m_roots_for_polynomials[n])) * sum); + + return xsimd::select(logical_mask, kronecker_delta, final_result); + } + + /** + * @brief Compute S_n(x) for \f$n = 1,\dots,\ell\f$ (with \f$\ell = order\f$) + * + * @tparam VectorType + * @tparam ComputationType + * @tparam Dim + * @param all_poly : vector containing all the evaluations of S_n(x) for \f$n = 1,\dots,\ell\f$ (with \f$\ell = + * order\f$ + * @param x : 1D evaluation point in [-1,1] + * @param order : order of the simulation + */ + template<typename VectorType, typename ComputationType, std::size_t Dim> + inline auto fill_all_polynomials_impl(VectorType& all_poly, container::point<ComputationType, Dim>& x, + std::size_t order) const -> void + { + for(std::size_t d = 0; d < Dim; ++d) + { + for(std::size_t o = 0; o < order; ++o) + { + all_poly[o][d] = this->polynomials_impl(x[d], o); + } + } + } + + /** + * @brief d/dx S_n(x) gradient of the Lagrange function based on barycentric formula + * Computed as \f$d/dx S_n(x) = \frac{ \frac{ \omega_n }{ x - x_n } \sum_{j = 0}^{n}{ \frac{ \omega_j (x_j - + * x_n) }{ ( x - x_j )^2 ( x - x_n ) } } }{\left( \sum_{j = 0}^{\ell}{ \frac{\omega_j}{ x - x_j } } \right)^2} + * \f$ for \f$n=1,\dots,\ell\f$ (with \f$\ell = order\f$) + * + * @tparam ComputationType + * @param x : 1D evaluation point in [-1,1] + * @param n : index of the Lagrange function + * @return function value d/dx S_n(x) + */ + template<typename ComputationType> + [[nodiscard]] inline auto derivative_impl(ComputationType x, std::size_t n) const -> ComputationType + { + simd::compare(x); + const auto order{this->order()}; + ComputationType num{0.}, denom{0.}, local_diff, final_result, local_term; + ComputationType flag{-1.}, singularity{0.}; + + for(unsigned int k = 0; k < order; ++k) + { + local_diff = ComputationType(x) - ComputationType(m_roots_for_polynomials[k]); + + auto logical_mask = xsimd::abs(local_diff) < m_tolerance; + flag = xsimd::select(logical_mask, ComputationType(k), flag); + singularity = xsimd::select(logical_mask, ComputationType(m_derivative_of_roots.at(k, n)), singularity); + + local_term = ComputationType(m_weights_for_polynomials[k]) / local_diff; + num += local_term * + (ComputationType(m_roots_for_polynomials[k]) - ComputationType(m_roots_for_polynomials[n])) / + (local_diff * (ComputationType(x) - ComputationType(m_roots_for_polynomials[n]))); + denom += local_term; + } + + final_result = (ComputationType(m_weights_for_polynomials[n]) * num) / + ((ComputationType(x) - ComputationType(m_roots_for_polynomials[n])) * denom * denom); + + return xsimd::select((flag > ComputationType(-1)), singularity, final_result); + } + + /** + * @brief Compute the quadrature weights (for the enhancement of low-rank approximation) + * + * @param order : order of the simulation + * @return xtensor container filled with the quadrature weights + */ + [[nodiscard]] inline auto generate_weights_impl(std::size_t order) const -> xt::xarray<value_type> + { +#ifdef WEIGHTS_FIRST_KIND + + const value_type coeff = value_type(3.14159265358979323846264338327950) / (value_type(this->order())); + xt::xarray<value_type> roots = xt::cos(coeff * (xt::arange(int(this->order() - 1), int(-1), -1) + 0.5)); + + const auto weights_1d = + xt::sqrt(value_type(3.14159265358979323846264338327950) / order * xt::sqrt(1 - (roots * roots))); + xt::xarray<value_type> roots_weights(std::vector(dimension, order)); + + auto generate_weights = [&roots_weights, &weights_1d](auto... is) + { roots_weights.at(is...) = (... * weights_1d.at(is)); }; + + std::array<int, dimension> starts{}; + std::array<int, dimension> stops{}; + starts.fill(0); + stops.fill(order); + meta::looper_range<dimension>{}(generate_weights, starts, stops); + return roots_weights; +#endif + +#ifdef WEIGHTS_UNIFORM + return xt::xarray<value_type>(std::vector{math::pow(order, dimension)}, value_type(1.)); +#endif + } + + private: + /** + * @brief Chebyshev polynomials of the first kind \f$ T_n(x) = \cos(n \acos(x)) \f$ + * * \acos(x))}{\sqrt{1-x^2}} \f$ are needed. + * + * @tparam ComputationType + * @param[in] n : index of the Cheyshev polynomial (1st kind) + * @param[in] x : 1D evaluation point in [-1,1] + * @return function value T_n(x) + */ + template<typename ComputationType> + [[nodiscard]] inline auto T(const unsigned int n, ComputationType x) const -> ComputationType + { + simd::compare(x); + + return xsimd::cos(ComputationType(n) * xsimd::acos(x)); + } + + /** + * @brief Chebyshev polynomials of the second kind \f$ U_n(x) = \frac{\sin( (n+1)\acos(x) )}{\sqrt{1 - x^2}} \f$ + * + * For the derivation of the Chebyshev polynomials of first kind + * \f$ \frac{\mathrm{d} T_n(x)}{\mathrm{d}x} = n U_{n-1}(x) \f$ the Chebyshev + * polynomials of second kind \f$ U_{n-1}(x) = \frac{\sin(n \acos(x))}{\sqrt{1-x^2}} \f$ + * are needed. + * + * @param[in] n : index of the Cheyshev polynomial (2nd kind) + * @param[in] x : 1D evaluation point in [-1,1] + * + * @return function value U_n(x) + */ + template<typename ComputationType> + [[nodiscard]] inline auto U(const unsigned int n, ComputationType x) const -> ComputationType + { + simd::compare(x); + + return ComputationType((xsimd::sin((n + 1) * acos(x))) / xsimd::sqrt(1 - x * x)); + } + + /** + * @brief Compute \f$\left{ d/dx S_j(x_i) | i,j = 1,\dots,\ell \right}\f$ with \f$\ell = order\f$ + * + * @param order : order of the simulation + * @return xtensor container filled with d/dx S_j(x_i) + */ + inline auto set_m_derivative_of_roots(size_type order) -> xt::xarray<value_type> + { + xt::xarray<value_type> derivative_of_roots(xt::zeros<value_type>(std::vector<std::size_t>(2, order))); + auto roots = this->roots_impl(); + auto weights = this->weights_impl(); + value_type local_w_i, local_root_i, local_coeff; + + for(unsigned int i = 0; i < order; ++i) + { + local_w_i = weights[i]; + local_root_i = roots[i]; + for(unsigned int j = 0; j < i; ++j) + { + local_coeff = (weights[j] / local_w_i) / (local_root_i - roots[j]); + derivative_of_roots.at(i, j) = local_coeff; + derivative_of_roots.at(i, i) -= local_coeff; + } + for(unsigned int j = i + 1; j < order; ++j) + { + local_coeff = (weights[j] / local_w_i) / (local_root_i - roots[j]); + derivative_of_roots.at(i, j) = local_coeff; + derivative_of_roots.at(i, i) -= local_coeff; + } + } + return derivative_of_roots; + } + + // private members + + /** + * @brief + * + */ + xt::xarray<value_type> m_roots_for_polynomials{}; + + /** + * @brief + * + */ + xt::xarray<value_type> m_weights_for_polynomials{}; + + /** + * @brief + * + */ + value_type m_tolerance{}; + + /** + * @brief + * + */ + xt::xarray<value_type> m_derivative_of_roots{}; + }; + + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam FarFieldMatrixKernel + * @tparam Settings + */ + template<typename ValueType, std::size_t Dimension, typename FarFieldMatrixKernel, typename Settings> + struct interpolator_traits<barycentric_interpolator<ValueType, Dimension, FarFieldMatrixKernel, Settings>> + { + using value_type = ValueType; + using matrix_kernel_type = FarFieldMatrixKernel; + + /** + * @brief + * + */ + static constexpr std::size_t dimension = Dimension; + + /** + * @brief + * + */ + static constexpr bool enable_symmetries = (dimension < 4) ? true : false; + + /** + * @brief + * + */ + static constexpr bool symmetry_support{ + (matrix_kernel_type::symmetry_tag == matrix_kernels::symmetry::symmetric) && enable_symmetries}; + + /** + * @brief + * + */ + static constexpr std::size_t kn = matrix_kernel_type::kn; + + /** + * @brief + * + */ + static constexpr std::size_t km = matrix_kernel_type::km; + + using settings = Settings; + using self_type = barycentric_interpolator<value_type, dimension, matrix_kernel_type, settings>; + using base_interpolator_type = impl::interpolator<self_type>; + using base_m2l_handler_type = impl::m2l_handler<self_type>; + using storage_type = component::grid_storage<value_type, dimension, km, kn>; + /// Temporary buffer to aggregate the multipole in order to perform matrix-matrix product + using buffer_value_type = value_type; + // the type of the element inside the buffer + using buffer_inner_type = std::conditional_t<symmetry_support, xt::xarray<value_type>, meta::empty_inner>; + // matrix of size 2 to store the multipole and the local) + using buffer_shape_type = std::conditional_t<symmetry_support, xt::xshape<2>, meta::empty_shape>; + // matrix of size 2 to store the multipole and the local + + using buffer_type = + std::conditional_t<symmetry_support, xt::xtensor_fixed<buffer_inner_type, buffer_shape_type>, meta::empty>; + + using multipoles_container_type = typename storage_type::multipoles_container_type; + using locals_container_type = typename storage_type::locals_container_type; + using k_tensor_type = xt::xarray<value_type>; + }; +} // namespace scalfmm::interpolation +#endif // SCALFMM_INTERPOLATION_BARYCENTRIC_BARYCENTRIC_STORAGE_HPP diff --git a/include/scalfmm/interpolation/barycentric/barycentric_storage.hpp b/include/scalfmm/interpolation/barycentric/barycentric_storage.hpp new file mode 100644 index 0000000000000000000000000000000000000000..60d2943ff4346a7276385538714a3b50296787aa --- /dev/null +++ b/include/scalfmm/interpolation/barycentric/barycentric_storage.hpp @@ -0,0 +1,111 @@ + +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/interpolation/barycentric/barycentric_storage.hpp +// -------------------------------- +#ifndef SCALFMM_INTERPOLATION_BARYCENTRIC_BARYCENTRIC_STORAGE_HPP +#define SCALFMM_INTERPOLATION_BARYCENTRIC_BARYCENTRIC_STORAGE_HPP + +#include "scalfmm/memory/storage.hpp" + +namespace scalfmm::component +{ + /** + * @brief + * + * This is the specialization for the barycentric interpolator that + * requires to store the transformed multipoles. + * + * @tparam ValueType + * @tparam Dimension + * @tparam Inputs + * @tparam Outputs + */ + template<typename ValueType, std::size_t Dimension, std::size_t Inputs, std::size_t Outputs> + struct alignas(XTENSOR_FIXED_ALIGN) barycentric_storage + : public memory::aggregate_storage<memory::multipoles_storage<ValueType, Dimension, Inputs>, + memory::locals_storage<ValueType, Dimension, Outputs>> + { + /** + * @brief + * + */ + struct empty + { + }; + + /** + * @brief + * + */ + static constexpr std::size_t dimension = Dimension; + + /** + * @brief + * + */ + static constexpr std::size_t inputs_size = Inputs; + + /** + * @brief + * + */ + static constexpr std::size_t outputs_size = Outputs; + + using value_type = ValueType; + + using multipoles_storage_type = memory::multipoles_storage<ValueType, Dimension, Inputs>; + using locals_storage_type = memory::locals_storage<ValueType, Dimension, Outputs>; + + using base_type = memory::aggregate_storage<multipoles_storage_type, locals_storage_type>; + + using multipoles_container_type = typename memory::storage_traits<multipoles_storage_type>::tensor_type; + using locals_container_type = typename memory::storage_traits<locals_storage_type>::tensor_type; + using buffer_value_type = empty; + using buffer_inner_type = empty; + using buffer_shape_type = empty; + using buffer_type = empty; + + using base_type::base_type; + + /** + * @brief Construct a new barycentric storage object + * + */ + barycentric_storage() = default; + + /** + * @brief Construct a new barycentric storage object + * + */ + barycentric_storage(barycentric_storage const&) = default; + + /** + * @brief Construct a new barycentric storage object + * + */ + barycentric_storage(barycentric_storage&&) noexcept = default; + + /** + * @brief + * + * @return barycentric_storage& + */ + inline auto operator=(barycentric_storage const&) -> barycentric_storage& = default; + + /** + * @brief + * + * @return barycentric_storage& + */ + inline auto operator=(barycentric_storage&&) noexcept -> barycentric_storage& = default; + + /** + * @brief Destroy the barycentric storage object + * + */ + ~barycentric_storage() = default; + }; +} // namespace scalfmm::component + +#endif // SCALFMM_INTERPOLATION_BARYCENTRIC_BARYCENTRIC_STORAGE_HPP diff --git a/include/scalfmm/interpolation/builders.hpp b/include/scalfmm/interpolation/builders.hpp index e7019af864a93f2feadeb8b96fb76c5d22d2c137..6f1a03df52617ab38b3e25d5de96eefa8be237f6 100644 --- a/include/scalfmm/interpolation/builders.hpp +++ b/include/scalfmm/interpolation/builders.hpp @@ -1,40 +1,63 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/builders.hpp +// File : scalfmm/interpolation/builders.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_BUILDERS_HPP #define SCALFMM_INTERPOLATION_BUILDERS_HPP -#include <cstddef> -#include <scalfmm/meta/const_functions.hpp> -#include <tuple> -#include <utility> -#include <xtensor-blas/xlinalg.hpp> -#include <xtensor/xtensor.hpp> +#include "scalfmm/meta/const_functions.hpp" +#include "xtensor-blas/xlinalg.hpp" #include "xtensor/xarray.hpp" #include "xtensor/xbuilder.hpp" #include "xtensor/xcontainer.hpp" +#include "xtensor/xtensor.hpp" #include "xtensor/xtensor_forward.hpp" +#include <cstddef> +#include <tuple> +#include <utility> + namespace scalfmm::interpolation { + + /** + * @brief + * + * @tparam td::size_t + * @tparam T + * @param t + * @return constexpr auto + */ template<std::size_t, typename T> constexpr auto id(T t) { return std::forward<T>(t); } + /** + * @brief + * + * @tparam Gen + * @tparam I + */ template<typename Gen, std::size_t... I> constexpr auto get_generator(Gen&& gen, std::index_sequence<I...> /*unused*/) { return std::move(xt::stack(std::make_tuple(id<I>(gen)...))); } - template<std::size_t dim> + /** + * @brief + * + * @tparam Dim + * @param order + * @return constexpr auto + */ + template<std::size_t Dim> constexpr auto get(const std::size_t order) { - return get_generator(xt::linspace(std::size_t{0}, order - 1, order), std::make_index_sequence<dim>{}); + return get_generator(xt::linspace(std::size_t{0}, order - 1, order), std::make_index_sequence<Dim>{}); } // TODO for nodes @@ -52,21 +75,48 @@ namespace scalfmm::interpolation //} // std::cout << "ids: " << saved << '\n'; - template<std::size_t dim> + /** + * @brief + * + * @tparam Dim + */ + template<std::size_t Dim> struct nodes { - static_assert(dim < 4, "Dimension for interpolation node not supported."); + static_assert(Dim < 4, "Dimension for interpolation node not supported."); }; + /** + * @brief + * + * @tparam + */ template<> struct nodes<1> { + /** + * @brief + * + * @param order + * @return auto + */ inline static auto get(std::size_t order) { return xt::linspace(std::size_t{0}, order - 1, order); } }; + /** + * @brief + * + * @tparam + */ template<> struct nodes<2> { + /** + * @brief + * + * @param order + * @return auto + */ inline static auto get(std::size_t order) { const std::size_t s{meta::pow(order, std::size_t(2))}; @@ -81,9 +131,20 @@ namespace scalfmm::interpolation } }; + /** + * @brief + * + * @tparam + */ template<> struct nodes<3> { + /** + * @brief + * + * @param order + * @return auto + */ inline static auto get(std::size_t order) { const auto s{meta::pow(order, 3)}; @@ -99,9 +160,20 @@ namespace scalfmm::interpolation } }; + /** + * @brief + * + * @tparam + */ template<> struct nodes<4> { + /** + * @brief + * + * @param order + * @return auto + */ inline static auto get(std::size_t order) { const auto s{meta::pow(order, 4)}; diff --git a/include/scalfmm/interpolation/chebyshev.hpp b/include/scalfmm/interpolation/chebyshev.hpp index 5325260dbcb1a69e4f3d38937ed3e0f58fc4ce02..4bf37b0ebc28d6035951a918b6161938bc9720c9 100644 --- a/include/scalfmm/interpolation/chebyshev.hpp +++ b/include/scalfmm/interpolation/chebyshev.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/chebyshev.hpp +// File : scalfmm/interpolation/chebyshev.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_CHEBYSHEV_HPP #define SCALFMM_INTERPOLATION_CHEBYSHEV_HPP diff --git a/include/scalfmm/interpolation/chebyshev/chebyshev_interpolator.hpp b/include/scalfmm/interpolation/chebyshev/chebyshev_interpolator.hpp index 9efa728bef3f134002f2daf479cff8352b3276cd..ffea501c2fca7bda1d36bec3a0c9200135c7b813 100644 --- a/include/scalfmm/interpolation/chebyshev/chebyshev_interpolator.hpp +++ b/include/scalfmm/interpolation/chebyshev/chebyshev_interpolator.hpp @@ -1,39 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/chebyshev.hpp +// File : scalfmm/interpolation/chebyshev/chebyshev_interpolator.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_CHEBYSHEV_CHEBYSHEV_INTERPOLATOR_HPP #define SCALFMM_INTERPOLATION_CHEBYSHEV_CHEBYSHEV_INTERPOLATOR_HPP -#include <algorithm> -#include <array> -#include <cassert> -#include <cmath> -#include <cstddef> -#include <iterator> -#include <limits> -#include <memory> -#include <tuple> -#include <type_traits> -#include <utility> -#include <vector> -#include <xsimd/xsimd.hpp> -#include <xtensor-blas/xblas.hpp> -#include <xtensor-blas/xblas_config.hpp> -#include <xtensor/xarray.hpp> -#include <xtensor/xbuilder.hpp> -#include <xtensor/xcomplex.hpp> -#include <xtensor/xio.hpp> -#include <xtensor/xmanipulation.hpp> -#include <xtensor/xmath.hpp> -#include <xtensor/xnoalias.hpp> -#include <xtensor/xslice.hpp> -#include <xtensor/xtensor.hpp> -#include <xtensor/xtensor_forward.hpp> -#include <xtensor/xtensor_simd.hpp> -#include <xtensor/xvectorize.hpp> -#include <xtl/xclosure.hpp> - #include "scalfmm/container/point.hpp" #include "scalfmm/container/variadic_adaptor.hpp" #include "scalfmm/interpolation/grid_storage.hpp" @@ -53,8 +24,46 @@ #include "xtensor/xlayout.hpp" #include "xtensor/xoperation.hpp" +#include "xsimd/xsimd.hpp" +#include "xtensor-blas/xblas.hpp" +#include "xtensor-blas/xblas_config.hpp" +#include "xtensor/xarray.hpp" +#include "xtensor/xbuilder.hpp" +#include "xtensor/xcomplex.hpp" +#include "xtensor/xio.hpp" +#include "xtensor/xmanipulation.hpp" +#include "xtensor/xmath.hpp" +#include "xtensor/xnoalias.hpp" +#include "xtensor/xslice.hpp" +#include "xtensor/xtensor.hpp" +#include "xtensor/xtensor_forward.hpp" +#include "xtensor/xtensor_simd.hpp" +#include "xtensor/xvectorize.hpp" +#include "xtl/xclosure.hpp" + +#include <algorithm> +#include <array> +#include <cassert> +#include <cmath> +#include <cstddef> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + namespace scalfmm::interpolation { + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + * @tparam Settings + */ template<typename ValueType, std::size_t Dimension, typename MatrixKernel, typename Settings> struct chebyshev_interpolator : public impl::interpolator<chebyshev_interpolator<ValueType, Dimension, MatrixKernel, Settings>> @@ -78,65 +87,102 @@ namespace scalfmm::interpolation using base_interpolator_type::base_interpolator_type; using base_m2l_handler_type::base_m2l_handler_type; + /** + * @brief Construct a new chebyshev interpolator object + * + */ chebyshev_interpolator() = delete; + + /** + * @brief Construct a new chebyshev interpolator object + * + */ chebyshev_interpolator(chebyshev_interpolator const&) = delete; + + /** + * @brief Construct a new chebyshev interpolator object + * + */ chebyshev_interpolator(chebyshev_interpolator&&) noexcept = delete; + + /** + * @brief + * + * @return chebyshev_interpolator& + */ auto operator=(chebyshev_interpolator const&) -> chebyshev_interpolator& = delete; + + /** + * @brief + * + * @return chebyshev_interpolator& + */ auto operator=(chebyshev_interpolator&&) noexcept -> chebyshev_interpolator& = delete; + + /** + * @brief Destroy the chebyshev interpolator object + * + */ ~chebyshev_interpolator() = default; - /// @brief - /// @param far_field - /// @param order - /// @param tree_height - /// @param root_cell_width - /// @param cell_width_extension - chebyshev_interpolator(matrix_kernel_type const& far_field, size_type order, size_type tree_height = 3, - value_type root_cell_width = value_type(1.), - value_type cell_width_extension = value_type(0.)) - : - base_interpolator_type(order, tree_height, root_cell_width, cell_width_extension, true) - , base_m2l_handler_type(far_field, roots_impl(), tree_height, root_cell_width, cell_width_extension, true), -m_T_of_roots(set_m_T_of_roots(order)) + /** + * @brief Construct a new chebyshev interpolator object + * + * @param far_field + * @param order + * @param tree_height + * @param root_cell_width + * @param cell_width_extension + */ + chebyshev_interpolator(matrix_kernel_type const& far_field, size_type order, size_type tree_height, + value_type root_cell_width, value_type cell_width_extension = value_type(0.)) + : base_interpolator_type(order, tree_height, root_cell_width, cell_width_extension, true) + , base_m2l_handler_type(far_field, roots_impl(), tree_height, root_cell_width, cell_width_extension, true) + , m_T_of_roots(set_m_T_of_roots(order)) { base_interpolator_type::initialize(); base_m2l_handler_type::initialize(order, root_cell_width, tree_height); } - /// @brief - /// @param - /// @param order - /// @param tree_height - /// @param root_cell_width - /// @param cell_width_extension - chebyshev_interpolator(size_type order, size_type tree_height = 3, value_type root_cell_width = value_type(1.), + /** + * @brief Construct a new chebyshev interpolator object + * + * @param order + * @param tree_height + * @param root_cell_width + * @param cell_width_extension + */ + chebyshev_interpolator(size_type order, size_type tree_height, value_type root_cell_width, value_type cell_width_extension = value_type(0.)) : chebyshev_interpolator(matrix_kernel_type{}, order, tree_height, root_cell_width, cell_width_extension) { } - /// - /// \brief roots_impl Chebyshev roots in [-1,1] - /// - /// computed as \f$\bar x_n = \cos\left(\frac{\pi}{2}\frac{2n-1}{\ell}\right)\f$ for \f$n=1,\dots,\ell\f$ - /// \param order - /// \return the Chebyshev roots - /// + /** + * @brief roots_impl Chebyshev roots in [-1,1] + * + * computed as \f$\bar x_n = \cos\left(\frac{\pi}{2}\frac{2n-1}{\ell}\right)\f$ for \f$n=1,\dots,\ell\f$ + * @param order + * @return the Chebyshev roots + */ [[nodiscard]] inline auto roots_impl() const -> xt::xarray<value_type> { const value_type coeff = value_type(3.14159265358979323846264338327950) / (value_type(this->order())); return xt::cos(coeff * (xt::arange(int(this->order() - 1), int(-1), -1) + 0.5)); } + /** + * @brief + * * S_k(x) Lagrange function based on Chebyshev polynomials of first kind \f$ * - *for \f$p = order+1 \f$ the interpolator S associated to point n + * for \f$p = order+1 \f$ the interpolator S associated to point n * \f$ S_n(x) = \frac{1}{p} + \frac{2}{p} \sum_{k=1}{p-1}{ T_k(x)T_k(x_n)}\f$ * then we have \f$ S_n(x_m) =\delta_{n,m} \f$ * We use the recurrence relation to construct \f$ T_k(x) \f$ * - * @param[in] n index * @param[in] x coordinate in [-1,1] + * @param[in] n index * @return function value */ template<typename ComputationType> @@ -161,6 +207,18 @@ m_T_of_roots(set_m_T_of_roots(order)) } return coeff * L; } + + /** + * @brief + * + * @tparam VectorType + * @tparam ComputationType + * @tparam Dim + * @param all_poly + * @param x + * @param order + * @return auto + */ template<typename VectorType, typename ComputationType, std::size_t Dim> inline auto fill_all_polynomials_impl(VectorType& all_poly, container::point<ComputationType, Dim> x, std::size_t order) const @@ -206,17 +264,21 @@ m_T_of_roots(set_m_T_of_roots(order)) #endif // return std::move(all_poly); } + /** + * @brief + * * d/dx S_n(x) gradient of the Lagrange function based on Chebyshev - *polynomials of first kind \f$ + * polynomials of first kind \f$ * - *for \f$p = order+1 \f$ the interpolator S associated to point n + * for \f$p = order+1 \f$ the interpolator S associated to point n * \f$ d/dx S_n(x) = \frac{2}{p} \sum_{k=1}{p-1}{ T_k(x_n) d/dx T_k(x)}\f$ * then * \f$ d/dx S_n(x) = \frac{2}{p} \sum_{k=1}{p}{ T_k(x_n) k U_{k-1}(x)}\f$ * * - * @param[in] n index * @param[in] x coordinate in [-1,1] + * @param[in] n index + * * @return function value */ template<typename ComputationType> @@ -247,6 +309,15 @@ m_T_of_roots(set_m_T_of_roots(order)) } return coeff * L; } + + /** + * @brief + * + * @tparam ComputationType + * @param x + * @param n + * @return ComputationType + */ template<typename ComputationType> [[nodiscard]] inline auto derivative_impl_old(ComputationType x, std::size_t n) const -> ComputationType { @@ -265,10 +336,16 @@ m_T_of_roots(set_m_T_of_roots(order)) } return coeff * L; } + /** + * @brief + * * Sets the roots of the Chebyshev quadrature weights defined as \f$w_i = * \frac{\pi}{\ell}\sqrt{1-\bar x_i^2}\f$ with the Chebyshev roots \f$\bar * x\f$. + * + * @param order + * @return xt::xarray<value_type> */ [[nodiscard]] inline auto generate_weights_impl(std::size_t order) const -> xt::xarray<value_type> { @@ -289,12 +366,15 @@ m_T_of_roots(set_m_T_of_roots(order)) } private: - /// - /// \brief Chebyshev polynomials of first kind \f$ T_n(x) = \cos(n \arccos(x)) \f$ - /// * \arccos(x))}{\sqrt{1-x^2}} \f$ are needed. - /// \param[in] n index - /// \param[in] x coordinate in [-1,1] - /// \return function value U_n(x) + /** + * @brief Chebyshev polynomials of first kind \f$ T_n(x) = \cos(n \arccos(x)) \f$ + * * \arccos(x))}{\sqrt{1-x^2}} \f$ are needed. + * + * @tparam ComputationType + * @param[in] n index + * @param[in] x coordinate in [-1,1] + * @return function value T_n(x) + */ template<typename ComputationType> [[nodiscard]] inline auto T(const unsigned int n, ComputationType x) const -> ComputationType { @@ -302,17 +382,20 @@ m_T_of_roots(set_m_T_of_roots(order)) return xsimd::cos(ComputationType(n) * xsimd::acos(x)); } - /// - /// \brief U the derivation of the Chebyshev polynomials of first kind - /// - /// For the derivation of the Chebyshev polynomials of first kind - /// \f$ \frac{\mathrm{d} T_n(x)}{\mathrm{d}x} = n U_{n-1}(x) \f$ the Chebyshev - /// polynomials of second kind \f$ U_{n-1}(x) = \frac{\sin(n \arccos(x))}{\sqrt{1-x^2}} \f$ - /// are needed. - /// \param[in] n index - /// \param[in] x coordinate in [-1,1] - /// \return function value U_n(x) - /// + + /** + * @brief U the derivation of the Chebyshev polynomials of first kind + * + * For the derivation of the Chebyshev polynomials of first kind + * \f$ \frac{\mathrm{d} T_n(x)}{\mathrm{d}x} = n U_{n-1}(x) \f$ the Chebyshev + * polynomials of second kind \f$ U_{n-1}(x) = \frac{\sin(n \arccos(x))}{\sqrt{1-x^2}} \f$ + * are needed. + * + * @tparam ComputationType + * @param[in] n index + * @param[in] x coordinate in [-1,1] + * @return function value U_n(x) + */ template<typename ComputationType> [[nodiscard]] inline auto U(const unsigned int n, ComputationType x) const -> ComputationType { @@ -320,7 +403,13 @@ m_T_of_roots(set_m_T_of_roots(order)) return ComputationType((xsimd::sin((n + 1) * acos(x))) / xsimd::sqrt(1 - x * x)); } - /// Store Tn(x_i) + + /** + * @brief Store Tn(x_i) + * + * @param order + * @return xt::xarray<value_type> + */ inline auto set_m_T_of_roots(size_type order) -> xt::xarray<value_type> { xt::xarray<value_type> T_of_roots(std::vector<std::size_t>(2, order)); @@ -336,11 +425,16 @@ m_T_of_roots(set_m_T_of_roots(order)) } return T_of_roots; } + // private members - // (j,o) for contiguous access when x_j is fixed used in polynomials_impl - xt::xarray<value_type> - m_T_of_roots{}; ///< The chebyshev polynomials of root nodes: m_T_of_roots[j,o] = T_o(x_j) + + /** + * @brief The chebyshev polynomials of root nodes: m_T_of_roots[j,o] = T_o(x_j) + * (j,o) for contiguous access when x_j is fixed used in polynomials_impl + */ + xt::xarray<value_type> m_T_of_roots{}; }; + /** * @brief * @@ -355,13 +449,38 @@ m_T_of_roots(set_m_T_of_roots(order)) { using value_type = ValueType; using matrix_kernel_type = FarFieldMatrixKernel; + + /** + * @brief + * + */ static constexpr std::size_t dimension = Dimension; + + /** + * @brief + * + */ static constexpr bool enable_symmetries = (dimension < 4) ? true : false; + + /** + * @brief + * + */ static constexpr bool symmetry_support{ (matrix_kernel_type::symmetry_tag == matrix_kernels::symmetry::symmetric) && enable_symmetries}; + /** + * @brief + * + */ static constexpr std::size_t kn = matrix_kernel_type::kn; + + /** + * @brief + * + */ static constexpr std::size_t km = matrix_kernel_type::km; + using settings = Settings; using self_type = chebyshev_interpolator<value_type, dimension, matrix_kernel_type, settings>; using base_interpolator_type = impl::interpolator<self_type>; diff --git a/include/scalfmm/interpolation/chebyshev/chebyshev_storage.hpp b/include/scalfmm/interpolation/chebyshev/chebyshev_storage.hpp index 178ce94cbd863c7c847975939b6b9de3011187f4..7e3dee7873b763518cc7e768eba5a1e9c11f5f41 100644 --- a/include/scalfmm/interpolation/chebyshev/chebyshev_storage.hpp +++ b/include/scalfmm/interpolation/chebyshev/chebyshev_storage.hpp @@ -1,25 +1,54 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/chebyshev/chebyshev_storage.hpp +// File : scalfmm/interpolation/chebyshev/chebyshev_storage.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_CHEBYSHEV_CHEBYSHEV_STORAGE_HPP #define SCALFMM_INTERPOLATION_CHEBYSHEV_CHEBYSHEV_STORAGE_HPP #include "scalfmm/memory/storage.hpp" - namespace scalfmm::component { - // This is the specialization for the uniform interpolator that - // requires to store the transformed multipoles. + /** + * @brief + * + * This is the specialization for the uniform interpolator that + * requires to store the transformed multipoles. + * + * @tparam ValueType + * @tparam Dimension + * @tparam Inputs + * @tparam Outputs + */ template<typename ValueType, std::size_t Dimension, std::size_t Inputs, std::size_t Outputs> struct alignas(XTENSOR_FIXED_ALIGN) chebyshev_storage : public memory::aggregate_storage<memory::multipoles_storage<ValueType, Dimension, Inputs>, memory::locals_storage<ValueType, Dimension, Outputs>> { - struct empty{}; + /** + * @brief + * + */ + struct empty + { + }; + + /** + * @brief + * + */ static constexpr std::size_t dimension = Dimension; + + /** + * @brief + * + */ static constexpr std::size_t inputs_size = Inputs; + + /** + * @brief + * + */ static constexpr std::size_t outputs_size = Outputs; using value_type = ValueType; @@ -27,8 +56,7 @@ namespace scalfmm::component using multipoles_storage_type = memory::multipoles_storage<ValueType, Dimension, Inputs>; using locals_storage_type = memory::locals_storage<ValueType, Dimension, Outputs>; - using base_type = - memory::aggregate_storage<multipoles_storage_type, locals_storage_type>; + using base_type = memory::aggregate_storage<multipoles_storage_type, locals_storage_type>; using multipoles_container_type = typename memory::storage_traits<multipoles_storage_type>::tensor_type; using locals_container_type = typename memory::storage_traits<locals_storage_type>::tensor_type; @@ -39,11 +67,42 @@ namespace scalfmm::component using base_type::base_type; + /** + * @brief Construct a new chebyshev storage object + * + */ chebyshev_storage() = default; + + /** + * @brief Construct a new chebyshev storage object + * + */ chebyshev_storage(chebyshev_storage const&) = default; + + /** + * @brief Construct a new chebyshev storage object + * + */ chebyshev_storage(chebyshev_storage&&) noexcept = default; + + /** + * @brief + * + * @return chebyshev_storage& + */ inline auto operator=(chebyshev_storage const&) -> chebyshev_storage& = default; + + /** + * @brief + * + * @return chebyshev_storage& + */ inline auto operator=(chebyshev_storage&&) noexcept -> chebyshev_storage& = default; + + /** + * @brief Destroy the chebyshev storage object + * + */ ~chebyshev_storage() = default; }; } // namespace scalfmm::component diff --git a/include/scalfmm/interpolation/generate_circulent.hpp b/include/scalfmm/interpolation/generate_circulent.hpp index 56ff7f6706726628528397eaf5356ba9719806de..0e4db439b63a3a16b77a93b7caa8c89209b89d12 100644 --- a/include/scalfmm/interpolation/generate_circulent.hpp +++ b/include/scalfmm/interpolation/generate_circulent.hpp @@ -1,27 +1,49 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/generate_circulent.hpp +// File : scalfmm/interpolation/generate_circulent.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_GENERATE_CIRCULENT_HPP #define SCALFMM_INTERPOLATION_GENERATE_CIRCULENT_HPP -#include <xtensor/xslice.hpp> -#include <utility> -#include <cstddef> -#include <vector> - #include "scalfmm/meta/utils.hpp" #include "scalfmm/utils/tensor.hpp" + #include "xtensor/xtensor_forward.hpp" #include "xtensor/xview.hpp" +#include <cstddef> +#include <utility> +#include <vector> +#include <xtensor/xslice.hpp> + namespace scalfmm::interpolation { + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam SavedDimension + */ template<typename ValueType, std::size_t Dimension, std::size_t SavedDimension> struct build { using value_type = ValueType; + /** + * @brief + * + * @tparam TensorViewX + * @tparam TensorViewY + * @tparam FarField + * @param X + * @param Y + * @param order + * @param far_field + * @param n + * @param m + * @return auto + */ template<typename TensorViewX, typename TensorViewY, typename FarField> [[nodiscard]] inline auto operator()(TensorViewX&& X, TensorViewY&& Y, std::size_t order, FarField const& far_field, std::size_t n, std::size_t m) @@ -60,11 +82,31 @@ namespace scalfmm::interpolation } }; + /** + * @brief + * + * @tparam ValueType + * @tparam SavedDimension + */ template<typename ValueType, std::size_t SavedDimension> struct build<ValueType, 1, SavedDimension> { using value_type = ValueType; + /** + * @brief + * + * @tparam TensorViewX + * @tparam TensorViewY + * @tparam FarField + * @param X + * @param Y + * @param order + * @param far_field + * @param n + * @param m + * @return auto + */ template<typename TensorViewX, typename TensorViewY, typename FarField> [[nodiscard]] inline auto operator()(TensorViewX&& X, TensorViewY&& Y, std::size_t order, FarField const& far_field, std::size_t n, std::size_t m) @@ -77,16 +119,15 @@ namespace scalfmm::interpolation for(std::size_t i = 0; i < order; ++i) { c1_column(i) = - far_field.evaluate(std::forward<TensorViewX>(X)(i), std::forward<TensorViewY>(Y)(0)) - .at(n*km + m); + far_field.evaluate(std::forward<TensorViewX>(X)(i), std::forward<TensorViewY>(Y)(0)).at(n * km + m); } auto c1_row = xt::view(c, xt::range(order, ntilde)); for(std::size_t i = 1; i < order; ++i) { - c1_row(i - 1) = far_field.evaluate(std::forward<TensorViewX>(X)(0), - std::forward<TensorViewY>(Y)(order - i)) - .at(n*km + m); + c1_row(i - 1) = + far_field.evaluate(std::forward<TensorViewX>(X)(0), std::forward<TensorViewY>(Y)(order - i)) + .at(n * km + m); } return c; } diff --git a/include/scalfmm/interpolation/grid_storage.hpp b/include/scalfmm/interpolation/grid_storage.hpp index 44c9175564f749f50308182cf81e69568a1c2e88..cf278fe74ec087ded8673fb3b78c8bb0a0c00913 100644 --- a/include/scalfmm/interpolation/grid_storage.hpp +++ b/include/scalfmm/interpolation/grid_storage.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/uniform.hpp +// File : scalfmm/interpolation/grid_storage.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_GRID_STORAGE_HPP #define SCALFMM_INTERPOLATION_GRID_STORAGE_HPP @@ -18,8 +18,22 @@ namespace scalfmm::component // , // memory::transformed_multipoles_storage<ValueType, Dimension, 2>> { + /** + * @brief + * + */ static constexpr std::size_t dimension = Dimension; + + /** + * @brief + * + */ static constexpr std::size_t inputs_size = Inputs; + + /** + * @brief + * + */ static constexpr std::size_t outputs_size = Outputs; using value_type = ValueType; @@ -37,20 +51,73 @@ namespace scalfmm::component using base_type::base_type; // more using in memory + /** + * @brief Construct a new grid storage object + * + */ grid_storage() = default; + + /** + * @brief Construct a new grid storage object + * + */ grid_storage(grid_storage const&) = default; + + /** + * @brief Construct a new grid storage object + * + */ grid_storage(grid_storage&&) noexcept = default; + + /** + * @brief + * + * @return grid_storage& + */ inline auto operator=(grid_storage const&) -> grid_storage& = default; + + /** + * @brief + * + * @return grid_storage& + */ inline auto operator=(grid_storage&&) noexcept -> grid_storage& = default; + + /** + * @brief Destroy the grid storage object + * + */ ~grid_storage() = default; + /** + * @brief Get the transfer nultipole object + * + * @return auto& + */ auto& get_transfer_nultipole() { base_type::get(); } + + /** + * @brief Get the transfer nultipole object + * + * @return auto const& + */ auto const& get_transfer_nultipole() const { base_type::get(); } + /** + * @brief + * + * @return multipoles_container_type const& + */ auto transfer_multipoles() const noexcept -> multipoles_container_type const& { return base_type::multipoles(); } + + /** + * @brief + * + * @return multipoles_container_type& + */ auto transfer_multipoles() noexcept -> multipoles_container_type& { return base_type::multipoles(); } }; } // namespace scalfmm::component diff --git a/include/scalfmm/interpolation/interpolation.hpp b/include/scalfmm/interpolation/interpolation.hpp index 2418b95942e597d78a2404841e730a3ef07445f8..6ff42ef100378b9ef5f1aa0aacb866c6538babc3 100644 --- a/include/scalfmm/interpolation/interpolation.hpp +++ b/include/scalfmm/interpolation/interpolation.hpp @@ -1,58 +1,138 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/interpolation.hpp +// File : scalfmm/interpolation/interpolation.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_INTERPOLATION_HPP #define SCALFMM_INTERPOLATION_INTERPOLATION_HPP -#include "scalfmm/interpolation/uniform/uniform_interpolator.hpp" +#include "scalfmm/interpolation/barycentric/barycentric_interpolator.hpp" #include "scalfmm/interpolation/chebyshev/chebyshev_interpolator.hpp" +#include "scalfmm/interpolation/uniform/uniform_interpolator.hpp" #include "scalfmm/options/options.hpp" namespace scalfmm::interpolation { + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + * @tparam Settings + */ template<typename ValueType, std::size_t Dimension, typename MatrixKernel, typename Settings> struct get_interpolator { static_assert(options::support(options::_s(Settings{}), - options::_s(options::uniform_dense, options::uniform_low_rank, options::uniform_fft, - options::chebyshev_dense, options::chebyshev_low_rank)), + options::_s(options::uniform_dense, options::uniform_low_rank, + options::uniform_fft, options::chebyshev_dense, + options::chebyshev_low_rank, options::barycentric_dense, + options::barycentric_low_rank)), "unsupported interpolator options!"); }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + */ template<typename ValueType, std::size_t Dimension, typename MatrixKernel> struct get_interpolator<ValueType, Dimension, MatrixKernel, options::uniform_<options::dense_>> { - using type = uniform_interpolator<ValueType, Dimension, MatrixKernel, options::dense_>; + using type = uniform_interpolator<ValueType, Dimension, MatrixKernel, options::dense_>; }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + */ template<typename ValueType, std::size_t Dimension, typename MatrixKernel> struct get_interpolator<ValueType, Dimension, MatrixKernel, options::uniform_<options::low_rank_>> { - using type = uniform_interpolator<ValueType, Dimension, MatrixKernel, options::low_rank_>; + using type = uniform_interpolator<ValueType, Dimension, MatrixKernel, options::low_rank_>; }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + */ template<typename ValueType, std::size_t Dimension, typename MatrixKernel> struct get_interpolator<ValueType, Dimension, MatrixKernel, options::uniform_<options::fft_>> { - using type = uniform_interpolator<ValueType, Dimension, MatrixKernel, options::fft_>; + using type = uniform_interpolator<ValueType, Dimension, MatrixKernel, options::fft_>; }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + */ template<typename ValueType, std::size_t Dimension, typename MatrixKernel> struct get_interpolator<ValueType, Dimension, MatrixKernel, options::chebyshev_<options::dense_>> { - using type = chebyshev_interpolator<ValueType, Dimension, MatrixKernel, options::dense_>; + using type = chebyshev_interpolator<ValueType, Dimension, MatrixKernel, options::dense_>; }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + */ template<typename ValueType, std::size_t Dimension, typename MatrixKernel> struct get_interpolator<ValueType, Dimension, MatrixKernel, options::chebyshev_<options::low_rank_>> { - using type = chebyshev_interpolator<ValueType, Dimension, MatrixKernel, options::low_rank_>; + using type = chebyshev_interpolator<ValueType, Dimension, MatrixKernel, options::low_rank_>; + }; + + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + */ + template<typename ValueType, std::size_t Dimension, typename MatrixKernel> + struct get_interpolator<ValueType, Dimension, MatrixKernel, options::barycentric_<options::dense_>> + { + using type = barycentric_interpolator<ValueType, Dimension, MatrixKernel, options::dense_>; + }; + + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + */ + template<typename ValueType, std::size_t Dimension, typename MatrixKernel> + struct get_interpolator<ValueType, Dimension, MatrixKernel, options::barycentric_<options::low_rank_>> + { + using type = barycentric_interpolator<ValueType, Dimension, MatrixKernel, options::low_rank_>; }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam MatrixKernel + * @tparam Settings + */ template<typename ValueType, std::size_t Dimension, typename MatrixKernel, typename Settings> using interpolator = typename get_interpolator<ValueType, Dimension, MatrixKernel, Settings>::type; -} +} // namespace scalfmm::interpolation -#endif // SCALFMM_INTERPOLATION_INTERPOLATION_HPP +#endif // SCALFMM_INTERPOLATION_INTERPOLATION_HPP diff --git a/include/scalfmm/interpolation/interpolator.hpp b/include/scalfmm/interpolation/interpolator.hpp index 84427fab0bbc82c6d9e67280ba4f63eae5187bd5..341aa998790fadc1abcbe999f0f9fabf01ef8735 100644 --- a/include/scalfmm/interpolation/interpolator.hpp +++ b/include/scalfmm/interpolation/interpolator.hpp @@ -1,27 +1,13 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/interpolator.hpp +// File : scalfmm/interpolation/interpolator.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_INTERPOLATOR_HPP #define SCALFMM_INTERPOLATION_INTERPOLATOR_HPP -#include <algorithm> -#include <any> -#include <array> -#include <cassert> -#include <cmath> -#include <cstddef> -#include <functional> -#include <iterator> -#include <type_traits> -#include <vector> - -#include "xtensor/xtensor_config.hpp" -#include <xtensor-blas/xlinalg.hpp> -#include <xtensor/xvectorize.hpp> - #include "scalfmm/container/point.hpp" #include "scalfmm/interpolation/builders.hpp" +#include "scalfmm/interpolation/m2l_handler.hpp" #include "scalfmm/interpolation/mapping.hpp" #include "scalfmm/interpolation/permutations.hpp" #include "scalfmm/matrix_kernels/mk_common.hpp" @@ -30,21 +16,46 @@ #include "scalfmm/meta/traits.hpp" #include "scalfmm/meta/utils.hpp" #include "scalfmm/options/options.hpp" +#include "scalfmm/utils/io_helpers.hpp" #include "scalfmm/utils/low_rank.hpp" #include "scalfmm/utils/math.hpp" #include "scalfmm/utils/tensor.hpp" -#include "scalfmm/utils/io_helpers.hpp" + +#include "xtensor-blas/xlinalg.hpp" +#include "xtensor/xtensor_config.hpp" +#include "xtensor/xvectorize.hpp" + +#include <algorithm> +#include <any> +#include <array> +#include <cassert> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <type_traits> +#include <vector> using namespace scalfmm::io; namespace scalfmm::interpolation { + /** + * @brief + * + * @tparam Derived + */ template<typename Derived> struct interpolator_traits; namespace impl { - + /** + * @brief + * + * @tparam Derived + * @tparam Enable + */ template<typename Derived, typename Enable = void> class interpolator { @@ -52,12 +63,21 @@ namespace scalfmm::interpolation "interpolator ValueType needs to be floating point."); }; + /** + * @brief + * + * @tparam Derived + */ template<typename Derived> struct alignas(XTENSOR_FIXED_ALIGN) interpolator< Derived, typename std::enable_if_t<std::is_floating_point_v<typename interpolator_traits<Derived>::value_type>>> { private: + /** + * @brief + * + */ struct empty { }; @@ -67,6 +87,10 @@ namespace scalfmm::interpolation using value_type = typename interpolator_traits<Derived>::value_type; using size_type = std::size_t; + /** + * @brief + * + */ static constexpr std::size_t dimension = interpolator_traits<Derived>::dimension; using array_type = xt::xarray<value_type>; @@ -79,20 +103,54 @@ namespace scalfmm::interpolation using grid_permutations_type = std::conditional_t<(dimension > 3), empty, xt::xtensor<std::size_t, 2>>; using settings = typename interpolator_traits<derived_type>::settings; + /** + * @brief Construct a new interpolator object + * + */ interpolator() = delete; + + /** + * @brief Construct a new interpolator object + * + * @param other + */ interpolator(interpolator const& other) = delete; + + /** + * @brief Construct a new interpolator object + * + */ interpolator(interpolator&&) noexcept = delete; + + /** + * @brief + * + * @return interpolator& + */ auto operator=(interpolator const&) -> interpolator& = delete; + + /** + * @brief + * + * @return interpolator& + */ auto operator=(interpolator&&) noexcept -> interpolator& = delete; + + /** + * @brief Destroy the interpolator object + * + */ ~interpolator() = default; - /// @brief constructor of the interpolator - /// - /// @param order number of terms of the 1d expansion () - /// @param tree_height height of the tree - /// @param root_cell_width width of the simulation box - /// @param cell_width_extension width of the extension of the cell - /// @param late_init if true the initialization is done later + /** + * @brief constructor of the interpolator + * + * @param order number of terms of the 1d expansion () + * @param tree_height height of the tree + * @param root_cell_width width of the simulation box + * @param cell_width_extension width of the extension of the cell + * @param late_init if true the initialization is done later + */ interpolator(size_type order, size_type tree_height, value_type root_cell_width, value_type cell_width_extension, bool late_init = false) : m_order(order) @@ -109,83 +167,160 @@ namespace scalfmm::interpolation } } - /// @brief constructor of the interpolator - /// - /// @param late_init - /// @param order number of terms of the 1d expansion () - /// @param tree_height height of the tree - /// @param root_cell_width width of the simulation box - /// @param late_init if true the initialization is done later + /** + * @brief Construct a new interpolator object + * + * @param order number of terms of the 1d expansion () + * @param tree_height height of the tree + * @param root_cell_width width of the simulation box + * @param late_init if true the initialization is done later + */ interpolator(size_type order, size_type tree_height, value_type root_cell_width, bool late_init = false) : interpolator(order, tree_height, root_cell_width, value_type(0.), late_init) { } - /// @brief return the number of 1d points (number of terms in the 1d-expansion) + + /** + * @brief return the number of 1d points (number of terms in the 1d-expansion) + * + * @return std::size_t + */ [[nodiscard]] inline auto order() const noexcept -> std::size_t { return m_order; } - /// @brief return the number of points in de dimension grid + + /** + * @brief return the number of points in de dimension grid + * + * @return auto + */ [[nodiscard]] inline auto nnodes() const noexcept { return m_nnodes; } - /// @brief get the cell extension - [[nodiscard]] inline auto cell_width_extension() const noexcept -> value_type + + /** + * @brief get the cell extension + * + * @return value_type + */ + [[nodiscard]] inline auto cell_width_extension() const noexcept -> value_type { return m_cell_width_extension; } - /// @brief tell is we use the cell extension + + /** + * @brief tell is we use the cell extension + * + * @return true + * @return false + */ [[nodiscard]] inline auto use_cell_width_extension() const noexcept -> bool { return m_use_cell_width_extension; } - /// @brief + + /** + * @brief + * + * @return array_type& + */ [[nodiscard]] inline auto interpolator_tensor() noexcept -> array_type& { return m_child_parent_interpolators; } + /** + * @brief + * + * @return array_type const& + */ [[nodiscard]] inline auto interpolator_tensor() const noexcept -> array_type const& { return m_child_parent_interpolators; } + /** + * @brief + * + * @return array_type const& + */ [[nodiscard]] inline auto cinterpolator_tensor() const noexcept -> array_type const& { return m_child_parent_interpolators; } - /// @brief return the roots of the polynomial + + /** + * @brief return the roots of the polynomial + * + * @return array_type + */ [[nodiscard]] inline auto roots() const -> array_type { return this->derived_cast().roots_impl(); } + /** + * @brief + * + * @tparam ComputationType + * @param x + * @param n + * @return ComputationType + */ template<typename ComputationType> [[nodiscard]] inline auto polynomials(ComputationType x, std::size_t n) const -> ComputationType { return this->derived_cast().polynomials_impl(x, n); } + + /** + * @brief + * + * @tparam VectorType + * @tparam ComputationType + * @tparam Dim + * @param all_poly + * @param x + * @param order + */ template<typename VectorType, typename ComputationType, std::size_t Dim> inline auto fill_all_polynomials(VectorType& all_poly, container::point<ComputationType, Dim> x, std::size_t order) const -> void { this->derived_cast().fill_all_polynomials_impl(all_poly, x, order); } + + /** + * @brief + * + * @tparam ComputationType + * @param x + * @param n + * @return auto + */ template<typename ComputationType> [[nodiscard]] inline auto derivative(ComputationType x, std::size_t n) const { return this->derived_cast().derivative_impl(x, n); } - [[nodiscard]] inline auto grid_permutations() const -> grid_permutations_type const& + /** + * @brief + * + * @return grid_permutations_type const& + */ + [[nodiscard]] inline auto grid_permutations() const -> grid_permutations_type const& { return m_grid_permutations; } private: - /// @brief This function pre-calculates all the child-parent interpolators required for M2M and L2L passes. - /// - /// This function pre-calculates all the child-parent interpolators required for M2M and L2L passes - /// for all levels that are not leaf levels. If there is no cell width extension, the same set of - /// child-parent interpolators is used for all levels. However, if there is a cell width extension, the - /// child-parent interpolators must be recalculated for each level because the ratio of "child cell width - /// over parent cell width" is not the same. - /// - /// @warning(homogeneity not yet supported when cell_width_extension > 0) - /// - void set_child_parent_interpolator() + /** + * @brief This function pre-calculates all the child-parent interpolators required for M2M and L2L passes. + * + * This function pre-calculates all the child-parent interpolators required for M2M and L2L passes + * for all levels that are not leaf levels. If there is no cell width extension, the same set of + * child-parent interpolators is used for all levels. However, if there is a cell width extension, the + * child-parent interpolators must be recalculated for each level because the ratio of "child cell width + * over parent cell width" is not the same. + * + * @warning(homogeneity not yet supported when cell_width_extension > 0) + * + */ + auto set_child_parent_interpolator() -> void { using point_type = scalfmm::container::point<value_type, dimension>; @@ -305,6 +440,12 @@ namespace scalfmm::interpolation } } + /** + * @brief + * + * @param order + * @return auto + */ inline auto generate_grid_permutations(std::size_t order) { grid_permutations_type perms{}; @@ -345,932 +486,90 @@ namespace scalfmm::interpolation } protected: + /** + * @brief + * + */ inline auto initialize() -> void { set_child_parent_interpolator(); } + /** + * @brief + * + * @return derived_type& + */ [[nodiscard]] inline auto derived_cast() & noexcept -> derived_type& { return *static_cast<derived_type*>(this); } + /** + * @brief + * + * @return derived_type const& + */ [[nodiscard]] inline auto derived_cast() const& noexcept -> derived_type const& { return *static_cast<const derived_type*>(this); } + /** + * @brief + * + * @return derived_type + */ [[nodiscard]] inline auto derived_cast() && noexcept -> derived_type { return *static_cast<derived_type*>(this); } private: - const size_type m_order{}; ///< number of terms of the expansion (1d) - const size_type m_nnodes{}; ///< number of modes m_order^dimension - const size_type m_tree_height{}; ///< height of the tree - const value_type m_root_cell_width{}; ///< width of the simulation box - const value_type m_cell_width_extension{}; ///< width of the extension of the cell - const bool m_use_cell_width_extension{}; ///< true if we use the cell extension - array_type m_child_parent_interpolators{}; ///< - grid_permutations_type m_grid_permutations{}; ///< - }; - - /** - * @warning Cell width extension is not yet supported for homogeneous kernels in the latest version of ScalFMM! - */ - template<typename Derived> - struct m2l_handler - { - private: - struct empty - { - }; - - public: - using derived_type = Derived; - using value_type = typename interpolator_traits<derived_type>::value_type; - using size_type = std::size_t; - - static constexpr std::size_t dimension = interpolator_traits<derived_type>::dimension; - - using matrix_kernel_type = typename interpolator_traits<derived_type>::matrix_kernel_type; - - using settings = typename interpolator_traits<derived_type>::settings; - - static constexpr std::size_t kn = matrix_kernel_type::kn; - static constexpr std::size_t km = matrix_kernel_type::km; - static constexpr auto separation_criterion = matrix_kernel_type::separation_criterion; - static constexpr auto homogeneity_tag = matrix_kernel_type::homogeneity_tag; - static constexpr auto symmetry_tag = matrix_kernel_type::symmetry_tag; - static constexpr auto enable_symmetries = interpolator_traits<derived_type>::enable_symmetries; - static constexpr std::size_t max_number_of_cell{7}; - static constexpr bool symmetry_support{ - (symmetry_tag == matrix_kernels::symmetry::symmetric && (enable_symmetries == true) && (dimension < 4))}; - - using scale_factor_type = typename matrix_kernel_type::template vector_type<value_type>; - using sym_permutations_type = std::conditional_t<symmetry_support, xt::xarray<int>, empty>; - using k_indices_type = std::conditional_t<symmetry_support, std::vector<std::size_t>, empty>; - - using array_type = xt::xarray<value_type>; - using array_shape_type = typename array_type::shape_type; - - template<std::size_t d> - using tensor_type = xt::xtensor<value_type, d>; - template<std::size_t d> - using tensor_shape_type = typename tensor_type<d>::shape_type; - - using storage_type = typename interpolator_traits<derived_type>::storage_type; - using buffer_value_type = typename interpolator_traits<derived_type>::buffer_value_type; - using buffer_inner_type = typename interpolator_traits<derived_type>::buffer_inner_type; - using k_tensor_type = std::conditional_t<std::is_same_v<settings, options::low_rank_>, - std::tuple<xt::xtensor<value_type, 2>, xt::xtensor<value_type, 2>>, - typename interpolator_traits<derived_type>::k_tensor_type>; - using interaction_matrix_type = xt::xtensor_fixed<k_tensor_type, xt::xshape<kn, km>>; - using buffer_shape_type = typename interpolator_traits<derived_type>::buffer_shape_type; - using buffer_type = typename interpolator_traits<derived_type>::buffer_type; - using multipoles_inner_type = - typename memory::storage_traits<typename storage_type::multipoles_storage_type>::inner_type; - using locals_inner_type = - typename memory::storage_traits<typename storage_type::locals_storage_type>::inner_type; - - m2l_handler() = delete; - m2l_handler(m2l_handler const& other) = delete; - m2l_handler(m2l_handler&&) noexcept = delete; - auto operator=(m2l_handler const&) -> m2l_handler& = delete; - auto operator=(m2l_handler&&) noexcept -> m2l_handler& = delete; - ~m2l_handler() = default; - - m2l_handler(matrix_kernel_type const& far_field, array_type roots, size_type tree_height = 3, - value_type root_cell_width = value_type(1.), value_type cell_width_extension = value_type(0.), - bool late_init = false) - : m_far_field(far_field) - , m_m2l_interactions(math::pow(max_number_of_cell, dimension)) - , m_nnodes(meta::pow(roots.size(), dimension)) - , m_order(roots.size()) - , m_roots(roots) - , m_epsilon(std::pow(value_type(10.), -value_type(roots.size() - 1))) - , m_cell_width_extension(cell_width_extension) - { - if((cell_width_extension > 0) && (homogeneity_tag == matrix_kernels::homogeneity::homogenous)) - { - std::cout << "cell_width_extension: " << cell_width_extension << std::endl; - std::cout << "matrix kernel name: " << far_field.name() << std::endl; - throw std::runtime_error( - "m2lhandler: Cell width extension is not yet supported for homogeneous kernels in the " - "latest version of ScalFMM!"); - } - - if(late_init == false) - { - this->initialize(roots.size(), root_cell_width, tree_height); - } - - if constexpr(symmetry_support) - { - std::tie(m_sym_permutations, m_k_indices) = get_permutations_and_indices<dimension>( - roots.size(), meta::pow(roots.size(), dimension), this->m2l_interactions()); - } - } - - [[nodiscard]] inline auto weights() const noexcept -> array_type const& { return m_weights_of_roots; } - - [[nodiscard]] inline auto epsilon() noexcept -> value_type& { return m_epsilon; } - [[nodiscard]] inline auto epsilon() const noexcept -> value_type { return m_epsilon; } - // member function to get the index of K corresponding to the interaction index in the interaction matrice - // vector. - [[nodiscard]] inline auto symmetry_k_index(std::size_t neighbor_idx) const -> std::size_t - { - return m_k_indices.at(neighbor_idx); - } - - template<typename TensorOrViewX, typename TensorOrViewY> - [[nodiscard]] inline auto generate_matrix_k(TensorOrViewX&& X, TensorOrViewY&& Y, std::size_t n, - std::size_t m, [[maybe_unused]] size_type thread_id = 0) const - -> std::enable_if_t< - !decltype(meta::sig_gen_k_f(std::declval<derived_type>(), X, Y, n, m, thread_id))::value, k_tensor_type> - - { - if constexpr(std::is_same_v<settings, options::dense_>) - { - auto const& matrix_kernel{this->matrix_kernel()}; - - auto n_d = math::pow(m_order, dimension); - - k_tensor_type K(std::vector(2, n_d)); - auto flat_x = xt::flatten(X); - auto flat_y = xt::flatten(Y); - // TODO : SIMD! - - for(std::size_t i{0}; i < n_d; ++i) - { - for(std::size_t j{0}; j < n_d; ++j) - { - K.at(i, j) = matrix_kernel.evaluate(flat_x.at(i), flat_y.at(j)).at(n * km + m); - } - } - // std::cout << cpp_tools::colors::cyan; - // std::cout << K << std::endl; - // std::cout << cpp_tools::colors::reset; - // xt::dump_npy("interaction_matrix_non_homogenous_with_ext.npy",K); - return K; - } - else if constexpr(std::is_same_v<settings, options::low_rank_>) - { - auto const& matrix_kernel{this->matrix_kernel()}; - return low_rank::generate(matrix_kernel, std::forward<TensorOrViewX>(X), - std::forward<TensorOrViewY>(Y), this->weights(), this->epsilon(), n, m); - } - else - { - throw std::runtime_error("Missing generate_matrix_k function!"); - } - } - template<typename TensorOrViewX, typename TensorOrViewY> - [[nodiscard]] inline auto generate_matrix_k(TensorOrViewX&& X, TensorOrViewY&& Y, std::size_t n, - std::size_t m, [[maybe_unused]] size_type thread_id = 0) const - -> std::enable_if_t< - decltype(meta::sig_gen_k_f(std::declval<derived_type>(), X, Y, n, m, thread_id))::value, k_tensor_type> - { - return this->derived_cast().generate_matrix_k_impl(std::forward<TensorOrViewX>(X), - std::forward<TensorOrViewX>(Y), n, m, thread_id); - } - - [[nodiscard]] inline auto interactions_matrices() noexcept - -> std::vector<interaction_matrix_type, XTENSOR_DEFAULT_ALLOCATOR(interaction_matrix_type)>& - { - return m_interactions_matrices; - } - - [[nodiscard]] inline auto interactions_matrices() const noexcept - -> std::vector<interaction_matrix_type> const& - { - return m_interactions_matrices; - } - - [[nodiscard]] inline auto cinteractions_matrices() const noexcept - -> std::vector<interaction_matrix_type> const& - { - return m_interactions_matrices; - } - - [[nodiscard]] inline auto m2l_interactions() const noexcept { return m_m2l_interactions; } - - [[nodiscard]] inline auto sym_permutations() const -> sym_permutations_type const& - { - return m_sym_permutations; - } - - [[nodiscard]] inline auto matrix_kernel() const noexcept -> matrix_kernel_type const& - { - return m_far_field; - } - - template<typename D = derived_type> - [[nodiscard]] auto buffer_initialization() const - -> std::enable_if_t<decltype(meta::sig_buffer_init_f(std::declval<D>()))::value, buffer_type> - { - return this->derived_cast().buffer_initialization_impl(); - } /** - * @brief Initialize the buffer to aggregate the multipoles and teh locals when the kernel is symmetric - * - * if the kernel is non symmetric we have an empty buffers otherwise the element of the buffer - * is a tensor of size (number of multipole associated to the current symmetry, the number of nodes) + * @brief number of terms of the expansion (1d) * - * @tparam D - * @return std::enable_if_t<!decltype(meta::sig_buffer_init_f(std::declval<D>()))::value, buffer_type> */ - template<typename D = derived_type> - [[nodiscard]] auto buffer_initialization() const - -> std::enable_if_t<!decltype(meta::sig_buffer_init_f(std::declval<D>()))::value, buffer_type> - { - std::vector<std::size_t> shape(2, m_nnodes); - if constexpr(enable_symmetries) - { - shape[1] = interpolation::largest_number_permutation<dimension>(); - } - return buffer_type(buffer_shape_type{}, buffer_inner_type(shape, 0.)); - } + const size_type m_order{}; /** - * @brief Reset the buffer by calling buffer_reset_impl (specialization) + * @brief number of modes m_order^dimension * - * @tparam D - * @param buffers */ - template<typename D = derived_type> - inline auto buffer_reset(buffer_type& buffers) const - -> std::enable_if_t<decltype(meta::sig_buffer_reset_f(std::declval<D>(), buffers))::value, void> - { - this->derived_cast().buffer_reset_impl(buffers); - } + const size_type m_nnodes{}; + /** - * @brief Reset the buffer, generic function + * @brief height of the tree * - * @tparam D - * @param buffers */ - template<typename D = derived_type> - inline auto buffer_reset(buffer_type& buffers) const - -> std::enable_if_t<!decltype(meta::sig_buffer_reset_f(std::declval<D>(), buffers))::value, void> - { - if constexpr(symmetry_support) - { - for(std::size_t n{0}; n < 2; ++n) - { - buffers.at(n).fill(buffer_value_type(0.)); - } - } - } + const size_type m_tree_height{}; - template<typename D = derived_type> - inline auto initialize_k() const - -> std::enable_if_t<!decltype(meta::sig_init_k_f(std::declval<D>()))::value, k_tensor_type> - { - if constexpr(std::is_same_v<settings, options::low_rank_>) - { - return std::make_tuple(xt::xtensor<value_type, 2>{}, xt::xtensor<value_type, 2>{}); - } - else if constexpr(std::is_same_v<settings, options::dense_>) - { - return k_tensor_type{}; - } - else - { - throw std::runtime_error("Missing initialize_k function!"); - } - } - template<typename D = derived_type> - inline auto initialize_k() const - -> std::enable_if_t<decltype(meta::sig_init_k_f(std::declval<D>()))::value, k_tensor_type> - { - return this->derived_cast().initialize_k_impl(); - } - - template<typename Cell> - auto apply_multipoles_preprocessing(Cell& current_cell, [[maybe_unused]] size_type thread_id = 0) const - -> std::enable_if_t< - decltype(meta::sig_preprocess_f(std::declval<derived_type>(), current_cell, thread_id))::value, void> - { - return this->derived_cast().apply_multipoles_preprocessing_impl(current_cell, thread_id); - } - // default fallback - template<typename Cell> - auto apply_multipoles_preprocessing(Cell& current_cell, [[maybe_unused]] size_type thread_id = 0) const - -> std::enable_if_t< - !decltype(meta::sig_preprocess_f(std::declval<derived_type>(), current_cell, thread_id))::value, void> - { - } - - template<typename Cell> - auto apply_multipoles_postprocessing(Cell& current_cell, [[maybe_unused]] buffer_type const& products, - [[maybe_unused]] size_type thread_id = 0) const - -> std::enable_if_t<decltype(meta::sig_postprocess_f(std::declval<derived_type>(), current_cell, products, - thread_id))::value, - void> - { - return this->derived_cast().apply_multipoles_postprocessing_impl(current_cell, products, thread_id); - } - - template<typename Cell> - auto apply_multipoles_postprocessing(Cell& current_cell, [[maybe_unused]] buffer_type const& products, - [[maybe_unused]] size_type thread_id = 0) const - -> std::enable_if_t<!decltype(meta::sig_postprocess_f(std::declval<derived_type>(), current_cell, - products, thread_id))::value, - void> - { - } /** - * @brief Compute the matrix vector product for the M2L operator + * @brief width of the simulation box * - * The operation is - * locals := scale_factor*A*multipoles + beta*locals, or y := alpha*A**T*x + beta*y, - * if the settings is options::dense_ classical matrix vector product - * and if the settings is options::low_rank_ A = UV we perform two matrix vector product - * - * @tparam T - * @tparam T2 - * @param multipoles the multipole values - * @param locals the local values - * @param tmp a working array need for low rank approximation - * @param knm the kernel matrix - * @param scale_factor the scaling factor - * @param acc if true we accumulate (beta=1.0 0 otherwise) */ - template<typename T, typename T2> - inline auto product(T const& multipoles, T& locals, T2& tmp, k_tensor_type const& knm, - value_type scale_factor, bool acc) const -> void - { - if constexpr(std::is_same_v<settings, options::low_rank_>) - { - auto const& u = meta::get<0>(knm); - auto const& v = meta::get<1>(knm); - tensor::blas2_product(multipoles, tmp, v, value_type(1.0), false, true); - tensor::blas2_product(tmp, locals, u, scale_factor, acc, false); - } - else if constexpr(std::is_same_v<settings, options::dense_>) - { - tensor::blas2_product(multipoles, locals, knm, scale_factor, acc); - } - else - { - throw std::runtime_error("No implementation found for m2l product !"); - } - } - template<typename T> - inline auto product(T const& multipoles, T& locals, k_tensor_type const& knm, value_type scale_factor, - bool acc) const -> void - { - if constexpr(std::is_same_v<settings, options::low_rank_>) - { - auto const& u = meta::get<0>(knm); - auto const& v = meta::get<1>(knm); - T tmp(locals.shape(), value_type(0.)); - tensor::blas2_product(multipoles, tmp, v, value_type(1.0), false, true); - tensor::blas2_product(tmp, locals, u, scale_factor, acc, false); - } - else if constexpr(std::is_same_v<settings, options::dense_>) - { - tensor::blas2_product(multipoles, locals, knm, scale_factor, acc); - } - else - { - throw std::runtime_error("No implementation found for m2l product !"); - } - } + const value_type m_root_cell_width{}; /** - * @brief Compute the matrix vector product for the M2L operator + * @brief width of the extension of the cell * - * The operation is - * locals := scale_factor*A*multipoles + beta*locals, or y := alpha*A**T*x + beta*y, - * if the settings is options::dense_ classical matrix vector product - * and if the settings is options::low_rank_ A = UV we perform two matrix vector product - * - * @tparam T - * @tparam T2 - * @param multipoles the multipole values - * @param locals the local values - * @param tmp a working array need for low rank approximation - * @param knm the kernel matrix - * @param tmp A temporary matrix used in low rank approximation - * @param nb_mult the number of multipole to treat (number of column of multipoles) - * @param scale_factor the scaling factor - * @param acc if true we accumulate (beta=1.0 0 otherwise) */ - template<typename T, typename T1> - inline auto product_m(T const& multipoles, T& locals, k_tensor_type const& Knm, T1& tmp, int nb_mult, - value_type scale_factor, bool acc) const -> void - { - if constexpr(std::is_same_v<settings, options::low_rank_>) - { - // Knm = U V^T - auto const& U = meta::get<0>(Knm); - auto const& V = meta::get<1>(Knm); - constexpr bool accumulate = false; - - constexpr bool transposeV = true; - - tensor::blas3_product(multipoles, tmp, V, nb_mult, value_type(1.0), accumulate, transposeV); - - tensor::blas3_product(tmp, locals, U, nb_mult, scale_factor, acc); - } - else if constexpr(std::is_same_v<settings, options::dense_>) - { - tensor::blas3_product(multipoles, locals, Knm, nb_mult, scale_factor, acc); - } - else - { - throw std::runtime_error("No implementation found for m2l product !"); - } - } - - template<typename Cell> - auto apply_m2l_single(Cell const& source_cell, Cell& target_cell, std::size_t neighbor_idx, - std::size_t tree_level, [[maybe_unused]] buffer_type& products, - [[maybe_unused]] size_type thread_id = 0) const -> void - { - std::size_t level{}; - scale_factor_type scale_factor{}; - - if constexpr(homogeneity_tag == matrix_kernels::homogeneity::homogenous) - { - level = 0; - // here we scale the target cell width to (homogenous case) match - // the [-1,1] unitary cell used to generate the corresponding - // interaction matrix. - scale_factor = m_far_field.scale_factor((target_cell.width()) / value_type(2.)); - } - else // non-homogenous case. - { - // root level is 0 (i.e the simulation box) first level of cell is level 1 - // we start the indexing of matrixes at 0, hence the -1 on the cell level - level = tree_level - 2; - scale_factor.fill(1.0); - } - if constexpr(symmetry_support) - { - // the kernel is symmetric - // Decrease the number of matrix-vector product due to the symmetry - // see https://hal.inria.fr/hal-00746089v2 - const auto neighbor_sym = this->symmetry_k_index(neighbor_idx); - const auto number_of_interactions = number_of_matrices_in_orthant<dimension>(); - - auto const& k = m_interactions_matrices.at(level * number_of_interactions + neighbor_sym); - auto& permuted_multipoles = products.at(0); - auto& permuted_locals = products.at(1); - - for(std::size_t n = 0; n < kn; ++n) - { - for(std::size_t m = 0; m < km; ++m) - { - auto const& multipoles = source_cell.cmultipoles(m); - auto& locals = target_cell.locals(n); - // the multipole is permuted - const auto m_ptr = multipoles.data(); - const auto l_ptr = locals.data(); - const auto m_p_ptr = permuted_multipoles.data(); - const auto l_p_ptr = permuted_locals.data(); - const auto perm_ptr = &m_sym_permutations.at(neighbor_idx, 0); - - for(std::size_t i{0}; i < m_nnodes; ++i) - { - m_p_ptr[perm_ptr[i]] = m_ptr[i]; - } + const value_type m_cell_width_extension{}; - this->product(permuted_multipoles, permuted_locals, k.at(n, m), scale_factor.at(n), false); - - // the local expansion is permuted to the original. - for(std::size_t i{0}; i < m_nnodes; ++i) - { - l_ptr[i] += l_p_ptr[perm_ptr[i]]; - } - } - } - } - else if constexpr(std::is_same_v<settings, options::low_rank_> || - std::is_same_v<settings, options::dense_>) - { - // non symmetric kernel and low rank or dense approximation of the kernel - - auto const& k = m_interactions_matrices.at(level * m_m2l_interactions + neighbor_idx); - auto const& multipoles = source_cell.cmultipoles(); - auto& locals = target_cell.locals(); // we generate km*kn products - // loop on km - for(std::size_t m = 0; m < km; ++m) - { - // meta loop on kn - for(std::size_t n = 0; n < kn; ++n) - { - this->product(multipoles.at(m), locals.at(n), k.at(n, m), scale_factor.at(n), true); - } - } - } - else - { - // non symmetric kernel and specific product (fft for uniform approximation) - // auto const& multipoles = source_cell.cmultipoles(); - // auto& locals = target_cell.locals(); - auto const& k = m_interactions_matrices.at(level * m_m2l_interactions + neighbor_idx); - // we generate km*kn products - // loop on km - for(std::size_t m = 0; m < km; ++m) - { - // meta loop on kn - for(std::size_t n = 0; n < kn; ++n) - { - this->derived_cast().apply_m2l_impl(source_cell, target_cell, products, k, scale_factor, n, - m, thread_id); - } - } - } - } - template<typename Cell> - auto apply_m2l_loop(Cell& target_cell, std::size_t tree_level, [[maybe_unused]] buffer_type& products, - [[maybe_unused]] size_type thread_id = 0) const - { - std::size_t level{}; - scale_factor_type scale_factor{}; - // - if constexpr(homogeneity_tag == matrix_kernels::homogeneity::homogenous) - { - level = 0; - // here we scale the target cell width to (homogenous case) match - // the [-1,1] unitary cell used to generate the corresponding - // interaction matrix. - scale_factor = m_far_field.scale_factor((target_cell.width()) / value_type(2.)); - } - else // non-homogenous case. - { - // root level is 0 (i.e the simulation box) first level of cell is level 1 - // we start the indexing of matrixes at 0, hence the -1 on the cell level - level = tree_level - 2; - scale_factor.fill(1.0); - } - auto const& cell_symbolics = target_cell.csymbolics(); - auto const& interaction_positions = cell_symbolics.interaction_positions; - auto const& interaction_iterators = cell_symbolics.interaction_iterators; - // #ifdef M2L_NEW_SYM - // for symmetry support inner_type of the buffer for low_rank approximation otherwise the type of the - // local approximation - using local_type = std::conditional_t<std::is_same_v<settings, options::low_rank_>, - std::conditional_t<symmetry_support, buffer_inner_type, - std::decay_t<decltype(target_cell.locals(0))>>, - meta::empty>; - // #else - // using local_type = std::decay_t<decltype(target_cell.locals(0))>; - // #endif - local_type work; - - if constexpr(std::is_same_v<settings, options::low_rank_>) - { - // // - if constexpr(symmetry_support) - { - // rank x max_symm - work.resize(products.at(1).shape()); - // work.resize(std::array<std::size_t, 2>{m_nnodes, largest_number_permutation<dimension>()}); - } - else - { - work.resize(target_cell.locals(0).shape()); - } - } - // - // Selection product operator depending on symmetry, product optimization - // - if constexpr(symmetry_support) - { - // the kernel is symmetric - // Decrease the number of matrix-vector product due to the symmetry - // see https://hal.inria.fr/hal-00746089v2 - - constexpr auto number_of_symmetries = number_of_matrices_in_orthant<dimension>(); - - std::array<std::array<int, largest_number_permutation<dimension>()>, number_of_symmetries> - neighbors_perm; - std::array<int, number_of_symmetries> number_of_permutation{}; - // - // access the buffer to store temporary array - // aggregate_multipoles and aggregate_locals are vectors of - // xt::xarray of size number of permutation - auto aggregate_multipoles = products.at(0); - auto aggregate_locals = products.at(1); - // set cells in neighbors_perm according to their symmetry - for(std::size_t index{0}; index < cell_symbolics.existing_neighbors; ++index) - { - auto const& neighbor_idx = interaction_positions.at(index); - const auto neighbor_sym = this->symmetry_k_index(neighbor_idx); - // - neighbors_perm[neighbor_sym][number_of_permutation[neighbor_sym]] = index; - number_of_permutation[neighbor_sym] += 1; - } - - // Loop on the number of rhs (input) - for(std::size_t m = 0; m < km; ++m) - { - // loop on the number of symmetries - for(std::size_t idx{0}; idx < number_of_symmetries; ++idx) - { - if(number_of_permutation[idx] > 0) - { - auto const& neighs = neighbors_perm[idx]; - // Fill the temporary array with all multipoles - for(int i = 0; i < number_of_permutation[idx]; ++i) - { - auto const& index = neighs[i]; - auto const& neighbor_idx = interaction_positions.at(index); - // get the permutation associated to the position of the cell - const auto perm_ptr = &m_sym_permutations.at(neighbor_idx, 0); - // - // Gather the multipoles for the permutation - // get the set of multipole for the current symmetry - // get the xt::xarray of aggregated multipole for the current symmetry - // permuted_multipoles is a xarray of size(max cells in symmetry, the grid of - // nodes) - // the order of the tensor is dimension + 1 - // get multipole - auto const& source_cell = *interaction_iterators.at(index); - auto const& multipoles = source_cell.cmultipoles(m); - // get the pointer of the source multipole - const auto m_ptr = multipoles.data(); - - for(std::size_t j{0}; j < m_nnodes; ++j) - { - aggregate_multipoles(perm_ptr[j], i) = m_ptr[j]; - // current_permuted_multipole_ptr[perm_ptr[j]] = m_ptr[j]; - } - } // end of the aggregation for the current symmetry idx - - ////////////////////////////////////////////////////////////// - // Performed the matrix matrix product - // - auto const& k = m_interactions_matrices.at(level * number_of_symmetries + idx); - - for(std::size_t n = 0; n < kn; ++n) - { - auto& locals = target_cell.locals(n); - const auto l_ptr = locals.data(); - // Perform matrix-matrix product with aggregate multipoles - this->product_m(aggregate_multipoles, aggregate_locals, k.at(n, m), work, - number_of_permutation[idx], scale_factor.at(n), false); - // - - // the local expansion is permuted to their original. - for(int i = 0; i < number_of_permutation[idx]; ++i) - { - auto const& neighbor_idx = interaction_positions.at(neighs[i]); - // get the permutation associated to the position of the cell - const auto perm_ptr = &m_sym_permutations.at(neighbor_idx, 0); - // add current multipole to aggregate_multipoles - // auto current_permuted_locals = aggregate_locals.data() + m_nnodes * i; - for(std::size_t j{0}; j < m_nnodes; ++j) - { - l_ptr[j] += aggregate_locals(perm_ptr[j], i); - } - } - - } // end kn loop on the output of the matrix kernel - } // end if - } // end loop number of cells in the symmetry - } // end km loop on the input of the matrix kernel - - } // end constexpr - else if constexpr(std::is_same_v<settings, options::low_rank_> || - std::is_same_v<settings, options::dense_>) - { - // non symmetric kernel and low rank or dense approximation of the kernel - - for(std::size_t index{0}; index < cell_symbolics.existing_neighbors; ++index) - { - auto const& source_cell = *interaction_iterators.at(index); - const auto neighbor_idx = static_cast<std::size_t>(interaction_positions.at(index)); - - auto const& k = m_interactions_matrices.at(level * m_m2l_interactions + neighbor_idx); - // we generate km*kn products - // loop on km - auto const& multipoles = source_cell.cmultipoles(); - auto& locals = target_cell.locals(); - - for(std::size_t m = 0; m < km; ++m) - { - // meta loop on kn - for(std::size_t n = 0; n < kn; ++n) - { - this->product(multipoles.at(m), locals.at(n), work, k.at(n, m), scale_factor.at(n), - true); - } - } - } - } - else - { - // non symmetric kernel and specific product (fft for uniform approximation) - - for(std::size_t index{0}; index < cell_symbolics.existing_neighbors; ++index) - { - auto const& source_cell = *interaction_iterators.at(index); - const auto neighbor_idx = static_cast<std::size_t>(interaction_positions.at(index)); - - auto const& k = m_interactions_matrices.at(level * m_m2l_interactions + neighbor_idx); - // we generate km*kn products - // loop on km - for(std::size_t m = 0; m < km; ++m) - { - // meta loop on kn - for(std::size_t n = 0; n < kn; ++n) - { - this->derived_cast().apply_m2l_impl(source_cell, target_cell, products, k, scale_factor, - n, m, thread_id); - } - } - } - } - } - - private: - inline auto generate_interactions_matrices(size_type order, value_type width, std::size_t tree_height) - -> void - { - std::size_t number_of_level{1}; - std::size_t number_of_interactions{0}; - value_type local_cell_width_extension{0}; - - if constexpr(symmetry_support) - { - number_of_interactions = number_of_matrices_in_orthant<dimension>(); - } - else - { - number_of_interactions = this->m2l_interactions(); - } - - // here width is the root width - // so first level of cell is of width because we skip level 0 (the root) and (the first level)): - value_type current_width{width}; - const value_type half{0.5}; - const value_type quarter{0.25}; - // we get the half width to scale the roots - - if constexpr(homogeneity_tag == matrix_kernels::homogeneity::non_homogenous) - { - // (tree_heigh = 4 -> [0-3]) the bottom cells and leaves have the same symbolic level ! - // but we remove level 0 (the root ie simulation box) and level 1. - number_of_level = tree_height - 2; - current_width = width * quarter; - local_cell_width_extension = m_cell_width_extension; - } - - value_type half_width{current_width * half}; - // we need to keep the tensorial view - // let the same view goes down to tensorial of point - // X and Y are Td tensors storing point. - - // get the ref of the vector - auto& interactions_matrices{this->interactions_matrices()}; - - // resizing and initializing the vector - // homogenous -> only one level - // non_homogenous -> we generate interaction matrices for each tree level processed by the m2l - // operator - interactions_matrices.resize( - number_of_interactions * number_of_level, - // xtensor_fixed<xshape<km,kn>> - interaction_matrix_type(typename interaction_matrix_type::shape_type{}, - // in Chebyshev, K_mn is a matrix of size [nnodes, nnodes] - initialize_k(), xt::layout_type::row_major)); - // loop on levels - for(std::size_t l{0}; l < number_of_level; ++l) - { - // homogenous -> X is the [-1,1] box reference - // non_homogenous -> X is the [-cell_width_at_level/2, cell_width_at_level/2] - // X is a multidimensional grid generator returning a grid for X, another one for Y,... - // X is a multidimensional grid of points scaled on the size of cell - auto X_points = tensor::generate_grid_of_points<dimension>( - (half_width + half * local_cell_width_extension) * m_roots); - - // lambda for generation the matrixes corresponding to one interaction - std::size_t flat_idx{0}; - auto generate_all_interactions = [order, this, &interactions_matrices, &flat_idx, &X_points, - current_width, number_of_interactions, l](auto... is) - { - if(((std::abs(is) > separation_criterion) || ...)) - { - // constructing centers to generate Y - xt::xarray<container::point<value_type, dimension>> centers(std::vector(dimension, order)); - xt::xarray<container::point<value_type, dimension>> Y_points(std::vector(dimension, order)); - - // here we fill the centers with the current loop indexes - // X_points is scaled with the width of cell so, Y_points will be scaled directly - centers.fill( - container::point<value_type, dimension>({(value_type(is) * current_width)...})); - // then we calculate the Y points - // TODO : add directly the point value_type here. - Y_points = X_points + centers; - - // we get the ref of interaction matrices to generate - auto& nm_fc_tensor = interactions_matrices.at(l * number_of_interactions + flat_idx); - // and we generate each matrix needed for the product (kn*km matrices) - for(std::size_t n = 0; n < kn; ++n) - { - for(std::size_t m = 0; m < km; ++m) - { - nm_fc_tensor.at(n, m) = std::move(generate_matrix_k(X_points, Y_points, n, m)); - } - } - } - ++flat_idx; - }; - - if constexpr(symmetry_support) - { - // we generate only the matrices in the positive cone of symmetries. - meta::looper_symmetries<dimension>{}(generate_all_interactions); - } - else - { - // loop range [-3,4[, ie range concept exclude the last value. - std::array<int, dimension> starts{}; - std::array<int, dimension> stops{}; - starts.fill(-3); - stops.fill(4); - // here we expand at compile time d loops of the range - // the indices of the d loops are input parameters of the lambda generate_all_interactions - meta::looper_range<dimension>{}(generate_all_interactions, starts, stops); - } - - // we divide the widths for the next tree level - current_width *= half; - half_width *= half; - } - } - - protected: - /// @brief Initialise the interaction matrices K. - /// - /// Homogenous : we construct only K on the points in [-1,1]^d cell, the length is - /// then 2. The matrix is applied on a cell of size width, then we have to scale - /// by cell_width/2. - /// Non-homogenous : The interaction matrices are constructed at the from root level - /// to the bottom of the tree (last cell level ie. same as the leaf level) - /// hence, we don't need the scale_factor of the kernel. - /// @param order : number of therms of the polynomial approximation. order^d is the number of grid points. - /// @param root_cell_width : width of the top root cell needed for the non-homogenous case. - /// @param tree_height : hight of the tree. - /// - /// @return - inline auto initialize(size_type order, value_type root_cell_width, std::size_t tree_height) -> void - { - if constexpr(std::is_same_v<settings, options::low_rank_>) - { - m_weights_of_roots = generate_weights(m_order); - } - generate_interactions_matrices( - order, - (homogeneity_tag == matrix_kernels::homogeneity::non_homogenous) ? root_cell_width : value_type(2.), - tree_height); - } - - template<typename D = derived_type> - [[nodiscard]] inline auto generate_weights(std::size_t order) const - -> std::enable_if_t<decltype(meta::sig_gen_w_f(std::declval<D>(), order))::value, array_type> - { - return this->derived_cast().generate_weights_impl(order); - } - template<typename D = derived_type> - [[nodiscard]] inline auto generate_weights(std::size_t order) const - -> std::enable_if_t<!decltype(meta::sig_gen_w_f(std::declval<D>(), order))::value, array_type> - { - return array_type{}; - } - - [[nodiscard]] inline auto derived_cast() & noexcept -> derived_type& - { - return *static_cast<derived_type*>(this); - } - - [[nodiscard]] inline auto derived_cast() const& noexcept -> derived_type const& - { - return *static_cast<const derived_type*>(this); - } + /** + * @brief true if we use the cell extension + * + */ + const bool m_use_cell_width_extension{}; - [[nodiscard]] inline auto derived_cast() && noexcept -> derived_type - { - return *static_cast<derived_type*>(this); - } + /** + * @brief + * + */ + array_type m_child_parent_interpolators{}; - private: - std::vector<interaction_matrix_type, XTENSOR_DEFAULT_ALLOCATOR(interaction_matrix_type)> - m_interactions_matrices{}; - sym_permutations_type m_sym_permutations{}; - k_indices_type m_k_indices{}; - matrix_kernel_type m_far_field{}; - xt::xarray<value_type> - m_weights_of_roots{}; ///< the weight associated to the roots of the m_order-1 polynomial - const size_type m_m2l_interactions{}; - const size_type m_nnodes{}; ///< number of modes m_order^dimension - const size_type m_order{}; ///< number of terms of the expansion (1d) - const array_type m_roots{}; ///< the roots of the m_order-1 polynomial - const value_type m_epsilon{}; ///< the accuracy for low-rank approximation (10^(-o)) @todo check 10^(1-o) - const value_type m_cell_width_extension{}; // w width of the extension of the cell + /** + * @brief + * + */ + grid_permutations_type m_grid_permutations{}; }; } // namespace impl } // namespace scalfmm::interpolation diff --git a/include/scalfmm/interpolation/m2l_handler.hpp b/include/scalfmm/interpolation/m2l_handler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e92e1122b18fa17bb08366be622c90a898813071 --- /dev/null +++ b/include/scalfmm/interpolation/m2l_handler.hpp @@ -0,0 +1,1306 @@ +// ----------------------------------- +// See LICENCE file at project root +// File : scalfmm/interpolation/m2l_handler.hpp +// ----------------------------------- +#ifndef SCALFMM_INTERPOLATION_M2L_HANDLER_HPP +#define SCALFMM_INTERPOLATION_M2L_HANDLER_HPP + +#include "scalfmm/container/point.hpp" +#include "scalfmm/interpolation/builders.hpp" +#include "scalfmm/interpolation/permutations.hpp" +#include "scalfmm/matrix_kernels/mk_common.hpp" +#include "scalfmm/memory/storage.hpp" +#include "scalfmm/meta/const_functions.hpp" +#include "scalfmm/meta/traits.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/options/options.hpp" +#include "scalfmm/utils/io_helpers.hpp" +#include "scalfmm/utils/low_rank.hpp" +#include "scalfmm/utils/math.hpp" +#include "scalfmm/utils/tensor.hpp" + +#include "xsimd/config/xsimd_config.hpp" +#include "xsimd/memory/xsimd_aligned_allocator.hpp" +#include "xtensor-blas/xlinalg.hpp" +#include "xtensor/xlayout.hpp" +#include "xtensor/xnpy.hpp" +#include "xtensor/xslice.hpp" +#include "xtensor/xtensor_config.hpp" +#include "xtensor/xvectorize.hpp" +#include "xtensor/xview.hpp" + +#include <algorithm> +#include <any> +#include <array> +#include <cassert> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <type_traits> +#include <vector> + +using namespace scalfmm::io; + +namespace scalfmm::interpolation +{ + /** + * @brief + * + * @tparam Derived + */ + template<typename Derived> + struct interpolator_traits; + + namespace impl + { + /** + * @brief + * + * @warning Cell width extension is not yet supported for homogeneous kernels in the latest version of ScalFMM! + * + * @tparam Derived + */ + template<typename Derived> + struct m2l_handler + { + private: + /** + * @brief + * + */ + struct empty + { + }; + + public: + using derived_type = Derived; + using value_type = typename interpolator_traits<derived_type>::value_type; + using size_type = std::size_t; + + static constexpr std::size_t dimension = interpolator_traits<derived_type>::dimension; + + using matrix_kernel_type = typename interpolator_traits<derived_type>::matrix_kernel_type; + + using settings = typename interpolator_traits<derived_type>::settings; + + /** + * @brief + * + */ + static constexpr std::size_t kn = matrix_kernel_type::kn; + + /** + * @brief + * + */ + static constexpr std::size_t km = matrix_kernel_type::km; + + /** + * @brief + * + */ + static constexpr auto separation_criterion = matrix_kernel_type::separation_criterion; + + /** + * @brief + * + */ + static constexpr auto homogeneity_tag = matrix_kernel_type::homogeneity_tag; + + /** + * @brief + * + */ + static constexpr auto symmetry_tag = matrix_kernel_type::symmetry_tag; + + /** + * @brief + * + */ + static constexpr auto enable_symmetries = interpolator_traits<derived_type>::enable_symmetries; + + /** + * @brief + * + */ + static constexpr std::size_t max_number_of_cell{7}; + + /** + * @brief + * + */ + static constexpr bool symmetry_support{ + (symmetry_tag == matrix_kernels::symmetry::symmetric && (enable_symmetries == true) && (dimension < 4))}; + + using scale_factor_type = typename matrix_kernel_type::template vector_type<value_type>; + using sym_permutations_type = std::conditional_t<symmetry_support, xt::xarray<int>, empty>; + using k_indices_type = std::conditional_t<symmetry_support, std::vector<std::size_t>, empty>; + + using array_type = xt::xarray<value_type>; + using array_shape_type = typename array_type::shape_type; + + template<std::size_t d> + using tensor_type = xt::xtensor<value_type, d>; + template<std::size_t d> + using tensor_shape_type = typename tensor_type<d>::shape_type; + + using storage_type = typename interpolator_traits<derived_type>::storage_type; + using buffer_value_type = typename interpolator_traits<derived_type>::buffer_value_type; + using buffer_inner_type = typename interpolator_traits<derived_type>::buffer_inner_type; + using k_tensor_type = std::conditional_t<std::is_same_v<settings, options::low_rank_>, + std::tuple<xt::xtensor<value_type, 2>, xt::xtensor<value_type, 2>>, + typename interpolator_traits<derived_type>::k_tensor_type>; + using interaction_matrix_type = xt::xtensor_fixed<k_tensor_type, xt::xshape<kn, km>>; + using buffer_shape_type = typename interpolator_traits<derived_type>::buffer_shape_type; + using buffer_type = typename interpolator_traits<derived_type>::buffer_type; + using multipoles_inner_type = + typename memory::storage_traits<typename storage_type::multipoles_storage_type>::inner_type; + using locals_inner_type = + typename memory::storage_traits<typename storage_type::locals_storage_type>::inner_type; + + /** + * @brief Construct a new m2l handler object + * + */ + m2l_handler() = delete; + + /** + * @brief Construct a new m2l handler object + * + * @param other + */ + m2l_handler(m2l_handler const& other) = delete; + + /** + * @brief Construct a new m2l handler object + * + */ + m2l_handler(m2l_handler&&) noexcept = delete; + + /** + * @brief + * + * @return m2l_handler& + */ + auto operator=(m2l_handler const&) -> m2l_handler& = delete; + + /** + * @brief + * + * @return m2l_handler& + */ + auto operator=(m2l_handler&&) noexcept -> m2l_handler& = delete; + + /** + * @brief Destroy the m2l handler object + * + */ + ~m2l_handler() = default; + + /** + * @brief Construct a new m2l handler object + * + * @param far_field + * @param roots + * @param tree_height + * @param root_cell_width + * @param cell_width_extension + * @param late_init + */ + m2l_handler(matrix_kernel_type const& far_field, array_type roots, size_type tree_height = 3, + value_type root_cell_width = value_type(1.), value_type cell_width_extension = value_type(0.), + bool late_init = false) + : m_far_field(far_field) + , m_m2l_interactions(math::pow(max_number_of_cell, dimension)) + , m_nnodes(meta::pow(roots.size(), dimension)) + , m_order(roots.size()) + , m_roots(roots) + , m_epsilon(std::pow(value_type(10.), -value_type(roots.size() - 1))) + , m_cell_width_extension(cell_width_extension) + { + if((cell_width_extension > 0) && (homogeneity_tag == matrix_kernels::homogeneity::homogenous)) + { + std::cout << "cell_width_extension: " << cell_width_extension << std::endl; + std::cout << "matrix kernel name: " << far_field.name() << std::endl; + throw std::runtime_error( + "m2lhandler: Cell width extension is not yet supported for homogeneous kernels in the " + "latest version of ScalFMM!"); + } + + if(late_init == false) + { + this->initialize(roots.size(), root_cell_width, tree_height); + } + + if constexpr(symmetry_support) + { + std::tie(m_sym_permutations, m_k_indices) = get_permutations_and_indices<dimension>( + roots.size(), meta::pow(roots.size(), dimension), this->m2l_interactions()); + } + } + + /** + * @brief + * + * @return array_type const& + */ + [[nodiscard]] inline auto weights() const noexcept -> array_type const& { return m_weights_of_roots; } + + /** + * @brief + * + * @return value_type& + */ + [[nodiscard]] inline auto epsilon() noexcept -> value_type& { return m_epsilon; } + + /** + * @brief + * + * @return value_type + */ + [[nodiscard]] inline auto epsilon() const noexcept -> value_type { return m_epsilon; } + + /** + * @brief member function to get the index of K corresponding to the interaction index in the interaction matrix vector. + * + * @param neighbor_idx + * @return std::size_t + */ + [[nodiscard]] inline auto symmetry_k_index(std::size_t neighbor_idx) const -> std::size_t + { + return m_k_indices.at(neighbor_idx); + } + + /** + * @brief + * + * @tparam TensorOrViewX + * @tparam TensorOrViewY + * @param X + * @param Y + * @param n + * @param m + * @param thread_id + * @return std::enable_if_t< + * !decltype(meta::sig_gen_k_f(std::declval<derived_type>(), X, Y, n, m, thread_id))::value, k_tensor_type> + */ + template<typename TensorOrViewX, typename TensorOrViewY> + [[nodiscard]] inline auto generate_matrix_k(TensorOrViewX&& X, TensorOrViewY&& Y, std::size_t n, + std::size_t m, [[maybe_unused]] size_type thread_id = 0) const + -> std::enable_if_t< + !decltype(meta::sig_gen_k_f(std::declval<derived_type>(), X, Y, n, m, thread_id))::value, k_tensor_type> + + { + if constexpr(std::is_same_v<settings, options::dense_>) + { + auto const& matrix_kernel{this->matrix_kernel()}; + + auto n_d = math::pow(m_order, dimension); + + k_tensor_type K(std::vector(2, n_d)); + auto flat_x = xt::flatten(X); + auto flat_y = xt::flatten(Y); + // TODO : SIMD! + + for(std::size_t i{0}; i < n_d; ++i) + { + for(std::size_t j{0}; j < n_d; ++j) + { + K.at(i, j) = matrix_kernel.evaluate(flat_x.at(i), flat_y.at(j)).at(n * km + m); + } + } + // std::cout << cpp_tools::colors::cyan; + // std::cout << K << std::endl; + // std::cout << cpp_tools::colors::reset; + // xt::dump_npy("interaction_matrix_non_homogenous_with_ext.npy",K); + return K; + } + else if constexpr(std::is_same_v<settings, options::low_rank_>) + { + auto const& matrix_kernel{this->matrix_kernel()}; + return low_rank::generate(matrix_kernel, std::forward<TensorOrViewX>(X), + std::forward<TensorOrViewY>(Y), this->weights(), this->epsilon(), n, m); + } + else + { + throw std::runtime_error("Missing generate_matrix_k function!"); + } + } + + /** + * @brief + * + * @tparam TensorOrViewX + * @tparam TensorOrViewY + * @param X + * @param Y + * @param n + * @param m + * @param thread_id + * @return std::enable_if_t< + * decltype(meta::sig_gen_k_f(std::declval<derived_type>(), X, Y, n, m, thread_id))::value, k_tensor_type> + */ + template<typename TensorOrViewX, typename TensorOrViewY> + [[nodiscard]] inline auto generate_matrix_k(TensorOrViewX&& X, TensorOrViewY&& Y, std::size_t n, + std::size_t m, [[maybe_unused]] size_type thread_id = 0) const + -> std::enable_if_t< + decltype(meta::sig_gen_k_f(std::declval<derived_type>(), X, Y, n, m, thread_id))::value, k_tensor_type> + { + return this->derived_cast().generate_matrix_k_impl(std::forward<TensorOrViewX>(X), + std::forward<TensorOrViewX>(Y), n, m, thread_id); + } + + /** + * @brief + * + * @return std::vector<interaction_matrix_type, XTENSOR_DEFAULT_ALLOCATOR(interaction_matrix_type)>& + */ + [[nodiscard]] inline auto interactions_matrices() noexcept + -> std::vector<interaction_matrix_type, XTENSOR_DEFAULT_ALLOCATOR(interaction_matrix_type)>& + { + return m_interactions_matrices; + } + + /** + * @brief + * + * @return std::vector<interaction_matrix_type> const& + */ + [[nodiscard]] inline auto + interactions_matrices() const noexcept -> std::vector<interaction_matrix_type> const& + { + return m_interactions_matrices; + } + + /** + * @brief + * + * @return std::vector<interaction_matrix_type> const& + */ + [[nodiscard]] inline auto + cinteractions_matrices() const noexcept -> std::vector<interaction_matrix_type> const& + { + return m_interactions_matrices; + } + + /** + * @brief + * + * @return auto + */ + [[nodiscard]] inline auto m2l_interactions() const noexcept { return m_m2l_interactions; } + + /** + * @brief + * + * @return sym_permutations_type const& + */ + [[nodiscard]] inline auto sym_permutations() const -> sym_permutations_type const& + { + return m_sym_permutations; + } + + [[nodiscard]] inline auto matrix_kernel() const noexcept -> matrix_kernel_type const& + { + return m_far_field; + } + + /** + * @brief + * + * @tparam D + * @return std::enable_if_t<decltype(meta::sig_buffer_init_f(std::declval<D>()))::value, buffer_type> + */ + template<typename D = derived_type> + [[nodiscard]] auto buffer_initialization() const + -> std::enable_if_t<decltype(meta::sig_buffer_init_f(std::declval<D>()))::value, buffer_type> + { + return this->derived_cast().buffer_initialization_impl(); + } + + /** + * @brief Initialize the buffer to aggregate the multipoles and teh locals when the kernel is symmetric + * + * if the kernel is non symmetric we have an empty buffers otherwise the element of the buffer + * is a tensor of size (number of multipole associated to the current symmetry, the number of nodes) + * + * @tparam D + * @return std::enable_if_t<!decltype(meta::sig_buffer_init_f(std::declval<D>()))::value, buffer_type> + */ + template<typename D = derived_type> + [[nodiscard]] auto buffer_initialization() const + -> std::enable_if_t<!decltype(meta::sig_buffer_init_f(std::declval<D>()))::value, buffer_type> + { + std::vector<std::size_t> shape(2, m_nnodes); + if constexpr(enable_symmetries) + { + shape[1] = interpolation::largest_number_permutation<dimension>(); + } + return buffer_type(buffer_shape_type{}, buffer_inner_type(shape, 0.)); + } + + /** + * @brief Reset the buffer by calling buffer_reset_impl (specialization) + * + * @tparam D + * @param buffers + */ + template<typename D = derived_type> + inline auto buffer_reset(buffer_type& buffers) const + -> std::enable_if_t<decltype(meta::sig_buffer_reset_f(std::declval<D>(), buffers))::value, void> + { + this->derived_cast().buffer_reset_impl(buffers); + } + + /** + * @brief Reset the buffer, generic function + * + * @tparam D + * @param buffers + */ + template<typename D = derived_type> + inline auto buffer_reset(buffer_type& buffers) const + -> std::enable_if_t<!decltype(meta::sig_buffer_reset_f(std::declval<D>(), buffers))::value, void> + { + if constexpr(symmetry_support) + { + for(std::size_t n{0}; n < 2; ++n) + { + buffers.at(n).fill(buffer_value_type(0.)); + } + } + } + + /** + * @brief + * + * @tparam D + * @return std::enable_if_t<!decltype(meta::sig_init_k_f(std::declval<D>()))::value, k_tensor_type> + */ + template<typename D = derived_type> + inline auto initialize_k() const + -> std::enable_if_t<!decltype(meta::sig_init_k_f(std::declval<D>()))::value, k_tensor_type> + { + if constexpr(std::is_same_v<settings, options::low_rank_>) + { + return std::make_tuple(xt::xtensor<value_type, 2>{}, xt::xtensor<value_type, 2>{}); + } + else if constexpr(std::is_same_v<settings, options::dense_>) + { + return k_tensor_type{}; + } + else + { + throw std::runtime_error("Missing initialize_k function!"); + } + } + + /** + * @brief + * + * @tparam D + * @return std::enable_if_t<decltype(meta::sig_init_k_f(std::declval<D>()))::value, k_tensor_type> + */ + template<typename D = derived_type> + inline auto initialize_k() const + -> std::enable_if_t<decltype(meta::sig_init_k_f(std::declval<D>()))::value, k_tensor_type> + { + return this->derived_cast().initialize_k_impl(); + } + + /** + * @brief + * + * @tparam Cell + * @param current_cell + * @param thread_id + * @return std::enable_if_t< + * decltype(meta::sig_preprocess_f(std::declval<derived_type>(), current_cell, thread_id))::value, void> + */ + template<typename Cell> + auto apply_multipoles_preprocessing(Cell& current_cell, [[maybe_unused]] size_type thread_id = 0) const + -> std::enable_if_t< + decltype(meta::sig_preprocess_f(std::declval<derived_type>(), current_cell, thread_id))::value, void> + { + return this->derived_cast().apply_multipoles_preprocessing_impl(current_cell, thread_id); + } + + /** + * @brief Default fallback + * + * @tparam Cell + * @param current_cell + * @param thread_id + * @return std::enable_if_t< + * !decltype(meta::sig_preprocess_f(std::declval<derived_type>(), current_cell, thread_id))::value, void> + */ + template<typename Cell> + auto apply_multipoles_preprocessing(Cell& current_cell, [[maybe_unused]] size_type thread_id = 0) const + -> std::enable_if_t< + !decltype(meta::sig_preprocess_f(std::declval<derived_type>(), current_cell, thread_id))::value, void> + { + } + + /** + * @brief + * + * @tparam Cell + * @param current_cell + * @param products + * @param thread_id + * @return std::enable_if_t<decltype(meta::sig_postprocess_f(std::declval<derived_type>(), current_cell, products, + * thread_id))::value, + * void> + */ + template<typename Cell> + auto apply_multipoles_postprocessing(Cell& current_cell, [[maybe_unused]] buffer_type const& products, + [[maybe_unused]] size_type thread_id = 0) const + -> std::enable_if_t<decltype(meta::sig_postprocess_f(std::declval<derived_type>(), current_cell, products, + thread_id))::value, + void> + { + return this->derived_cast().apply_multipoles_postprocessing_impl(current_cell, products, thread_id); + } + + /** + * @brief + * + * @tparam Cell + * @param current_cell + * @param products + * @param thread_id + * @return std::enable_if_t<!decltype(meta::sig_postprocess_f(std::declval<derived_type>(), current_cell, + * products, thread_id))::value, + * void> + */ + template<typename Cell> + auto apply_multipoles_postprocessing(Cell& current_cell, [[maybe_unused]] buffer_type const& products, + [[maybe_unused]] size_type thread_id = 0) const + -> std::enable_if_t<!decltype(meta::sig_postprocess_f(std::declval<derived_type>(), current_cell, + products, thread_id))::value, + void> + { + } + + /** + * @brief Compute the matrix vector product for the M2L operator + * + * The operation is + * locals := scale_factor*A*multipoles + beta*locals, or y := alpha*A**T*x + beta*y, + * if the settings is options::dense_ classical matrix vector product + * and if the settings is options::low_rank_ A = UV we perform two matrix vector product + * + * @tparam T + * @tparam T2 + * @param multipoles the multipole values + * @param locals the local values + * @param tmp a working array need for low rank approximation + * @param knm the kernel matrix + * @param scale_factor the scaling factor + * @param acc if true we accumulate (beta=1.0 0 otherwise) + */ + template<typename T, typename T2> + inline auto product(T const& multipoles, T& locals, T2& tmp, k_tensor_type const& knm, + value_type scale_factor, bool acc) const -> void + { + if constexpr(std::is_same_v<settings, options::low_rank_>) + { + auto const& u = meta::get<0>(knm); + auto const& v = meta::get<1>(knm); + tensor::blas2_product(multipoles, tmp, v, value_type(1.0), false, true); + tensor::blas2_product(tmp, locals, u, scale_factor, acc, false); + } + else if constexpr(std::is_same_v<settings, options::dense_>) + { + tensor::blas2_product(multipoles, locals, knm, scale_factor, acc); + } + else + { + throw std::runtime_error("No implementation found for m2l product !"); + } + } + + /** + * @brief + * + * @tparam T + * @param multipoles + * @param locals + * @param knm + * @param scale_factor + * @param acc + */ + template<typename T> + inline auto product(T const& multipoles, T& locals, k_tensor_type const& knm, value_type scale_factor, + bool acc) const -> void + { + if constexpr(std::is_same_v<settings, options::low_rank_>) + { + auto const& u = meta::get<0>(knm); + auto const& v = meta::get<1>(knm); + T tmp(locals.shape(), value_type(0.)); + tensor::blas2_product(multipoles, tmp, v, value_type(1.0), false, true); + tensor::blas2_product(tmp, locals, u, scale_factor, acc, false); + } + else if constexpr(std::is_same_v<settings, options::dense_>) + { + tensor::blas2_product(multipoles, locals, knm, scale_factor, acc); + } + else + { + throw std::runtime_error("No implementation found for m2l product !"); + } + } + + /** + * @brief Compute the matrix vector product for the M2L operator + * + * The operation is + * locals := scale_factor*A*multipoles + beta*locals, or y := alpha*A**T*x + beta*y, + * if the settings is options::dense_ classical matrix vector product + * and if the settings is options::low_rank_ A = UV we perform two matrix vector product + * + * @tparam T + * @tparam T2 + * @param multipoles the multipole values + * @param locals the local values + * @param tmp a working array need for low rank approximation + * @param knm the kernel matrix + * @param tmp A temporary matrix used in low rank approximation + * @param nb_mult the number of multipole to treat (number of column of multipoles) + * @param scale_factor the scaling factor + * @param acc if true we accumulate (beta=1.0 0 otherwise) + */ + template<typename T, typename T1> + inline auto product_m(T const& multipoles, T& locals, k_tensor_type const& Knm, T1& tmp, int nb_mult, + value_type scale_factor, bool acc) const -> void + { + if constexpr(std::is_same_v<settings, options::low_rank_>) + { + // Knm = U V^T + auto const& U = meta::get<0>(Knm); + auto const& V = meta::get<1>(Knm); + constexpr bool accumulate = false; + + constexpr bool transposeV = true; + + tensor::blas3_product(multipoles, tmp, V, nb_mult, value_type(1.0), accumulate, transposeV); + + tensor::blas3_product(tmp, locals, U, nb_mult, scale_factor, acc); + } + else if constexpr(std::is_same_v<settings, options::dense_>) + { + tensor::blas3_product(multipoles, locals, Knm, nb_mult, scale_factor, acc); + } + else + { + throw std::runtime_error("No implementation found for m2l product !"); + } + } + + /** + * @brief + * + * @tparam Cell + * @param source_cell + * @param target_cell + * @param neighbor_idx + * @param tree_level + * @param products + * @param thread_id + */ + template<typename Cell> + auto apply_m2l_single(Cell const& source_cell, Cell& target_cell, std::size_t neighbor_idx, + std::size_t tree_level, [[maybe_unused]] buffer_type& products, + [[maybe_unused]] size_type thread_id = 0) const -> void + { + std::size_t level{}; + scale_factor_type scale_factor{}; + + if constexpr(homogeneity_tag == matrix_kernels::homogeneity::homogenous) + { + level = 0; + // here we scale the target cell width to (homogenous case) match + // the [-1,1] unitary cell used to generate the corresponding + // interaction matrix. + scale_factor = m_far_field.scale_factor((target_cell.width()) / value_type(2.)); + } + else // non-homogenous case. + { + // root level is 0 (i.e the simulation box) first level of cell is level 1 + // we start the indexing of matrixes at 0, hence the -1 on the cell level + level = tree_level - 2; + scale_factor.fill(1.0); + } + if constexpr(symmetry_support) + { + // the kernel is symmetric + // Decrease the number of matrix-vector product due to the symmetry + // see https://hal.inria.fr/hal-00746089v2 + const auto neighbor_sym = this->symmetry_k_index(neighbor_idx); + const auto number_of_interactions = number_of_matrices_in_orthant<dimension>(); + + auto const& k = m_interactions_matrices.at(level * number_of_interactions + neighbor_sym); + auto& permuted_multipoles = products.at(0); + auto& permuted_locals = products.at(1); + + for(std::size_t n = 0; n < kn; ++n) + { + for(std::size_t m = 0; m < km; ++m) + { + auto const& multipoles = source_cell.cmultipoles(m); + auto& locals = target_cell.locals(n); + // the multipole is permuted + const auto m_ptr = multipoles.data(); + const auto l_ptr = locals.data(); + const auto m_p_ptr = permuted_multipoles.data(); + const auto l_p_ptr = permuted_locals.data(); + const auto perm_ptr = &m_sym_permutations.at(neighbor_idx, 0); + + for(std::size_t i{0}; i < m_nnodes; ++i) + { + m_p_ptr[perm_ptr[i]] = m_ptr[i]; + } + + this->product(permuted_multipoles, permuted_locals, k.at(n, m), scale_factor.at(n), false); + + // the local expansion is permuted to the original. + for(std::size_t i{0}; i < m_nnodes; ++i) + { + l_ptr[i] += l_p_ptr[perm_ptr[i]]; + } + } + } + } + else if constexpr(std::is_same_v<settings, options::low_rank_> || + std::is_same_v<settings, options::dense_>) + { + // non symmetric kernel and low rank or dense approximation of the kernel + + auto const& k = m_interactions_matrices.at(level * m_m2l_interactions + neighbor_idx); + auto const& multipoles = source_cell.cmultipoles(); + auto& locals = target_cell.locals(); // we generate km*kn products + // loop on km + for(std::size_t m = 0; m < km; ++m) + { + // meta loop on kn + for(std::size_t n = 0; n < kn; ++n) + { + this->product(multipoles.at(m), locals.at(n), k.at(n, m), scale_factor.at(n), true); + } + } + } + else + { + // non symmetric kernel and specific product (fft for uniform approximation) + // auto const& multipoles = source_cell.cmultipoles(); + // auto& locals = target_cell.locals(); + auto const& k = m_interactions_matrices.at(level * m_m2l_interactions + neighbor_idx); + // we generate km*kn products + // loop on km + for(std::size_t m = 0; m < km; ++m) + { + // meta loop on kn + for(std::size_t n = 0; n < kn; ++n) + { + this->derived_cast().apply_m2l_impl(source_cell, target_cell, products, k, scale_factor, n, + m, thread_id); + } + } + } + } + + /** + * @brief + * + * @tparam Cell + * @param target_cell + * @param tree_level + * @param products + * @param thread_id + * @return auto + */ + template<typename Cell> + auto apply_m2l_loop(Cell& target_cell, std::size_t tree_level, [[maybe_unused]] buffer_type& products, + [[maybe_unused]] size_type thread_id = 0) const + { + std::size_t level{}; + scale_factor_type scale_factor{}; + // + if constexpr(homogeneity_tag == matrix_kernels::homogeneity::homogenous) + { + level = 0; + // here we scale the target cell width to (homogenous case) match + // the [-1,1] unitary cell used to generate the corresponding + // interaction matrix. + scale_factor = m_far_field.scale_factor((target_cell.width()) / value_type(2.)); + } + else // non-homogenous case. + { + // root level is 0 (i.e the simulation box) first level of cell is level 1 + // we start the indexing of matrixes at 0, hence the -1 on the cell level + level = tree_level - 2; + scale_factor.fill(1.0); + } + auto const& cell_symbolics = target_cell.csymbolics(); + auto const& interaction_positions = cell_symbolics.interaction_positions; + auto const& interaction_iterators = cell_symbolics.interaction_iterators; + // #ifdef M2L_NEW_SYM + // for symmetry support inner_type of the buffer for low_rank approximation otherwise the type of the + // local approximation + using local_type = std::conditional_t<std::is_same_v<settings, options::low_rank_>, + std::conditional_t<symmetry_support, buffer_inner_type, + std::decay_t<decltype(target_cell.locals(0))>>, + meta::empty>; + // #else + // using local_type = std::decay_t<decltype(target_cell.locals(0))>; + // #endif + local_type work; + + if constexpr(std::is_same_v<settings, options::low_rank_>) + { + // // + if constexpr(symmetry_support) + { + // rank x max_symm + work.resize(products.at(1).shape()); + // work.resize(std::array<std::size_t, 2>{m_nnodes, largest_number_permutation<dimension>()}); + } + else + { + work.resize(target_cell.locals(0).shape()); + } + } + // + // Selection product operator depending on symmetry, product optimization + // + if constexpr(symmetry_support) + { + // the kernel is symmetric + // Decrease the number of matrix-vector product due to the symmetry + // see https://hal.inria.fr/hal-00746089v2 + + constexpr auto number_of_symmetries = number_of_matrices_in_orthant<dimension>(); + + std::array<std::array<int, largest_number_permutation<dimension>()>, number_of_symmetries> + neighbors_perm; + std::array<int, number_of_symmetries> number_of_permutation{}; + // + // access the buffer to store temporary array + // aggregate_multipoles and aggregate_locals are vectors of + // xt::xarray of size number of permutation + auto aggregate_multipoles = products.at(0); + auto aggregate_locals = products.at(1); + // set cells in neighbors_perm according to their symmetry + for(std::size_t index{0}; index < cell_symbolics.existing_neighbors; ++index) + { + auto const& neighbor_idx = interaction_positions.at(index); + const auto neighbor_sym = this->symmetry_k_index(neighbor_idx); + // + neighbors_perm[neighbor_sym][number_of_permutation[neighbor_sym]] = index; + number_of_permutation[neighbor_sym] += 1; + } + + // Loop on the number of rhs (input) + for(std::size_t m = 0; m < km; ++m) + { + // loop on the number of symmetries + for(std::size_t idx{0}; idx < number_of_symmetries; ++idx) + { + if(number_of_permutation[idx] > 0) + { + auto const& neighs = neighbors_perm[idx]; + // Fill the temporary array with all multipoles + for(int i = 0; i < number_of_permutation[idx]; ++i) + { + auto const& index = neighs[i]; + auto const& neighbor_idx = interaction_positions.at(index); + // get the permutation associated to the position of the cell + const auto perm_ptr = &m_sym_permutations.at(neighbor_idx, 0); + // + // Gather the multipoles for the permutation + // get the set of multipole for the current symmetry + // get the xt::xarray of aggregated multipole for the current symmetry + // permuted_multipoles is a xarray of size(max cells in symmetry, the grid of + // nodes) + // the order of the tensor is dimension + 1 + // get multipole + auto const& source_cell = *interaction_iterators.at(index); + auto const& multipoles = source_cell.cmultipoles(m); + // get the pointer of the source multipole + const auto m_ptr = multipoles.data(); + + for(std::size_t j{0}; j < m_nnodes; ++j) + { + aggregate_multipoles(perm_ptr[j], i) = m_ptr[j]; + // current_permuted_multipole_ptr[perm_ptr[j]] = m_ptr[j]; + } + } // end of the aggregation for the current symmetry idx + + ////////////////////////////////////////////////////////////// + // Performed the matrix matrix product + // + auto const& k = m_interactions_matrices.at(level * number_of_symmetries + idx); + + for(std::size_t n = 0; n < kn; ++n) + { + auto& locals = target_cell.locals(n); + const auto l_ptr = locals.data(); + // Perform matrix-matrix product with aggregate multipoles + this->product_m(aggregate_multipoles, aggregate_locals, k.at(n, m), work, + number_of_permutation[idx], scale_factor.at(n), false); + // + + // the local expansion is permuted to their original. + for(int i = 0; i < number_of_permutation[idx]; ++i) + { + auto const& neighbor_idx = interaction_positions.at(neighs[i]); + // get the permutation associated to the position of the cell + const auto perm_ptr = &m_sym_permutations.at(neighbor_idx, 0); + // add current multipole to aggregate_multipoles + // auto current_permuted_locals = aggregate_locals.data() + m_nnodes * i; + for(std::size_t j{0}; j < m_nnodes; ++j) + { + l_ptr[j] += aggregate_locals(perm_ptr[j], i); + } + } + + } // end kn loop on the output of the matrix kernel + } // end if + } // end loop number of cells in the symmetry + } // end km loop on the input of the matrix kernel + + } // end constexpr + else if constexpr(std::is_same_v<settings, options::low_rank_> || + std::is_same_v<settings, options::dense_>) + { + // non symmetric kernel and low rank or dense approximation of the kernel + + for(std::size_t index{0}; index < cell_symbolics.existing_neighbors; ++index) + { + auto const& source_cell = *interaction_iterators.at(index); + const auto neighbor_idx = static_cast<std::size_t>(interaction_positions.at(index)); + + auto const& k = m_interactions_matrices.at(level * m_m2l_interactions + neighbor_idx); + // we generate km*kn products + // loop on km + auto const& multipoles = source_cell.cmultipoles(); + auto& locals = target_cell.locals(); + + for(std::size_t m = 0; m < km; ++m) + { + // meta loop on kn + for(std::size_t n = 0; n < kn; ++n) + { + this->product(multipoles.at(m), locals.at(n), work, k.at(n, m), scale_factor.at(n), + true); + } + } + } + } + else + { + // non symmetric kernel and specific product (fft for uniform approximation) + + for(std::size_t index{0}; index < cell_symbolics.existing_neighbors; ++index) + { + auto const& source_cell = *interaction_iterators.at(index); + const auto neighbor_idx = static_cast<std::size_t>(interaction_positions.at(index)); + + auto const& k = m_interactions_matrices.at(level * m_m2l_interactions + neighbor_idx); + // we generate km*kn products + // loop on km + for(std::size_t m = 0; m < km; ++m) + { + // meta loop on kn + for(std::size_t n = 0; n < kn; ++n) + { + this->derived_cast().apply_m2l_impl(source_cell, target_cell, products, k, scale_factor, + n, m, thread_id); + } + } + } + } + } + + private: + /** + * @brief + * + * @param order + * @param width + * @param tree_height + */ + inline auto generate_interactions_matrices(size_type order, value_type width, + std::size_t tree_height) -> void + { + std::size_t number_of_level{1}; + std::size_t number_of_interactions{0}; + value_type local_cell_width_extension{0}; + + if constexpr(symmetry_support) + { + number_of_interactions = number_of_matrices_in_orthant<dimension>(); + } + else + { + number_of_interactions = this->m2l_interactions(); + } + + // here width is the root width + // so first level of cell is of width because we skip level 0 (the root) and (the first level)): + value_type current_width{width}; + const value_type half{0.5}; + const value_type quarter{0.25}; + // we get the half width to scale the roots + + if constexpr(homogeneity_tag == matrix_kernels::homogeneity::non_homogenous) + { + // (tree_heigh = 4 -> [0-3]) the bottom cells and leaves have the same symbolic level ! + // but we remove level 0 (the root ie simulation box) and level 1. + number_of_level = tree_height - 2; + current_width = width * quarter; + local_cell_width_extension = m_cell_width_extension; + } + + value_type half_width{current_width * half}; + // we need to keep the tensorial view + // let the same view goes down to tensorial of point + // X and Y are Td tensors storing point. + + // get the ref of the vector + auto& interactions_matrices{this->interactions_matrices()}; + + // resizing and initializing the vector + // homogenous -> only one level + // non_homogenous -> we generate interaction matrices for each tree level processed by the m2l + // operator + interactions_matrices.resize( + number_of_interactions * number_of_level, + // xtensor_fixed<xshape<km,kn>> + interaction_matrix_type(typename interaction_matrix_type::shape_type{}, + // in Chebyshev, K_mn is a matrix of size [nnodes, nnodes] + initialize_k(), xt::layout_type::row_major)); + // loop on levels + for(std::size_t l{0}; l < number_of_level; ++l) + { + // homogenous -> X is the [-1,1] box reference + // non_homogenous -> X is the [-cell_width_at_level/2, cell_width_at_level/2] + // X is a multidimensional grid generator returning a grid for X, another one for Y,... + // X is a multidimensional grid of points scaled on the size of cell + auto X_points = tensor::generate_grid_of_points<dimension>( + (half_width + half * local_cell_width_extension) * m_roots); + + // lambda for generation the matrixes corresponding to one interaction + std::size_t flat_idx{0}; + auto generate_all_interactions = [order, this, &interactions_matrices, &flat_idx, &X_points, + current_width, number_of_interactions, l](auto... is) + { + if(((std::abs(is) > separation_criterion) || ...)) + { + // constructing centers to generate Y + xt::xarray<container::point<value_type, dimension>> centers(std::vector(dimension, order)); + xt::xarray<container::point<value_type, dimension>> Y_points(std::vector(dimension, order)); + + // here we fill the centers with the current loop indexes + // X_points is scaled with the width of cell so, Y_points will be scaled directly + centers.fill( + container::point<value_type, dimension>({(value_type(is) * current_width)...})); + // then we calculate the Y points + // TODO : add directly the point value_type here. + Y_points = X_points + centers; + + // we get the ref of interaction matrices to generate + auto& nm_fc_tensor = interactions_matrices.at(l * number_of_interactions + flat_idx); + // and we generate each matrix needed for the product (kn*km matrices) + for(std::size_t n = 0; n < kn; ++n) + { + for(std::size_t m = 0; m < km; ++m) + { + nm_fc_tensor.at(n, m) = std::move(generate_matrix_k(X_points, Y_points, n, m)); + } + } + } + ++flat_idx; + }; + + if constexpr(symmetry_support) + { + // we generate only the matrices in the positive cone of symmetries. + meta::looper_symmetries<dimension>{}(generate_all_interactions); + } + else + { + // loop range [-3,4[, ie range concept exclude the last value. + std::array<int, dimension> starts{}; + std::array<int, dimension> stops{}; + starts.fill(-3); + stops.fill(4); + // here we expand at compile time d loops of the range + // the indices of the d loops are input parameters of the lambda generate_all_interactions + meta::looper_range<dimension>{}(generate_all_interactions, starts, stops); + } + + // we divide the widths for the next tree level + current_width *= half; + half_width *= half; + } + } + + protected: + /** + * @brief Initialise the interaction matrices K. + * + * Homogenous : we construct only K on the points in [-1,1]^d cell, the length is + * then 2. The matrix is applied on a cell of size width, then we have to scale + * by cell_width/2. + * Non-homogenous : The interaction matrices are constructed at the from root level + * to the bottom of the tree (last cell level ie. same as the leaf level) + * hence, we don't need the scale_factor of the kernel. + * + * @param order : number of therms of the polynomial approximation. order^d is the number of grid points. + * @param root_cell_width : width of the top root cell needed for the non-homogenous case. + * @param tree_height : hight of the tree. + */ + inline auto initialize(size_type order, value_type root_cell_width, std::size_t tree_height) -> void + { + if constexpr(std::is_same_v<settings, options::low_rank_>) + { + m_weights_of_roots = generate_weights(m_order); + } + generate_interactions_matrices( + order, + (homogeneity_tag == matrix_kernels::homogeneity::non_homogenous) ? root_cell_width : value_type(2.), + tree_height); + } + + /** + * @brief + * + * @tparam D + * @param order + * @return std::enable_if_t<decltype(meta::sig_gen_w_f(std::declval<D>(), order))::value, array_type> + */ + template<typename D = derived_type> + [[nodiscard]] inline auto generate_weights(std::size_t order) const + -> std::enable_if_t<decltype(meta::sig_gen_w_f(std::declval<D>(), order))::value, array_type> + { + return this->derived_cast().generate_weights_impl(order); + } + + /** + * @brief + * + * @tparam D + * @param order + * @return std::enable_if_t<!decltype(meta::sig_gen_w_f(std::declval<D>(), order))::value, array_type> + */ + template<typename D = derived_type> + [[nodiscard]] inline auto generate_weights(std::size_t order) const + -> std::enable_if_t<!decltype(meta::sig_gen_w_f(std::declval<D>(), order))::value, array_type> + { + return array_type{}; + } + + /** + * @brief + * + * @return derived_type& + */ + [[nodiscard]] inline auto derived_cast() & noexcept -> derived_type& + { + return *static_cast<derived_type*>(this); + } + + /** + * @brief + * + * @return derived_type const& + */ + [[nodiscard]] inline auto derived_cast() const& noexcept -> derived_type const& + { + return *static_cast<const derived_type*>(this); + } + + /** + * @brief + * + * @return derived_type + */ + [[nodiscard]] inline auto derived_cast() && noexcept -> derived_type + { + return *static_cast<derived_type*>(this); + } + + private: + /** + * @brief + * + */ + std::vector<interaction_matrix_type, XTENSOR_DEFAULT_ALLOCATOR(interaction_matrix_type)> + m_interactions_matrices{}; + + /** + * @brief + * + */ + sym_permutations_type m_sym_permutations{}; + + /** + * @brief + * + */ + k_indices_type m_k_indices{}; + + /** + * @brief + * + */ + matrix_kernel_type m_far_field{}; + + /** + * @brief the weight associated to the roots of the m_order-1 polynomial + * + */ + xt::xarray<value_type> m_weights_of_roots{}; + + /** + * @brief + * + */ + const size_type m_m2l_interactions{}; + + /** + * @brief number of modes m_order^dimension + * + */ + const size_type m_nnodes{}; + + /** + * @brief number of terms of the expansion (1d) + * + */ + const size_type m_order{}; + + /** + * @brief the roots of the m_order-1 polynomial + * + */ + const array_type m_roots{}; + + /** + * @brief the accuracy for low-rank approximation (10^(-o)) @todo check 10^(1-o) + * + */ + const value_type m_epsilon{}; + + /** + * @brief width of the extension of the cell + * + */ + const value_type m_cell_width_extension{}; + }; + } // namespace impl +} // namespace scalfmm::interpolation +#endif // SCALFMM_INTERPOLATION_M2L_HANDLER_HPP diff --git a/include/scalfmm/interpolation/mapping.hpp b/include/scalfmm/interpolation/mapping.hpp index 7ae19c826ef24dd15c45481eeb210075a9e35c82..badad90ec2ffcca101ca73410d108a742412642d 100644 --- a/include/scalfmm/interpolation/mapping.hpp +++ b/include/scalfmm/interpolation/mapping.hpp @@ -1,12 +1,17 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/mapping.hpp +// File : scalfmm/interpolation/mapping.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_MAPPING_HPP #define SCALFMM_INTERPOLATION_MAPPING_HPP namespace scalfmm::interpolation { + /** + * @brief + * + * @tparam T + */ template<typename T> class map_loc_glob { @@ -14,17 +19,35 @@ namespace scalfmm::interpolation using value_type = T; using inner_type = typename T::value_type; + /** + * @brief Construct a new map loc glob object + * + * @param center + * @param width + */ explicit map_loc_glob(const value_type& center, const value_type& width) : m_a(center - (width * half)) , m_b(center + (width * half)) { } + /** + * @brief + * + * @param loc_pos + * @param glob_pos + */ inline void operator()(const value_type& loc_pos, value_type& glob_pos) const { glob_pos = ((m_a + m_b) * (half)) + (m_b - m_a) * loc_pos * half; } + /** + * @brief + * + * @param loc_pos + * @return value_type + */ [[nodiscard]] inline auto operator()(const value_type& loc_pos) const -> value_type { return (((m_a + m_b) * half) + (m_b - m_a) * loc_pos * half); @@ -36,6 +59,11 @@ namespace scalfmm::interpolation const value_type m_b; }; + /** + * @brief + * + * @tparam T + */ template<typename T> class map_glob_loc { @@ -43,26 +71,46 @@ namespace scalfmm::interpolation using value_type = T; using inner_type = typename T::value_type; + /** + * @brief Construct a new map glob loc object + * + * @param center + * @param width + */ explicit map_glob_loc(const value_type& center, const value_type& width) : m_a(center - (width * half)) , m_b(center + (width * half)) { } + /** + * @brief + * + * @param glob_pos + * @param loc_pos + */ inline void operator()(const value_type& glob_pos, value_type& loc_pos) const { loc_pos = (two * glob_pos - m_b - m_a) / (m_b - m_a); } + /** + * @brief + * + * @param glob_pos + * @return value_type + */ [[nodiscard]] inline auto operator()(const value_type& glob_pos) const -> value_type { return (two * glob_pos - m_b - m_a) / (m_b - m_a); } - inline auto jacobian() const -> value_type - { - return two / (m_b - m_a); - } + /** + * @brief + * + * @return value_type + */ + inline auto jacobian() const -> value_type { return two / (m_b - m_a); } private: const inner_type two{2.}; diff --git a/include/scalfmm/interpolation/matrix_kernel.hpp b/include/scalfmm/interpolation/matrix_kernel.hpp index ee40fef226e8bf7fcbb6c18c687b57ee111a235c..594296fbdea583170dfd4669302ca21979759b5b 100644 --- a/include/scalfmm/interpolation/matrix_kernel.hpp +++ b/include/scalfmm/interpolation/matrix_kernel.hpp @@ -1,12 +1,9 @@ // -------------------------------- // See LICENCE file at project root -// File : matrix_kernel.hpp +// File : scalfmm/interpolation/matrix_kernel.hpp // -------------------------------- -#ifndef SCALFMM_INTERPOLATION_MATRIX_KERNEL_HPP -#define SCALFMM_INTERPOLATION_MATRIX_KERNEL_HPP +#pragma once namespace scalfmm::interpolation { } // namespace scalfmm::interpolation - -#endif // SCALFMM_INTERPOLATION_MATRIX_KERNEL_HPP diff --git a/include/scalfmm/interpolation/permutations.hpp b/include/scalfmm/interpolation/permutations.hpp index 73b461207fec821f97755bebf456b7b52298767c..3364ed1199cab2bd0215d4eb1972503a14a7bf5b 100644 --- a/include/scalfmm/interpolation/permutations.hpp +++ b/include/scalfmm/interpolation/permutations.hpp @@ -1,10 +1,21 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/permutations.hpp +// File : scalfmm/interpolation/permutations.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_PERMUTATIONS_HPP #define SCALFMM_INTERPOLATION_PERMUTATIONS_HPP +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/utils/io_helpers.hpp" +#include "scalfmm/utils/math.hpp" + +#include "xtensor/xarray.hpp" +#include "xtensor/xbuilder.hpp" +#include "xtensor/xgenerator.hpp" +#include "xtensor/xmath.hpp" +#include "xtensor/xoperation.hpp" +#include "xtensor/xtensor_forward.hpp" + #include <array> #include <cmath> #include <cstddef> @@ -14,27 +25,18 @@ #include <type_traits> #include <utility> #include <vector> -#include <xtensor/xarray.hpp> -#include <xtensor/xbuilder.hpp> -#include <xtensor/xtensor_forward.hpp> - -#include "scalfmm/meta/utils.hpp" -#include "scalfmm/utils/io_helpers.hpp" -#include "scalfmm/utils/math.hpp" - -#include "xtensor/xgenerator.hpp" -#include "xtensor/xmath.hpp" -#include "xtensor/xoperation.hpp" namespace scalfmm::meta { - /// @brief The looper symmetries extends loop nests according to the dimension. - /// Indices correspond the symmetries of the positive orthant. - /// A cone in 3D - /// A triangle in 2D - /// A segment in 1D - /// - /// @tparam N : is the dimension. + /** + * @brief The looper symmetries extends loop nests according to the dimension. + * Indices correspond the symmetries of the positive orthant. + * A cone in 3D + * A triangle in 2D + * A segment in 1D + * + * @tparam N the dimension. + */ template<std::size_t N> struct looper_symmetries { @@ -42,6 +44,11 @@ namespace scalfmm::meta "available. Please provide it !"); }; + /** + * @brief + * + * @tparam + */ template<> struct looper_symmetries<3> { @@ -61,6 +68,11 @@ namespace scalfmm::meta } }; + /** + * @brief + * + * @tparam + */ template<> struct looper_symmetries<2> { @@ -77,6 +89,11 @@ namespace scalfmm::meta } }; + /** + * @brief + * + * @tparam + */ template<> struct looper_symmetries<1> { @@ -95,6 +112,15 @@ namespace scalfmm::interpolation { namespace impl { + /** + * @brief Get the orthant index object + * + * @tparam Indexes + * @tparam Is + * @param s + * @param is + * @return constexpr auto + */ template<typename... Indexes, std::size_t... Is> inline constexpr auto get_orthant_index(std::index_sequence<Is...> s, Indexes... is) { @@ -104,24 +130,27 @@ namespace scalfmm::interpolation } } // namespace impl - /// @brief Returns the index of the orthant according to the box position. - /// - /// @tparam Indexes : the type of the position indices - /// @param is : the position indices - /// - /// @return : the orthant index. + /** + * @brief Get the orthant index object according to the box position. + * + * @tparam Indexes the type of the position indices. + * @param is the position indices. + * @return the orthant index. + */ template<typename... Indexes> - inline constexpr auto get_orthant_index(Indexes... is) - -> std::enable_if_t<std::conjunction_v<std::is_signed<Indexes>...>, std::size_t> + inline constexpr auto + get_orthant_index(Indexes... is) -> std::enable_if_t<std::conjunction_v<std::is_signed<Indexes>...>, std::size_t> { return impl::get_orthant_index(std::index_sequence_for<Indexes...>{}, is...); } - /// @brief Returns the number of K matrices in the positive orthant. - /// - /// @tparam dim : the dimension - /// - /// @return : the number of k matrices + /** + * @brief Returns the number of interactions matrices K in the positive orthant. + * + * @tparam dim the dimension. + * @param check + * @return the number of matrices. + */ template<std::size_t dim> inline constexpr auto number_of_matrices_in_orthant(bool check = true) -> std::size_t { @@ -150,11 +179,12 @@ namespace scalfmm::interpolation } } - /// @brief Returns the maximum number of identical permutations in the grid \f$ [-3,3]^d \f$. - /// - /// @tparam dim : the dimension - /// - /// @return : the maximal number of same permutation + /** + * @brief Returns the maximum number of identical permutations in the grid \f$ [-3,3]^d \f$. + * + * @tparam dim the dimension. + * @return the maximum number of same permutation. + */ template<std::size_t dim> inline constexpr auto largest_number_permutation() -> std::size_t { @@ -177,14 +207,16 @@ namespace scalfmm::interpolation "available. Please provide it !"); } } - /// @brief Computes the cone index corresponding the position indices and - /// also permutes the position indices to correspond to the indices in the - /// optimized cone. - /// - /// @tparam Integral : the type of the position indices of the interaction boxes - /// @param is : the position indices of the interaction boxes - /// - /// @return : a tuple with the cone index and the permuted indices + + /** + * @brief Computes the cone index corresponding the position indices and + * also permutes the position indices to correspond to the indices in the + * optimized cone. + * + * @tparam Integral the type of the position indices of the interaction boxes. + * @param is the position indices of the interaction boxes. + * @return a tuple with the cone index and the permuted indices. + */ template<typename... Integral> inline auto compute_cone_and_permuted_indexe(Integral... is) { @@ -258,22 +290,25 @@ namespace scalfmm::interpolation return std::make_tuple(cidx, is_permuted); } - /// @brief Returns a tuple holding an xarray of permutations corresponding to all m2l - /// interactions and a std::vector the corresponding K linear index in the interaction - /// matrix vector of Ks. - /// - /// @tparam dimension : the dimension - /// @param order : the order of the interpolator - /// @param nnodes : the size of the tensors - /// @param m2l_interactions : the number of interactions - /// - /// @return : a tuple holding the the xarray of permutations and the std::vector of K indices. - template<std::size_t dimension> + /** + * @brief Returns a tuple holding an xarray of permutations corresponding to all m2l + * interactions and a std::vector the corresponding K linear index in the interaction + * matrix vector of Ks. + * + * @tparam Dimension : the dimension. + * @param order : the order of the interpolator. + * @param nnodes : the size of the tensors. + * @param m2l_interactions : the number of interactions. + * @return a tuple holding the xarray of permutations and the std::vector of K indices. + */ + template<std::size_t Dimension> inline auto get_permutations_and_indices(std::size_t order, std::size_t nnodes, std::size_t m2l_interactions) { using permutations_type = xt::xarray<int>; using k_indices_type = std::vector<std::size_t>; + static constexpr std::size_t dimension = Dimension; + constexpr auto number_of_orthant{math::pow(2, dimension)}; std::size_t flat_index{0}; diff --git a/include/scalfmm/interpolation/traits.hpp b/include/scalfmm/interpolation/traits.hpp index 11acf6e99651631f7c7a73f050ca28bb38ba5fdb..e40673e8d669713cf3b3f3e96037224eea34a5ba 100644 --- a/include/scalfmm/interpolation/traits.hpp +++ b/include/scalfmm/interpolation/traits.hpp @@ -1,14 +1,16 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/traits.hpp +// File : scalfmm/interpolation/traits.hpp // -------------------------------- -#ifndef SCALFMM_INTERPOLATION_TRAITS_HPP -#define SCALFMM_INTERPOLATION_TRAITS_HPP +#pragma once namespace scalfmm::interpolation { + /** + * @brief + * + * @tparam Derived + */ template<typename Derived> struct interpolator_traits; -} - -#endif // SCALFMM_INTERPOLATION_TRAITS_HPP +} // namespace scalfmm::interpolation diff --git a/include/scalfmm/interpolation/uniform.hpp b/include/scalfmm/interpolation/uniform.hpp index 0ca258ec222f10e09327b208a394e77abf8636c1..df2dd8830c55e4676d7e0bad26908892e5e097f1 100644 --- a/include/scalfmm/interpolation/uniform.hpp +++ b/include/scalfmm/interpolation/uniform.hpp @@ -1,12 +1,8 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/uniform.hpp +// File : scalfmm/interpolation/uniform.hpp // -------------------------------- -#ifndef SCALFMM_INTERPOLATION_UNIFORM_HPP -#define SCALFMM_INTERPOLATION_UNIFORM_HPP - +#pragma once #include "scalfmm/interpolation/uniform/uniform_interpolator.hpp" -#include "scalfmm/interpolation/uniform/uniform_storage.hpp" - -#endif // SCALFMM_INTERPOLATION_UNIFORM_HPP +#include "scalfmm/interpolation/uniform/uniform_storage.hpp" \ No newline at end of file diff --git a/include/scalfmm/interpolation/uniform/uniform_interpolator.hpp b/include/scalfmm/interpolation/uniform/uniform_interpolator.hpp index 5943a5ae35d8e7c4e7b1dbf45f51b2c0c67e0c9a..612e322fb4bafbedc21bf2bee402fb2b36e7a051 100644 --- a/include/scalfmm/interpolation/uniform/uniform_interpolator.hpp +++ b/include/scalfmm/interpolation/uniform/uniform_interpolator.hpp @@ -1,13 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/uniform_iinterpolator.hpp +// File : scalfmm/interpolation/uniform/uniform_interpolator.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_UNIFORM_UNIFORM_INTERPOLATOR_HPP #define SCALFMM_INTERPOLATION_UNIFORM_UNIFORM_INTERPOLATOR_HPP -#include <array> -#include <vector> - #include <xsimd/xsimd.hpp> #include <xtensor-fftw/basic.hpp> #include <xtensor/xarray.hpp> @@ -36,8 +33,19 @@ #include <omp.h> #endif +#include <array> +#include <vector> + namespace scalfmm::interpolation { + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam FarFieldMatrixKernel + * @tparam Settings + */ template<typename ValueType, std::size_t Dimension, typename FarFieldMatrixKernel, typename Settings> struct uniform_interpolator : public impl::interpolator<uniform_interpolator<ValueType, Dimension, FarFieldMatrixKernel, Settings>> @@ -59,22 +67,56 @@ namespace scalfmm::interpolation using base_interpolator_type::base_interpolator_type; using base_m2l_handler_type::base_m2l_handler_type; + /** + * @brief Construct a new uniform interpolator object + * + */ uniform_interpolator() = delete; + + /** + * @brief Construct a new uniform interpolator object + * + * @param other + */ uniform_interpolator(uniform_interpolator const& other) = delete; + + /** + * @brief Construct a new uniform interpolator object + * + */ uniform_interpolator(uniform_interpolator&&) noexcept = delete; + + /** + * @brief + * + * @return uniform_interpolator& + */ auto operator=(uniform_interpolator const&) noexcept -> uniform_interpolator& = delete; + + /** + * @brief + * + * @return uniform_interpolator& + */ auto operator=(uniform_interpolator&&) noexcept -> uniform_interpolator& = delete; + + /** + * @brief Destroy the uniform interpolator object + * + */ ~uniform_interpolator() = default; - /// @brief - /// @param far_field - /// @param order - /// @param tree_height - /// @param root_cell_width - /// @param cell_width_extension - uniform_interpolator(matrix_kernel_type const& far_field, size_type order, size_type tree_height = 3, - value_type root_cell_width = value_type(1.), - value_type cell_width_extension = value_type(0.)) + /** + * @brief Construct a new uniform interpolator object + * + * @param far_field + * @param order + * @param tree_height + * @param root_cell_width + * @param cell_width_extension + */ + uniform_interpolator(matrix_kernel_type const& far_field, size_type order, size_type tree_height, + value_type root_cell_width, value_type cell_width_extension = value_type(0.)) : base_interpolator_type(order, tree_height, root_cell_width, cell_width_extension, true) , base_m2l_handler_type(far_field, roots_impl(), tree_height, root_cell_width, cell_width_extension, true) { @@ -82,22 +124,38 @@ namespace scalfmm::interpolation base_m2l_handler_type::initialize(order, root_cell_width, tree_height); } - /// @brief - /// @param order - /// @param tree_height - /// @param root_cell_width - /// @param cell_width_extension - uniform_interpolator(size_type order, size_type tree_height = 3, value_type root_cell_width = value_type(1.), + /** + * @brief Construct a new uniform interpolator object + * + * @param order + * @param tree_height + * @param root_cell_width + * @param cell_width_extension + */ + uniform_interpolator(size_type order, size_type tree_height, value_type root_cell_width, value_type cell_width_extension = value_type(0.)) : uniform_interpolator(matrix_kernel_type{}, order, tree_height, root_cell_width, cell_width_extension) { } + /** + * @brief + * + * @return auto + */ [[nodiscard]] inline auto roots_impl() const { return xt::linspace(value_type(-1.), value_type(1), this->order()); } + /** + * @brief + * + * @tparam ComputationType + * @param x + * @param n + * @return ComputationType + */ template<typename ComputationType> [[nodiscard]] inline auto polynomials_impl(ComputationType x, std::size_t n) const -> ComputationType { @@ -146,6 +204,17 @@ namespace scalfmm::interpolation L *= scale; return L; } + + /** + * @brief + * + * @tparam VectorType + * @tparam ComputationType + * @tparam Dim + * @param all_poly + * @param x + * @param order + */ template<typename VectorType, typename ComputationType, std::size_t Dim> inline auto fill_all_polynomials_impl(VectorType& all_poly, container::point<ComputationType, Dim> x, std::size_t order) const -> void @@ -167,6 +236,15 @@ namespace scalfmm::interpolation // } // return std::move(all_poly); } + + /** + * @brief + * + * @tparam ComputationType + * @param x + * @param n + * @return ComputationType + */ template<typename ComputationType> [[nodiscard]] inline auto derivative_impl(ComputationType x, std::size_t n) const -> ComputationType { @@ -200,27 +278,65 @@ namespace scalfmm::interpolation NdL += tmpNdL; DdL *= roots_n - roots_p; } // endif - } // p + } // p return NdL / DdL; } + /** + * @brief + * + * @param order + * @return xt::xarray<value_type> + */ [[nodiscard]] inline auto generate_weights_impl(std::size_t order) const -> xt::xarray<value_type> { return xt::xarray<value_type>(std::vector{math::pow(order, dimension)}, value_type(1.)); } }; - // traits class to register types inside the interpolator generic class + /** + * @brief Traits class to register types inside the interpolator generic class + * + * @tparam ValueType + * @tparam Dimension + * @tparam FarFieldMatrixKernel + * @tparam Settings + */ template<typename ValueType, std::size_t Dimension, typename FarFieldMatrixKernel, typename Settings> struct interpolator_traits<uniform_interpolator<ValueType, Dimension, FarFieldMatrixKernel, Settings>> { using value_type = ValueType; using matrix_kernel_type = FarFieldMatrixKernel; + + /** + * @brief + * + */ static constexpr std::size_t dimension = Dimension; + + /** + * @brief + * + */ static constexpr std::size_t kn = matrix_kernel_type::kn; + + /** + * @brief + * + */ static constexpr std::size_t km = matrix_kernel_type::km; + + /** + * @brief + * + */ static constexpr bool enable_symmetries = (dimension < 4) ? true : false; + + /** + * @brief + * + */ static constexpr bool symmetry_support{ (enable_symmetries && (matrix_kernel_type::symmetry_tag == matrix_kernels::symmetry::symmetric))}; @@ -244,8 +360,16 @@ namespace scalfmm::interpolation using k_tensor_type = xt::xarray<value_type>; }; - // uniform_interpolator is a CRTP based class - // meaning that it implements the functions needed by the interpolator class + /** + * @brief + * + * uniform_interpolator is a CRTP based class + * meaning that it implements the functions needed by the interpolator class + * + * @tparam ValueType + * @tparam Dimension + * @tparam FarFieldMatrixKernel + */ template<typename ValueType, std::size_t Dimension, typename FarFieldMatrixKernel> struct uniform_interpolator<ValueType, Dimension, FarFieldMatrixKernel, options::fft_> : public impl::interpolator<uniform_interpolator<ValueType, Dimension, FarFieldMatrixKernel, options::fft_>> @@ -282,11 +406,43 @@ namespace scalfmm::interpolation using base_interpolator_type::base_interpolator_type; using base_m2l_handler_type::base_m2l_handler_type; + /** + * @brief Construct a new uniform interpolator object + * + */ uniform_interpolator() = delete; + + /** + * @brief Construct a new uniform interpolator object + * + * @param other + */ uniform_interpolator(uniform_interpolator const& other) = delete; + + /** + * @brief Construct a new uniform interpolator object + * + */ uniform_interpolator(uniform_interpolator&&) noexcept = delete; + + /** + * @brief + * + * @return uniform_interpolator& + */ auto operator=(uniform_interpolator const&) noexcept -> uniform_interpolator& = delete; + + /** + * @brief + * + * @return uniform_interpolator& + */ auto operator=(uniform_interpolator&&) noexcept -> uniform_interpolator& = delete; + + /** + * @brief Destroy the uniform interpolator object + * + */ ~uniform_interpolator() { for(auto& fftw_ptr: fft_handler) @@ -295,9 +451,17 @@ namespace scalfmm::interpolation } } - uniform_interpolator(matrix_kernel_type const& far_field, size_type order, size_type tree_height = 3, - value_type root_cell_width = value_type(1.), - value_type cell_width_extension = value_type(0.)) + /** + * @brief Construct a new uniform interpolator object + * + * @param far_field + * @param order + * @param tree_height + * @param root_cell_width + * @param cell_width_extension + */ + uniform_interpolator(matrix_kernel_type const& far_field, size_type order, size_type tree_height, + value_type root_cell_width, value_type cell_width_extension = value_type(0.)) : base_interpolator_type(order, tree_height, root_cell_width, cell_width_extension, true) , base_m2l_handler_type(far_field, base_interpolator_type::roots(), tree_height, root_cell_width, cell_width_extension, true) @@ -324,16 +488,30 @@ namespace scalfmm::interpolation base_m2l_handler_type::initialize(order, root_cell_width, tree_height); } - uniform_interpolator(size_type order, size_type tree_height = 3, value_type root_cell_width = value_type(1.), + /** + * @brief Construct a new uniform interpolator object + * + * @param order + * @param tree_height + * @param root_cell_width + * @param cell_width_extension + */ + uniform_interpolator(size_type order, size_type tree_height, value_type root_cell_width, value_type cell_width_extension = value_type(0.)) : uniform_interpolator(matrix_kernel_type{}, order, tree_height, root_cell_width, cell_width_extension) { } + /** + * @brief + * + * @return auto + */ [[nodiscard]] inline auto roots_impl() const { return xt::linspace(value_type(-1.), value_type(1), this->order()); } + /** * @brief compute the nth lagrange function at point x * @@ -374,6 +552,17 @@ namespace scalfmm::interpolation return L; } + + /** + * @brief + * + * @tparam VectorType + * @tparam ComputationType + * @tparam Dim + * @param all_poly + * @param x + * @param order + */ template<typename VectorType, typename ComputationType, std::size_t Dim> inline auto fill_all_polynomials_impl(VectorType& all_poly, container::point<ComputationType, Dim>& x, std::size_t order) const -> void @@ -402,10 +591,11 @@ namespace scalfmm::interpolation * The derivative uses the following formula * \f$ L'_j(x) = \frac{ \sum_{i = 0, i \ne j}^{N-1}{\prod_{k = 0, k \ne i, k \ne j}^{N-1}{x - x_k} } }{\prod_{m * = 0, m \ne j}^{N - 1}{x_j - x_m}} \f$ + * + * @tparam ComputationType * @param x the points * @param n the order * @return ComputationType - * */ template<typename ComputationType> [[nodiscard]] inline auto derivative_impl(ComputationType x, std::size_t n) const -> ComputationType @@ -440,11 +630,19 @@ namespace scalfmm::interpolation NdL += tmpNdL; DdL *= roots_n - roots_p; } // endif - } // p + } // p return NdL / DdL; } + /** + * @brief + * + * @tparam ComputationType + * @param x + * @param n + * @return ComputationType + */ template<typename ComputationType> [[nodiscard]] inline auto derivative_impl1(ComputationType x, std::size_t n) const -> ComputationType { @@ -465,7 +663,11 @@ namespace scalfmm::interpolation return L * sum; } - // Returns the buffers initialized for the optimized fft + /** + * @brief Returns the buffers initialized for the optimized fft + * + * @return buffer_type + */ [[nodiscard]] inline auto buffer_initialization_impl() const -> buffer_type { // shape for the output fft is [2*order-1, ..., order] @@ -476,6 +678,11 @@ namespace scalfmm::interpolation return buffer_type(buffer_shape_type{}, buffer_inner_type(shape, 0.)); } + /** + * @brief + * + * @return std::vector<std::size_t> + */ [[nodiscard]] inline auto buffer_shape_impl() const -> std::vector<std::size_t> { std::vector<std::size_t> shape(dimension, 2 * this->order() - 1); @@ -483,6 +690,11 @@ namespace scalfmm::interpolation return shape; } + /** + * @brief + * + * @param buffers + */ inline auto buffer_reset_impl(buffer_type& buffers) const -> void { for(std::size_t n{0}; n < kn; ++n) @@ -544,8 +756,8 @@ namespace scalfmm::interpolation inline auto apply_m2l_impl(Cell const& source_cell, [[maybe_unused]] Cell& target_cell, [[maybe_unused]] buffer_type& products, interaction_matrix_type const& k, ArrayScaleFactor scale_factor, [[maybe_unused]] std::size_t n, - [[maybe_unused]] std::size_t m, [[maybe_unused]] size_type thread_id = 0) const - -> void + [[maybe_unused]] std::size_t m, + [[maybe_unused]] size_type thread_id = 0) const -> void { // get the transformed multipoles (only available for uniform approximation) auto const& transformed_multipoles = source_cell.ctransformed_multipoles(); @@ -558,6 +770,7 @@ namespace scalfmm::interpolation * @brief postprocessing locals * * We apply the inverse fft to construct the real local arrays (for all outputs) of the current_cell + * * @tparam Cell * @param current_cell the cell containing the local array to post-process * @param products the spectral coefficients of the local array @@ -585,13 +798,29 @@ namespace scalfmm::interpolation } } + /** + * @brief + * + * @return k_tensor_type + */ inline auto initialize_k_impl() const -> k_tensor_type { return k_tensor_type{}; } - // This function generates the circulant tensors for generating interaction matrixes. + /** + * @brief This function generates the circulant tensors for generating interaction matrixes. + * + * @tparam TensorViewX + * @tparam TensorViewY + * @param X + * @param Y + * @param n + * @param m + * @param thread_id + * @return k_tensor_type + */ template<typename TensorViewX, typename TensorViewY> - [[nodiscard]] inline auto generate_matrix_k_impl(TensorViewX&& X, TensorViewY&& Y, std::size_t n, std::size_t m, - [[maybe_unused]] size_type thread_id = 0) const - -> k_tensor_type + [[nodiscard]] inline auto + generate_matrix_k_impl(TensorViewX&& X, TensorViewY&& Y, std::size_t n, std::size_t m, + [[maybe_unused]] size_type thread_id = 0) const -> k_tensor_type { build<value_type, dimension, dimension> build_c{}; // generate the circulant tensor @@ -608,6 +837,12 @@ namespace scalfmm::interpolation } private: + /** + * @brief Set the factorials object + * + * @param order + * @return std::vector<value_type> + */ inline auto set_factorials(size_type order) -> std::vector<value_type> { std::vector<value_type> factorials(order); @@ -619,6 +854,13 @@ namespace scalfmm::interpolation return factorials; } + + /** + * @brief Set the omn object + * + * @param order + * @return std::vector<value_type> + */ inline auto set_omn(size_type order) -> std::vector<value_type> { std::vector<value_type> omn(order, value_type(1.0)); @@ -631,12 +873,33 @@ namespace scalfmm::interpolation } return omn; } + + /** + * @brief + * + */ std::vector<value_type> m_factorials; + + /** + * @brief + * + */ std::vector<value_type> m_omn; + + /** + * @brief + * + */ std::vector<fftw::fft<value_type, dimension>*> fft_handler; }; - // traits class to register types inside the interpolator generic class + /** + * @brief Traits class to register types inside the interpolator generic class. + * + * @tparam ValueType + * @tparam Dimension + * @tparam FarFieldMatrixKernel + */ template<typename ValueType, std::size_t Dimension, typename FarFieldMatrixKernel> struct interpolator_traits<uniform_interpolator<ValueType, Dimension, FarFieldMatrixKernel, options::fft_>> { diff --git a/include/scalfmm/interpolation/uniform/uniform_storage.hpp b/include/scalfmm/interpolation/uniform/uniform_storage.hpp index 65cc7f25a8fbf43ce5e61327c7cb64b8e0d531b2..98a492b0fdece77c16fb9b6d63ae2d93ed4e1d26 100644 --- a/include/scalfmm/interpolation/uniform/uniform_storage.hpp +++ b/include/scalfmm/interpolation/uniform/uniform_storage.hpp @@ -1,26 +1,51 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/uniform.hpp +// File : scalfmm/interpolation/uniform/uniform_storage.hpp // -------------------------------- #ifndef SCALFMM_INTERPOLATION_UNIFORM_UNIFORM_STORAGE_HPP #define SCALFMM_INTERPOLATION_UNIFORM_UNIFORM_STORAGE_HPP #include "scalfmm/container/variadic_adaptor.hpp" #include "scalfmm/memory/storage.hpp" + #include <complex> namespace scalfmm::component { - // This is the specialization for the uniform interpolator that - // requires to store the transformed multipoles. + /** + * @brief + * + * This is the specialization for the uniform interpolator that + * requires to store the transformed multipoles. + * + * @tparam ValueType + * @tparam Dimension + * @tparam Inputs + * @tparam Outputs + */ template<typename ValueType, std::size_t Dimension, std::size_t Inputs, std::size_t Outputs> struct alignas(XTENSOR_FIXED_ALIGN) uniform_fft_storage - : public memory::aggregate_storage<memory::multipoles_storage<ValueType, Dimension, Inputs>, - memory::locals_storage<ValueType, Dimension, Outputs>, - memory::transformed_multipoles_storage<std::complex<ValueType>, Dimension, Inputs>> + : public memory::aggregate_storage< + memory::multipoles_storage<ValueType, Dimension, Inputs>, + memory::locals_storage<ValueType, Dimension, Outputs>, + memory::transformed_multipoles_storage<std::complex<ValueType>, Dimension, Inputs>> { + /** + * @brief + * + */ static constexpr std::size_t dimension = Dimension; + + /** + * @brief + * + */ static constexpr std::size_t inputs_size = Inputs; + + /** + * @brief + * + */ static constexpr std::size_t outputs_size = Outputs; using value_type = ValueType; @@ -43,17 +68,59 @@ namespace scalfmm::component using base_type::base_type; + /** + * @brief Construct a new uniform fft storage object + * + */ uniform_fft_storage() = default; + + /** + * @brief Construct a new uniform fft storage object + * + */ uniform_fft_storage(uniform_fft_storage const&) = default; + + /** + * @brief Construct a new uniform fft storage object + * + */ uniform_fft_storage(uniform_fft_storage&&) noexcept = default; + + /** + * @brief + * + * @return uniform_fft_storage& + */ inline auto operator=(uniform_fft_storage const&) -> uniform_fft_storage& = default; + + /** + * @brief + * + * @return uniform_fft_storage& + */ inline auto operator=(uniform_fft_storage&&) noexcept -> uniform_fft_storage& = default; + + /** + * @brief Destroy the uniform fft storage object + * + */ ~uniform_fft_storage() = default; + /** + * @brief + * + * @return transformed_container_type const& + */ auto transfer_multipoles() const noexcept -> transformed_container_type const& { return base_type::transformed_multipoles(); } + + /** + * @brief + * + * @return transformed_container_type& + */ auto transfer_multipoles() noexcept -> transformed_container_type& { return base_type::transformed_multipoles(); diff --git a/include/scalfmm/lists/lists.hpp b/include/scalfmm/lists/lists.hpp index a9a9cc6153eb1c0c93e01f77dbfee62309036e9b..efa0499916b9c10c4ab31829350e55934832aa3f 100644 --- a/include/scalfmm/lists/lists.hpp +++ b/include/scalfmm/lists/lists.hpp @@ -1,3 +1,7 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/lists/lists.hpp +// -------------------------------- #pragma once #include "scalfmm/lists/omp.hpp" diff --git a/include/scalfmm/lists/omp.hpp b/include/scalfmm/lists/omp.hpp index 7b059e449b58f53b7b438867e5310f7213da9b9e..d89fe2fb494789108d2ee8ea873cc7b6f2e938ed 100644 --- a/include/scalfmm/lists/omp.hpp +++ b/include/scalfmm/lists/omp.hpp @@ -1,31 +1,38 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/lists/omp.hpp +// -------------------------------- #ifndef SCALFMM_LISTS_OMP_HPP #define SCALFMM_LISTS_OMP_HPP #define SCALFMM_LISTS_OMP + #include "scalfmm/algorithms/omp/priorities.hpp" -#include <scalfmm/lists/utils.hpp> -#include <scalfmm/utils/io_helpers.hpp> +#include "scalfmm/lists/utils.hpp" +#include "scalfmm/utils/io_helpers.hpp" namespace scalfmm::list::omp { /** * @brief Construct the P2P interaction list for the target tree * - * @param[in] tree_source the tree containing the sources - * @param[inout] tree_target the tree containing the targets. + * @tparam SourceTreeType + * @tparam TargetTreeType + * @param[in] source_tree the tree containing the sources + * @param[inout] target_tree the tree containing the targets. * @param[in] neighbour_separation separation criterion use to separate teh near and the far field * @param[in] mutual boolean to specify if the direct pass use a symmetric algorithm (mutual interactions) */ - template<typename TREE_S, typename TREE_T> - inline auto build_p2p_interaction_list(TREE_S const& tree_source, TREE_T& tree_target, + template<typename SourceTreeType, typename TargetTreeType> + inline auto build_p2p_interaction_list(SourceTreeType const& source_tree, TargetTreeType& target_tree, const int& neighbour_separation, const bool mutual) -> void { // We iterate on the leaves // true if source == target bool source_target{false}; - if constexpr(std::is_same_v<std::decay_t<TREE_S>, std::decay_t<TREE_T>>) + if constexpr(std::is_same_v<std::decay_t<SourceTreeType>, std::decay_t<TargetTreeType>>) { - source_target = (&tree_source == &tree_target); + source_target = (&source_tree == &target_tree); } if((!source_target) && mutual) { @@ -33,14 +40,14 @@ namespace scalfmm::list::omp " Mutual set to true is prohibited when the sources are different from the targets.\n"); } // std::cout << std::boolalpha << "source == target " << source_target << std::endl; - const auto& period = tree_target.box().get_periodicity(); - const auto leaf_level = tree_target.leaf_level(); - auto begin_of_source_groups = std::get<0>(tree_source.begin()); - auto end_of_source_groups = std::get<0>(tree_source.end()); + const auto& period = target_tree.box().get_periodicity(); + const auto leaf_level = target_tree.leaf_level(); + auto begin_of_source_groups = std::get<0>(source_tree.begin()); + auto end_of_source_groups = std::get<0>(source_tree.end()); // Iterate on the group of leaves I own component::for_each( - tree_target.begin_mine_leaves(), tree_target.end_mine_leaves(), + target_tree.begin_mine_leaves(), target_tree.end_mine_leaves(), [&period, &neighbour_separation, &begin_of_source_groups, &end_of_source_groups, &leaf_level, mutual, source_target](auto& group_target) { @@ -72,37 +79,40 @@ namespace scalfmm::list::omp } }); #pragma omp taskwait - tree_target.is_interaction_p2p_lists_built() = true; + target_tree.is_interaction_p2p_lists_built() = true; } + /** * @brief Construct the M2L interaction list for the target tree * - * @param[in] tree_source the tree containing the sources - * @param[inout] tree_target the tree containing the targets. + * @tparam SourceTreeType + * @tparam TargetTreeType + * @param[in] source_tree the tree containing the sources + * @param[inout] target_tree the tree containing the targets. * @param[in] neighbour_separation separation criterion use to separate teh near and the far field */ - template<typename TREE_S, typename TREE_T> - inline auto build_m2l_interaction_list(TREE_S& tree_source, TREE_T& tree_target, const int& neighbour_separation) - -> void + template<typename SourceTreeType, typename TargetTreeType> + inline auto build_m2l_interaction_list(SourceTreeType& source_tree, TargetTreeType& target_tree, + const int& neighbour_separation) -> void { // Iterate on the group of leaves // here we get the first level of cells (leaf_level up to the top_level) - auto tree_height = tree_target.height(); + auto tree_height = target_tree.height(); int leaf_level = int(tree_height) - 1; - auto const& period = tree_target.box().get_periodicity(); - auto const& top_level = tree_target.box().is_periodic() ? 1 : 2; + auto const& period = target_tree.box().get_periodicity(); + auto const& top_level = target_tree.box().is_periodic() ? 1 : 2; // - // auto cell_source_level_it = std::get<1>(tree_source.begin()) + leaf_level; + // auto cell_source_level_it = std::get<1>(source_tree.begin()) + leaf_level; for(int level = leaf_level; level >= top_level; --level) { // target - auto group_of_cell_begin = tree_target.begin_mine_cells(level); - auto group_of_cell_end = tree_target.end_mine_cells(level); + auto group_of_cell_begin = target_tree.begin_mine_cells(level); + auto group_of_cell_end = target_tree.end_mine_cells(level); // source // auto begin_of_source_cell_groups = std::begin(*cell_source_level_it); // auto end_of_source_cell_groups = std::end(*cell_source_level_it); - auto begin_of_source_cell_groups = tree_source.begin_cells(level); - auto end_of_source_cell_groups = tree_source.end_cells(level); + auto begin_of_source_cell_groups = source_tree.begin_cells(level); + auto end_of_source_cell_groups = source_tree.end_cells(level); // loop on target group component::for_each(group_of_cell_begin, group_of_cell_end, @@ -113,7 +123,7 @@ namespace scalfmm::list::omp #pragma omp task untied default(none) \ firstprivate(group_target, begin_of_source_cell_groups, end_of_source_cell_groups, level) \ - shared(period, neighbour_separation) priority(prio) + shared(period, neighbour_separation) priority(prio) { // loop on target cell group component::for_each( @@ -131,30 +141,32 @@ namespace scalfmm::list::omp // --cell_source_level_it; } #pragma omp taskwait - tree_target.is_interaction_m2l_lists_built() = true; + target_tree.is_interaction_m2l_lists_built() = true; } /** - * @brief Construct the P2P and the M2L interaction lists for the target tree + * @brief Construct the P2P and the M2L interaction lists for the target tree. * - * @param[in] tree_source the tree containing the sources - * @param[inout] tree_target the tree containing the targets. - * @param[in] neighbour_separation separation criterion use to separate teh near and the far field - * @param[in] mutual boolean to specify if the direct pass use a symmetric algorithm (mutual interactions) + * @tparam SourceTreeType + * @tparam TargetTreeType + * @param[in] source_tree the tree containing the sources. + * @param[inout] target_tree the tree containing the targets. + * @param[in] neighbour_separation separation criterion use to separate teh near and the far field. + * @param[in] mutual boolean to specify if the direct pass use a symmetric algorithm (mutual interactions). */ - template<typename TREE_S, typename TREE_T> - inline auto build_interaction_lists(TREE_S& tree_source, TREE_T& tree_target, const int& neighbour_separation, - const bool mutual) -> void + template<typename SourceTreeType, typename TargetTreeType> + inline auto build_interaction_lists(SourceTreeType& source_tree, TargetTreeType& target_tree, + const int& neighbour_separation, const bool mutual) -> void { -#pragma omp parallel default(none) shared(tree_source, tree_target, neighbour_separation, mutual) +#pragma omp parallel default(none) shared(source_tree, target_tree, neighbour_separation, mutual) { #pragma omp single nowait { - build_m2l_interaction_list(tree_source, tree_target, neighbour_separation); + build_m2l_interaction_list(source_tree, target_tree, neighbour_separation); } #pragma omp single { - build_p2p_interaction_list(tree_source, tree_target, neighbour_separation, mutual); + build_p2p_interaction_list(source_tree, target_tree, neighbour_separation, mutual); } } } diff --git a/include/scalfmm/lists/policies.hpp b/include/scalfmm/lists/policies.hpp index af03de0d1fc1751e2eec3a92e099cb04f311ee3d..953f57471fc98e0d28761d7881fcf173d45cbfe3 100644 --- a/include/scalfmm/lists/policies.hpp +++ b/include/scalfmm/lists/policies.hpp @@ -1,16 +1,15 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/omp/priorities.hpp +// File : scalfmm/lists/policies.hpp // -------------------------------- #ifndef SCALFMM_LISTS_POLICIES_HPP #define SCALFMM_LISTS_POLICIES_HPP - namespace scalfmm::list -{ +{ /** - * @brief The different policies available for algorithms (lists construction) - * + * @brief The different policies available for algorithms (lists construction) + * */ struct policies { @@ -18,6 +17,6 @@ namespace scalfmm::list static constexpr int omp = 2; ///< for OpenMP algorithm static constexpr int starpu = 3; ///< for StarPU runtime algorithm (not yet implemented) }; -} +} // namespace scalfmm::list -#endif // SCALFMM_LISTS_POLICIES_HPP +#endif // SCALFMM_LISTS_POLICIES_HPP diff --git a/include/scalfmm/lists/sequential.hpp b/include/scalfmm/lists/sequential.hpp index 9490cbfe8d4a00f3aede02af54fee0db7c5b5f4a..6f54967cd71b73bba2fd6b9bb0c36200ad9367a5 100644 --- a/include/scalfmm/lists/sequential.hpp +++ b/include/scalfmm/lists/sequential.hpp @@ -1,30 +1,36 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/lists/sequential.hpp +// -------------------------------- #ifndef SCALFMM_LISTS_SEQUENTIAL_HPP #define SCALFMM_LISTS_SEQUENTIAL_HPP -#include <scalfmm/lists/utils.hpp> -#include <scalfmm/utils/io_helpers.hpp> +#include "scalfmm/lists/utils.hpp" +#include "scalfmm/utils/io_helpers.hpp" namespace scalfmm::list::sequential { /** * @brief Construct the P2P interaction list for the target tree * - * @param[in] tree_source the tree containing the sources - * @param[inout] tree_target the tree containing the targets. + * @tparam SourceTreeType + * @tparam TargetTreeType + * @param[in] source_tree the tree containing the sources + * @param[inout] target_tree the tree containing the targets. * @param[in] neighbour_separation separation criterion use to separate teh near and the far field * @param[in] mutual boolean to specify if the direct pass use a symmetric algorithm (mutual interactions) */ - template<typename TREE_S, typename TREE_T> - inline auto build_p2p_interaction_list(TREE_S const& tree_source, TREE_T& tree_target, + template<typename SourceTreeType, typename TargetTreeType> + inline auto build_p2p_interaction_list(SourceTreeType const& source_tree, TargetTreeType& target_tree, const int& neighbour_separation, const bool mutual) -> void { // We iterate on the leaves // true if source == target bool source_target{false}; // - if constexpr(std::is_same_v<std::decay_t<TREE_S>, std::decay_t<TREE_T>>) + if constexpr(std::is_same_v<std::decay_t<SourceTreeType>, std::decay_t<TargetTreeType>>) { - source_target = (&tree_source == &tree_target); + source_target = (&source_tree == &target_tree); } if((!source_target) && mutual) { @@ -32,15 +38,15 @@ namespace scalfmm::list::sequential " Mutual set to true is prohibited when the sources are different from the targets.\n"); } //std::cout << std::boolalpha << "source == target " << source_target << std::endl; - const auto& period = tree_target.box().get_periodicity(); - const auto leaf_level = tree_target.leaf_level(); - auto begin_of_source_groups = std::get<0>(tree_source.begin()); - auto end_of_source_groups = std::get<0>(tree_source.end()); + const auto& period = target_tree.box().get_periodicity(); + const auto leaf_level = target_tree.leaf_level(); + auto begin_of_source_groups = std::get<0>(source_tree.begin()); + auto end_of_source_groups = std::get<0>(source_tree.end()); // Iterate on the group of leaves I own component::for_each( - // std::get<0>(tree_target.begin()), std::get<0>(tree_target.end()), - tree_target.begin_mine_leaves(), tree_target.end_mine_leaves(), + // std::get<0>(target_tree.begin()), std::get<0>(target_tree.end()), + target_tree.begin_mine_leaves(), target_tree.end_mine_leaves(), [&period, &neighbour_separation, &begin_of_source_groups, &end_of_source_groups, &leaf_level, mutual, source_target](auto& group_target) { @@ -58,7 +64,7 @@ namespace scalfmm::list::sequential ++index_in_group; }); // - // if constexpr(!std::is_same_v<TREE_S, TREE_T>) + // if constexpr(!std::is_same_v<SourceTreeType, TargetTreeType>) { if(mutual) { @@ -68,41 +74,45 @@ namespace scalfmm::list::sequential } }); - tree_target.is_interaction_p2p_lists_built() = true; + target_tree.is_interaction_p2p_lists_built() = true; } - /** + + /** * @brief Construct the M2L interaction list for the target tree * - * @param[in] tree_source the tree containing the sources - * @param[inout] tree_target the tree containing the targets. + * @tparam SourceTreeType + * @tparam TargetTreeType + * @param[in] source_tree the tree containing the sources + * @param[inout] target_tree the tree containing the targets. * @param[in] neighbour_separation separation criterion use to separate teh near and the far field */ - template<typename TREE_S, typename TREE_T> - inline auto build_m2l_interaction_list(TREE_S& tree_source, TREE_T& tree_target, const int& neighbour_separation) -> void + template<typename SourceTreeType, typename TargetTreeType> + inline auto build_m2l_interaction_list(SourceTreeType& source_tree, TargetTreeType& target_tree, + const int& neighbour_separation) -> void { // Iterate on the group of leaves // here we get the first level of cells (leaf_level up to the top_level) - auto tree_height = tree_target.height(); + auto tree_height = target_tree.height(); int leaf_level = int(tree_height) - 1; - auto const& period = tree_target.box().get_periodicity(); - auto const top_level = tree_target.box().is_periodic() ? 1 : 2; + auto const& period = target_tree.box().get_periodicity(); + auto const top_level = target_tree.box().is_periodic() ? 1 : 2; // for(int level = leaf_level; level >= top_level; --level) { // target - auto group_of_cell_begin = tree_target.begin_mine_cells(level); - auto group_of_cell_end = tree_target.end_mine_cells(level); + auto group_of_cell_begin = target_tree.begin_mine_cells(level); + auto group_of_cell_end = target_tree.end_mine_cells(level); // source - auto begin_of_source_cell_groups = tree_source.begin_cells(level); - auto end_of_source_cell_groups = tree_source.end_cells(level); + auto begin_of_source_cell_groups = source_tree.begin_cells(level); + auto end_of_source_cell_groups = source_tree.end_cells(level); // loop on target group - component::for_each( - group_of_cell_begin, group_of_cell_end, - [begin_of_source_cell_groups, end_of_source_cell_groups, level, &period, - &neighbour_separation](auto& group_target) - { - // loop on target cell group - component::for_each(std::begin(*group_target), std::end(*group_target), + component::for_each(group_of_cell_begin, group_of_cell_end, + [begin_of_source_cell_groups, end_of_source_cell_groups, level, &period, + &neighbour_separation](auto& group_target) + { + // loop on target cell group + component::for_each( + std::begin(*group_target), std::end(*group_target), [&group_target, begin_of_source_cell_groups, end_of_source_cell_groups, level, &period, &neighbour_separation](auto& cell_target) { @@ -110,30 +120,27 @@ namespace scalfmm::list::sequential *group_target, cell_target, begin_of_source_cell_groups, end_of_source_cell_groups, level, period, neighbour_separation); }); - }); + }); } - tree_target.is_interaction_m2l_lists_built() = true; + target_tree.is_interaction_m2l_lists_built() = true; } /** * @brief Construct the P2P and the M2L interaction lists for the target tree * - * @param[in] tree_source the tree containing the sources - * @param[inout] tree_target the tree containing the targets. + * @tparam SourceTreeType + * @tparam TargetTreeType + * @param[in] source_tree the tree containing the sources + * @param[inout] target_tree the tree containing the targets. * @param[in] neighbour_separation separation criterion use to separate teh near and the far field * @param[in] mutual boolean to specify if the direct pass use a symmetric algorithm (mutual interactions) */ - template<typename TREE_S, typename TREE_T> - inline auto build_interaction_lists(TREE_S& tree_source, TREE_T& tree_target, const int& neighbour_separation, - const bool mutual) -> void + template<typename SourceTreeType, typename TargetTreeType> + inline auto build_interaction_lists(SourceTreeType& source_tree, TargetTreeType& target_tree, + const int& neighbour_separation, const bool mutual) -> void { - // std::cout << "start sequential::build_interaction_lists \n"; - - build_p2p_interaction_list(tree_source, tree_target, neighbour_separation, mutual); - // std::cout << " build_m2l_interaction_list\n"; - - build_m2l_interaction_list(tree_source, tree_target, neighbour_separation); - // std::cout << "end sequential::build_interaction_lists \n"; + build_p2p_interaction_list(source_tree, target_tree, neighbour_separation, mutual); + build_m2l_interaction_list(source_tree, target_tree, neighbour_separation); } } // namespace scalfmm::list::sequential diff --git a/include/scalfmm/lists/utils.hpp b/include/scalfmm/lists/utils.hpp index 9e0c0bb0bbb9f3dc55ce74e31d63dec263432d3f..57643087baeb2866e26034091ac4713b337e3e6f 100644 --- a/include/scalfmm/lists/utils.hpp +++ b/include/scalfmm/lists/utils.hpp @@ -1,15 +1,10 @@ // -------------------------------- -// -------------------------------- // See LICENCE file at project root -// File : scalfmm/tree/interaction_list.hpp +// File : scalfmm/lists/utils.hpp // -------------------------------- #ifndef SCALFMM_LISTS_UTIL_HPP #define SCALFMM_LISTS_UTIL_HPP -#include <algorithm> -#include <iostream> -#include <tuple> - #include "scalfmm/operators/tags.hpp" #include "scalfmm/tree/cell.hpp" #include "scalfmm/tree/group.hpp" @@ -19,20 +14,27 @@ #include <cpp_tools/colors/colorized.hpp> +#include <algorithm> +#include <iostream> +#include <tuple> + namespace scalfmm::list { /** * @brief find the iterator of the component in (begin,end) with index idx * + * @tparam GroupIteratorType + * @tparam IndexType * @param begin start iterator on the groups * @param end end iterator on the groups * @param idx index to find * @return tuple containing {position in the groups, iterator} */ - template<typename group_iterator_type, typename Index_type> - inline auto find_component_group_near(group_iterator_type const& begin, group_iterator_type const& end, - Index_type idx) + template<typename GroupIteratorType, typename IndexType> + inline auto find_component_group_near(GroupIteratorType const& begin, GroupIteratorType const& end, IndexType idx) { + using group_iterator_type = GroupIteratorType; + std::int64_t id_group{0}; group_iterator_type found_group_it{}; std::size_t found_group_id{0}; @@ -47,7 +49,6 @@ namespace scalfmm::list { found_group_id = std::distance(b_component_it, e_component_it) - 1; return std::make_tuple(found_group_id, *last_group); - } // Iterate on the group of components for(group_iterator_type it = b_component_it; it != e_component_it; ++it) @@ -66,6 +67,9 @@ namespace scalfmm::list /** * @brief * + * @tparam GroupIteratorType + * @tparam IndexType + * @tparam GroupSourceType * @param begin iterator on the first source group * @param end iterator on the last source group * @param idx current Morton index of the target components (cell/leaf) @@ -74,10 +78,10 @@ namespace scalfmm::list * @param source_eq_target false if source and target particles are different ; true otherwise * @return a tuple containing tuple containing {position in the groups, iterator} */ - template<typename group_iterator_type, typename Index_type, typename group_iterator1_type> - inline auto find_component_group_near(group_iterator_type const& begin, group_iterator_type const& end, - Index_type idx, group_iterator1_type group_source, - std::size_t& group_source_index, bool source_eq_target) + template<typename GroupIteratorType, typename IndexType, typename GroupSourceType> + inline auto find_component_group_near(GroupIteratorType const& begin, GroupIteratorType const& end, IndexType idx, + GroupSourceType group_source, std::size_t& group_source_index, + bool source_eq_target) { if(source_eq_target) { @@ -88,30 +92,31 @@ namespace scalfmm::list ////////////////////////////////////////////////////////////////////////////////// /// Routines to build the M2L interaction list - /// - /// @ingroup update_interaction_list - /// @brief This function takes a target cell and its level in the tree - /// and updates the component's symbolics information : the morton indices - /// of its neighbors and the number of neighbors. It returns the theoretical - /// interaction list of the leaf. - /// - /// @tparam Component - /// @param[in] t tag to select the good algorithm m2l or p2p - /// @param[inout] component (target) cell - /// @param[in] tree_level level in the tree to construct the interation list - /// @param[in] period array of periodicity in each direction - /// @param[in] neighbour_separation distance between neighbors and component - /// - template<typename Component, typename Array> - inline auto build_symbolic_interaction_list(operators::impl::tag_m2l t, Component& component, - std::size_t tree_level, Array const& period, + + /** + * @brief This function takes a target cell and its level in the tree + * and updates the component's symbolics information : the morton indices + * of its neighbors and the number of neighbors. It returns the theoretical + * interaction list of the leaf. + * + * @tparam ComponentType + * @tparam ArrayType + * @param[in] t tag to select the good algorithm m2l or p2p + * @param[inout] component (target) cell + * @param[in] tree_level level in the tree to construct the interation list + * @param[in] period array of periodicity in each direction + * @param[in] neighbour_separation distance between neighbors and component + */ + template<typename ComponentType, typename ArrayType> + inline auto build_symbolic_interaction_list(operators::impl::tag_m2l t, ComponentType& component, + std::size_t tree_level, ArrayType const& period, const int& neighbour_separation) -> void { // Get symbolics infos on the group and the component auto& component_symbolics = component.symbolics(); // Calculate the coordinate of the component from the morton index auto coordinate{ - index::get_coordinate_from_morton_index<Component::dimension>(component_symbolics.morton_index)}; + index::get_coordinate_from_morton_index<ComponentType::dimension>(component_symbolics.morton_index)}; // update component's symbolics information std::tie(component_symbolics.interaction_indexes, component_symbolics.interaction_positions, component_symbolics.number_of_neighbors) = @@ -123,21 +128,21 @@ namespace scalfmm::list * * This function updates the interaction list in the target component symbolics with the source components available * in the current group - + * + * @tparam GroupTree + * @tparam ComponentType * @param[in] group the source group to find the index * @param[inout] target the target cell/leaf * @param[in] morton_index_interaction the morton index we search in group * @param[in] counter_existing_component * @param[in] interaction_index the index in the list of interaction - * * @return true if iterator (morton index) found in the group * @return false otherwise */ - template<typename Group, typename Component> - inline auto get_interacting_component_in(Group& group, Component& target, - std::size_t morton_index_interaction, - std::size_t counter_existing_component, std::size_t interaction_index) - -> bool + template<typename GroupTree, typename ComponentType> + inline auto + get_interacting_component_in(GroupTree& group, ComponentType& target, std::size_t morton_index_interaction, + std::size_t counter_existing_component, std::size_t interaction_index) -> bool { // Get symbolics infos on the group and the component auto& component_symbolics = target.symbolics(); @@ -149,14 +154,29 @@ namespace scalfmm::list group.component_iterator(leaf_position)); } - return (leaf_position != -1) ; + return (leaf_position != -1); } - // This function updates the interaction list in the target component symbolics with the source components out of - // the current group being proceed. - template<typename GroupIterator, typename Component> - inline auto get_interacting_component_out_left(GroupIterator group_begin, GroupIterator group_end, - Component& target, std::size_t morton_index_interaction, + /** + * @brief This function updates the interaction list in the target + * component symbolics with the source components out of the current + * group being proceed. + * + * @tparam GroupIteratorType + * @tparam ComponentType + * @param group_begin + * @param group_end + * @param target + * @param morton_index_interaction + * @param group_index + * @param counter_existing_component + * @param index_interaction + * @param last_component_index + * @return auto + */ + template<typename GroupIteratorType, typename ComponentType> + inline auto get_interacting_component_out_left(GroupIteratorType group_begin, GroupIteratorType group_end, + ComponentType& target, std::size_t morton_index_interaction, std::size_t group_index, std::size_t counter_existing_component, std::size_t index_interaction, std::size_t last_component_index) { @@ -209,11 +229,14 @@ namespace scalfmm::list static_cast<std::size_t>(leaf_position)); } } - // This function updates the interaction list in the target component symbolics with the source components out of - // the current group being processed. + /** - * @brief Get the interacting component out right object + * @brief This function updates the interaction list in the target + * component symbolics with the source components out of the current + * group being processed. * + * @tparam GroupIteratorType + * @tparam ComponentType * @param group_begin * @param group_end * @param target @@ -224,9 +247,9 @@ namespace scalfmm::list * @param last_component_index * @return auto */ - template<typename GroupIterator, typename Component> - inline auto get_interacting_component_out_right(GroupIterator group_begin, GroupIterator group_end, - Component& target, std::size_t morton_index_interaction, + template<typename GroupIteratorType, typename ComponentType> + inline auto get_interacting_component_out_right(GroupIteratorType group_begin, GroupIteratorType group_end, + ComponentType& target, std::size_t morton_index_interaction, std::size_t group_index, std::size_t counter_existing_component, std::size_t index_interaction, std::size_t last_component_index) { @@ -234,8 +257,8 @@ namespace scalfmm::list auto& component_symbolics = target.symbolics(); // Iterators for going left and right auto right_group_it{group_begin}; - long int number_of_groups{std::distance(group_begin, group_end)}; - std::size_t going_right{group_index}; + long int number_of_groups{std::distance(group_begin, group_end)}; + std::size_t going_right{group_index}; bool found_interaction{false}; int leaf_position{}; std::advance(right_group_it, going_right); @@ -272,24 +295,25 @@ namespace scalfmm::list } else { - return std::make_tuple(found_interaction, static_cast<std::size_t>(going_right), static_cast<std::size_t>(leaf_position)); + return std::make_tuple(found_interaction, static_cast<std::size_t>(going_right), + static_cast<std::size_t>(leaf_position)); } } + /** * @brief Build group dependencies for a given target group * - * @tparam GroupIterator - * @tparam Storage + * @tparam GroupIteratorType + * @tparam StorageType * @param grp current target group (containing the locals) * @param begin_of_groups iterator on the source group (containing the mutipoles) * @param group_index * @param last_group_index * @todo Merge begin_of_groups, std::size_t group_index -> std::advance(begin_of_groups, last_group_index) - * */ - template<typename GroupIterator, typename Storage> - void build_m2l_dependencies(scalfmm::component::group<scalfmm::component::cell<Storage>>& grp, - GroupIterator begin_of_groups, std::size_t group_index, + template<typename GroupIteratorType, typename StorageType> + void build_m2l_dependencies(scalfmm::component::group<scalfmm::component::cell<StorageType>>& grp, + GroupIteratorType begin_of_groups, std::size_t group_index, std::size_t const& last_group_index) { #ifdef _OPENMP @@ -297,20 +321,26 @@ namespace scalfmm::list std::advance(found_in_group_it, last_group_index); auto& g_d = grp.symbolics().group_dependencies_m2l; auto ptr = &(*found_in_group_it)->ccomponent(0).cmultipoles(0); - // std ::cout << " add depend " << ptr << std::endl; - // io::print(" g_d ", g_d); - // if(last_group_index != group_index && std::end(g_d) == std::find(std::begin(g_d), std::end(g_d), ptr)) + if(std::end(g_d) == std::find(std::begin(g_d), std::end(g_d), ptr)) { - // std::cout << " add ptr !\n"; g_d.push_back(ptr); - // io::print("g_d ", g_d); } #endif } - // general version - template<typename Group, typename GroupIterator> - void build_m2l_dependencies(Group& group, GroupIterator begin_of_groups, std::size_t group_index, + + /** + * @brief + * + * @tparam GroupType + * @tparam GroupIteratorType + * @param group + * @param begin_of_groups + * @param group_index + * @param last_group_index + */ + template<typename GroupType, typename GroupIteratorType> + void build_m2l_dependencies(GroupType& group, GroupIteratorType begin_of_groups, std::size_t group_index, std::size_t const& last_group_index) { } @@ -319,17 +349,23 @@ namespace scalfmm::list * @brief Updates the list of cell iterators in the M2L interaction list. Also updates dependencies for task-based * calculations * + * @tparam GroupIteratorType + * @tparam TargetGroupType + * @tparam SourceGroupType + * @tparam ComponentType * @param begin_of_groups begin iterator of the source groups * @param end_of_groups end iterator of the source groups * @param group group near or contains the Morton index of the component (target) * @param group_source_index index of the group in the array on group * @param group_target target containing the component (target) * @param cell_target cell or leaf for which we construct the interaction list + * @param m2l_list */ - template<typename GroupIterator, typename GroupT, typename GroupS, typename Component> - inline auto build_interaction_list_iterators(GroupIterator begin_of_groups, GroupIterator end_of_groups, - GroupS& group, std::size_t group_source_index, GroupT& group_target, - Component& cell_target, bool m2l_list) -> void + template<typename GroupIteratorType, typename TargetGroupType, typename SourceGroupType, typename ComponentType> + inline auto build_interaction_list_iterators(GroupIteratorType begin_of_groups, GroupIteratorType end_of_groups, + SourceGroupType& group, std::size_t group_source_index, + TargetGroupType& group_target, ComponentType& cell_target, + bool m2l_list) -> void { // group = source group auto& group_symbolics = group.symbolics(); @@ -343,7 +379,8 @@ namespace scalfmm::list bool first_out_left{true}; int pos{0}; // Loop on the number of neighbors - for(size_t index_interaction = 0; index_interaction < component_symbolics.number_of_neighbors; ++index_interaction) + for(size_t index_interaction = 0; index_interaction < component_symbolics.number_of_neighbors; + ++index_interaction) { bool found{false}; // morton index of the interaction @@ -429,40 +466,6 @@ namespace scalfmm::list component_symbolics.finalize(true, counter_existing_component); } - // /** - // * @ingroup update_interaction_list - // * @brief update the M2L interaction list - // * - // * This function regroups the entire update mechanism of the interaction - // * lists updating. It will dispatch the call to @ref update_interaction_list - // * which will update the list stored in the component's symbolics and also update - // * the iterator list according to the interaction list just updated. - // * - // * @param begin_of_source_groups first iterator on source group - // * @param end_of_source_groups last iterator on source group - // * @param group the source group near or containing the Morton index - // * @param group_source_index index in vector of group of current source group of the component - // * @param component : The component to update its symbolics information. - // * @param level the level in the tree of the component and the group - // * @param period : Array containing the periodicity of the box. - // * @param neighbour_separation The separation criteria - // */ - // template<typename GroupIterator, typename GroupS, typename GroupT, typename Component, typename Array> - // inline auto build_interaction_list(GroupIterator begin_of_source_groups, GroupIterator end_of_source_groups, - // GroupS& group_source, std::size_t group_source_index, GroupT& group_target, - // Component& component, std::size_t level, Array const& period, - // const int& neighbour_separation, bool verbose = false) -> void - // { - // // Build the theoretical interaction list. - // list::build_symbolic_interaction_list(operators::impl::tag_m2l{}, component, level, period, - // neighbour_separation); - - // // Here we search the iterator for the multipoles (source cells) - - // list::build_interaction_list_iterators(begin_of_source_groups, end_of_source_groups, group_target, - // group_source, - // group_source_index, component, verbose); - // } /** * @brief Compute and set the interaction list for the group (morton indexes and iterators) * This function regroups the entire update mechanism of the interaction @@ -470,9 +473,10 @@ namespace scalfmm::list * which will update the list stored in the component's symbolics and also update * the iterator list according to the interaction list just updated. * - * @tparam GroupIterator - * @tparam Component - * @tparam Array + * @tparam GroupType + * @tparam SourceGroupIteratorType + * @tparam ComponentType + * @tparam ArrayType * @param group_target the current target group containing cell * @param cell_target the current target cell * @param begin_of_source_cell_groups iterator on the first group of source cells @@ -481,11 +485,11 @@ namespace scalfmm::list * @param period the vector of periodic conditions * @param neighbour_separation ste separation criterion */ - template<typename GroupT, typename GroupIteratorS, typename Component, typename Array> - void build_m2l_interaction_list_for_group(GroupT& group_target, Component& cell_target, - GroupIteratorS begin_of_source_cell_groups, - GroupIteratorS end_of_source_cell_groups, std::size_t level, - Array const& period, const int& neighbour_separation) + template<typename GroupType, typename SourceGroupIteratorType, typename ComponentType, typename ArrayType> + auto build_m2l_interaction_list_for_group(GroupType& group_target, ComponentType& cell_target, + SourceGroupIteratorType begin_of_source_cell_groups, + SourceGroupIteratorType end_of_source_cell_groups, std::size_t level, + ArrayType const& period, const int& neighbour_separation) -> void { constexpr bool m2l_list = true; // find group_source containing the index of the target cell @@ -493,9 +497,7 @@ namespace scalfmm::list list::find_component_group_near(begin_of_source_cell_groups, end_of_source_cell_groups, cell_target.index()); auto group_source_index = std::get<0>(group_source); auto group_source_it = std::get<1>(group_source); - // std::cout << " group_target " << group_target.csymbolics().idx_global << " group_source_index " - // << group_source_index << std::endl; - // + // Build the theoretical interaction list. list::build_symbolic_interaction_list(operators::impl::tag_m2l{}, cell_target, level, period, neighbour_separation); @@ -504,43 +506,40 @@ namespace scalfmm::list list::build_interaction_list_iterators(begin_of_source_cell_groups, end_of_source_cell_groups, *group_source_it, group_source_index, group_target, cell_target, m2l_list); - - // list::build_interaction_list(begin_of_source_cell_groups, end_of_source_cell_groups, *group_source_it, - // group_source_index, group_target, cell_target, level, period, - // neighbour_separation, verbose); } /// /// END M2L ROUTINES ////////////////////////////////////////////////////////////////////////////////// /// Routines to build the P2P interaction list - /// - /// @brief This function set the iterators list stored in the component symbolics for the p2p interaction list. - /// - /// This function updates 2 lists, one containing the iterators to the components - /// inside the current group being processed, and another stored in the group symbolic infos - /// that stores the out_of_block_interaction structure, allowing you to reconstruct mutual - /// application of an operator like p2p. - /// - /// The method is optimized for mutual p2p operators. We only search iterators with Morton - /// index smaller than Morton index of component - /// Warning in the out_of_block list in the group we only have the indexes and not the iterators - /// - /// - /// @param group : group of the component to update (target group) - /// @param component : the component to update the iterator list - /// @param component_index_in_group : The component index in its group. - /// - /// @return - template<typename Group, typename Component> - inline auto build_interaction_list_iterators(Group& group, Component& component, + + /** + * @brief This function set the iterators list stored in the component symbolics for the p2p interaction list. + * + * This function updates 2 lists, one containing the iterators to the components + * inside the current group being processed, and another stored in the group symbolic infos + * that stores the out_of_block_interaction structure, allowing you to reconstruct mutual + * application of an operator like p2p. + * + * The method is optimized for mutual p2p operators. We only search iterators with Morton + * index smaller than Morton index of component + * Warning in the out_of_block list in the group we only have the indexes and not the iterators + * + * @tparam GroupType + * @tparam ComponentType + * @param group : group of the component to update (target group) + * @param component : the component to update the iterator list + * @param component_index_in_group : The component index in its group. + */ + template<typename GroupType, typename ComponentType> + inline auto build_interaction_list_iterators(GroupType& group, ComponentType& component, int component_index_in_group) -> void { - using group_type = Group; + using group_type = GroupType; // Source group using out_of_block_interaction_type = typename scalfmm::component::symbolics_data<group_type>::out_of_block_interaction_type; - // static constexpr std::size_t number_of_interactions{ - // scalfmm::component::symbolics_data<Component>::number_of_interactions}; + // static constexpr std::size_t number_of_interactions{ + // scalfmm::component::symbolics_data<ComponentType>::number_of_interactions}; // Get symbolics infos on the group and the component auto& group_symbolics = group.symbolics(); auto& component_symbolics = component.symbolics(); @@ -554,7 +553,8 @@ namespace scalfmm::list // Loop on the number of neighbors const auto my_index = component.index(); - for(std::size_t index_interaction = 0; index_interaction < component_symbolics.number_of_neighbors; ++index_interaction) + for(std::size_t index_interaction = 0; index_interaction < component_symbolics.number_of_neighbors; + ++index_interaction) { // Get the morton index of the neighbor std::size_t current_interaction_morton_index = @@ -587,7 +587,6 @@ namespace scalfmm::list out_of_block_interaction_type property(component_symbolics.morton_index, current_interaction_morton_index, component_index_in_group); group_symbolics.outside_interactions.push_back(property); - } } component_symbolics.existing_neighbors_in_group = counter_existing_component; @@ -597,25 +596,28 @@ namespace scalfmm::list group.symbolics().outside_interactions_exists = true; } } - /// @brief Calculates the range of outside interactions to compute - /// according to the current group and the current outside interaction - /// index. - - /// This function takes the current group symbolics in the loop - /// and the group symbolics you have already processed. This means that, - /// if you are processing the group 2 for outside interactions, you want - /// get the range of interaction indices you need to processed in group 0 and - /// group 1. - /// - /// @tparam GroupSymbolics: The group type - /// @param current_group_symbolics : Current group symbolics information - /// @param group_symbolics The symbolics infos of the group we search the index. - /// @param current_out_interaction - /// - /// @return - template<typename GroupSymbolics1, typename GroupSymbolics2> - inline auto get_outside_interaction_range(GroupSymbolics1 const& current_group_symbolics, - GroupSymbolics2 const& group_symbolics, + + /** + * @brief Calculates the range of outside interactions to compute + * according to the current group and the current outside interaction + * index. + * + * This function takes the current group symbolics in the loop + * and the group symbolics you have already processed. This means that, + * if you are processing the group 2 for outside interactions, you want + * get the range of interaction indices you need to processed in group 0 and + * group 1. + * + * @tparam GroupSymbolicsType1 + * @tparam GroupSymbolicsType2 + * @param current_group_symbolics current group symbolics information. + * @param group_symbolics the symbolics infos of the group we search the index. + * @param current_out_interaction + * @return auto + */ + template<typename GroupSymbolicsType1, typename GroupSymbolicsType2> + inline auto get_outside_interaction_range(GroupSymbolicsType1 const& current_group_symbolics, + GroupSymbolicsType2 const& group_symbolics, std::size_t current_out_interaction) { const std::size_t block_start_index = group_symbolics.starting_index; @@ -642,45 +644,35 @@ namespace scalfmm::list return std::make_tuple(current_out_interaction, last_out_interaction); } + /** * @brief Sort by group then by morton index inside a group the out of block interactions * - * @tparam GroupIterator - * @tparam Component - * @param begin_of_groups The iterator on the set of groups - * @param group the current group we treat + * @tparam GroupIteratorType + * @tparam GroupType + * @param begin_of_groups the iterator on the set of groups. + * @param group the current group we treat. */ - template<typename GroupIterator, typename Group> - void sort_out_of_group_interactions(GroupIterator begin_of_groups, Group& group){ - + template<typename GroupIteratorType, typename GroupType> + auto sort_out_of_group_interactions(GroupIteratorType begin_of_groups, GroupType& group) -> void + { auto& group_symbolics = group.symbolics(); - // const auto group_idx = group_symbolics.idx_global; + // const auto group_idx = group_symbolics.idx_global; // Get interactions outside of the current group. auto& outside_interactions = group_symbolics.outside_interactions; - // bool verbose = true; { // Sort the interactions to have continuous interactions in a group. std::sort(std::begin(outside_interactions), std::end(outside_interactions), [](auto const& a, auto const& b) { return a.outside_index < b.outside_index; }); } - // if (verbose) { - // std::clog << cpp_tools::colors::green; - // std::clog << "out_of block: "; - // for(auto& u: outside_interactions) - // { - // std::clog << u << " "; - // } - // } - // std::clog << "\n" << cpp_tools::colors::reset; - // - // Sort the interactions inside a group to have continuous morton index. + // Sort the interactions inside a group to have continuous morton index. auto beg = std::begin(outside_interactions); auto current_group_iterator = begin_of_groups; const auto group_idx = group_symbolics.idx_global; std::advance(current_group_iterator, group_idx); - std::size_t current_out_interaction{0}; + std::size_t current_out_interaction{0}; std::size_t first_out_interaction{0}; std::size_t last_out_interaction{0}; while(begin_of_groups != current_group_iterator && current_out_interaction < outside_interactions.size()) @@ -689,23 +681,15 @@ namespace scalfmm::list // and before the curent group std::tie(first_out_interaction, last_out_interaction) = list::get_outside_interaction_range( group_symbolics, (*begin_of_groups)->csymbolics(), current_out_interaction); - std::sort(beg+first_out_interaction, beg+last_out_interaction, - [](auto const& a, auto const& b) { - return a.inside_index < b.inside_index;}); + std::sort(beg + first_out_interaction, beg + last_out_interaction, + [](auto const& a, auto const& b) { return a.inside_index < b.inside_index; }); - ++begin_of_groups ; + ++begin_of_groups; } - // if (verbose) { - // std::cout << "out_of block: "; - // for(auto& u: outside_interactions) - // { - // std::clog << u << " "; - // } - // } - // std::clog << "\n"; - group_symbolics.outside_interactions_sorted = true; + group_symbolics.outside_interactions_sorted = true; } + /** * @brief Finalize the structure containing the out of block interactions of the group * @@ -721,13 +705,15 @@ namespace scalfmm::list * * @todo improve the function group.component_index(block.outside_index) by using the previous find * - * @tparam GroupIterator - * @tparam MatrixKernel - * @param group current block + * + * @tparam GroupIteratorType + * @tparam GroupType * @param begin_of_groups the iterator on the set of blocks + * @param group current block */ - template<typename GroupIterator, typename Group> - void build_out_of_group_interactions(GroupIterator begin_of_groups, /*scalfmm::component::group<Component>*/Group& group) + template<typename GroupIteratorType, typename GroupType> + auto build_out_of_group_interactions(GroupIteratorType begin_of_groups, + /*scalfmm::component::group<Component>*/ GroupType& group) -> void { auto& group_symbolics = group.symbolics(); const auto group_idx = group_symbolics.idx_global; @@ -788,36 +774,33 @@ namespace scalfmm::list ++begin_of_groups; } outside_interactions.resize(pos); - } - /// @defgroup update_interaction_list update_interaction_list - /// - - /// @ingroup update_interaction_list - /// @brief This function takes a leaf and its level in the tree - /// and updates the component's symbolics information : the morton indices - /// of its neighbors and the number of neighbors. It returns the theoretical - /// interaction list of the leaf. - /// - /// @tparam[in] Tag : operator tag to select specific overload of @ref get_interaction_neighbors - /// @param[inout] component : the component in which the list will be updated i.e its symbolics component - /// @param[in] tree_level : the component's level in the tree - /// @param[in] period array of periodicity in each direction - /// @param[in] neighbour_separation distance between neighbors and component - /// @param[in] source_target specify if the source leaf is equal to the target leaf - /// - - template<typename Component, typename Array> - inline auto build_symbolic_interaction_list(operators::impl::tag_p2p t, Component& component, - std::size_t tree_level, Array const& period, + /** + * @brief This function takes a leaf and its level in the tree + * and updates the component's symbolics information : the morton indices + * of its neighbors and the number of neighbors. It returns the theoretical + * interaction list of the leaf. + * + * @tparam ComponentType + * @tparam ArrayType + * @tparam[in] t operator tag to select specific overload of @ref get_interaction_neighbors + * @param[inout] component the component in which the list will be updated i.e its symbolics component + * @param[in] tree_level the component's level in the tree + * @param[in] period array of periodicity in each direction + * @param[in] neighbour_separation distance between neighbors and component + * @param[in] source_target specify if the source leaf is equal to the target leaf + */ + template<typename ComponentType, typename ArrayType> + inline auto build_symbolic_interaction_list(operators::impl::tag_p2p t, ComponentType& component, + std::size_t tree_level, ArrayType const& period, const int& neighbour_separation, const bool source_target) -> void { // Get symbolics infos on the group and the component auto& component_symbolics = component.symbolics(); // Calculate the coordinate of the component from the morton index auto coordinate{ - index::get_coordinate_from_morton_index<Component::dimension>(component_symbolics.morton_index)}; + index::get_coordinate_from_morton_index<ComponentType::dimension>(component_symbolics.morton_index)}; // build component's symbolics information auto interaction_neighbors = index::get_interaction_neighbors(t, coordinate, tree_level, period, neighbour_separation); @@ -832,30 +815,31 @@ namespace scalfmm::list std::sort(std::begin(num1), std::begin(num1) + component_symbolics.number_of_neighbors); } } + /** * @brief Build the interactions inside the group * - * @tparam GroupIterator + * @tparam GroupIteratorType * @tparam GroupType - * @tparam Component - * @tparam Array - * @param leaf target leaf - * @param begin_of_source_groups iterator on the first source groups - * @param end_of_source_groups iterator on the ast source groups - * @param group_target target group containing the leaf target - * @param index_in_group index of group_target in the vector of target groups - * @param leaf_level Tle level of the leaf - * @param mutual if we consider mutual interactions or no - * @param period the vector of periodicity - * @param neighbour_separation the separation criterion - * @param source_target if sources = targets or not + * @tparam ComponentType + * @tparam ArrayType + * @param leaf target leaf. + * @param begin_of_source_groups iterator on the first source group. + * @param end_of_source_groups iterator on the last source group. + * @param group_target target group containing the leaf target. + * @param index_in_group index of group_target in the vector of target groups. + * @param leaf_level the level of the leaf. + * @param mutual whether we consider mutual interactions or not. + * @param period the vector of periodicity. + * @param neighbour_separation the separation criterion. + * @param source_target whether source == target or not. */ - template<typename GroupIterator, typename GroupType, typename Component, typename Array> - void build_p2p_interaction_list_inside_group(Component& leaf, GroupIterator begin_of_source_groups, - GroupIterator end_of_source_groups, GroupType& group_target, + template<typename GroupIteratorType, typename GroupType, typename ComponentType, typename ArrayType> + auto build_p2p_interaction_list_inside_group(ComponentType& leaf, GroupIteratorType begin_of_source_groups, + GroupIteratorType end_of_source_groups, GroupType& group_target, std::size_t& index_in_group, std::size_t leaf_level, const bool mutual, - Array const& period, const int& neighbour_separation, - const bool source_target) + ArrayType const& period, const int& neighbour_separation, + const bool source_target) -> void { constexpr bool m2l_list = false; @@ -890,15 +874,18 @@ namespace scalfmm::list /// End of routines to build the P2P interaction list ////////////////////////////////////////////////////////////////////////////////// /// + /** * @brief Reconstruct the p2p interaction list by adding the interaction between groups * * This function is just for debug purpose + * + * @tparam TreeType * @warning After this call the interaction list in the tree do not be used for an algorithm. * @param[inout] tree */ - template<typename TREE> - void reconstruct_p2p_mutual_interaction_lists(TREE& tree) + template<typename TreeType> + auto reconstruct_p2p_mutual_interaction_lists(TreeType& tree) -> void { component::for_each(std::get<0>(tree.begin()), std::get<0>(tree.end()), [&tree](auto& group) diff --git a/include/scalfmm/matrix_kernels/debug.hpp b/include/scalfmm/matrix_kernels/debug.hpp index c96b0a75590a158330dbad65e36fb8e5cb3ebc19..96854e0e78446b4c40116a0edd43239b9a4f5170 100644 --- a/include/scalfmm/matrix_kernels/debug.hpp +++ b/include/scalfmm/matrix_kernels/debug.hpp @@ -1,9 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : matrix_kernels/debug.hpp +// File : scalfmm/matrix_kernels/debug.hpp // -------------------------------- -#ifndef SCALFMM_MATRIX_KERNELS_DEBUG_HPP -#define SCALFMM_MATRIX_KERNELS_DEBUG_HPP +#pragma once + +#include "scalfmm/meta/utils.hpp" #include <array> #include <cmath> @@ -19,94 +20,390 @@ #include <utility> #include <xtensor/xmath.hpp> -#include "scalfmm/meta/utils.hpp" - namespace scalfmm::matrix_kernels::debug { - // This matrix kernel is here to debug the non homogenous case + /** + * @brief The one_over_r_non_homogenous struct corresponds to the \f$1/r\f$ kernel + * (non-homogeneous case). + * + * This matrix kernel is here to debug the non homogenous case. + * + * The kernel is defined as \f$k(x,y): R^{km} -> R^{kn}\f$ with \f$ kn = km = 1\f$ + * \f$k(x,y) = | x - y |^{-1}\f$ + */ struct one_over_r_non_homogenous { - static constexpr auto homogeneity_tag{homogeneity::non_homogenous}; - static constexpr auto symmetry_tag{symmetry::symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{1}; + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::non_homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs. + static constexpr std::size_t kn{1}; // The number of outputs. + + // Mandatory types template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ const std::string name() const { return std::string("one_over_r_non_homogenous"); } + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { return vector_type<ValueType>{ValueType(1.)}; } - template<typename ValueType, std::size_t Dim> - [[nodiscard]] inline auto evaluate(container::point<ValueType, Dim> const& x, - container::point<ValueType, Dim> const& y) const noexcept + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto operator()(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return evaluate(x, y); + } + + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { - return variadic_evaluate(x, y, std::make_index_sequence<Dim>{}); + return variadic_evaluate(x, y, std::make_index_sequence<Dimension>{}); } - template<typename ValueType, std::size_t Dim, std::size_t... Is> - [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType, Dim> const& xs, - container::point<ValueType, Dim> const& ys, + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Dimension The dimension of the points. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension, std::size_t... Is> + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dimension> const& xs, + container::point<ValueType2, Dimension> const& ys, std::index_sequence<Is...> is) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { using std::sqrt; - using decayed_type = typename std::decay_t<ValueType>; - + using decayed_type = typename std::decay_t<ValueType1>; return matrix_type<decayed_type>{decayed_type(1.0) / sqrt((((xs.at(Is) - ys.at(Is)) * (xs.at(Is) - ys.at(Is))) + ...))}; } - static constexpr int separation_criterion{1}; + + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; + /** + * @brief The one_over_r_non_homogenous struct corresponds to the \f$1/r\f$ kernel + * (non-symmetric case). + * + * This matrix kernel is here to debug the non symmetric case. + * + * The kernel is defined as \f$k(x,y): R^{km} -> R^{kn}\f$ with \f$ kn = km = 1\f$ + * \f$k(x,y) = | x - y |^{-1}\f$ + */ struct one_over_r_non_symmetric { - static constexpr auto homogeneity_tag{homogeneity::homogenous}; - static constexpr auto symmetry_tag{symmetry::non_symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{1}; + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::non_symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs. + static constexpr std::size_t kn{1}; // The number of outputs. + + // Mandatory types template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ const std::string name() const { return std::string("one_over_r_non_symmetric"); } + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ + template<typename ValueType> + [[nodiscard]] inline constexpr auto mutual_coefficient() const + { + return vector_type<ValueType>{ValueType(1.)}; + } + + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto operator()(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return evaluate(x, y); + } + + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return variadic_evaluate(x, y, std::make_index_sequence<Dimension>{}); + } + + /** + * @brief Returns the scale factor of the kernel. + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. + * + * @return A vector representing the scale factor. + */ + template<typename ValueType> + [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept + { + return vector_type<ValueType>{ValueType(1.) / cell_width}; + } + + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Dimension The dimension of the points. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension, std::size_t... Is> + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dimension> const& xs, + container::point<ValueType2, Dimension> const& ys, + std::index_sequence<Is...> is) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + using std::sqrt; + using decayed_type = typename std::decay_t<ValueType1>; + return matrix_type<decayed_type>{decayed_type(1.0) / + sqrt((((xs.at(Is) - ys.at(Is)) * (xs.at(Is) - ys.at(Is))) + ...))}; + } + + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. + }; + + /** + * @brief The one_over_r_non_homogenous struct corresponds to the \f$1/r\f$ kernel + * (non-homogenous and non-symmetric case). + * + * This matrix kernel is here to debug the non-homogeneous and non-symmetric case. + * + * The kernel is defined as \f$k(x,y): R^{km} -> R^{kn}\f$ with \f$ kn = km = 1\f$ + * \f$k(x,y) = | x - y |^{-1}\f$ + */ + struct one_over_r_non_homogenous_non_symmetric + { + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::non_homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::non_symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs of the kernel. + static constexpr std::size_t kn{1}; // The number of outputs of the kernel. + + // Mandatory types + template<typename ValueType> + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. + template<typename ValueType> + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ + const std::string name() const { return std::string("one_over_r_non_homogenous_non_symmetric"); } + + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { return vector_type<ValueType>{ValueType(1.)}; } - template<typename ValueType, std::size_t Dim> - [[nodiscard]] inline auto evaluate(container::point<ValueType, Dim> const& x, - container::point<ValueType, Dim> const& y) const noexcept + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto operator()(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return evaluate(x, y); + } + + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { - return variadic_evaluate(x, y, std::make_index_sequence<Dim>{}); + return variadic_evaluate(x, y, std::make_index_sequence<Dimension>{}); } + /** + * @brief Returns the scale factor of the kernel. + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. + * + * @return A vector representing the scale factor. + */ template<typename ValueType> [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept { return vector_type<ValueType>{ValueType(1.) / cell_width}; } - template<typename ValueType, std::size_t Dim, std::size_t... Is> - [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType, Dim> const& xs, - container::point<ValueType, Dim> const& ys, + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension, std::size_t... Is> + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dimension> const& xs, + container::point<ValueType2, Dimension> const& ys, std::index_sequence<Is...> is) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { using std::sqrt; - using decayed_type = typename std::decay_t<ValueType>; + using decayed_type = typename std::decay_t<ValueType1>; return matrix_type<decayed_type>{decayed_type(1.0) / sqrt((((xs.at(Is) - ys.at(Is)) * (xs.at(Is) - ys.at(Is))) + ...))}; } - static constexpr int separation_criterion{1}; + + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; } // namespace scalfmm::matrix_kernels::debug -#endif // SCALFMM_MATRIX_KERNELS_DEBUG_HPP diff --git a/include/scalfmm/matrix_kernels/gaussian.hpp b/include/scalfmm/matrix_kernels/gaussian.hpp index 8ffbf6fad116a0547aa9dd2448d33f4ba2e428f5..9822548428d92b9a2eb25fe3198ca8f1f60c0749 100644 --- a/include/scalfmm/matrix_kernels/gaussian.hpp +++ b/include/scalfmm/matrix_kernels/gaussian.hpp @@ -1,104 +1,143 @@ -#ifndef SCALFMM_MATRIX_KERNELS_GAUSSIAN_HPP -#define SCALFMM_MATRIX_KERNELS_GAUSSIAN_HPP +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/matrix_kernels/gaussian.hpp +// -------------------------------- +#pragma once #include <scalfmm/container/point.hpp> #include <scalfmm/matrix_kernels/mk_common.hpp> namespace scalfmm::matrix_kernels { - - /////// - /// \brief The name struct corresponds to the \f$ K(x,y) = exp(-|x-y|/(2 sigma^2)) \f$ kernel - /// - /// The kernel \f$K(x,y): R^{km} -> R^{kn}\f$ - /// - /// The kernel is not homogeneous K(ax,ay) != a^p K(x,y) - /// The kernel is symmetric - /// - template<typename ValueType> + /** + * @brief This structure corresponds to the Gaussian kernel. + * + * The kernel is defined as \f$k(x,y): R^{km} -> R^{kn}\f$ with \f$ kn = km = 1\f$ + * \f$k(x,y) = exp(-| x - y |/(\sigma^2))\f$ + * + * - The kernel is not homogeneous. + * - The kernel is not symmetric. + * + * @GlobalValueType The value type of the coefficient \f$\sigma\f$. + */ + template<typename GlobalValueType> struct gaussian { - static constexpr auto homogeneity_tag{homogeneity::non_homogenous}; - static constexpr auto symmetry_tag{symmetry::non_symmetric}; // symmetry::symmetric or symmetry::non_symmetric - static constexpr std::size_t km{1}; // the dimension - static constexpr std::size_t kn{1}; - /** - * @brief - * - */ - ValueType m_coeff{ValueType(1.)}; + using value_type = GlobalValueType; + + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::non_homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::non_symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs of the kernel. + static constexpr std::size_t kn{1}; // The number of outputs of the kernel. - /** - * @brief Set the coeff object - * - * @param inCoeff - */ - void set_coeff(ValueType inCoeff) { m_coeff = inCoeff; } - // // Mandatory type - template<typename ValueType1> - using matrix_type = std::array<ValueType1, kn * km>; - template<typename ValueType1> - using vector_type = std::array<ValueType1, kn>; - // + template<typename ValueType> + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. + template<typename ValueType> + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + + value_type m_coeff{value_type(1.)}; // parameter/coefficient of the kernel. + /** - * @brief return the name of the kernel + * @brief Set the value of the coefficient of the kernel. * + * @param in_coeff value of the coefficient to be set. */ + void set_coeff(value_type in_coeff) { m_coeff = in_coeff; } + + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ const std::string name() const { return std::string("gaussian ") + " coeff = " + std::to_string(m_coeff); } - // template<typename ValueType> - // /** - // * @brief Return the mutual coefficient of size kn - // * - // * The coefficient is used in the direct pass when the kernel is used - // * to compute the interactions inside the leaf when we use the symmetry - // * of tke kernel ( N^2/2 when N is the number of particles) - // * - // * @return constexpr auto - // */ - template<typename ValueType1> + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ + template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { - return vector_type<ValueType1>{ValueType1(1.)}; + return vector_type<ValueType>{ValueType(1.)}; } /** - * @brief evaluate the kernel at points x and y - * - * - * @param x d point - * @param y d point - * @return return the matrix K(x,y) - */ - template<typename PointType1, typename PointType2> - [[nodiscard]] inline auto evaluate(PointType1 const& x, PointType2 const& y) const noexcept -> std::enable_if_t< - std::is_same_v<std::decay_t<typename PointType1::value_type>, std::decay_t<typename PointType2::value_type>>, - matrix_type<std::decay_t<typename PointType1::value_type>>> + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto operator()(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return evaluate(x, y); + } + + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { - return variadic_evaluate(x, y, std::make_index_sequence<PointType1::dimension>{}); + return variadic_evaluate(x, y, std::make_index_sequence<Dimension>{}); } - template<typename PointType1, typename PointType2, std::size_t... Is> - [[nodiscard]] inline auto variadic_evaluate(PointType1 const& xs, PointType2 const& ys, - std::index_sequence<Is...> is) const noexcept + + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Dimension The dimension of the points. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension, std::size_t... Is> + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dimension> const& xs, + container::point<ValueType2, Dimension> const& ys, + std::index_sequence<Is...>) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { - using decayed_type = std::decay_t<typename PointType1::value_type>; + using decayed_type = std::decay_t<ValueType1>; auto l2 = decayed_type(-1.0) / (m_coeff * m_coeff); decayed_type r2 = (((xs.at(Is) - ys.at(Is)) * (xs.at(Is) - ys.at(Is))) + ...); return matrix_type<decayed_type>{xsimd::exp(r2 * l2)}; } - /** - * @brief return the scale factor of the kernel - * - * the method is used only if the kernel is homogeneous - */ - // template<typename ValueType> - // [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept - // { - // return vector_type<ValueType>{...}; - // } - static constexpr int separation_criterion{1}; + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; } // namespace scalfmm::matrix_kernels -#endif diff --git a/include/scalfmm/matrix_kernels/laplace.hpp b/include/scalfmm/matrix_kernels/laplace.hpp index 2db7734afcc38b78a12d65d6d2dff422ac3d4661..85a984c3077ccf1e52a0dd0b3108f9ab1b6032f3 100644 --- a/include/scalfmm/matrix_kernels/laplace.hpp +++ b/include/scalfmm/matrix_kernels/laplace.hpp @@ -1,9 +1,14 @@ // -------------------------------- // See LICENCE file at project root -// File : kernels/kernels.hpp +// File : scalfmm/matrix_kernels/laplace.hpp // -------------------------------- -#ifndef SCALFMM_MATRIX_KERNELS_LAPLACE_HPP -#define SCALFMM_MATRIX_KERNELS_LAPLACE_HPP +#pragma once + +#include "scalfmm/container/point.hpp" +#include "scalfmm/matrix_kernels/mk_common.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/utils/math.hpp" +#include "xtensor/xtensor_forward.hpp" #include <array> #include <cmath> @@ -12,36 +17,48 @@ #include <type_traits> #include <utility> -#include "scalfmm/meta/utils.hpp" -#include <scalfmm/container/point.hpp> -#include <scalfmm/matrix_kernels/mk_common.hpp> -#include <scalfmm/meta/utils.hpp> -#include <scalfmm/utils/math.hpp> -#include <xtensor/xtensor_forward.hpp> - namespace scalfmm::matrix_kernels::laplace { - /////// - /// \brief The one_over_r struct corresponds to the \f$ 1/r\f$ kernel - /// - /// The kernel \f$k(x,y): R^{km} -> R^{kn}\f$ with\f$ kn = km = 1\f$ - /// \f$k(x,y) = | x - y |^{-1}\f$ - /// The kernel is homogeneous\f$ k(ax,ay) = 1/a k(x,y)\f$ - /// scale factor is \f$1/a\f$ - /// + /** + * @brief The one_over_r struct corresponds to the \f$1/r\f$ kernel. + * + * The kernel is defined as \f$k(x,y): R^{km} -> R^{kn}\f$ with \f$ kn = km = 1\f$ + * \f$k(x,y) = | x - y |^{-1}\f$ + * + * - The kernel is homogeneous \f$ k(ax,ay) = 1/a k(x,y)\f$. + * - The scale factor is \f$1/a\f$. + */ struct one_over_r { - static constexpr auto homogeneity_tag{homogeneity::homogenous}; - static constexpr auto symmetry_tag{symmetry::symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{1}; + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs of the kernel. + static constexpr std::size_t kn{1}; // The number of outputs of the kernel. + + // Mandatory types template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ const std::string name() const { return std::string("one_over_r"); } + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { @@ -49,60 +66,194 @@ namespace scalfmm::matrix_kernels::laplace return vector_type<decayed_type>{decayed_type(1.)}; } - template<typename ValueType1, typename ValueType2, std::size_t Dim> - [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dim> const& x, - container::point<ValueType2, Dim> const& y) const noexcept + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto operator()(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, matrix_type<std::decay_t<ValueType1>>> { - return variadic_evaluate(x, y, std::make_index_sequence<Dim>{}); + return evaluate(x, y); } + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return variadic_evaluate(x, y, std::make_index_sequence<Dimension>{}); + } + + /** + * @brief Returns the scale factor of the kernel. + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. + * + * @return A vector representing the scale factor. + */ template<typename ValueType> [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept { return vector_type<ValueType>{ValueType(1.) / cell_width}; } - template<typename ValueType, std::size_t Dim, std::size_t... Is> - [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType, Dim> const& xs, - container::point<ValueType, Dim> const& ys, + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Dimension The dimension of the points. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension, std::size_t... Is> + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dimension> const& xs, + container::point<ValueType2, Dimension> const& ys, std::index_sequence<Is...>) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { - using decayed_type = typename std::decay_t<ValueType>; + using decayed_type = typename std::decay_t<ValueType1>; return matrix_type<decayed_type>{decayed_type(1.0) / xsimd::sqrt((((xs.at(Is) - ys.at(Is)) * (xs.at(Is) - ys.at(Is))) + ...))}; } - static constexpr int separation_criterion{1}; + + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; - /////// - /// \brief The like_mrhs simulates two FMMs with independent charges - /// - /// The kernel \f$k(x,y): R^2 -> R^2\f$ - /// \f$ (q1,q2) --> (p1,p2)\f$ - /// \f$k(x,y) = | x - y |^{-1} Id_{2x2}\f$ - /// The kernel is homogeneous \f$k(ax,ay) = 1/a k(x,y)\f$ - /// scale factor is\f$ (1/a, 1/a)\f$ + /** + * @brief The like_mrhs simulates two FMMs with independent charges. + * + * The kernel is defined as \f$k(x,y): R^2 -> R^2\f$ + * \f$ (q1,q2) --> (p1,p2)\f$ + * \f$k(x,y) = | x - y |^{-1} Id_{2x2}\f$ + * + * - The kernel is homogeneous \f$k(ax,ay) = 1/a k(x,y)\f$. + * - The scale factor is \f$(1/a, 1/a)\f$. + */ struct like_mrhs { - static constexpr auto homogeneity_tag{homogeneity::homogenous}; - static constexpr auto symmetry_tag{symmetry::symmetric}; - static constexpr std::size_t km{2}; - static constexpr std::size_t kn{2}; + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{2}; // The number of inputs. + static constexpr std::size_t kn{2}; // The number of outputs. + + // Mandatory types template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ const std::string name() const { return std::string("like_mrhs multiple charges for 1/r"); } + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { return vector_type<ValueType>({ValueType(1.), ValueType(1.)}); } + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto operator()(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return evaluate(x, y); + } + + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return variadic_evaluate(x, y, std::make_index_sequence<Dimension>{}); + } + + /** + * @brief Returns the scale factor of the kernel. + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. + * + * @return A vector representing the scale factor. + */ template<typename ValueType> [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept { @@ -110,19 +261,26 @@ namespace scalfmm::matrix_kernels::laplace return vector_type<ValueType>{tmp, tmp}; } - template<typename ValueType, std::size_t Dim> - [[nodiscard]] inline auto evaluate(container::point<ValueType, Dim> const& x, - container::point<ValueType, Dim> const& y) const noexcept - { - return variadic_evaluate(x, y, std::make_index_sequence<Dim>{}); - } - /// - /// @return the matrix K stored by rows in a tuple of size kn x mm organiz by rows - /// - template<typename ValueType1, typename ValueType2, std::size_t Dim, std::size_t... Is> - [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dim> const& xs, - container::point<ValueType2, Dim> const& ys, + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Dimension The dimension of the points. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension, std::size_t... Is> + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dimension> const& xs, + container::point<ValueType2, Dimension> const& ys, std::index_sequence<Is...>) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { using decayed_type = typename std::decay_t<ValueType1>; @@ -130,32 +288,54 @@ namespace scalfmm::matrix_kernels::laplace return matrix_type<decayed_type>{val, decayed_type(0.), decayed_type(0.), val}; } - static constexpr int separation_criterion{1}; + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; - //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief grad_one_over_r matrix kernel to compute the gradient of Laplace kernel. - /// - /// grad_one_over_r\f$ k(x,y) : R -> R^{d}\f$ with \f$d \f$ the space - /// dimension. - /// - /// \f$k(x,y) = \grad | x - y |^{-1} = -(x-y) | x - y |^{-3}\f$ - /// - /// scale factor \f$k(ax,ay)= 1/a^2 k(x,y)\f$ - template<std::size_t Dim = 3> + /** + * @brief The grad_one_over_r matrix kernel computes the gradient of Laplace kernel. + * + * The kernel is defined as \f$k(x,y) : R^{km} -> R^{kn}\f$ with \f$km = 1\f$ and \fkn = d\f + * with \f$d\f$ the space dimension. + * + * \f$k(x,y) = \grad | x - y |^{-1} = -(x-y) | x - y |^{-3}\f$ + * + * - The kernel is homogeneous \f$k(ax,ay)= 1/a^2 k(x,y)\f$. + * - The scale factor is \f$1/a^2\f$. + */ + template<std::size_t Dimension = 3> struct grad_one_over_r { - static constexpr auto homogeneity_tag{homogeneity::homogenous}; - static constexpr auto symmetry_tag{symmetry::non_symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{Dim}; - template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; - template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + static constexpr std::size_t dimension = Dimension; - const std::string name() const { return std::string("grad_one_over_r<" + std::to_string(Dim) + ">"); } + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::non_symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs of the kernel. + static constexpr std::size_t kn{Dimension}; // The number of outputs of the kernel. + // Mandatory types + template<typename ValueType> + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. + template<typename ValueType> + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ + const std::string name() const { return std::string("grad_one_over_r<" + std::to_string(Dimension) + ">"); } + + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { @@ -164,15 +344,57 @@ namespace scalfmm::matrix_kernels::laplace return mc; } + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ template<typename ValueType1, typename ValueType2> - [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dim> const& x, - container::point<ValueType2, Dim> const& y) const noexcept + [[nodiscard]] inline auto operator()(container::point<ValueType1, dimension> const& x, + container::point<ValueType2, dimension> const& y) const noexcept -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, matrix_type<std::decay_t<ValueType1>>> { - return variadic_evaluate(x, y, std::make_index_sequence<Dim>{}); + return evaluate(x, y); } + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, dimension> const& x, + container::point<ValueType2, dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return variadic_evaluate(x, y, std::make_index_sequence<dimension>{}); + } + + /** + * @brief Returns the scale factor of the kernel. + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. + * + * @return A vector representing the scale factor. + */ template<typename ValueType> [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept { @@ -182,10 +404,25 @@ namespace scalfmm::matrix_kernels::laplace return sf; } + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ template<typename ValueType1, typename ValueType2, std::size_t... Is> - [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dim> const& xs, - container::point<ValueType2, Dim> const& ys, + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, dimension> const& xs, + container::point<ValueType2, dimension> const& ys, std::index_sequence<Is...>) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { using decayed_type = typename std::decay_t<ValueType1>; @@ -193,39 +430,59 @@ namespace scalfmm::matrix_kernels::laplace decayed_type(1.0) / xsimd::sqrt((((xs.at(Is) - ys.at(Is)) * (xs.at(Is) - ys.at(Is))) + ...)); decayed_type r3{xsimd::pow(tmp, int(3))}; return matrix_type<decayed_type>{r3 * (ys.at(Is) - xs.at(Is))...}; - //-r3 * (xs - ys); } - static constexpr int separation_criterion{1}; + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; - //////////////////////////////////////////////////////////////////////////////////////////////// - /// - /// \brief The val_grad_one_over_r struct is the matrix kernel to compute the value and the gradient of the Laplace - /// kernel. - /// - /// val_grad_one_over_r \f$k(x,y) : R^{km} -> R^{kn}\f$ with \f$ km = 1\f$ and \f$kn = d+1\f$ with \f$ d\f$ the - /// space dimension. - /// - /// \f$k(x,y) = ( | x - y |^{-1}, \grad | x - y |^{-1} ) =(| x - y |^{-1}, -(x-y) | x - y |^{-3}\f)$ - /// - /// Is a specific kernel used to compute the value of the kernel and its gradient - /// - /// scale factor \f$ k(ax,ay)= (1/a, 1/a^2 ... 1/a^2) k(x,y)\f$ - template<std::size_t Dim = 3> + /** + * @brief The val_grad_one_over_r struct is the kernel that computes the value and the gradient of the Laplace + * kernel. + * + * This is a specific kernel used to compute the value of the kernel and its gradient + * + * The kernel is defined as \f$k(x,y): R^{km} -> R^{kn}\f$ with \f$km = 1\f$ and \f$kn = d+1\f$ + * with \f$d\f$ the space dimension. + * + * \f$k(x,y) = ( | x - y |^{-1}, \grad | x - y |^{-1} ) = (| x - y |^{-1}, -(x-y) | x - y |^{-3}\f)$ + * + * - The kernel is homogeneous. + * - The scale factor \f$(1/a, 1/a^2 ... 1/a^2)\f$. + */ + template<std::size_t Dimension = 3> struct val_grad_one_over_r { - static constexpr auto homogeneity_tag{homogeneity::homogenous}; - static constexpr auto symmetry_tag{symmetry::non_symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{1 + Dim}; - template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; - template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + static constexpr std::size_t dimension = Dimension; - const std::string name() const { return std::string("val_grad_one_over_r<" + std::to_string(Dim) + ">"); } + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::non_symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs. + static constexpr std::size_t kn{1 + Dimension}; // The number of outputs. + // Mandatory types + template<typename ValueType> + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. + template<typename ValueType> + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ + const std::string name() const { return std::string("val_grad_one_over_r<" + std::to_string(Dimension) + ">"); } + + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { @@ -236,16 +493,57 @@ namespace scalfmm::matrix_kernels::laplace return mc; } + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ template<typename ValueType1, typename ValueType2> - [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dim> const& x, - container::point<ValueType2, Dim> const& y) const noexcept + [[nodiscard]] inline auto operator()(container::point<ValueType1, dimension> const& x, + container::point<ValueType2, dimension> const& y) const noexcept -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, matrix_type<std::decay_t<ValueType1>>> { - return variadic_evaluate(x, y, std::make_index_sequence<Dim>{}); + return evaluate(x, y); } - // No meaning + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, dimension> const& x, + container::point<ValueType2, dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return variadic_evaluate(x, y, std::make_index_sequence<dimension>{}); + } + + /** + * @brief Returns the scale factor of the kernel (no meaning here). + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. + * + * @return A vector representing the scale factor. + */ template<typename ValueType> [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept { @@ -258,10 +556,25 @@ namespace scalfmm::matrix_kernels::laplace return sf; } + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ template<typename ValueType1, typename ValueType2, std::size_t... Is> - [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dim> const& xs, - container::point<ValueType2, Dim> const& ys, + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, dimension> const& xs, + container::point<ValueType2, dimension> const& ys, std::index_sequence<Is...>) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { using decayed_type = typename std::decay_t<ValueType1>; @@ -271,73 +584,178 @@ namespace scalfmm::matrix_kernels::laplace return matrix_type<decayed_type>{tmp, (r3 * (ys.at(Is) - xs.at(Is)))...}; //-r3 * (xs - ys); } - static constexpr int separation_criterion{1}; + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; - /////// - /// \brief The ln_2d struct corresponds to the \f$ log(r) \f$ kernel - /// - /// The kernel \f$k(x,y): R^{1} -> R^{kn}\f$ with\f$ kn = km = 1\f$ - /// \f$k(x,y) = \ln| x - y |\f$ - /// The kernel is non homogeneous - /// + /** + * @brief The ln_2d struct corresponds to the \f$log(r)\f$ kernel (only in 2D). + * + * The kernel is defined as \f$k(x,y): R^{1} -> R^{kn}\f$ with\f$ kn = km = 1\f$. + * + * \f$k(x,y) = \ln| x - y |\f$ + * + * - The kernel is non homogeneous. + */ struct ln_2d { - static constexpr auto homogeneity_tag{homogeneity::non_homogenous}; - static constexpr auto symmetry_tag{symmetry::symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{1}; + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::non_homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs. + static constexpr std::size_t kn{1}; // The number of outputs. + + // Mandatory types template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ const std::string name() const { return std::string("ln_2d"); } + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { return vector_type<ValueType>{ValueType(1.)}; } - template<typename ValueType> - [[nodiscard]] inline auto evaluate(container::point<ValueType, 2> const& x, - container::point<ValueType, 2> const& y) const noexcept + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x 2D point. + * @param y 2D point. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2> + [[nodiscard]] inline auto operator()(container::point<ValueType1, 2> const& x, + container::point<ValueType2, 2> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { - using decayed_type = typename std::decay_t<ValueType>; + return evaluate(x, y); + } + + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x 2D point. + * @param y 2D point. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, 2> const& x, + container::point<ValueType2, 2> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + using decayed_type = typename std::decay_t<ValueType1>; return matrix_type<decayed_type>{xsimd::log( xsimd::sqrt((x.at(0) - y.at(0)) * (x.at(0) - y.at(0)) + (x.at(1) - y.at(1)) * (x.at(1) - y.at(1))))}; } - static constexpr int separation_criterion{1}; + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; - /////// - /// \brief The grad ln_2d struct corresponds to the \f$ ld/dr log(r) \f$ kernel - /// - /// The kernel \f$k(x,y): R^{1} -> R^{2}\f$ with\f$ kn =2 ; km = 1\f$ - /// \f$k(x,y) = ( (x-y)/| x - y |)\f$ - /// The kernel is homogeneous with coefficient (1.0, 1.0) - /// + /** + * \brief The grad ln_2d struct corresponds to the \f$ ld/dr log(r)\f$ kernel (only in 2D). + * + * The kernel is defined as \f$k(x,y): R^{1} -> R^{2}\f$ with \f$kn = 2\$ and \f$km = 1\f$. + * + * \f$k(x,y) = ( (x-y)/| x - y |)\f$ + * + * - The kernel is homogeneous + */ struct grad_ln_2d { - static constexpr auto homogeneity_tag{homogeneity::homogenous}; - static constexpr auto symmetry_tag{symmetry::non_symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{2}; + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::non_symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs. + static constexpr std::size_t kn{2}; // The number of outputs. + + // Mandatory types template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ const std::string name() const { return std::string("grad_ln_2d"); } + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { return vector_type<ValueType>({ValueType(-1.), ValueType(-1.)}); } + + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x 2D point. + * @param y 2D point. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2> + [[nodiscard]] inline auto operator()(container::point<ValueType1, 2> const& x, + container::point<ValueType2, 2> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return evaluate(x, y); + } + + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x 2D point. + * @param y 2D point. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ template<typename ValueType1, typename ValueType2> [[nodiscard]] inline auto evaluate(container::point<ValueType1, 2> const& x, container::point<ValueType2, 2> const& y) const noexcept @@ -351,39 +769,103 @@ namespace scalfmm::matrix_kernels::laplace return matrix_type<decayed_type>{tmp * diff.at(0), tmp * diff.at(1)}; } + /** + * @brief Returns the scale factor of the kernel. + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. + * + * @return A vector representing the scale factor. + */ template<typename ValueType> [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept { return vector_type<ValueType>{ValueType(1. / cell_width), ValueType(1. / cell_width)}; } - static constexpr int separation_criterion{1}; + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; - /////// - /// \brief The val_grad ln_2d struct corresponds to the \f$ ld/dr log(r) \f$ kernel - /// - /// The kernel \f$k(x,y): R^{1} -> R^{kn}\f$ with\f$ kn =3 ; km = 1\f$ - /// \f$k(x,y) = (ln(| x - y |^), (x-y)/| x - y |^2)\f$ - /// The kernel is non homogeneous - /// + + /** + * @brief The val_grad_ln_2d struct corresponds to the \f$ ld/dr log(r)\f$ kernel. + * + * The kernel is defined as \f$k(x,y): R^{1} -> R^{kn}\f$ with \f$kn = 3\f$ and \f$km = 1\f$. + * + * \f$k(x,y) = (ln(| x - y |^), (x-y)/| x - y |^2)\f$ + * + * - The kernel is non homogeneous + */ struct val_grad_ln_2d { - static constexpr auto homogeneity_tag{homogeneity::non_homogenous}; - static constexpr auto symmetry_tag{symmetry::non_symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{3}; + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::non_homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::non_symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // Specify the number of inputs. + static constexpr std::size_t kn{3}; // Specify the number of outputs. + + // Mandatory types template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ const std::string name() const { return std::string("val_grad_ln_2d"); } + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { return vector_type<ValueType>({ValueType(1.), ValueType(-1.), ValueType(-1.)}); } + + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x 2D point. + * @param y 2D point. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2> + [[nodiscard]] inline auto operator()(container::point<ValueType1, 2> const& x, + container::point<ValueType2, 2> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return evaluate(x, y); + } + + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x 2D point. + * @param y 2D point. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ template<typename ValueType1, typename ValueType2> [[nodiscard]] inline auto evaluate(container::point<ValueType1, 2> const& x, container::point<ValueType2, 2> const& y) const noexcept @@ -397,9 +879,7 @@ namespace scalfmm::matrix_kernels::laplace return matrix_type<decayed_type>{xsimd::log(norm), tmp * diff.at(0), tmp * diff.at(1)}; } - static constexpr int separation_criterion{1}; + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; } // namespace scalfmm::matrix_kernels::laplace - -#endif // SCALFMM_MATRIX_KERNELS_LAPLACE_HPP diff --git a/include/scalfmm/matrix_kernels/mk_common.hpp b/include/scalfmm/matrix_kernels/mk_common.hpp index ca9548407ca98468b08af61ae236064129880bfe..0682211afd317b06b679a4d734ec18e7b8055458 100644 --- a/include/scalfmm/matrix_kernels/mk_common.hpp +++ b/include/scalfmm/matrix_kernels/mk_common.hpp @@ -1,20 +1,22 @@ -#ifndef SCALFMM_MATRIX_KERNELS_MK_COMMON_HPP -#define SCALFMM_MATRIX_KERNELS_MK_COMMON_HPP +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/matrix_kernels/mk_common.hpp +// -------------------------------- +#pragma once namespace scalfmm::matrix_kernels { /** - * @brief specify if the kernel is homogeneous or not - * + * @brief specifies whether the kernel is homogeneous or not. */ enum struct homogeneity { - homogenous, + homogenous, non_homogenous }; + /** - * @brief specify if the kernel is symmetric or not - * + * @brief specifies whether the kernel is symmetric or not. */ enum struct symmetry { @@ -24,10 +26,8 @@ namespace scalfmm::matrix_kernels }; /** - * @brief describe the convergence of the expansion at level 0 + * @brief describes the convergence of the expansion at level 0 * in the periodic case - * - * */ enum struct periodicity { @@ -36,4 +36,3 @@ namespace scalfmm::matrix_kernels }; } // namespace scalfmm::matrix_kernels -#endif diff --git a/include/scalfmm/matrix_kernels/scalar_kernels.hpp b/include/scalfmm/matrix_kernels/scalar_kernels.hpp index ff821c04939572fe3c0e20e878c81d74e0affd8a..6c86d1b93854cd925d2cf66c922342ec989d9cc5 100644 --- a/include/scalfmm/matrix_kernels/scalar_kernels.hpp +++ b/include/scalfmm/matrix_kernels/scalar_kernels.hpp @@ -1,9 +1,8 @@ // -------------------------------- // See LICENCE file at project root -// File : kernels/kernels.hpp +// File : scalfmm/matrix_kernels/scalar_kernels.hpp // -------------------------------- -#ifndef SCALFMM_MATRIX_KERNELS_SCALAR_KERNELS_HPP -#define SCALFMM_MATRIX_KERNELS_SCALAR_KERNELS_HPP +#pragma once #include <array> #include <cmath> @@ -24,96 +23,188 @@ namespace scalfmm::matrix_kernels::others { - /////// - /// \brief The one_over_r2 struct - /// - /// The kernel \f$k(x,y) : R^{km} -> R^{kn}\f$ with \f$ km = kn =1\f$ <p> - /// \f$ k(x,y) = | x - y |^{-2} \f$ with \f$x \in R^d \f$ - /// - /// For \f$d = 2 \f$ <p> - /// \f$x = (x1,x2), y = (y1,y2)\f$ and \f$|x-y|^2 = (x1-y1)^2 + (x2-y2)^2 \f$ - /// - /// The kernel is homogeneous \f$k(ax,ay) = 1/a^2 k(x,y) \f$ - /// - /// scale factor is \f$1/a^2 \f$ - /// + /** + * @brief The one_over_r2 struct corresponds to the \f$1/r^2\f$ kernel. + * + * The kernel is defined as \f$k(x,y): R^{km} -> R^{kn}\f$ with \f$ kn = km = 1\f$ + * \f$k(x,y) = | x - y |^{-2}\f$ + * + * - The kernel is homogeneous \f$ k(ax,ay) = 1/a^2 k(x,y)\f$. + * - The scale factor is \f$1/a^2\f$. + */ struct one_over_r2 { - static constexpr auto homogeneity_tag{homogeneity::homogenous}; - static constexpr auto symmetry_tag{symmetry::symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{1}; + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The nmuber of inputs of the kernel. + static constexpr std::size_t kn{1}; // The number of outputs of the kernels. + + // Mandatory types template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ const std::string name() const { return std::string("one_over_r2"); } + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { return vector_type<ValueType>{ValueType(1.)}; } - template<typename ValueType1, typename ValueType2, std::size_t Dim> - [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dim> const& x, - container::point<ValueType2, Dim> const& y) const noexcept + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto operator()(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return evaluate(x, y); + } + + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, matrix_type<std::decay_t<ValueType1>>> { - return variadic_evaluate(x, y, std::make_index_sequence<Dim>{}); + return variadic_evaluate(x, y, std::make_index_sequence<Dimension>{}); } + /** + * @brief Returns the scale factor of the kernel. + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. + * + * @return A vector representing the scale factor. + */ template<typename ValueType> [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept { return vector_type<ValueType>({ValueType(1.) / (cell_width * cell_width)}); } - template<typename ValueType1, typename ValueType2, std::size_t Dim, std::size_t... Is> - [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dim> const& xs, - container::point<ValueType2, Dim> const& ys, + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Dimension The dimension of the points. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension, std::size_t... Is> + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, Dimension> const& xs, + container::point<ValueType2, Dimension> const& ys, std::index_sequence<Is...>) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { using decayed_type = typename std::decay_t<ValueType1>; return matrix_type<decayed_type>{decayed_type(1.0) / (((xs.at(Is) - ys.at(Is)) * (xs.at(Is) - ys.at(Is))) + ...)}; } - static constexpr int separation_criterion{1}; + + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; - //////////////////////////////////////////////////////////////////////////////////////////////// - /// \brief grad_one_over_r matrix kernel to compute the gradient of Laplace kernel. - /// - /// grad_one_over_r\f$ k(x,y) : R -> R^{d}\f$ with \f$d \f$ the space - /// dimension. - /// - /// \f$k(x,y) = \grad | x - y |^{-2}\f$ - /// For \f$d = 2 \f$ <p> - /// \f$x = (x1,x2), y = (y1,y2)\f$ and \f$|x-y|^2 = (x1-y1)^2 + (x2-y2)^2 \f - /// - /// \f$k(x,y) = \grad | x - y |^{-2} = \frac{-2 *(x-y)}{|x-y|^4 }\f$ - /// - /// The kernel is homogeneous \f$k(ax,ay) = 1/a^3 k(x,y) \f$ - /// - /// scale factor is \f$1/a^3 \f$ - /// - template<std::size_t Dim = 3> + /** + * @brief The grad_one_over_r2 matrix kernel computes the gradient of the one_over_r2 kernel. + * + * The kernel is defined as \f$k(x,y) : R^{km} -> R^{kn}\f$ with \f$km = 1\f$ and \fkn = d\f + * with \f$d\f$ the space dimension. + * + * \f$k(x,y) = \grad | x - y |^{-2}\f$ + * + * - The kernel is homogeneous \f$k(ax,ay)= 1/a^3 k(x,y)\f$. + * - The scale factor is \f$1/a^3\f$. + * + * @tparam Dimension The dimension of the space. + */ + template<std::size_t Dimension = 3> struct grad_one_over_r2 { - static constexpr auto homogeneity_tag{homogeneity::homogenous}; - static constexpr auto symmetry_tag{symmetry::symmetric}; - static constexpr std::size_t km{1}; - static constexpr std::size_t kn{Dim}; - const std::string name() const { return std::string("grad_one_over_r2<" + std::to_string(Dim) + ">"); } + static constexpr std::size_t dimension = Dimension; + // Mandatory constants + static constexpr auto homogeneity_tag{homogeneity::homogenous}; // Specify the homogeneity of the kernel. + static constexpr auto symmetry_tag{symmetry::symmetric}; // Specify the symmetry of the kernel. + static constexpr std::size_t km{1}; // The number of inputs of the kernel. + static constexpr std::size_t kn{Dimension}; // The number of outputs of the kernels. + + // Mandatory types template<typename ValueType> - using matrix_type = std::array<ValueType, kn * km>; + using matrix_type = std::array<ValueType, kn * km>; // Matrix type that is used in the kernel. template<typename ValueType> - using vector_type = std::array<ValueType, kn>; + using vector_type = std::array<ValueType, kn>; // Vector type that is used in the kernel. + + /** + * @brief Returns the name of the kernel. + * + * @return A string representing the kernel's name. + */ + const std::string name() const { return std::string("grad_one_over_r2<" + std::to_string(Dimension) + ">"); } + /** + * @brief Returns the mutual coefficient of size \f$kn\f$. + * + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). + * + * @return A vector containing the mutual coefficients. + */ template<typename ValueType> [[nodiscard]] inline constexpr auto mutual_coefficient() const { @@ -122,14 +213,57 @@ namespace scalfmm::matrix_kernels::others return mc; } - template<typename ValueType> - [[nodiscard]] inline auto evaluate(container::point<ValueType, Dim> const& x, - container::point<ValueType, Dim> const& y) const noexcept + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2> + [[nodiscard]] inline auto operator()(container::point<ValueType1, dimension> const& x, + container::point<ValueType2, dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { - return variadic_evaluate(x, y, std::make_index_sequence<Dim>{}); + return evaluate(x, y); } - // + /** + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, dimension> const& x, + container::point<ValueType2, dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> + { + return variadic_evaluate(x, y, std::make_index_sequence<dimension>{}); + } + + /** + * @brief Returns the scale factor of the kernel. + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. + * + * @return A vector representing the scale factor. + */ template<typename ValueType> [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept { @@ -139,23 +273,35 @@ namespace scalfmm::matrix_kernels::others return sf; } - template<typename ValueType, std::size_t... Is> - [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType, Dim> const& xs, - container::point<ValueType, Dim> const& ys, + /** + * @brief Helper variadic function that evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the element in the first point. + * @tparam ValueType2 Type of the element in the second point. + * @tparam Is A parameter pack representing the indices for accessing the coordinates of the points. + * + * @param xs The first multidimensional point (e.g., a 2D point). + * @param ys The second multidimensional point (e.g., a 2D point). + * @param std::index_sequence<Is...> An index sequence used to unpack and iterate over the dimensions. + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t... Is> + [[nodiscard]] inline auto variadic_evaluate(container::point<ValueType1, dimension> const& xs, + container::point<ValueType2, dimension> const& ys, std::index_sequence<Is...>) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { using std::sqrt; - using decayed_type = typename std::decay_t<ValueType>; + using decayed_type = typename std::decay_t<ValueType1>; - ValueType tmp = decayed_type(1.0) / (((xs.at(Is) - ys.at(Is)) * (xs.at(Is) - ys.at(Is))) + ...); - ValueType r4{pow(tmp, 2)}; - return matrix_type<decayed_type>{(ValueType(-2.0) * r4 * (xs.at(Is) - ys.at(Is)))...}; - //-2.0*r4 * (xs - ys); + decayed_type tmp = decayed_type(1.0) / (((xs.at(Is) - ys.at(Is)) * (xs.at(Is) - ys.at(Is))) + ...); + decayed_type r4{pow(tmp, 2)}; + return matrix_type<decayed_type>{(decayed_type(-2.0) * r4 * (xs.at(Is) - ys.at(Is)))...}; } - static constexpr int separation_criterion{1}; + static constexpr int separation_criterion{1}; // Criterion used to separate near and far field. }; } // namespace scalfmm::matrix_kernels::others - -#endif diff --git a/include/scalfmm/matrix_kernels/template_matrix_kernel.hpp b/include/scalfmm/matrix_kernels/template_matrix_kernel.hpp index ebd56d1f88efdca23cb6e2484ae24a900d04c645..78a6d0dd41601d9535a3a4f56c6300d277e6e75b 100644 --- a/include/scalfmm/matrix_kernels/template_matrix_kernel.hpp +++ b/include/scalfmm/matrix_kernels/template_matrix_kernel.hpp @@ -1,64 +1,122 @@ -#ifndef SCALFMM_MATRIX_KERNELS_TEMPLATE_HPP -#define SCALFMM_MATRIX_KERNELS_TEMPLATE_HPP -/////// -/// \brief The name struct corresponds to the the name of the kernel \f$ K(x,y) \f$ -/// -/// The kernel \f$K(x,y): R^{km} -> R^{kn}\f$ -/// -/// The kernel is homogeneous if K(ax,ay) = a^p K(x,y) holds -/// The kernel is symmetric if the kernel satisfies all symmetries (axes, x=y, ...) -/// +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/matrix_kernels/template_matrix_kernel.hpp +// -------------------------------- +#pragma once + +/** + * @brief Represents a kernel with properties and methods for evaluation and configuration. + * + * The name of the struct corresponds to the kernel \f$ K(x,y) \f$. + * The kernel is defined as \f$K(x,y): R^{km} -> R^{kn}\f$. + * + * - The kernel is homogeneous if \f$K(ax, ay) = a^p K(x, y)\f$ holds. + * - The kernel is symmetric if it satisfies all symmetries (e.g., axes, \f$x = y\f$, etc.). + */ struct name { - static constexpr auto homogeneity_tag{}; // homogeneity::homogenous or homogeneity::non_homogenous - static constexpr auto symmetry_tag{}; // symmetry::symmetric or symmetry::non_symmetric - static constexpr std::size_t km{}; // the dimension + // Mandatory constants + /** + * @brief Specifies the homogeneity of the kernel. + * + * Can be `homogeneity::homogeneous` or `homogeneity::non_homogeneous`. + */ + static constexpr auto homogeneity_tag{}; + + /** + * @brief Specifies the symmetry of the kernel. + * + * Can be `symmetry::symmetric` or `symmetry::non_symmetric`. + */ + static constexpr auto symmetry_tag{}; + + /** + * @brief Number of inputs (\f$km\f$) for the kernel. + */ + static constexpr std::size_t km{}; + + /** + * @brief Number of outputs (\f$kn\f$) for the kernel. + */ static constexpr std::size_t kn{}; + /** - * @brief + * @brief Criterion used to separate near and far fields. + */ + static constexpr int separation_criterion{1}; + + // Mandatory types + /** + * @brief Type representing a matrix used in the kernel. * + * @tparam ValueType Type of the elements in the matrix. */ - static constexpr int separation_criterion{1}; // the separation criterion used to separate near and far field. - // - // Mandatory type template<typename ValueType> using matrix_type = std::array<ValueType, kn * km>; + + /** + * @brief Type representing a vector used in the kernel. + * + * @tparam ValueType Type of the elements in the vector. + */ template<typename ValueType> using vector_type = std::array<ValueType, kn>; - // + /** - * @brief return the name of the kernel + * @brief Returns the name of the kernel. * + * @return A string representing the kernel's name. */ const std::string name() const { return std::string("name"); } - template<typename ValueType> /** - * @brief Return the mutual coefficient of size kn + * @brief Returns the mutual coefficient of size \f$kn\f$. * - * The coefficient is used in the direct pass when the kernel is used - * to compute the interactions inside the leaf when we use the symmetry - * of tke kernel ( N^2/2 when N is the number of particles) + * This coefficient is used during the direct pass when the kernel is applied + * to compute interactions inside the leaf. Utilizing the kernel's symmetry + * reduces the computational complexity from \f$N^2\f$ to \f$N^2 / 2\f$ (where \f$N\f$ is + * the number of particles). * - * @return constexpr auto + * @return A vector containing the mutual coefficients. */ - [[nodiscard]] inline constexpr auto mutual_coefficient() const + [[nodiscard]] inline constexpr auto mutual_coefficient() const { return vector_type<ValueType>{ValueType(1.)}; } + + /** + * @brief Overload of the `()` operator to evaluate the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. + * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). + * + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). + */ + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto operator()(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept + -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, + matrix_type<std::decay_t<ValueType1>>> { - return vector_type<ValueType>{ValueType(1.)}; + return evaluate(x, y); } - template<typename ValueType> /** - * @brief evaluate the kernel at points x and y + * @brief Evaluates the kernel at points \f$x\f$ and \f$y\f$. + * + * @tparam ValueType1 Type of the elements in the first point. + * @tparam ValueType2 Type of the elements in the second point. + * @tparam Dimension The dimension of the points. * + * @param x Multidimensional point (e.g., a 2D point). + * @param y Multidimensional point (e.g., a 2D point). * - * @param x 2d point - * @param y 2d point - * @return return the matrix K(x,y) in a vector (row storage) + * @return The matrix \f$K(x, y)\f$ in a vector (row-major storage). */ - template<typename ValueType1, typename ValueType2, int Dim> - [[nodiscard]] inline auto evaluate(container::point<ValueType1, 2> const& x, - container::point<ValueType2, 2> const& y) const noexcept + template<typename ValueType1, typename ValueType2, std::size_t Dimension> + [[nodiscard]] inline auto evaluate(container::point<ValueType1, Dimension> const& x, + container::point<ValueType2, Dimension> const& y) const noexcept -> std::enable_if_t<std::is_same_v<std::decay_t<ValueType1>, std::decay_t<ValueType2>>, matrix_type<std::decay_t<ValueType1>>> { @@ -66,10 +124,17 @@ struct name return matrix_type<decayed_type>{...}; } + /** - * @brief return the scale factor of the kernel + * @brief Returns the scale factor of the kernel. + * + * This method is used only if the kernel is homogeneous. + * + * @tparam ValueType Type of the cell width. + * + * @param cell_width The width of the cell. * - * the method is used only if the kernel is homogeneous + * @return A vector representing the scale factor. */ template<typename ValueType> [[nodiscard]] inline auto scale_factor(ValueType cell_width) const noexcept @@ -77,4 +142,3 @@ struct name return vector_type<ValueType>{...}; } }; -#endif diff --git a/include/scalfmm/memory/storage.hpp b/include/scalfmm/memory/storage.hpp index 70248c17db1286a539fcffaa36d53d515822b2f5..1ebd6d243c8812c7f3758186a86fa5118b0906c9 100644 --- a/include/scalfmm/memory/storage.hpp +++ b/include/scalfmm/memory/storage.hpp @@ -1,16 +1,16 @@ // -------------------------------- // See LICENCE file at project root -// File : memory/storage.hpp +// File : scalfmm/memory/storage.hpp // -------------------------------- #ifndef SCALFMM_MEMORY_STORAGE_HPP #define SCALFMM_MEMORY_STORAGE_HPP -#include <scalfmm/meta/utils.hpp> -#include <scalfmm/options/options.hpp> +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/options/options.hpp" -#include <xtensor/xfixed.hpp> -#include <xtensor/xtensor.hpp> -#include <xtensor/xarray.hpp> +#include "xtensor/xarray.hpp" +#include "xtensor/xfixed.hpp" +#include "xtensor/xtensor.hpp" #include <array> #include <complex> @@ -18,9 +18,22 @@ namespace scalfmm::memory { + /** + * @brief + * + * @tparam S + */ template<typename S> struct storage_traits; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam _static + * @tparam axes + */ template<typename ValueType, std::size_t Dimension, bool _static, std::size_t... axes> struct alignas(XTENSOR_FIXED_ALIGN) tensor_storage { @@ -31,13 +44,50 @@ namespace scalfmm::memory using tensor_type = typename storage_traits<self_type>::tensor_type; using outer_shape = typename storage_traits<self_type>::outer_shape; + /** + * @brief Construct a new tensor storage object + * + */ tensor_storage() = default; + + /** + * @brief Construct a new tensor storage object + * + */ tensor_storage(tensor_storage const&) = default; + + /** + * @brief Construct a new tensor storage object + * + */ tensor_storage(tensor_storage&&) noexcept = default; + + /** + * @brief + * + * @return tensor_storage& + */ inline auto operator=(tensor_storage const&) -> tensor_storage& = default; + + /** + * @brief + * + * @return tensor_storage& + */ inline auto operator=(tensor_storage&&) noexcept -> tensor_storage& = default; + + /** + * @brief Destroy the tensor storage object + * + */ ~tensor_storage() = default; + /** + * @brief Construct a new tensor storage object + * + * @param size + * @param init + */ tensor_storage(std::size_t size, value_type init = value_type(0.)) { std::vector shape(dimension, size); @@ -54,6 +104,12 @@ namespace scalfmm::memory meta::looper<sizeof...(axes)>{}(fill, stops); } + /** + * @brief Construct a new tensor storage object + * + * @param shape + * @param init + */ tensor_storage(std::initializer_list<std::size_t> shape, value_type init = value_type(0.)) { std::array stops{axes...}; @@ -68,6 +124,12 @@ namespace scalfmm::memory meta::looper<sizeof...(axes)>{}(fill, stops); } + /** + * @brief Construct a new tensor storage object + * + * @param shape + * @param init + */ tensor_storage(std::vector<std::size_t> shape, value_type init = value_type(0.)) { std::array stops{axes...}; @@ -81,6 +143,11 @@ namespace scalfmm::memory meta::looper<sizeof...(axes)>{}(fill, stops); } + + /** + * @brief + * + */ auto reset() noexcept -> void { std::array stops{axes...}; @@ -93,17 +160,59 @@ namespace scalfmm::memory meta::looper<sizeof...(axes)>{}(fill, stops); } + + /** + * @brief + * + * @return tensor_type const& + */ auto get() const noexcept -> tensor_type const& { return m_tensor; } + + /** + * @brief + * + * @return tensor_type& + */ auto get() noexcept -> tensor_type& { return m_tensor; } + + /** + * @brief + * + * @tparam Is + * @param i + * @return inner_type const& + */ template<typename... Is> - constexpr auto at(Is... i) const noexcept -> inner_type const& { return m_tensor.at(i...); } + constexpr auto at(Is... i) const noexcept -> inner_type const& + { + return m_tensor.at(i...); + } + + /** + * @brief + * + * @tparam Is + * @param i + * @return inner_type& + */ template<typename... Is> - constexpr auto at(Is... i) noexcept -> inner_type& { return m_tensor.at(i...); } + constexpr auto at(Is... i) noexcept -> inner_type& + { + return m_tensor.at(i...); + } private: tensor_type m_tensor{}; }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam _static + * @tparam axes + */ template<typename ValueType, std::size_t Dimension, bool _static, std::size_t... axes> struct storage_traits<tensor_storage<ValueType, Dimension, _static, axes...>> { @@ -113,6 +222,13 @@ namespace scalfmm::memory using tensor_type = xt::xtensor_fixed<inner_type, outer_shape>; }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam k + */ template<typename ValueType, std::size_t Dimension, std::size_t k> struct alignas(XTENSOR_FIXED_ALIGN) multipoles_storage : protected tensor_storage<ValueType, Dimension, true, k> { @@ -124,78 +240,147 @@ namespace scalfmm::memory using tensor_type = typename storage_traits<base_type>::tensor_type; using outer_shape = typename storage_traits<base_type>::outer_shape; using multipoles_iterator_type = std::array<typename inner_type::storage_type::iterator, k>; - using multipoles_const_iterator_type = - std::array<typename inner_type::storage_type::const_iterator, k>; + using multipoles_const_iterator_type = std::array<typename inner_type::storage_type::const_iterator, k>; using base_type::base_type; using base_type::get; + /** + * @brief + * + * @return tensor_type const& + */ auto multipoles() const noexcept -> tensor_type const& { return get(); } + + /** + * @brief + * + * @return tensor_type const& + */ auto cmultipoles() const noexcept -> tensor_type const& { return get(); } + + /** + * @brief + * + * @return tensor_type& + */ auto multipoles() noexcept -> tensor_type& { return get(); } + + /** + * @brief + * + * @param i + * @return inner_type const& + */ constexpr auto multipoles(std::size_t i) const noexcept -> inner_type const& { return this->at(i); } + + /** + * @brief + * + * @param i + * @return inner_type const& + */ constexpr auto cmultipoles(std::size_t i) const noexcept -> inner_type const& { return this->at(i); } + + /** + * @brief + * + * @param i + * @return inner_type& + */ constexpr auto multipoles(std::size_t i) noexcept -> inner_type& { return this->at(i); } - auto inline reset_multipoles() noexcept -> void - { - base_type::reset(); - // for(std::size_t m{0}; m < k; ++m) { this->at(m).reset(); } - } + /** + * @brief + * + */ + inline auto reset_multipoles() noexcept -> void { base_type::reset(); } + // Accessors to multipoles and locals iterators + + /** + * @brief + * + * @return multipoles_iterator_type + */ [[nodiscard]] inline auto multipoles_begin() -> multipoles_iterator_type { multipoles_iterator_type its; - for(std::size_t m{0}; m<k; ++m) + for(std::size_t m{0}; m < k; ++m) { its.at(m) = this->at(m).begin(); } return its; } + /** + * @brief + * + * @return multipoles_const_iterator_type + */ [[nodiscard]] inline auto multipoles_begin() const -> multipoles_const_iterator_type { multipoles_const_iterator_type its; - for(std::size_t m{0}; m<k; ++m) + for(std::size_t m{0}; m < k; ++m) { its.at(m) = this->at(m).cbegin(); } return its; } + /** + * @brief + * + * @return multipoles_const_iterator_type + */ [[nodiscard]] inline auto cmultipoles_begin() const -> multipoles_const_iterator_type { multipoles_const_iterator_type its; - for(std::size_t m{0}; m<k; ++m) + for(std::size_t m{0}; m < k; ++m) { its.at(m) = this->at(m).cbegin(); } return its; } + /** + * @brief + * + * @return multipoles_iterator_type + */ [[nodiscard]] inline auto multipoles_end() -> multipoles_iterator_type { multipoles_iterator_type its; - for(std::size_t m{0}; m<k; ++m) + for(std::size_t m{0}; m < k; ++m) { its.at(m) = this->at(m).end(); } return its; } + /** + * @brief + * + * @return multipoles_const_iterator_type + */ [[nodiscard]] inline auto multipoles_end() const -> multipoles_const_iterator_type { multipoles_const_iterator_type its; - for(std::size_t m{0}; m<k; ++m) + for(std::size_t m{0}; m < k; ++m) { its.at(m) = this->at(m).cend(); } return its; } + /** + * @brief + * + * @return multipoles_const_iterator_type + */ [[nodiscard]] inline auto cmultipoles_end() const -> multipoles_const_iterator_type { multipoles_const_iterator_type its; - for(std::size_t m{0}; m<k; ++m) + for(std::size_t m{0}; m < k; ++m) { its.at(m) = this->at(m).cend(); } @@ -203,12 +388,26 @@ namespace scalfmm::memory } }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam axes + */ template<typename ValueType, std::size_t Dimension, std::size_t... axes> struct storage_traits<multipoles_storage<ValueType, Dimension, axes...>> : storage_traits<tensor_storage<ValueType, Dimension, true, axes...>> { }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam k + */ template<typename ValueType, std::size_t Dimension, std::size_t k> struct alignas(XTENSOR_FIXED_ALIGN) locals_storage : private tensor_storage<ValueType, Dimension, true, k> { @@ -220,77 +419,148 @@ namespace scalfmm::memory using tensor_type = typename storage_traits<base_type>::tensor_type; using outer_shape = typename storage_traits<base_type>::outer_shape; using locals_iterator_type = std::array<typename inner_type::storage_type::iterator, k>; - using locals_const_iterator_type = - std::array<typename inner_type::storage_type::const_iterator, k>; + using locals_const_iterator_type = std::array<typename inner_type::storage_type::const_iterator, k>; using base_type::base_type; using base_type::get; + /** + * @brief + * + * @return tensor_type const& + */ auto locals() const noexcept -> tensor_type const& { return get(); } + + /** + * @brief + * + * @return tensor_type const& + */ auto clocals() const noexcept -> tensor_type const& { return get(); } + + /** + * @brief + * + * @return tensor_type& + */ auto locals() noexcept -> tensor_type& { return get(); } + + /** + * @brief + * + * @param i + * @return inner_type const& + */ constexpr auto locals(std::size_t i) const noexcept -> inner_type const& { return this->at(i); } + + /** + * @brief + * + * @param i + * @return inner_type const& + */ constexpr auto clocals(std::size_t i) const noexcept -> inner_type const& { return this->at(i); } + + /** + * @brief + * + * @param i + * @return inner_type& + */ constexpr auto locals(std::size_t i) noexcept -> inner_type& { return this->at(i); } - auto inline reset_locals() noexcept -> void { base_type::reset(); } + /** + * @brief + * + */ + inline auto reset_locals() noexcept -> void { base_type::reset(); } + // Accesors to multipoles and locals iterators + + /** + * @brief + * + * @return locals_iterator_type + */ [[nodiscard]] inline auto locals_begin() -> locals_iterator_type { locals_iterator_type its; - for(std::size_t n{0}; n<k; ++n) + for(std::size_t n{0}; n < k; ++n) { its.at(n) = this->at(n).begin(); } return its; } + /** + * @brief + * + * @return locals_const_iterator_type + */ [[nodiscard]] inline auto locals_begin() const -> locals_const_iterator_type { locals_const_iterator_type its; - for(std::size_t n{0}; n<k; ++n) + for(std::size_t n{0}; n < k; ++n) { its.at(n) = this->at(n).cbegin(); } return its; - } + /** + * @brief + * + * @return locals_const_iterator_type + */ [[nodiscard]] inline auto clocals_begin() const -> locals_const_iterator_type { locals_const_iterator_type its; - for(std::size_t n{0}; n<k; ++n) + for(std::size_t n{0}; n < k; ++n) { its.at(n) = this->at(n).cbegin(); } return its; } + /** + * @brief + * + * @return locals_iterator_type + */ [[nodiscard]] inline auto locals_end() -> locals_iterator_type { locals_iterator_type its; - for(std::size_t n{0}; n<k; ++n) + for(std::size_t n{0}; n < k; ++n) { its.at(n) = this->at(n).end(); } return its; } + /** + * @brief + * + * @return locals_const_iterator_type + */ [[nodiscard]] inline auto locals_end() const -> locals_const_iterator_type { locals_const_iterator_type its; - for(std::size_t n{0}; n<k; ++n) + for(std::size_t n{0}; n < k; ++n) { its.at(n) = this->at(n).cend(); } return its; - } + /** + * @brief + * + * @return locals_const_iterator_type + */ [[nodiscard]] inline auto clocals_end() const -> locals_const_iterator_type { locals_const_iterator_type its; - for(std::size_t n{0}; n<k; ++n) + for(std::size_t n{0}; n < k; ++n) { its.at(n) = this->at(n).cend(); } @@ -298,14 +568,29 @@ namespace scalfmm::memory } }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam axes + */ template<typename ValueType, std::size_t Dimension, std::size_t... axes> struct storage_traits<locals_storage<ValueType, Dimension, axes...>> : storage_traits<tensor_storage<ValueType, Dimension, true, axes...>> { }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam k + */ template<typename ValueType, std::size_t Dimension, std::size_t k> - struct alignas(XTENSOR_FIXED_ALIGN) transformed_multipoles_storage : private tensor_storage<ValueType, Dimension, false, k> + struct alignas(XTENSOR_FIXED_ALIGN) transformed_multipoles_storage + : private tensor_storage<ValueType, Dimension, false, k> { static constexpr std::size_t dimension = Dimension; using base_type = tensor_storage<ValueType, Dimension, false, k>; @@ -318,26 +603,107 @@ namespace scalfmm::memory using base_type::base_type; using base_type::get; + /** + * @brief Construct a new transformed multipoles storage object + * + */ transformed_multipoles_storage() = default; + + /** + * @brief Construct a new transformed multipoles storage object + * + */ transformed_multipoles_storage(transformed_multipoles_storage const&) = default; + + /** + * @brief Construct a new transformed multipoles storage object + * + */ transformed_multipoles_storage(transformed_multipoles_storage&&) noexcept = default; + + /** + * @brief + * + * @return transformed_multipoles_storage& + */ inline auto operator=(transformed_multipoles_storage const&) -> transformed_multipoles_storage& = default; + + /** + * @brief + * + * @return transformed_multipoles_storage& + */ inline auto operator=(transformed_multipoles_storage&&) noexcept -> transformed_multipoles_storage& = default; + + /** + * @brief Destroy the transformed multipoles storage object + * + */ ~transformed_multipoles_storage() = default; + /** + * @brief Construct a new transformed multipoles storage object + * + * @param size + * @param init + */ transformed_multipoles_storage(std::size_t size, value_type init = value_type(0.)) : base_type(compute_shape(size), init) { } + /** + * @brief + * + * @return tensor_type const& + */ auto transformed_multipoles() const noexcept -> tensor_type const& { return get(); } + + /** + * @brief + * + * @return tensor_type const& + */ auto ctransformed_multipoles() const noexcept -> tensor_type const& { return get(); } + + /** + * @brief + * + * @return tensor_type& + */ auto transformed_multipoles() noexcept -> tensor_type& { return get(); } + + /** + * @brief + * + * @param i + * @return inner_type const& + */ constexpr auto transformed_multipoles(std::size_t i) const noexcept -> inner_type const& { return get(i); } + + /** + * @brief + * + * @param i + * @return inner_type const& + */ constexpr auto ctransformed_multipoles(std::size_t i) const noexcept -> inner_type const& { return get(i); } + + /** + * @brief + * + * @param i + * @return inner_type& + */ constexpr auto transformed_multipoles(std::size_t i) noexcept -> inner_type& { return get(i); } private: + /** + * @brief + * + * @param size + * @return std::vector<std::size_t> + */ auto compute_shape(std::size_t size) -> std::vector<std::size_t> { std::vector shape(dimension, 2 * size - 1); @@ -346,16 +712,30 @@ namespace scalfmm::memory } }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam axes + */ template<typename ValueType, std::size_t Dimension, std::size_t... axes> struct storage_traits<transformed_multipoles_storage<ValueType, Dimension, axes...>> : storage_traits<tensor_storage<ValueType, Dimension, false, axes...>> { }; + /** + * @brief + * + * @tparam D_ + * @tparam D + * @return std::size_t + */ template<std::size_t D_, std::size_t... D> static constexpr auto check_dimensions() -> std::size_t { - if constexpr (((D_ == D) && ...)) + if constexpr(((D_ == D) && ...)) { return D_; } @@ -365,40 +745,101 @@ namespace scalfmm::memory } } + /** + * @brief + * + * @tparam Storages + */ template<typename... Storages> struct alignas(XTENSOR_FIXED_ALIGN) aggregate_storage : public Storages... { static constexpr std::size_t dimension{check_dimensions<Storages::dimension...>()}; + static constexpr std::size_t number_aggregates = sizeof...(Storages); static_assert(dimension != 0, "Storages have different dimensions !"); using self_type = aggregate_storage<Storages...>; using value_type = std::tuple<typename storage_traits<Storages>::value_type...>; - using inner_type = std::tuple<typename storage_traits<Storages>::inner_type...>; + using inner_type = std::tuple<typename storage_traits<Storages>::inner_type...>; // the true tensor using tensor_type = std::tuple<typename storage_traits<Storages>::tensor_type...>; using outer_shape = std::tuple<typename storage_traits<Storages>::outer_shape...>; + /** + * @brief Construct a new aggregate storage object + * + */ aggregate_storage() = default; + + /** + * @brief Construct a new aggregate storage object + * + */ aggregate_storage(aggregate_storage const&) = default; + + /** + * @brief Construct a new aggregate storage object + * + */ aggregate_storage(aggregate_storage&&) noexcept = default; + + /** + * @brief + * + * @return aggregate_storage& + */ inline auto operator=(aggregate_storage const&) -> aggregate_storage& = default; + + /** + * @brief + * + * @return aggregate_storage& + */ inline auto operator=(aggregate_storage&&) noexcept -> aggregate_storage& = default; + + /** + * @brief Destroy the aggregate storage object + * + */ ~aggregate_storage() = default; + /** + * @brief Construct a new aggregate storage object + * + * @param size + */ aggregate_storage(std::size_t size) : Storages(size)... { } + /** + * @brief Construct a new aggregate storage object + * + * @param size + * @param init + */ aggregate_storage(std::size_t size, typename storage_traits<Storages>::value_type... init) : Storages(size, init)... { } + /** + * @brief Construct a new aggregate storage object + * + * @tparam ShapeVector + * @param shape + */ template<typename... ShapeVector> aggregate_storage(ShapeVector... shape) : Storages(shape)... { } + /** + * @brief Construct a new aggregate storage object + * + * @tparam ShapeVector + * @tparam ValueType + * @param pair + */ template<typename... ShapeVector, typename... ValueType> aggregate_storage(std::pair<ShapeVector, ValueType>... pair) : Storages(pair.first, pair.second)... @@ -406,20 +847,38 @@ namespace scalfmm::memory } }; + /** + * @brief + * + * @tparam Storages + */ template<typename... Storages> struct storage_traits<aggregate_storage<Storages...>> { + static constexpr std::size_t number_aggregates = sizeof...(Storages); using value_type = std::tuple<typename storage_traits<Storages>::value_type...>; using inner_type = std::tuple<typename storage_traits<Storages>::inner_type...>; using tensor_type = std::tuple<typename storage_traits<Storages>::tensor_type...>; using outer_shape = std::tuple<typename storage_traits<Storages>::outer_shape...>; }; + /** + * @brief + * + * @tparam T + * @tparam typename + */ template<typename T, typename = void> struct id_value_type { using type = T; }; + + /** + * @brief + * + * @tparam T + */ template<typename T> struct id_value_type<T, std::void_t<typename T::value_type>> { diff --git a/include/scalfmm/meta/const_functions.hpp b/include/scalfmm/meta/const_functions.hpp index 2d68deb8f65890514c3c1aef6030d0dc297343a7..cbb11800f877b56849e80a8853caf30b0fe07165 100644 --- a/include/scalfmm/meta/const_functions.hpp +++ b/include/scalfmm/meta/const_functions.hpp @@ -1,14 +1,27 @@ +// -------------------------------- // See LICENCE file at project root -// +// File : scalfmm/meta/const_functions.hpp +// -------------------------------- #ifndef SCALFMM_META_CONST_FUNCTIONS_HPP #define SCALFMM_META_CONST_FUNCTIONS_HPP +#include <cstddef> #include <limits> #include <type_traits> -#include <cstddef> namespace scalfmm::meta { + /** + * @brief + * + * @tparam T + * @tparam U + * @param a + * @param b + * @param epsilon + * @return true + * @return false + */ template<typename T, typename U, typename = std::enable_if_t<std::is_floating_point<T>::value, T>, typename = std::enable_if_t<std::is_floating_point<U>::value, U>> constexpr auto feq(const T& a, const U& b, T epsilon = std::numeric_limits<T>::epsilon()) -> bool @@ -16,6 +29,14 @@ namespace scalfmm::meta return a - b < epsilon && b - a < epsilon; } + /** + * @brief + * + * @tparam T + * @param a + * @param p + * @return T + */ template<typename T> constexpr auto pow(T a, std::size_t p) -> T { diff --git a/include/scalfmm/meta/forward.hpp b/include/scalfmm/meta/forward.hpp index d4fff01eecf2c40dc5fcdfa4efb07f566f3b0766..0b3184a732d2abb34edf77e5512f9360e14bd0e2 100644 --- a/include/scalfmm/meta/forward.hpp +++ b/include/scalfmm/meta/forward.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : meta/forward.hpp +// File : scalfmm/meta/forward.hpp // -------------------------------- #ifndef SCALFMM_META_FORWARD_HPP #define SCALFMM_META_FORWARD_HPP @@ -10,25 +10,85 @@ namespace scalfmm::container { + /** + * @brief Forward declaration for 'variadic_adaptor'. + * + * @tparam Derived + * @tparam Containers + */ template<typename Derived, typename... Containers> struct variadic_adaptor; + + /** + * @brief Forward declaration for 'unique_variadic_container'. + * + * @tparam Derived + * @tparam Container + * @tparam Types + */ template<typename Derived, template<typename U, typename Allocator> class Container, typename... Types> struct unique_variadic_container; + + /** + * @brief Forward declaration for 'variadic_container'. + * + * @tparam Derived + * @tparam Types + */ template<typename Derived, typename... Types> struct variadic_container; + + /** + * @brief Forward declaration for 'variadic_container_tuple'. + * + * @tparam Derived + * @tparam Tuple + */ template<typename Derived, typename Tuple> struct variadic_container_tuple; + + /** + * @brief Forward declaration for 'point_impl'. + * + * @tparam ValueType + * @tparam Dimension + */ template<typename ValueType, std::size_t Dimension> struct point_impl; + + /** + * @brief Forward declaration for 'point_proxy'. + * + * @tparam ValueType + * @tparam Dimension + */ template<typename ValueType, std::size_t Dimension> struct point_proxy; + + /** + * @brief Forward declaration for 'point'. + * + * @tparam ValueType + * @tparam Dimension + * @tparam Enable + */ template<typename ValueType, std::size_t Dimension, typename Enable> struct point; + + /** + * @brief Forward declaration for 'particle'. + * + * @tparam PositionType + * @tparam PositionDim + * @tparam InputsType + * @tparam NInputs + * @tparam OutputsType + * @tparam MOutputs + * @tparam Variables + */ template<typename PositionType, std::size_t PositionDim, typename InputsType, std::size_t NInputs, typename OutputsType, std::size_t MOutputs, typename... Variables> struct particle; } // namespace scalfmm::container - -#endif // SCALFMM_META_FORWARD_HPP - +#endif // SCALFMM_META_FORWARD_HPP diff --git a/include/scalfmm/meta/is_valid.hpp b/include/scalfmm/meta/is_valid.hpp index 83eb8869adef04a40f1f83294cec62c0c7b170c8..903b151e4c272ece5357e64847a8032022ee16e5 100644 --- a/include/scalfmm/meta/is_valid.hpp +++ b/include/scalfmm/meta/is_valid.hpp @@ -3,6 +3,10 @@ // It's a attempt to a cleaner and more expressive code for the user. // Author : Pierre Esterie //============================================================================== +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/meta/is_valid.hpp +// -------------------------------- #ifndef SCALFMM_META_IS_VALID_HPP #define SCALFMM_META_IS_VALID_HPP @@ -12,21 +16,48 @@ namespace scalfmm::meta { namespace details { + /** + * @brief + * + * @tparam F + * @tparam Args + * @tparam typename + * @return constexpr auto + */ template<typename F, typename... Args, typename = decltype(std::declval<F&&>()(std::declval<Args&&>()...))> constexpr auto is_valid_impl(int) { return std::true_type{}; } + /** + * @brief + * + * @tparam F + * @tparam Args + * @param ... + * @return constexpr auto + */ template<typename F, typename... Args> constexpr auto is_valid_impl(...) { return std::false_type{}; } + /** + * @brief + * + * @tparam F + */ template<typename F> struct is_valid_fun { + /** + * @brief + * + * @tparam Args + * @return constexpr auto + */ template<typename... Args> constexpr auto operator()(Args&&...) const { @@ -35,14 +66,33 @@ namespace scalfmm::meta }; } // namespace details + /** + * @brief + * + */ struct is_valid_t { + /** + * @brief + * + * @tparam F + * @return constexpr auto + */ template<typename F> constexpr auto operator()(F&&) const { return details::is_valid_fun<F&&>{}; } + /** + * @brief + * + * @tparam F + * @tparam Args + * @param f + * @param args + * @return constexpr auto + */ template<typename F, typename... Args> constexpr auto operator()(F&& f, Args&&... args) const { @@ -50,24 +100,40 @@ namespace scalfmm::meta } }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct type_w { using type = T; }; + /** + * @brief + * + * @tparam T + */ template<typename T> constexpr type_w<T> type_c{}; + /** + * @brief + * + */ constexpr is_valid_t is_valid{}; + /** + * @brief + * + * @tparam F + * @tparam Args + */ template<typename F, typename... Args> inline constexpr bool is_valid_v = [](F&& f, Args&&... args) constexpr -> bool - { - return decltype(is_valid(f, args...))::value; - }(); - - + { return decltype(is_valid(f, args...))::value; }(); } // namespace scalfmm::meta diff --git a/include/scalfmm/meta/traits.hpp b/include/scalfmm/meta/traits.hpp index 8c4002e266332682dbaee52f7e22f7b48558b97a..df1ce1720decf227a9699f6a69ce4e345cc77229 100644 --- a/include/scalfmm/meta/traits.hpp +++ b/include/scalfmm/meta/traits.hpp @@ -3,30 +3,54 @@ // It's a attempt to a cleaner and more expressive code for the user. // Author : Pierre Esterie //============================================================================== +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/meta/traits.hpp +// -------------------------------- #ifndef SCALFMM_META_TRAITS_HPP #define SCALFMM_META_TRAITS_HPP +#include "scalfmm/meta/forward.hpp" +#include "scalfmm/meta/is_valid.hpp" + +#include "xsimd/xsimd.hpp" + #include <array> #include <cstddef> #include <functional> -#include <scalfmm/meta/forward.hpp> -#include <scalfmm/meta/is_valid.hpp> #include <tuple> #include <type_traits> #include <utility> -#include <xsimd/xsimd.hpp> namespace scalfmm { namespace container { + /** + * @brief Forward declaration for 'particle'. + * + * @tparam PositionType + * @tparam PositionDim + * @tparam InputsType + * @tparam NInputs + * @tparam OutputsType + * @tparam MOutputs + * @tparam Variables + */ template<typename PositionType, std::size_t PositionDim, typename InputsType, std::size_t NInputs, typename OutputsType, std::size_t MOutputs, typename... Variables> struct particle; } // namespace container } // namespace scalfmm + namespace xsimd { + /** + * @brief Forward declaration for 'batch'. + * + * @tparam T + * @tparam A + */ template<class T, class A> class batch; } // namespace xsimd @@ -34,125 +58,302 @@ namespace xsimd // Traits namespace scalfmm::meta { + /** + * @brief + * + * @tparam Whatever + * @tparam T + * @param t + * @return constexpr auto + */ template<std::size_t Whatever, typename T> inline constexpr auto id(T&& t) { return std::forward<T>(t); } + /** + * @brief + * + * @tparam T + * @tparam Trait + * @return constexpr auto + */ template<typename T, template<typename> class Trait> inline constexpr auto delayed_trait(std::true_type) { return typename Trait<T>::type{}; } + /** + * @brief + * + */ struct foo { }; + /** + * @brief + * + * @tparam T + * @tparam Trait + * @param b + * @return constexpr auto + */ template<typename T, template<typename> class Trait> inline constexpr auto delayed_trait(std::false_type b) { return foo{}; } + /** + * @brief + * + */ inline constexpr auto is_equality_comparable_f = meta::is_valid([](auto&& a, auto&& b) -> decltype(a == b) {}); + + /** + * @brief + * + */ inline constexpr auto has_begin_f = meta::is_valid([](auto&& a) -> decltype(a.begin()) {}); + + /** + * @brief + * + */ inline constexpr auto has_cbegin_f = meta::is_valid([](auto&& a) -> decltype(a.cbegin()) {}); + + /** + * @brief + * + */ inline constexpr auto has_rbegin_f = meta::is_valid([](auto&& a) -> decltype(a.rbegin()) {}); + + /** + * @brief + * + */ inline constexpr auto has_crbegin_f = meta::is_valid([](auto&& a) -> decltype(a.crbegin()) {}); + + /** + * @brief + * + */ inline constexpr auto has_end_f = meta::is_valid([](auto&& a) -> decltype(a.end()) {}); + + /** + * @brief + * + */ inline constexpr auto has_cend_f = meta::is_valid([](auto&& a) -> decltype(a.cend()) {}); + + /** + * @brief + * + */ inline constexpr auto has_rend_f = meta::is_valid([](auto&& a) -> decltype(a.rend()) {}); + + /** + * @brief + * + */ inline constexpr auto has_crend_f = meta::is_valid([](auto&& a) -> decltype(a.crend()) {}); + + /** + * @brief + * + */ inline constexpr auto has_plus_f = meta::is_valid([](auto&& a, auto&& b) -> decltype(a + b) {}); + + /** + * @brief + * + */ inline constexpr auto has_size_func_f = meta::is_valid([](auto t) -> decltype(t.size()) {}); + + /** + * @brief + * + */ inline constexpr auto has_max_size_func_f = meta::is_valid([](auto t) -> decltype(t.max_size()) {}); + + /** + * @brief + * + */ inline constexpr auto has_empty_func_f = meta::is_valid([](auto t) -> decltype(t.empty()) {}); + + /** + * @brief + * + */ inline constexpr auto has_resize_func_f = meta::is_valid([](auto t, auto count) -> decltype(t.resize(count)) {}); + + /** + * @brief + * + */ inline constexpr auto has_resize_valued_func_f = meta::is_valid([](auto t, auto count, auto value) -> decltype(t.resize(count, value)) {}); + + /** + * @brief + * + */ inline constexpr auto has_clear_func_f = meta::is_valid([](auto t) -> decltype(t.clear()) {}); // Containers related traits + + /** + * @brief + * + * @tparam T + */ template<class T> struct has_begin { using type = decltype(std::declval<std::decay_t<T>>().begin()); }; + /** + * @brief + * + * @tparam T + */ template<class T> struct has_cbegin { using type = decltype(std::declval<std::decay_t<T>>().cbegin()); }; + /** + * @brief + * + * @tparam T + */ template<class T> struct has_rbegin { using type = decltype(std::declval<std::decay_t<T>>().rbegin()); }; + /** + * @brief + * + * @tparam T + */ template<class T> struct has_crbegin { using type = decltype(std::declval<std::decay_t<T>>().crbegin()); }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_end { using type = decltype(std::declval<std::decay_t<T>>().end()); }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_cend { using type = decltype(std::declval<std::decay_t<T>>().cend()); }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_rend { using type = decltype(std::declval<std::decay_t<T>>().rend()); }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_crend { using type = decltype(std::declval<std::decay_t<T>>().crend()); }; + /** + * @brief + * + * @tparam T + */ template<class T> struct has_range_interface { static const bool value = has_begin<T>::value && has_end<T>::value; }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_size_func { using type = decltype(std::declval<std::decay_t<T>>().size()); }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_empty_func { using type = decltype(std::declval<std::decay_t<T>>().empty()); }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_max_size_func { using type = decltype(std::declval<std::decay_t<T>>().max_size()); }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_resize_func { using type = decltype(std::declval<std::decay_t<T>>().resize(typename T::size_type{})); }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_resize_valued_func { @@ -160,6 +361,11 @@ namespace scalfmm::meta decltype(std::declval<std::decay_t<T>>().resize(typename T::size_type{}, typename T::value_type{})); }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_clear_func { @@ -167,76 +373,256 @@ namespace scalfmm::meta }; // Type version + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_begin_t = decltype(std::declval<std::decay_t<T>>().begin()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_cbegin_t = decltype(std::declval<std::decay_t<T>>().cbegin()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_rbegin_t = decltype(std::declval<std::decay_t<T>>().rbegin()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_crbegin_t = decltype(std::declval<std::decay_t<T>>().crbegin()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_end_t = decltype(std::declval<std::decay_t<T>>().end()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_cend_t = decltype(std::declval<std::decay_t<T>>().cend()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_rend_t = decltype(std::declval<std::decay_t<T>>().rend()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_crend_t = decltype(std::declval<std::decay_t<T>>().crend()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_size_func_t = decltype(std::declval<std::decay_t<T>>().size()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_empty_func_t = decltype(std::declval<std::decay_t<T>>().empty()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_max_size_func_t = decltype(std::declval<std::decay_t<T>>().max_size()); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_resize_func_t = decltype(std::declval<std::decay_t<T>>().resize(typename T::size_type{})); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_resize_valued_func_t = decltype(std::declval<std::decay_t<T>>().resize(typename T::size_type{}, typename T::value_type{})); + + /** + * @brief + * + * @tparam T + */ template<typename T> using has_clear_func_t = decltype(std::declval<std::decay_t<T>>().clear()); // Value version + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_begin_v = decltype(has_begin_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_cbegin_v = decltype(has_cbegin_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_rbegin_v = decltype(has_rbegin_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_crbegin_v = decltype(has_crbegin_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_end_v = decltype(has_end_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_cend_v = decltype(has_cend_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_rend_v = decltype(has_rend_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_crend_v = decltype(has_crend_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_range_interface_v = has_range_interface<T>::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_size_func_v = decltype(has_size_func_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_empty_func_v = decltype(has_empty_func_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_max_size_func_v = decltype(has_max_size_func_f(T{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_resize_func_v = decltype(has_resize_func_f(T{}, typename T::size_type{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_resize_valued_func_v = decltype(has_resize_valued_func_f(T{}, typename T::size_type{}, typename T::value_type{}))::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_clear_func_v = decltype(has_clear_func_f(T{}))::value; // Other traits + /** + * @brief + * + * @tparam T + */ template<class T> struct is_equality_comparable { static const bool value = decltype(is_equality_comparable_f(T{}, T{}))::value; }; + /** * @brief check if the type is float (std::complex) or not * @@ -247,7 +633,11 @@ namespace scalfmm::meta { }; - // specialization recognizes types that do have a nested ::type member: + /** + * @brief specialization recognizes types that do have a nested ::type member: + * + * @tparam T + */ template<class T> struct is_float<T, std::void_t<float>> : std::true_type { @@ -262,10 +652,17 @@ namespace scalfmm::meta struct is_double : std::false_type { }; + + /** + * @brief + * + * @tparam T + */ template<class T> struct is_double<T, std::void_t<double>> : std::true_type { }; + /** * @brief check if the type is complex (std::complex) or not * @@ -276,20 +673,30 @@ namespace scalfmm::meta { }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_complex<std::complex<T>, std::void_t<std::complex<T>>> : std::true_type { }; + /** - * @brief return true if the type is complex - * - * @tparam T - */ + * @brief return true if the type is complex + * + * @tparam T + */ template<typename T> inline constexpr bool is_complex_v = is_complex<T>::value; - // - // Tuple cat + /** + * @brief Tuple cat + * + * @tparam T + * @tparam U + */ template<typename T, typename U> struct tuple_cat { @@ -310,84 +717,193 @@ namespace scalfmm::meta using type = T; }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type { using type = typename T::value_type; }; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline constexpr bool has_value_type_v = has_value_type<T>::value; + /** + * @brief + * + * @tparam T + */ template<typename T> using has_value_type_t = typename has_value_type<T>::type; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_addable { static const bool value = decltype(has_plus_f(T{}, T{}))::value; }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_simd : xsimd::is_batch<T> { }; - // tree related type traits + /** + * @brief Tree related type traits. + * + * @tparam ypename + * @tparam typename + */ template<typename, typename = std::void_t<>> struct is_leaf_group_symbolics : std::false_type { }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_leaf_group_symbolics<T, std::void_t<typename T::group_leaf_type>> : std::true_type { }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_arithmetic : std::is_arithmetic<T> { }; + + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_integral : std::is_integral<T> { }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_arithmetic<xsimd::batch<T>> : is_arithmetic<T> { }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_integral<xsimd::batch<T>> : is_integral<T> { }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_tuple : std::false_type { }; + + /** + * @brief + * + * @tparam Ts + */ template<typename... Ts> struct is_tuple<std::tuple<Ts...>> : std::true_type { }; + + /** + * @brief + * + * @tparam Ts + */ template<typename... Ts> inline constexpr bool is_tuple_v = is_tuple<Ts...>::value; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_array : std::false_type { }; + + /** + * @brief + * + * @tparam T + * @tparam N + */ template<typename T, std::size_t N> struct is_array<std::array<T, N>> : std::true_type { }; + + /** + * @brief + * + * @tparam Ts + */ template<typename... Ts> inline constexpr bool is_array_v = is_array<Ts...>::value; + /** + * @brief + * + * @tparam T + */ template<typename T> struct is_particle : std::false_type { }; + + /** + * @brief + * + * @tparam PositionType + * @tparam PositionDim + * @tparam InputsType + * @tparam NInputs + * @tparam OutputsType + * @tparam MOutputs + * @tparam Variables + */ template<typename PositionType, std::size_t PositionDim, typename InputsType, std::size_t NInputs, typename OutputsType, std::size_t MOutputs, typename... Variables> struct is_particle< @@ -395,30 +911,82 @@ namespace scalfmm::meta : std::true_type { }; + + /** + * @brief + * + * @tparam T + */ template<typename T> inline static constexpr bool is_particle_v = is_particle<T>::value; + /** + * @brief + * + * @tparam T + */ template<typename T> struct td; // interpolator + + /** + * @brief + * + */ inline constexpr auto sig_preprocess_f = meta::is_valid( [](auto&& inter, auto& cell, std::size_t th) -> decltype(inter.apply_multipoles_preprocessing_impl(cell, th)) {}); + + /** + * @brief + * + */ inline constexpr auto sig_postprocess_f = meta::is_valid([](auto&& inter, auto& cell, auto& buffer, std::size_t th) -> decltype(inter.apply_multipoles_postprocessing_impl(cell, buffer, th)) {}); + + /** + * @brief + * + */ inline constexpr auto sig_buffer_init_f = meta::is_valid([](auto&& inter) -> decltype(inter.buffer_initialization_impl()) {}); + + /** + * @brief + * + */ inline constexpr auto sig_buffer_shape_f = meta::is_valid([](auto&& inter) -> decltype(inter.buffer_shape_impl()) {}); + + /** + * @brief + * + */ inline constexpr auto sig_buffer_reset_f = meta::is_valid([](auto&& inter, auto& buf) -> decltype(inter.buffer_reset_impl(buf)) {}); + + /** + * @brief + * + */ inline constexpr auto sig_init_k_f = meta::is_valid([](auto&& inter) -> decltype(inter.initialize_k_impl()) {}); + + /** + * @brief + * + */ inline constexpr auto sig_gen_k_f = meta::is_valid([](auto&& inter, auto&& X, auto&& Y, std::size_t n, std::size_t m, std::size_t th) -> decltype(inter.generate_matrix_k_impl(X, Y, n, m, th)) {}); + + /** + * @brief + * + */ inline constexpr auto sig_gen_w_f = meta::is_valid([](auto&& inter, std::size_t order) -> decltype(inter.generate_weights_impl(order)) {}); + /** * @brief Internal structure used for interaction list in source target simulation * @@ -429,20 +997,42 @@ namespace scalfmm::meta { }; + /** + * @brief + * + * @tparam T + * @tparam typename + */ template<typename T, typename = void> struct exist : std::false_type { using type = void; }; + /** + * @brief + * + * @tparam T + */ template<typename T> struct exist<T, std::void_t<typename T::type>> : std::true_type { using type = typename T::type; }; + /** + * @brief + * + * @tparam T + */ template<typename T> static constexpr bool exist_v = exist<T>::value; + + /** + * @brief + * + * @tparam T + */ template<typename T> using exist_t = typename exist<T>::type; diff --git a/include/scalfmm/meta/type_pack.hpp b/include/scalfmm/meta/type_pack.hpp index 9fed61f753ef09e2ec7df5644590bc0071626732..c68c140292039331ee6dce252a0090ac342be469 100644 --- a/include/scalfmm/meta/type_pack.hpp +++ b/include/scalfmm/meta/type_pack.hpp @@ -1,15 +1,21 @@ // -------------------------------- // See LICENCE file at project root -// File : type_pack.hpp +// File : scalfmm/meta/type_pack.hpp // -------------------------------- #ifndef SCALFMM_META_TYPE_PACK_HPP #define SCALFMM_META_TYPE_PACK_HPP -#include <tuple> #include <cstddef> +#include <tuple> namespace scalfmm::meta { + /** + * @brief + * + * @tparam I + * @tparam T + */ template<std::size_t I, typename T> struct pack { @@ -22,21 +28,51 @@ namespace scalfmm::meta namespace details { + /** + * @brief + * + * @tparam Final + * @tparam ExpandedTuple + * @tparam ToExpand + */ template<template<class...> class Final, typename ExpandedTuple, typename... ToExpand> struct pack_expand_impl; + /** + * @brief + * + * @tparam Final + * @tparam Expanded + */ template<template<class...> class Final, typename... Expanded> struct pack_expand_impl<Final, std::tuple<Expanded...>> { using type = Final<Expanded...>; }; + /** + * @brief + * + * @tparam Final + * @tparam T + * @tparam Args + * @tparam Expanded + */ template<template<class...> class Final, typename T, typename... Args, typename... Expanded> struct pack_expand_impl<Final, std::tuple<Expanded...>, pack<0, T>, Args...> { using type = typename pack_expand_impl<Final, std::tuple<Expanded...>, Args...>::type; }; + /** + * @brief + * + * @tparam Final + * @tparam count + * @tparam T + * @tparam Args + * @tparam Expanded + */ template<template<class...> class Final, std::size_t count, typename T, typename... Args, typename... Expanded> struct pack_expand_impl<Final, std::tuple<Expanded...>, pack<count, T>, Args...> { @@ -44,6 +80,14 @@ namespace scalfmm::meta typename pack_expand_impl<Final, std::tuple<Expanded..., T>, pack<count - 1, T>, Args...>::type; }; + /** + * @brief + * + * @tparam Final + * @tparam T + * @tparam Args + * @tparam Expanded + */ template<template<class...> class Final, typename T, typename... Args, typename... Expanded> struct pack_expand_impl<Final, std::tuple<Expanded...>, T, Args...> { @@ -51,9 +95,20 @@ namespace scalfmm::meta }; } // namespace details + /** + * @brief + * + * @tparam VariadicType + * @tparam TypePacks + */ template<template<class...> class VariadicType, typename... TypePacks> using pack_expand = typename details::pack_expand_impl<VariadicType, std::tuple<>, TypePacks...>::type; + /** + * @brief + * + * @tparam TypePacks + */ template<typename... TypePacks> using pack_expand_tuple = pack_expand<std::tuple, TypePacks...>; diff --git a/include/scalfmm/meta/utils.hpp b/include/scalfmm/meta/utils.hpp index 49050f09724885555c17e8332f98a6d7bce1fa97..160101e1048da780f1e147fe26452162c7e813ec 100644 --- a/include/scalfmm/meta/utils.hpp +++ b/include/scalfmm/meta/utils.hpp @@ -1,10 +1,16 @@ // -------------------------------- // See LICENCE file at project root -// File : meta/utils.hpp +// File : scalfmm/meta/utils.hpp // -------------------------------- #ifndef SCALFMM_META_UTILS_HPP #define SCALFMM_META_UTILS_HPP +#include "scalfmm/meta/forward.hpp" +#include "scalfmm/meta/traits.hpp" +#include "xtensor/xlayout.hpp" + +#include "xtensor/xtensor_forward.hpp" + #include <algorithm> #include <any> #include <array> @@ -16,31 +22,88 @@ #include <tuple> #include <type_traits> #include <utility> -#include <xtensor/xtensor_forward.hpp> - -#include "scalfmm/meta/forward.hpp" -#include "scalfmm/meta/traits.hpp" -#include "xtensor/xlayout.hpp" namespace scalfmm { namespace container { + /** + * @brief Forward declaration for 'unique_variadic_container'. + * + * @tparam Derived + * @tparam Container + * @tparam Types + */ template<typename Derived, template<typename U, typename Allocator> class Container, typename... Types> struct unique_variadic_container; + + /** + * @brief Forward declaration for 'variadic_adaptor'. + * + * @tparam Derived + * @tparam Containers + */ template<typename Derived, typename... Containers> struct variadic_adaptor; + + /** + * @brief Forward declaration for 'variadic_container'. + * + * @tparam Derived + * @tparam Types + */ template<typename Derived, typename... Types> struct variadic_container; + + /** + * @brief Forward declaration for 'variadic_container_tuple'. + * + * @tparam Derived + * @tparam Tuple + */ template<typename Derived, typename Tuple> struct variadic_container_tuple; + + /** + * @brief Forward declaration for 'particle'. + * + * @tparam PositionType + * @tparam PositionDim + * @tparam InputsType + * @tparam NInputs + * @tparam OutputsType + * @tparam MOutputs + * @tparam Variables + */ template<typename PositionType, std::size_t PositionDim, typename InputsType, std::size_t NInputs, typename OutputsType, std::size_t MOutputs, typename... Variables> struct particle; + + /** + * @brief Forward declaration for 'point'. + * + * @tparam ValueType + * @tparam Dimension + * @tparam Enable + */ template<typename ValueType, std::size_t Dimension, typename Enable> struct point; + + /** + * @brief Forward declaration for 'point_impl'. + * + * @tparam ValueType + * @tparam Dimension + */ template<typename ValueType, std::size_t Dimension> struct point_impl; + + /** + * @brief Forward declaration for 'point_proxy'. + * + * @tparam ValueType + * @tparam Dimension + */ template<typename ValueType, std::size_t Dimension> struct point_proxy; } // namespace container @@ -48,6 +111,10 @@ namespace scalfmm namespace scalfmm::meta { + /** + * @brief + * + */ struct noop_t { template<typename... Types> @@ -56,6 +123,10 @@ namespace scalfmm::meta } }; + /** + * @brief + * + */ struct noop_f { template<typename... F> @@ -64,9 +135,21 @@ namespace scalfmm::meta } }; - template<typename, typename> + /** + * @brief + * + * @tparam L + * @tparam R + */ + template<typename L, typename R> struct cat; + /** + * @brief + * + * @tparam L + * @tparam R + */ template<typename... L, typename... R> struct cat<std::tuple<L...>, std::tuple<R...>> { @@ -74,62 +157,136 @@ namespace scalfmm::meta }; // standard traits and forward declaration + + /** + * @brief + * + * @tparam T + */ template<typename T> struct tuple_size : std::tuple_size<T> { }; + /** + * @brief + * + * @tparam T + */ template<typename T> static constexpr std::size_t tuple_size_v = meta::tuple_size<T>::value; + /** + * @brief + * + * @tparam ET + * @tparam S + * @tparam L + * @tparam SH + */ template<typename ET, typename S, xt::layout_type L, bool SH> struct tuple_size<xt::xtensor_fixed<ET, S, L, SH>> : std::tuple_size<typename xt::xtensor_fixed<ET, S, L, SH>::inner_shape_type> { }; + /** + * @brief + * + * @tparam Derived + * @tparam Containers + */ template<typename Derived, typename... Containers> struct tuple_size<container::variadic_adaptor<Derived, Containers...>> : tuple_size<typename container::variadic_adaptor<Derived, Containers...>::base_type> { }; + /** + * @brief + * + * @tparam Derived + * @tparam Container + * @tparam Types + */ template<typename Derived, template<typename U, typename Allocator> class Container, typename... Types> struct tuple_size<container::unique_variadic_container<Derived, Container, Types...>> : tuple_size<typename container::unique_variadic_container<Derived, Container, Types...>::base_type> { }; + /** + * @brief + * + * @tparam Derived + * @tparam Types + */ template<typename Derived, typename... Types> struct tuple_size<container::variadic_container<Derived, Types...>> : tuple_size<typename container::variadic_container<Derived, Types...>::base_type> { }; + /** + * @brief + * + * @tparam Derived + * @tparam Tuple + */ template<typename Derived, typename Tuple> struct tuple_size<container::variadic_container_tuple<Derived, Tuple>> : tuple_size<typename container::variadic_container_tuple<Derived, Tuple>::base_type> { }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + */ template<typename ValueType, std::size_t Dimension> struct tuple_size<scalfmm::container::point_impl<ValueType, Dimension>> : tuple_size<typename scalfmm::container::point_impl<ValueType, Dimension>::base_type> { }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + */ template<typename ValueType, std::size_t Dimension> struct tuple_size<scalfmm::container::point_proxy<ValueType, Dimension>> : tuple_size<typename scalfmm::container::point_proxy<ValueType, Dimension>::base_type> { }; + /** + * @brief + * + * @tparam ValueType + * @tparam Dimension + * @tparam Enable + */ template<typename ValueType, std::size_t Dimension, typename Enable> struct tuple_size<scalfmm::container::point<ValueType, Dimension, Enable>> : tuple_size<typename scalfmm::container::point<ValueType, Dimension, Enable>::base_type> { }; + /** + * @brief + * + * @tparam PositionType + * @tparam PositionDim + * @tparam InputsType + * @tparam NInputs + * @tparam OutputsType + * @tparam MOutputs + * @tparam Variables + */ template<typename PositionType, std::size_t PositionDim, typename InputsType, std::size_t NInputs, typename OutputsType, std::size_t MOutputs, typename... Variables> struct tuple_size< @@ -138,24 +295,63 @@ namespace scalfmm::meta { }; + /** + * @brief + * + * @tparam I + * @tparam T + * @param t + * @return auto&& + */ template<std::size_t I, typename T> inline constexpr auto get(T&& t) noexcept -> auto&& { return std::get<I>(std::forward<T>(t)); } + /** + * @brief + * + * @tparam ET + * @tparam S + * @tparam L + * @tparam SH + * @tparam Is + * @param exp + * @return auto&& + */ template<typename ET, typename S, xt::layout_type L, bool SH, std::size_t... Is> inline constexpr auto get(xt::xtensor_fixed<ET, S, L, SH>&& exp) noexcept -> auto&& { return std::forward<xt::xtensor_fixed<ET, S, L, SH>>(exp).at(Is...); } + /** + * @brief + * + * @tparam I + * @tparam ValueType + * @tparam Dimension + * @tparam Enable + * @param p + * @return ValueType const& + */ template<std::size_t I, typename ValueType, std::size_t Dimension, typename Enable> inline constexpr auto get(container::point<ValueType, Dimension, Enable> const& p) noexcept -> ValueType const& { return p.at(I); } + /** + * @brief + * + * @tparam I + * @tparam ValueType + * @tparam Dimension + * @tparam Enable + * @param p + * @return ValueType& + */ template<std::size_t I, typename ValueType, std::size_t Dimension, typename Enable> inline constexpr auto get(container::point<ValueType, Dimension, Enable>& p) noexcept -> ValueType& { @@ -163,12 +359,27 @@ namespace scalfmm::meta } ////////////////////////////////////////////// + + /** + * @brief + * + * @tparam Added + * @tparam Is + * @param seq + * @return constexpr auto + */ template<std::size_t Added, std::size_t... Is> inline constexpr auto add_to_sequence(std::index_sequence<Is...> seq) { return std::index_sequence<(Added + Is)...>{}; } + /** + * @brief + * + * @tparam T + * @tparam I + */ template<typename T, size_t... I> inline constexpr auto reverse_impl(T&& t, std::index_sequence<I...> /*unused*/) -> std::tuple<typename std::tuple_element<sizeof...(I) - 1 - I, T>::type...> @@ -176,30 +387,65 @@ namespace scalfmm::meta return std::make_tuple(meta::get<sizeof...(I) - 1 - I>(std::forward<T>(t))...); } + /** + * @brief + * + * @tparam T + * @param t + * @return decltype(reverse_impl(std::forward<T>(t), + * std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>())) + */ template<typename T> - inline constexpr auto reverse(T&& t) - -> decltype(reverse_impl(std::forward<T>(t), - std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>())) + inline constexpr auto + reverse(T&& t) -> decltype(reverse_impl(std::forward<T>(t), + std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>())) { return reverse_impl(std::forward<T>(t), std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>()); } // Generate a tuple of Ts + + /** + * @brief + * + * @tparam td::size_t + * @tparam T + */ template<std::size_t, typename T> using type_id = T; + /** + * @brief + * + * @tparam T + * @tparam Is + * @return constexpr auto + */ template<typename T, std::size_t... Is> inline constexpr auto generate_tuple(std::index_sequence<Is...>) { return std::tuple<type_id<Is, T>...>{}; } + /** + * @brief + * + * @tparam T + * @tparam N + * @return constexpr auto + */ template<typename T, std::size_t N> inline constexpr auto generate_tuple() { return generate_tuple<T>(std::make_index_sequence<N>{}); } + /** + * @brief + * + * @tparam T + * @tparam N + */ template<typename T, std::size_t N> using generate_tuple_t = decltype(generate_tuple<T, N>()); @@ -209,21 +455,46 @@ namespace scalfmm::meta // return std::tuple<type_id<Is, New<typename std::tuple_element<Is, Tuple>::type>>...>{}; // } + /** + * @brief + * + * @tparam New + * @tparam T + */ template<template<class> class New, typename T> struct replace_inner_tuple_type; + /** + * @brief + * + * @tparam New + * @tparam Ts + */ template<template<class> class New, typename... Ts> struct replace_inner_tuple_type<New, std::tuple<Ts...>> { using type = std::tuple<New<Ts>...>; }; + /** + * @brief + * + * @tparam New + * @tparam T + * @tparam N + */ template<template<class> class New, typename T, std::size_t N> struct replace_inner_tuple_type<New, std::array<T, N>> { using type = std::array<New<T>, N>; }; + /** + * @brief + * + * @tparam New + * @tparam T + */ template<template<class> class New, typename T> using replace_inner_tuple_type_t = typename replace_inner_tuple_type<New, T>::type; @@ -235,9 +506,13 @@ namespace scalfmm::meta //template<template<class> class New, typename Tuple> //using replace_inner_tuple_type_t = decltype(replace_inner_tuple_type<New, Tuple>()); + /** * @brief Transform a tuple in an array * + * @tparam Tuple + * @tparam T + * @tparam std::decay_t<Tuple>>> * @param t the tuple - all elements are in the same type * @return constexpr auto */ @@ -246,7 +521,9 @@ namespace scalfmm::meta { return std::apply([](auto... elems) { return std::array<T, sizeof...(elems)>{elems...}; }, t); } - // constexpr inline auto to_array(std::tuple<>) { return std::array<double, 0>{}; } + + // constexpr inline auto to_array(std::tuple<>) { return std::array<double, 0>{}; } + /** * @brief Transform an array in a tuple * @@ -259,41 +536,76 @@ namespace scalfmm::meta return std::apply([](auto... elems) { return std::make_tuple(elems...); }, a); } + /** + * @brief + * + * @tparam Offset + * @tparam Seq + */ template<std::size_t Offset, typename Seq> struct offset_sequence; + /** + * @brief + * + * @tparam Offset + * @tparam Ints + */ template<std::size_t Offset, std::size_t... Ints> struct offset_sequence<Offset, std::index_sequence<Ints...>> { using type = std::index_sequence<Offset + Ints...>; }; + /** + * @brief + * + * @tparam Offset + * @tparam Seq + */ template<std::size_t Offset, typename Seq> using offset_sequence_t = typename offset_sequence<Offset, Seq>::type; + /** + * @brief + * + * @tparam Begin + * @tparam End + */ template<std::size_t Begin, std::size_t End> struct make_range_sequence : public offset_sequence_t<Begin, std::make_index_sequence<End - Begin>> { }; + /** + * @brief + * + * @tparam T + * @tparam Is + * @param t + * @return constexpr auto + */ template<typename T, std::size_t... Is> constexpr inline auto sub_tuple(T&& t, std::index_sequence<Is...>) { return std::forward_as_tuple(meta::get<Is>(std::forward<T>(t))...); } + /** * @brief extract a sub-tuple of the tuple according to the sequence of indexes * - * + * @tparam T + * @tparam Is + * + * * @param t the tuple * @param s the sequence of indexes * @return constexpr auto - * \code {c++} + * \code {c++} * // outputs_iterator is an iterator on the outputs (potential, forces) * using range_pot = meta::make_range_sequence<0, 1>; * auto & pot = meta::sub_tuple(outputs_iterator, range_pot{}) * \endcode - */ template<typename T, std::size_t... Is> constexpr inline auto make_sub_tuple(T t, std::index_sequence<Is...> /*s*/) @@ -301,12 +613,27 @@ namespace scalfmm::meta return std::make_tuple(meta::get<Is>(t)...); } + /** + * @brief + * + * @tparam Args + * @param args + * @return constexpr auto + */ template<typename... Args> inline constexpr auto multiply(Args... args) { return (args * ...); } + /** + * @brief + * + * @tparam Args + * @param args + * @return true + * @return false + */ template<typename... Args> inline constexpr auto all(Args... args) -> bool { @@ -315,12 +642,30 @@ namespace scalfmm::meta namespace details { + /** + * @brief + * + * @tparam T + * @tparam U + * @tparam Is + * @param lhs + * @param rhs + */ template<typename T, typename U, std::size_t... Is> inline constexpr auto tuple_sum_update(T&& lhs, U&& rhs, std::index_sequence<Is...>) -> void { noop_t{((meta::get<Is>(std::forward<T>(lhs)) += meta::get<Is>(std::forward<U>(rhs))), 0)...}; } + /** + * @brief + * + * @tparam ArrayOfIt + * @tparam TupleLike + * @tparam Is + * @param lhs + * @param rhs + */ template<typename ArrayOfIt, typename TupleLike, std::size_t... Is> inline constexpr auto it_sum_update(ArrayOfIt&& lhs, TupleLike&& rhs, std::index_sequence<Is...>) -> void { @@ -328,12 +673,35 @@ namespace scalfmm::meta ((*meta::get<Is>(std::forward<ArrayOfIt>(lhs)) += meta::get<Is>(std::forward<TupleLike>(rhs))), 0)...}; } + /** + * @brief + * + * @tparam T + * @tparam U + * @tparam Is + * @param lhs + * @param rhs + */ template<typename T, typename U, std::size_t... Is> inline constexpr auto tuple_min_update(T&& lhs, U&& rhs, std::index_sequence<Is...>) -> void { noop_t{((meta::get<Is>(std::forward<T>(lhs)) -= meta::get<Is>(std::forward<U>(rhs))), 0)...}; } + /** + * @brief + * + * @tparam F + * @tparam LHS + * @tparam RHS1 + * @tparam RHS2 + * @tparam Is + * @param s + * @param f + * @param lhs + * @param rhs1 + * @param rhs2 + */ template<typename F, typename LHS, typename RHS1, typename RHS2, std::size_t... Is> inline constexpr auto for_each(std::index_sequence<Is...> s, F&& f, LHS&& lhs, RHS1&& rhs1, RHS2&& rhs2) -> void { @@ -343,6 +711,17 @@ namespace scalfmm::meta 0)...}; } + /** + * @brief + * + * @tparam T + * @tparam U + * @tparam F + * @tparam Is + * @param lhs + * @param rhs + * @param f + */ template<typename T, typename U, typename F, std::size_t... Is> inline constexpr auto for_each(T&& lhs, U&& rhs, F&& f, std::index_sequence<Is...>) -> void { @@ -351,12 +730,36 @@ namespace scalfmm::meta 0)...}; } + /** + * @brief + * + * @tparam T + * @tparam F + * @tparam Is + * @param t + * @param f + */ template<typename T, typename F, std::size_t... Is> inline constexpr auto for_each(T&& t, F&& f, std::index_sequence<Is...>) -> void { noop_t{(std::invoke(std::forward<F>(f), meta::get<Is>(std::forward<T>(t))), 0)...}; } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam T2 + * @tparam T3 + * @tparam Is + * @param f + * @param t0 + * @param t1 + * @param t2 + * @param t3 + */ template<typename F, typename T0, typename T1, typename T2, typename T3, std::size_t... Is> inline constexpr auto repeat(F&& f, T0&& t0, T1&& t1, T2&& t2, T3&& t3, std::index_sequence<Is...>) -> void { @@ -366,6 +769,19 @@ namespace scalfmm::meta 0)...}; } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam T2 + * @tparam Is + * @param f + * @param t0 + * @param t1 + * @param t2 + */ template<typename F, typename T0, typename T1, typename T2, std::size_t... Is> inline constexpr auto repeat(F&& f, T0&& t0, T1&& t1, T2&& t2, std::index_sequence<Is...>) -> void { @@ -374,6 +790,17 @@ namespace scalfmm::meta 0)...}; } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam Is + * @param f + * @param t0 + * @param t1 + */ template<typename F, typename T0, typename T1, std::size_t... Is> inline constexpr auto repeat(F&& f, T0&& t0, T1&& t1, std::index_sequence<Is...>) -> void { @@ -382,12 +809,31 @@ namespace scalfmm::meta 0)...}; } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam Is + * @param f + * @param t0 + */ template<typename F, typename T0, std::size_t... Is> inline constexpr auto repeat(F&& f, T0&& t0, std::index_sequence<Is...>) -> void { noop_t{((std::invoke(std::forward<F>(f), meta::get<Is>(std::forward<T0>(t0)))), 0)...}; } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam T2 + * @tparam T3 + * @tparam Is + */ template<typename F, typename T0, typename T1, typename T2, typename T3, std::size_t... Is> inline constexpr auto repeat_indexed(F&& f, T0&& t0, T1&& t1, T2&& t2, T3&& t3, std::index_sequence<Is...> /*s*/) -> void @@ -398,6 +844,19 @@ namespace scalfmm::meta 0)...}; } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam T2 + * @tparam Is + * @param f + * @param t0 + * @param t1 + * @param t2 + */ template<typename F, typename T0, typename T1, typename T2, std::size_t... Is> inline constexpr auto repeat_indexed(F&& f, T0&& t0, T1&& t1, T2&& t2, std::index_sequence<Is...>) -> void { @@ -406,6 +865,17 @@ namespace scalfmm::meta 0)...}; } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam Is + * @param f + * @param t0 + * @param t1 + */ template<typename F, typename T0, typename T1, std::size_t... Is> inline constexpr auto repeat_indexed(F&& f, T0&& t0, T1&& t1, std::index_sequence<Is...>) -> void { @@ -414,12 +884,31 @@ namespace scalfmm::meta 0)...}; } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam Is + * @param f + * @param t0 + */ template<typename F, typename T0, std::size_t... Is> inline constexpr auto repeat_indexed(F&& f, T0&& t0, std::index_sequence<Is...>) -> void { noop_t{((std::invoke(std::forward<F>(f), Is, meta::get<Is>(std::forward<T0>(t0)))), 0)...}; } + /** + * @brief + * + * @tparam T + * @tparam F + * @tparam Is + * @param t + * @param f + * @return constexpr auto + */ template<typename T, typename F, std::size_t... Is> inline constexpr auto apply(T&& t, F&& f, std::index_sequence<Is...>) { @@ -427,12 +916,21 @@ namespace scalfmm::meta } } // namespace details + /** + * @brief + * + * @tparam T + * @tparam U + * @param lhs + * @param rhs + */ template<typename T, typename U> inline constexpr auto tuple_sum_update(T&& lhs, U&& rhs) -> void { return details::tuple_sum_update(std::forward<T>(lhs), std::forward<U>(rhs), std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>{}); } + /** * @brief perform lhs += rhs on all the element of the tuple * @@ -448,15 +946,28 @@ namespace scalfmm::meta std::make_index_sequence<meta::tuple_size<std::decay_t<ArrayOfIt>>::value>{}); } + /** + * @brief + * + * @tparam T + * @tparam U + * @param lhs + * @param rhs + */ template<typename T, typename U> inline constexpr auto tuple_min_update(T&& lhs, U&& rhs) -> void { return details::tuple_min_update(std::forward<T>(lhs), std::forward<U>(rhs), std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>{}); } + /** * @brief Perform lhs = f(rhs1, rhs2) on each component of the three objects * + * @tparam F + * @tparam LHS + * @tparam RHS1 + * @tparam RHS2 * @param lhs the left hand-side * @param rhs1 the first object on the right hand-side * @param rhs2 the the second object on the right hand-side @@ -469,9 +980,13 @@ namespace scalfmm::meta std::forward<F>(f), std::forward<LHS>(lhs), std::forward<RHS1>(rhs1), std::forward<RHS2>(rhs2)); } + /** * @brief Perform lhs = f(rhs) on each component of the three objects * + * @tparam T + * @tparam U + * @tparam F * @param lhs the left hand-side * @param rhs the right hand-side * @param f the lambda function with one argument @@ -482,18 +997,20 @@ namespace scalfmm::meta return details::for_each(std::forward<T>(lhs), std::forward<U>(rhs), std::forward<F>(f), std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>{}); } + /** * @brief Apply f on the tuple - * \todo A reécrire - - * @param tuple - * @param f the lambda to apply - * * Example + * + * @tparam T + * @tparam F + * @param tuple + * @param f the lambda function to apply + * + * Example * \code * meta::for_each(tuples, [&os](auto const& v) { os << v << ", "; }); * meta::for_each(tuples, [](auto const& v) { return 0; }); * \endcode - */ template<typename T, typename F> inline constexpr auto for_each(T&& tuple, F&& f) -> void @@ -501,9 +1018,15 @@ namespace scalfmm::meta return details::for_each(std::forward<T>(tuple), std::forward<F>(f), std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>{}); } + /** * @brief apply f on each component of t0,t1,t2,t3 (see the case of 2 objects) * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam T2 + * @tparam T3 * @param f the lambda with 4 parameters * @param t0 first object * @param t1 second object @@ -516,9 +1039,16 @@ namespace scalfmm::meta details::repeat(std::forward<F>(f), std::forward<T0>(t0), std::forward<T1>(t1), std::forward<T2>(t2), std::forward<T3>(t3), std::make_index_sequence<meta::tuple_size<std::decay_t<T0>>::value>{}); } + /** * @brief apply f on each component of t0,t1,t2 (see the case of 2 objects) * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam T2 + * @param f + * @param t0 * @param t1 second object * @param t2 third object */ @@ -528,15 +1058,21 @@ namespace scalfmm::meta details::repeat(std::forward<F>(f), std::forward<T0>(t0), std::forward<T1>(t1), std::forward<T2>(t2), std::make_index_sequence<meta::tuple_size<std::decay_t<T0>>::value>{}); } + /** * @brief apply the function f on each component of t0, t1 * + * @tparam F + * @tparam T0 + * @tparam T1 * @param f the lambda function with 2 arguments * @param t0 the first object * @param t1 the second object + * * example: perform contribution_force *= jacobian on each component of contribution_force and jacobian - * \code meta::repeat([](auto& f, auto const& j) { f *= j; }, contribution_force, jacobian); - * \endcode + * @code + * meta::repeat([](auto& f, auto const& j) { f *= j; }, contribution_force, jacobian); + * @endcode */ template<typename F, typename T0, typename T1> inline constexpr auto repeat(F&& f, T0&& t0, T1&& t1) -> void @@ -544,9 +1080,12 @@ namespace scalfmm::meta details::repeat(std::forward<F>(f), std::forward<T0>(t0), std::forward<T1>(t1), std::make_index_sequence<meta::tuple_size<std::decay_t<T0>>::value>{}); } + /** * @brief Repeat f on each element of t0 * + * @tparam F + * @tparam T0 * @param f the lambda function to apply * @param t0 a set of elements (tuple, array) * @@ -562,6 +1101,20 @@ namespace scalfmm::meta std::make_index_sequence<meta::tuple_size<std::decay_t<T0>>::value>{}); } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam T2 + * @tparam T3 + * @param f + * @param t0 + * @param t1 + * @param t2 + * @param t3 + */ template<typename F, typename T0, typename T1, typename T2, typename T3> inline constexpr auto repeat_indexed(F&& f, T0&& t0, T1&& t1, T2&& t2, T3&& t3) -> void { @@ -570,12 +1123,35 @@ namespace scalfmm::meta std::make_index_sequence<meta::tuple_size<std::decay_t<T0>>::value>{}); } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam T1 + * @tparam T2 + * @param f + * @param t0 + * @param t1 + * @param t2 + */ template<typename F, typename T0, typename T1, typename T2> inline constexpr auto repeat_indexed(F&& f, T0&& t0, T1&& t1, T2&& t2) -> void { details::repeat_indexed(std::forward<F>(f), std::forward<T0>(t0), std::forward<T1>(t1), std::forward<T2>(t2), std::make_index_sequence<meta::tuple_size<std::decay_t<T0>>::value>{}); } + + /** + * @brief + * + * @tparam F + * @tparam T0 + * @tparam T1 + * @param f + * @param t0 + * @param t1 + */ template<typename F, typename T0, typename T1> inline constexpr auto repeat_indexed(F&& f, T0&& t0, T1&& t1) -> void { @@ -583,6 +1159,14 @@ namespace scalfmm::meta std::make_index_sequence<meta::tuple_size<std::decay_t<T0>>::value>{}); } + /** + * @brief + * + * @tparam F + * @tparam T0 + * @param f + * @param t0 + */ template<typename F, typename T0> inline constexpr auto repeat_indexed(F&& f, T0&& t0) -> void { @@ -590,6 +1174,15 @@ namespace scalfmm::meta std::make_index_sequence<meta::tuple_size<std::decay_t<T0>>::value>{}); } + /** + * @brief + * + * @tparam T + * @tparam F + * @param t + * @param f + * @return constexpr auto + */ template<typename T, typename F> inline constexpr auto apply(T&& t, F&& f) { @@ -597,9 +1190,29 @@ namespace scalfmm::meta std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>{}); } + /** + * @brief Meta-function to recursively iterate over a multi-dimensional range. + * + * This template structure is used for generating nested loops over multi-dimensional ranges. + * It recursively processes the dimensions in reverse order, invoking the provided callable + * object for every combination of indices in the specified range. + * + * @tparam N The number of dimensions to iterate over. + */ template<std::size_t N> struct looper { + /** + * @brief + * + * @tparam F + * @tparam Dimension + * @tparam Is + * @param f + * @param stops + * @param is + * @return constexpr auto + */ template<typename F, std::size_t Dimension, typename... Is> constexpr inline auto operator()(F&& f, std::array<std::size_t, Dimension> const& stops, Is&... is) { @@ -610,9 +1223,25 @@ namespace scalfmm::meta } }; + /** + * @brief + * + * @tparam + */ template<> struct looper<1> { + /** + * @brief + * + * @tparam F + * @tparam Dimension + * @tparam Is + * @param f + * @param stops + * @param is + * @return constexpr auto + */ template<typename F, std::size_t Dimension, typename... Is> constexpr inline auto operator()(F&& f, std::array<std::size_t, Dimension> const& stops, Is&... is) { @@ -623,9 +1252,40 @@ namespace scalfmm::meta } }; + /** + * @brief Meta-function to recursively iterate over a multi-dimensional range. + * \code {.c++} + * std::array<int, 3> starts = {0, 0, 0}; + * std::array<int, 3> stops = {3, 3, 3}; + * + * auto print_indices = [](auto... indices) { + * ((std::cout << indices << " "), ...) << '\n'; + * }; + * + * looper_range<3>()(print_indices, starts, stops); + * \endcode + * + * This template structure is used for generating nested loops over multi-dimensional ranges. + * It recursively processes the dimensions in reverse order, invoking the provided callable + * object for every combination of indices in the specified range. + * + * @tparam N The number of dimensions to iterate over. + */ template<std::size_t N> struct looper_range { + /** + * @brief Overloaded call operator to perform iteration over the range [starts, stops]. + * + * @tparam F Type of the callable object to invoke at each iteration. + * @tparam Dimension The total number of dimensions in the range. + * @tparam Is Types of the variadic parameter pack representing the accumulated indices. + * + * @param f The callable object to be invoked with the current indices. + * @param starts An array specifying the start indices for each dimension. + * @param stops An array specifying the stop indices for each dimension. + * @param is A parameter pack for the indices accumulated from previous dimensions. + */ template<typename F, std::size_t Dimension, typename... Is> constexpr inline auto operator()(F&& f, std::array<int, Dimension> const& starts, std::array<int, Dimension> const& stops, Is&... is) @@ -637,9 +1297,27 @@ namespace scalfmm::meta } }; + /** + * @brief Specialization of looper_range for the base case of a single dimension. + * + * This specialization handles the case where there is only one remaining dimension + * to iterate over, invoking the callable object directly with the accumulated indices. + */ template<> struct looper_range<1> { + /** + * @brief Overloaded call operator to perform iteration over the range. + * + * @tparam F Type of the callable object to invoke at each iteration. + * @tparam Dimension The total number of dimensions in the range. + * @tparam Is Types of the variadic parameter pack representing the accumulated indices. + * + * @param f The callable object to be invoked with the current indices. + * @param starts An array specifying the start indices for each dimension. + * @param stops An array specifying the stop indices for each dimension. + * @param is A parameter pack for the indices accumulated from previous dimensions. + */ template<typename F, std::size_t Dimension, typename... Is> constexpr inline auto operator()(F&& f, std::array<int, Dimension> const& starts, std::array<int, Dimension> const& stops, Is&... is) @@ -651,94 +1329,188 @@ namespace scalfmm::meta } }; + /** + * @brief Helper function that converts a particle or a tuple to a tuple. + * + * @tparam ParticleOrTuple Type of the particle or the tuple. + * + * @param particle_tuple Either a particle object or a tuple. + */ template<typename ParticleOrTuple> - auto inline as_tuple(ParticleOrTuple&& p) + inline auto as_tuple(ParticleOrTuple&& particle_tuple) { if constexpr(is_particle_v<std::decay_t<ParticleOrTuple>>) { - return std::forward<ParticleOrTuple>(p).as_tuple(); + return std::forward<ParticleOrTuple>(particle_tuple).as_tuple(); } else if constexpr(is_tuple_v<std::decay_t<ParticleOrTuple>>) { - return std::forward<ParticleOrTuple>(p); + return std::forward<ParticleOrTuple>(particle_tuple); } } + /** + * @brief Adds lvalue references to a single type. + * + * @tparam T The given type to add an lvalue reference. + */ template<typename T> struct add_lvalue_reference : std::add_lvalue_reference<T> { }; + /** + * @brief Adds lvalue references to a std::tuple type. + * + * @tparam Ts... The tuple types to add lvalue references. + */ template<typename... Ts> struct add_lvalue_reference<std::tuple<Ts...>> { using type = std::tuple<Ts&...>; }; + /** + * @brief Alias for the adapter type that generates an lvalue reference tuple. + * + * @tparam Ts... The tuple types to add lvalue references. + */ template<typename... Ts> using add_lvalue_reference_t = typename add_lvalue_reference<Ts...>::type; + /** + * @brief Adds const qualifiers to a single type. + * + * @tparam T The given type to add const qualifiers. + */ template<typename T> struct add_const : std::add_const<T> { }; + /** + * @brief Adds const qualifiers to a std::tuple type. + * + * @tparam Ts... The tuple types to add const qualifiers. + */ template<typename... Ts> struct add_const<std::tuple<Ts...>> { using type = std::tuple<const Ts...>; }; + /** + * @brief Alias for the adapter type that generates a const-qualified tuple. + * + * @tparam Ts... The tuple types to create a const-qualified tuple. + */ template<typename... Ts> using add_const_t = typename add_const<Ts...>::type; + /** - * @brief A constexpr pow function on integer + * @brief A compile-time pow function on integers. * * The function is necessary for LLVM to compute \f N^D\ f * \code {.c++} * constexpr int nb = meta::Pow<3, 3>::value * \endcode * - * @tparam N - * @tparam D + * @tparam N The base number for the power calculation. + * @tparam D The exponent for the power calculation. */ template<int N, int D> struct Pow { + /** + * @brief The value of the power calculation. + */ enum { value = N * Pow<N, D - 1>::value }; }; + /** + * @brief Specialization of the compile-time pow function on integers for the base case. + * + * @tparam N The base number for the power calculation. + */ template<int N> struct Pow<N, 0> { + /** + * @brief The value of the power calculation for the base case. + */ enum { value = 1 }; }; + + /** + * @brief + * + */ struct empty_t { + /** + * @brief Construct a new empty t object + * + */ empty_t() = default; + + /** + * @brief Construct a new empty t object + * + * @tparam T + * @tparam V + */ template<typename T, typename V> empty_t(T, V) { } }; + + /** + * @brief + * + */ struct empty_shape { }; + + /** + * @brief + * + */ struct empty_inner { + /** + * @brief Construct a new empty inner object + * + * @tparam S + */ template<typename S> empty_inner(S, double){}; }; + + /** + * @brief + * + */ struct empty { + /** + * @brief Construct a new empty object + * + */ empty() = default; - empty(empty_shape, empty_inner){}; + + /** + * @brief Construct a new empty object + * + */ + empty(empty_shape, empty_inner) {}; }; } // namespace scalfmm::meta diff --git a/include/scalfmm/operators/count_kernel/count_interactions_kernel.hpp b/include/scalfmm/operators/count_kernel/count_interactions_kernel.hpp index cf1e555b1b2ad2cc249e5282e68ff6cd4d8c02e6..5baa38584984d5e264cbf9789178f6a423374120 100644 --- a/include/scalfmm/operators/count_kernel/count_interactions_kernel.hpp +++ b/include/scalfmm/operators/count_kernel/count_interactions_kernel.hpp @@ -1,10 +1,13 @@ // -------------------------------- // See LICENCE file at project root -// File : core/operators.hpp +// File : scalfmm/operators/count_kernel/count_interactions_kernel.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_COUNT_KERNEL_COUNT_KERNEL_HPP #define SCALFMM_OPERATORS_COUNT_KERNEL_COUNT_KERNEL_HPP +#include "scalfmm/container/particle_container.hpp" +#include "scalfmm/matrix_kernels/mk_common.hpp" + #include <algorithm> #include <array> #include <cstddef> @@ -15,23 +18,67 @@ #include <utility> #include <vector> -#include "scalfmm/container/particle_container.hpp" -#include "scalfmm/matrix_kernels/mk_common.hpp" - namespace count_kernel { + /** + * @brief + * + */ struct execution_infos { + /** + * @brief + * + */ const std::string operator_name{}; + + /** + * @brief + * + */ std::size_t particles{0}; + + /** + * @brief + * + */ std::size_t number_of_component{0}; + + /** + * @brief + * + */ std::size_t call_count{0}; + + /** + * @brief + * + */ std::array<std::size_t, 10> multipoles_count{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + /** + * @brief + * + */ std::array<std::size_t, 10> locals_count{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + /** + * @brief + * + * @param os + * @param infos + * @return std::ostream& + */ friend inline auto operator<<(std::ostream& os, const execution_infos& infos) -> std::ostream&; }; + /** + * @brief + * + * @param os + * @param infos + * @return std::ostream& + */ inline auto operator<<(std::ostream& os, const execution_infos& infos) -> std::ostream& { os << "[\n operator : " << infos.operator_name << "\n particles : " << infos.particles @@ -49,9 +96,11 @@ namespace count_kernel std::cout << " ]"; return os; } - /// - /// \brief The count_matrix_kernel struct - /// + + /** + * @brief The count matrix kernel. + * + */ struct count_matrix_kernel { using map_type = std::unordered_map<std::string, execution_infos>; @@ -64,6 +113,10 @@ namespace count_kernel // inline auto separation_criterion() const -> std::size_t { return 1; } // inline auto get(std::string const& key) -> mapped_type& { return m_map.at(key); } + /** + * @brief + * + */ inline auto print() -> void { for(auto const& pair: m_map) @@ -73,29 +126,54 @@ namespace count_kernel } private: + /** + * @brief + * + */ map_type m_map{{ {"p2p_full_mutual", {"p2p_full_mutual"}}, {"p2p_inner", {"p2p_inner"}}, {"p2p_remote", {"p2p_remote"}}, }}; }; - /// - /// \brief The count_near_field struct - /// - /// An implementation of the near field for the counter kernel + + /** + * @brief An implementation of the near field for the counter kernel + * + */ struct count_near_field { + /** + * @brief + * + */ count_matrix_kernel mat; + /** + * @brief + * + * @return std::size_t + */ inline auto separation_criterion() const -> std::size_t { return 1; } + + /** + * @brief + * + * @return count_matrix_kernel + */ inline count_matrix_kernel matrix_kernel() { return mat; }; + + /** + * @brief + * + */ inline auto print() -> void { mat.print(); } }; - /// - /// \brief The count_interpolator struct - /// - /// An implementation of the far field for the counter kernel - // struct count_interpolator + + /** + * @brief An implementation of the far field for the counter kernel. + * + */ struct count_far_field { public: @@ -106,10 +184,27 @@ namespace count_kernel // static constexpr std::size_t dimension{3}; // static constexpr std::size_t kn = count_matrix_kernel::kn; // static constexpr std::size_t km = count_matrix_kernel::km; + + /** + * @brief + * + * @param key + * @return mapped_type& + */ inline auto get(std::string const& key) -> mapped_type& { return m_map.at(key); } + /** + * @brief + * + * @param key + * @return mapped_type const& + */ inline auto get(std::string const& key) const -> mapped_type const& { return m_map.at(key); } + /** + * @brief + * + */ inline auto print() -> void { for(auto const& pair: m_map) @@ -118,18 +213,42 @@ namespace count_kernel } } + /** + * @brief + * + * @return std::size_t + */ inline auto separation_criterion() const -> std::size_t { return 1; } - template<typename Cell> - void apply_multipoles_preprocessing(Cell& current_cell, std::size_t order) + /** + * @brief + * + * @tparam CellType + * @param current_cell + * @param order + */ + template<typename CellType> + void apply_multipoles_preprocessing(CellType& current_cell, std::size_t order) { } - template<typename Cell> - void apply_multipoles_postprocessing(Cell& current_cell, std::size_t order) + + /** + * @brief + * + * @tparam CellType + * @param current_cell + * @param order + */ + template<typename CellType> + void apply_multipoles_postprocessing(CellType& current_cell, std::size_t order) { } private: + /** + * @brief + * + */ map_type m_map{{ {"p2m", {"p2m"}}, {"m2m", {"m2m"}}, @@ -142,13 +261,42 @@ namespace count_kernel }}; }; + /** + * @brief + * + */ struct count_fmm_operator { + /** + * @brief + * + */ count_far_field m_far_field; + + /** + * @brief + * + */ count_near_field m_near_field; + + /** + * @brief + * + * @return count_far_field + */ inline count_far_field far_field() { return m_far_field; }; + + /** + * @brief + * + * @return count_near_field + */ inline count_near_field near_field() const { return m_near_field; }; + /** + * @brief + * + */ inline auto print() -> void { // far_field.print(); @@ -156,8 +304,14 @@ namespace count_kernel } }; - template<typename Leaf, typename Cell> - inline void p2m(count_far_field& interp, Leaf const& source_leaf, Cell& target_cell, std::size_t /*order*/) + /** + * @brief + * + * @tparam LeafType + * @tparam CellType + */ + template<typename LeafType, typename CellType> + inline void p2m(count_far_field& interp, LeafType const& source_leaf, CellType& target_cell, std::size_t /*order*/) { auto& infos = interp.get("p2m"); auto const& target_symb = target_cell.csymbolics(); @@ -168,8 +322,19 @@ namespace count_kernel infos.particles += source_leaf.size(); } - template<typename Cell> - inline void l2l(count_far_field& interp, Cell const& parent_cell, std::size_t child_index, Cell& child_cell, + /** + * @brief + * + * @tparam CellType + * @param interp + * @param parent_cell + * @param child_index + * @param child_cell + * @param order + * @param tree_level + */ + template<typename CellType> + inline void l2l(count_far_field& interp, CellType const& parent_cell, std::size_t child_index, CellType& child_cell, std::size_t order, std::size_t tree_level = 2) { auto& infos = interp.get("l2l"); @@ -181,8 +346,19 @@ namespace count_kernel infos.locals_count[target_symb.level]++; } - template<typename Cell> - inline void m2m(count_far_field& interp, Cell const& child_cell, std::size_t child_index, Cell& parent_cell, + /** + * @brief + * + * @tparam CellType + * @param interp + * @param child_cell + * @param child_index + * @param parent_cell + * @param order + * @param tree_level + */ + template<typename CellType> + inline void m2m(count_far_field& interp, CellType const& child_cell, std::size_t child_index, CellType& parent_cell, std::size_t order, std::size_t tree_level = 2) { auto& infos = interp.get("m2m"); @@ -194,9 +370,14 @@ namespace count_kernel infos.multipoles_count[target_symb.level]++; } - template<typename Cell> - inline void m2l(count_far_field& interp, Cell const& source_cell, std::size_t /*neighbor_idx*/, Cell& target_cell, - std::size_t /*order*/, std::size_t /*tree_level*/) + /** + * @brief + * + * @tparam CellType + */ + template<typename CellType> + inline void m2l(count_far_field& interp, CellType const& source_cell, std::size_t /*neighbor_idx*/, + CellType& target_cell, std::size_t /*order*/, std::size_t /*tree_level*/) { auto& infos = interp.get("m2l"); auto const& target_symb = target_cell.csymbolics(); @@ -206,8 +387,16 @@ namespace count_kernel infos.locals_count[target_symb.level]++; } - template<typename Cell, typename Leaf, bool ComputeForces = false> - inline void l2p(count_fmm_operator& fmm_operator, Cell const& source_cell, Leaf& target_leaf, std::size_t /*order*/) + /** + * @brief + * + * @tparam CellType + * @tparam LeafType + * @tparam ComputeForces + */ + template<typename CellType, typename LeafType, bool ComputeForces = false> + inline void l2p(count_fmm_operator& fmm_operator, CellType const& source_cell, LeafType& target_leaf, + std::size_t /*order*/) { auto interp = fmm_operator.far_field(); auto& infos = interp.get("l2p"); @@ -216,11 +405,17 @@ namespace count_kernel infos.call_count++; } - template<typename Leaf, typename ContainerOfLeafIterator> - inline void p2p_full_mutual(count_matrix_kernel const& /*mat*/, Leaf& target_leaf, - ContainerOfLeafIterator const& neighbor) + /** + * @brief + * + * @tparam LeafType + * @tparam ContainerOfLeafIteratorType + */ + template<typename LeafType, typename ContainerOfLeafIteratorType> + inline void p2p_full_mutual(count_matrix_kernel const& /*mat*/, LeafType& target_leaf, + ContainerOfLeafIteratorType const& neighbor) { - using value_type = typename Leaf::value_type; + using value_type = typename LeafType::value_type; // auto& infos = mat.get("p2p_full_mutual"); value_type nb_val{0}; std::for_each(std::begin(neighbor), std::end(neighbor), @@ -234,19 +429,30 @@ namespace count_kernel // infos.call_count++; } - template<typename Leaf> - inline void p2p_inner(count_matrix_kernel const& /*mat*/, Leaf& target_leaf, + /** + * @brief + * + * @tparam LeafType + */ + template<typename LeafType> + inline void p2p_inner(count_matrix_kernel const& /*mat*/, LeafType& target_leaf, [[maybe_unused]] const bool mutual = true) { - using value_type = typename Leaf::value_type; + using value_type = typename LeafType::value_type; // auto& infos = mat.get("p2p_inner"); std::get<0>(*scalfmm::container::outputs_begin(target_leaf.particles())) += static_cast<value_type>(target_leaf.size()); // infos.call_count++; } - template<typename Leaf, typename ContainerOfLeafIterator> - inline void p2p_remote(count_matrix_kernel const&, Leaf&, ContainerOfLeafIterator const&) + /** + * @brief + * + * @tparam LeafType + * @tparam ContainerOfLeafIteratorType + */ + template<typename LeafType, typename ContainerOfLeafIteratorType> + inline void p2p_remote(count_matrix_kernel const&, LeafType&, ContainerOfLeafIteratorType const&) { } } // namespace count_kernel diff --git a/include/scalfmm/operators/count_kernel/count_kernel.hpp b/include/scalfmm/operators/count_kernel/count_kernel.hpp index 2da4ab7188dc012fff56fee934ccfb29f9796806..a7417529111b754d588c3ef00103ca25c614b53f 100644 --- a/include/scalfmm/operators/count_kernel/count_kernel.hpp +++ b/include/scalfmm/operators/count_kernel/count_kernel.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : core/operators.hpp +// File : scalfmm/operators/count_kernel/count_kernel.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_COUNT_KERNEL_COUNT_KERNEL_HPP #define SCALFMM_OPERATORS_COUNT_KERNEL_COUNT_KERNEL_HPP @@ -17,134 +17,319 @@ namespace count_kernels { namespace particles { + /** + * @brief + * + */ struct empty_shape { }; + + /** + * @brief + * + */ struct empty_inner { + /** + * @brief Construct a new empty inner object + * + * @tparam S + */ template<typename S> empty_inner(S, double){}; }; + + /** + * @brief + * + */ struct empty { + /** + * @brief Construct a new empty object + * + */ empty() = default; - empty(empty_shape, empty_inner){}; + + /** + * @brief Construct a new empty object + * + */ + empty(empty_shape, empty_inner) {}; }; - /// - /// \brief The count_matrix_kernel struct - /// - /// For compatibility + + /** + * @brief The count matrix kernel (for compatibility). + * + */ struct count_matrix_kernel { + /** + * @brief + * + */ static constexpr std::size_t kn{1}; + + /** + * @brief + * + */ static constexpr std::size_t km{1}; + + /** + * @brief + * + */ static constexpr int separation_criterion{1}; }; - /// - /// \brief The count_near_field struct - /// - /// An implementation of the near field for the counter kernel + + /** + * @brief An implementation of the near field for the count kernel. + * + */ struct count_near_field { using matrix_kernel_type = count_matrix_kernel; + + /** + * @brief + * + */ count_matrix_kernel _mat; + + /** + * @brief + * + */ bool _mutual; + + /** + * @brief + * + * @return int + */ inline auto separation_criterion() const -> int { return matrix_kernel_type::separation_criterion; } + + /** + * @brief + * + * @return count_matrix_kernel + */ inline count_matrix_kernel matrix_kernel() const { return _mat; }; + + /** + * @brief + * + * @return true + * @return false + */ inline auto mutual() const -> bool { return _mutual; } + /** + * @brief Construct a new count near field object + * + * @param mutual + */ count_near_field(const bool mutual) : _mutual(mutual) { } }; + /** + * @brief + * + * @tparam Dimension + */ template<std::size_t Dimension = 3> struct count_approximation { - static constexpr std::size_t dimension{Dimension}; using value_type = double; using matrix_kernel_type = count_matrix_kernel; using storage_type = scalfmm::component::grid_storage<value_type, Dimension, 1, 1>; + + using buffer_type = empty; + using buffer_shape_type = empty_shape; + using buffer_inner_type = empty_inner; + + /** + * @brief + * + */ + static constexpr std::size_t dimension{Dimension}; + + /** + * @brief + * + */ count_matrix_kernel mat; + + /** + * @brief + * + * @return count_matrix_kernel const& + */ inline auto matrix_kernel() const noexcept -> count_matrix_kernel const& { return mat; } + /** + * @brief + * + * @return auto + */ inline auto cell_width_extension() const noexcept { return 0.; } - using buffer_type = empty; - using buffer_shape_type = empty_shape; - using buffer_inner_type = empty_inner; - template<typename Cell> - void apply_multipoles_preprocessing(Cell& /*current_cell*/, std::size_t t = 0) const + /** + * @brief + * + * @tparam CellType + */ + template<typename CellType> + void apply_multipoles_preprocessing(CellType& /*current_cell*/, std::size_t t = 0) const { } - template<typename Cell> - void apply_multipoles_postprocessing(Cell& /*current_cell*/, buffer_type, std::size_t t = 0) const + + /** + * @brief + * + * @tparam CellType + */ + template<typename CellType> + void apply_multipoles_postprocessing(CellType& /*current_cell*/, buffer_type, std::size_t t = 0) const { } + /** + * @brief + * + * @return buffer_type + */ inline auto buffer_initialization() const -> buffer_type { return empty{}; } + + /** + * @brief + * + * @return buffer_shape_type + */ inline auto buffer_shape() const -> buffer_shape_type { return empty_shape{}; } + + /** + * @brief + * + */ inline auto buffer_reset(buffer_type) const -> void {} }; - /// - /// \brief The count_interpolator struct - /// - /// An implementation of the far field for the counter kernel - /// + /** + * @brief An implementation of the far field for the count kernel. + * + * @tparam Dimension + */ template<std::size_t Dimension = 3> struct count_far_field { + using approximation_type = count_approximation<Dimension>; + private: + /** + * @brief + * + */ count_approximation<Dimension> m_count_approximation; public: - // inline auto separation_criterion() const -> std::size_t { return 1; } - using approximation_type = count_approximation<Dimension>; + /** + * @brief + * + */ static constexpr bool compute_gradient = false; + + /** + * @brief + * + * @return approximation_type const& + */ auto approximation() const -> approximation_type const& { return m_count_approximation; } + + /** + * @brief + * + * @return int + */ inline auto separation_criterion() const noexcept -> int { return 1; } }; - template<typename Leaf, typename Cell, std::size_t Dimension> - inline void p2m(count_far_field<Dimension> const& /*interp*/, Leaf const& source_leaf, Cell& target_cell) + /** + * @brief + * + * @tparam LeafType + * @tparam CellType + * @tparam Dimension + */ + template<typename LeafType, typename CellType, std::size_t Dimension> + inline auto p2m(count_far_field<Dimension> const& /*interp*/, LeafType const& source_leaf, + CellType& target_cell) -> void { auto& multipoles = target_cell.multipoles().at(0); multipoles += source_leaf.size(); } - template<typename Cell, std::size_t Dimension> - inline void l2l(count_approximation<Dimension> const& /*interp*/, Cell const& parent_cell, - std::size_t /*child_index*/, Cell& child_cell, std::size_t tree_level = 2) + /** + * @brief + * + * @tparam CellType + * @tparam Dimension + */ + template<typename CellType, std::size_t Dimension> + inline auto l2l(count_approximation<Dimension> const& /*interp*/, CellType const& parent_cell, + std::size_t /*child_index*/, CellType& child_cell, std::size_t tree_level = 2) -> void { auto& child_locals = child_cell.locals().at(0); auto const& current_locals = parent_cell.clocals().at(0); child_locals += current_locals; } - template<typename Cell, std::size_t Dimension> - inline void m2m(count_approximation<Dimension> const& /*interp*/, Cell const& child_cell, - std::size_t /*child_index*/, Cell& parent_cell, std::size_t tree_level = 2) + /** + * @brief + * + * @tparam CellType + * @tparam Dimension + */ + template<typename CellType, std::size_t Dimension> + inline auto m2m(count_approximation<Dimension> const& /*interp*/, CellType const& child_cell, + std::size_t /*child_index*/, CellType& parent_cell, std::size_t tree_level = 2) -> void { auto& parent_multipoles = parent_cell.multipoles().at(0); auto const& child_multipoles = child_cell.cmultipoles().at(0); parent_multipoles += child_multipoles; } - template<typename Cell, std::size_t Dimension> - inline void m2l(count_approximation<Dimension> const& /*interp*/, Cell const& source_cell, - std::size_t /*neighbor_idx*/, Cell& target_cell, std::size_t /*tree_level*/, - typename count_approximation<Dimension>::buffer_type& /*buffer*/) + /** + * @brief + * + * @tparam CellType + * @tparam Dimension + */ + template<typename CellType, std::size_t Dimension> + inline auto m2l(count_approximation<Dimension> const& /*interp*/, CellType const& source_cell, + std::size_t /*neighbor_idx*/, CellType& target_cell, std::size_t /*tree_level*/, + typename count_approximation<Dimension>::buffer_type& /*buffer*/) -> void { auto const& source_multipoles = source_cell.cmultipoles().at(0); auto& target_locals = target_cell.locals().at(0); target_locals += source_multipoles; } - template<typename Cell, std::size_t Dimension> - inline void m2l_loop(count_approximation<Dimension> const& /*interp*/, Cell& target_cell, + + /** + * @brief + * + * @tparam CellType + * @tparam Dimension + */ + template<typename CellType, std::size_t Dimension> + inline auto m2l_loop(count_approximation<Dimension> const& /*interp*/, CellType& target_cell, std::size_t /*tree_level*/, - typename count_approximation<Dimension>::buffer_type& /*buffer*/) + typename count_approximation<Dimension>::buffer_type& /*buffer*/) -> void { auto const& target_symb = target_cell.csymbolics(); auto const& interaction_iterators = target_symb.interaction_iterators; @@ -156,61 +341,84 @@ namespace count_kernels target_locals += source_multipoles; } } - template<typename Cell, typename Leaf, std::size_t Dimension> - inline void l2p(count_far_field<Dimension> const& /*fmm_operator*/, Cell const& source_cell, Leaf& target_leaf) + + /** + * @brief + * + * @tparam CellType + * @tparam LeafType + * @tparam Dimension + */ + template<typename CellType, typename LeafType, std::size_t Dimension> + inline auto l2p(count_far_field<Dimension> const& /*fmm_operator*/, CellType const& source_cell, + LeafType& target_leaf) -> void { - auto p = typename Leaf::proxy_type(*(target_leaf.begin())); + auto p = typename LeafType::proxy_type(*(target_leaf.begin())); p.outputs()[0] += *std::begin(source_cell.clocals().at(0)); } - template<typename Leaf, typename ContainerOfLeafIterator, typename ArrayT, typename ValueT> - inline void p2p_full_mutual(count_matrix_kernel const& /*mat*/, Leaf& target_leaf, - ContainerOfLeafIterator const& neighbors, const int& size, ArrayT const&, - ValueT const&) + /** + * @brief + * + * @tparam LeafType + * @tparam ContainerOfLeafIteratorType + * @tparam ArrayType + * @tparam ValueType + */ + template<typename LeafType, typename ContainerOfLeafIteratorType, typename ArrayType, typename ValueType> + inline auto p2p_full_mutual(count_matrix_kernel const& /*mat*/, LeafType& target_leaf, + ContainerOfLeafIteratorType const& neighbors, const int& size, ArrayType const&, + ValueType const&) -> void { - using value_type = typename Leaf::value_type; + using value_type = typename LeafType::value_type; value_type nb_val{0}; std::for_each(std::begin(neighbors), std::begin(neighbors) + size, [&nb_val, &target_leaf](auto& leaf_r) { nb_val += static_cast<value_type>(leaf_r->size()); - auto p_r = typename Leaf::proxy_type(*(leaf_r->begin())); + auto p_r = typename LeafType::proxy_type(*(leaf_r->begin())); p_r.outputs()[0] += target_leaf.size(); }); - auto p = typename Leaf::proxy_type(*(target_leaf.begin())); + auto p = typename LeafType::proxy_type(*(target_leaf.begin())); p.outputs()[0] += nb_val; } + /** + * @brief + * + * @tparam MutualComputation + * @tparam LEAF_T + * @param mat + * @param target_leaf + * @param mutual + */ template<bool MutualComputation = false, typename LEAF_T> - inline void p2p_inner([[maybe_unused]] count_matrix_kernel const& mat, LEAF_T& target_leaf, [[maybe_unused]] const bool mutual = true) + inline auto p2p_inner([[maybe_unused]] count_matrix_kernel const& mat, LEAF_T& target_leaf, + [[maybe_unused]] const bool mutual = true) -> void { using value_type = typename LEAF_T::value_type; - // if constexpr(MutualComputation) - // { - // { - // std::cout << cpp_tools::colors::cyan; - // std::cout << "\t --- P2P inner mutual ---" << std::endl; - // std::cout << cpp_tools::colors::reset; - // } - // } - // else - // { - // { - // std::cout << cpp_tools::colors::cyan; - // std::cout << "\t --- P2P inner non mutual ---" << std::endl; - // std::cout << cpp_tools::colors::reset; - // } - // } auto p = typename LEAF_T::proxy_type(*(target_leaf.begin())); p.outputs()[0] += static_cast<value_type>(target_leaf.size()); } - template<typename Leaf, typename ContainerOfLeafIterator, typename ArrayT, typename ValueT> - inline void p2p_outer([[maybe_unused]] count_matrix_kernel const& mat, Leaf& target_leaf, - ContainerOfLeafIterator const& neighbors, ArrayT const&, ValueT const&) + + /** + * @brief + * + * @tparam LeafType + * @tparam ContainerOfLeafIteratorType + * @tparam ArrayType + * @tparam ValueType + * @param mat + * @param target_leaf + * @param neighbors + */ + template<typename LeafType, typename ContainerOfLeafIteratorType, typename ArrayType, typename ValueType> + inline auto p2p_outer([[maybe_unused]] count_matrix_kernel const& mat, LeafType& target_leaf, + ContainerOfLeafIteratorType const& neighbors, ArrayType const&, ValueType const&) -> void { - using value_type = typename Leaf::value_type; + using value_type = typename LeafType::value_type; value_type nb_val{0}; std::for_each(std::begin(neighbors), std::begin(neighbors) + target_leaf.csymbolics().number_of_neighbors, [&nb_val](auto const& leaf) @@ -219,15 +427,28 @@ namespace count_kernels nb_val += static_cast<value_type>(leaf->size()); } }); - auto p = typename Leaf::proxy_type(*(target_leaf.begin())); + auto p = typename LeafType::proxy_type(*(target_leaf.begin())); p.outputs()[0] += nb_val; } - template<typename Leaf, typename ContainerOfLeafIterator, typename ArrayT, typename ValueT> - inline void p2p_outer([[maybe_unused]] count_matrix_kernel const& mat, Leaf& target_leaf, - ContainerOfLeafIterator const& neighbors, const int& number_of_neighbors, ArrayT const&, - ValueT const&) + + /** + * @brief + * + * @tparam LeafType + * @tparam ContainerOfLeafIteratorType + * @tparam ArrayType + * @tparam ValueType + * @param mat + * @param target_leaf + * @param neighbors + * @param number_of_neighbors + */ + template<typename LeafType, typename ContainerOfLeafIteratorType, typename ArrayType, typename ValueType> + inline auto p2p_outer([[maybe_unused]] count_matrix_kernel const& mat, LeafType& target_leaf, + ContainerOfLeafIteratorType const& neighbors, const int& number_of_neighbors, + ArrayType const&, ValueType const&) -> void { - using value_type = typename Leaf::value_type; + using value_type = typename LeafType::value_type; value_type nb_val{0}; std::for_each(std::begin(neighbors), std::begin(neighbors) + number_of_neighbors, [&nb_val](auto const& leaf) @@ -236,24 +457,47 @@ namespace count_kernels nb_val += static_cast<value_type>(leaf->size()); } }); - auto p = typename Leaf::proxy_type(*(target_leaf.begin())); + auto p = typename LeafType::proxy_type(*(target_leaf.begin())); p.outputs()[0] += nb_val; } - template<typename TargetLeaf, typename SourceLeaf, typename ArrayT> - inline void p2p_outer(count_matrix_kernel const&, TargetLeaf& target_leaf, SourceLeaf const& source_leaf, - [[maybe_unused]] ArrayT const& shift) + + /** + * @brief + * + * @tparam TargetLeafType + * @tparam SourceLeafType + * @tparam ArrayType + * @param target_leaf + * @param source_leaf + * @param shift + */ + template<typename TargetLeafType, typename SourceLeafType, typename ArrayType> + inline auto p2p_outer(count_matrix_kernel const&, TargetLeafType& target_leaf, + SourceLeafType const& source_leaf, [[maybe_unused]] ArrayType const& shift) -> void { - using value_type = typename TargetLeaf::value_type; - auto p = typename TargetLeaf::proxy_type(*(target_leaf.begin())); + using value_type = typename TargetLeafType::value_type; + auto p = typename TargetLeafType::proxy_type(*(target_leaf.begin())); p.outputs()[0] += static_cast<value_type>(source_leaf.size()); } } // namespace particles } // namespace count_kernels + namespace scalfmm::interpolation { + + /** + * @brief + * + * @tparam T + */ template<typename T> struct interpolator_traits; + /** + * @brief + * + * @tparam Dimension + */ template<std::size_t Dimension> struct interpolator_traits<count_kernels::particles::count_approximation<Dimension>> { diff --git a/include/scalfmm/operators/fmm_operators.hpp b/include/scalfmm/operators/fmm_operators.hpp index 6a3a7a0f6e6a350b2885c3db1c858135afe9ad4a..0f626e6182a38eaba20da989b34161f3922dd6a6 100644 --- a/include/scalfmm/operators/fmm_operators.hpp +++ b/include/scalfmm/operators/fmm_operators.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/fmm_operators.hpp +// File : scalfmm/operators/fmm_operators.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_FMM_OPERATORS_HPP #define SCALFMM_OPERATORS_FMM_OPERATORS_HPP @@ -10,65 +10,88 @@ namespace scalfmm::operators { - /// - /// / - /// \class near_field_operator - /// \brief The near_field_operator class - /// - /// This class concerns the near field operator for the fmm operators. - /// It is used in direct pass in all algorithms. - /// In general, it is enough to only have the matrix kernel member. - /// @tparam MATRIX_KERNEL_TYPE template for matrix kernel class - /// - /// The following example constructs the near field operator to compute the potential and the - /// force associated to the potential 1/r - /// \code - /// using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r; - /// - /// \endcode - template<typename MatrixKernel> + /** + * @brief The near_field_operator class + * + * This class concerns the near field operator for the fmm operators. + * It is used in direct pass in all algorithms. + * In general, it is enough to only have the matrix kernel member. + * + * The following example constructs the near field operator to compute the potential and the + * force associated to the potential 1/r + * + * \code + * using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r; + * using near_field_type = scalfmm::operators::near_field_operator<matrix_kernel_type>; + * \endcode + * + * @tparam MatrixKernelType + */ + template<typename MatrixKernelType> class near_field_operator { public: - using matrix_kernel_type = MatrixKernel; - /// - /// \brief near_field_operator constructor - /// \param[in] matrix_kernel - the matrix kernel used int the near field - /// - // near_field_operator() = delete; + using matrix_kernel_type = MatrixKernelType; + + /** + * @brief Construct a new near field operator object + * + */ near_field_operator(near_field_operator const&) = delete; + + /** + * @brief Construct a new near field operator object + * + */ near_field_operator(near_field_operator&&) = delete; + + /** + * @brief + * + * @return near_field_operator& + */ auto operator=(near_field_operator const&) -> near_field_operator& = default; + + /** + * @brief + * + * @return near_field_operator& + */ auto operator=(near_field_operator&&) -> near_field_operator& = delete; - /// - /// \brief near_field_operator constructor - /// The separation_criterion is the matrix_kernel one and mutual is set to true - /// and mutual is set to true - /// + + /** + * @brief near_field_operator constructor + * The separation_criterion is the matrix_kernel one and mutual is set to true + * and mutual is set to true + * + */ near_field_operator() : m_matrix_kernel(matrix_kernel_type()) , m_mutual(true) { m_separation_criterion = m_matrix_kernel.separation_criterion; } - // /// - /// \brief near_field_operator constructor - /// The separation_criterion is the matrix_kernel one and mutual is set to true and - // - /// - /// \param[in] mutual to specify if in p2p we use mutual interaction + + /** + * @brief near_field_operator constructor + * The separation_criterion is the matrix_kernel one and mutual is set to true and + * + * @param[in] mutual to specify if in p2p we use mutual interaction + */ near_field_operator(bool mutual) : m_matrix_kernel(matrix_kernel_type()) , m_mutual(mutual) { m_separation_criterion = m_matrix_kernel.separation_criterion; } - /// - /// \brief near_field_operator constructor - /// The separation_criterion is the matrix_kernel one and mutual is set to true - /// and - /// - /// \param[in] matrix_kernel the matrix kernel used int the near field + + /** + * @brief near_field_operator constructor + * The separation_criterion is the matrix_kernel one and mutual is set to true + * and + * + * @param[in] matrix_kernel the matrix kernel used int the near field + */ near_field_operator(matrix_kernel_type const& matrix_kernel) : m_matrix_kernel(matrix_kernel) , m_separation_criterion(matrix_kernel.separation_criterion) @@ -76,159 +99,269 @@ namespace scalfmm::operators { } - /// - /// \brief near_field_operator constructor - /// - /// The separation_criterion is the matrix_kernel one - /// - /// \param[in] matrix_kernel the matrix kernel used int the near field - /// \param[in] mutual the boolean to specify if the P2P is symmetric or not (Mutual interaction) - /// + /** + * @brief Construct a new near field operator object + * + * @param matrix_kernel the matrix kernel used in the near field. + * @param mutual the boolean to specify whether the p2p is symmetric or not (mutual interactions). + */ near_field_operator(matrix_kernel_type const& matrix_kernel, const bool mutual) : m_matrix_kernel(matrix_kernel) , m_separation_criterion(matrix_kernel.separation_criterion) , m_mutual(mutual) { } - /// - /// \brief matrix_kernel accessor - /// @return the matrix kernel operator - /// + + /** + * @brief + * + * @return matrix_kernel_type const& + */ auto matrix_kernel() const -> matrix_kernel_type const& { return m_matrix_kernel; } - /// @brief matrix_kernel accessor - /// @return + /** + * @brief + * + * @return matrix_kernel_type& + */ auto matrix_kernel() -> matrix_kernel_type& { return m_matrix_kernel; } - /// - /// \brief separation_criterion accessor - /// @return the separation criteria used in matrix kernel - /// + /** + * @brief + * + * @return int + */ auto separation_criterion() const -> int { return m_separation_criterion; } + + /** + * @brief + * + * @return int& + */ auto separation_criterion() -> int& { return m_separation_criterion; } - /// - /// \brief The accessor to specify if we are using the symmetric P2P (mutual interactions) - /// @return the separation criteria used in matrix kernel - /// + /** + * @brief The accessor to specify if we are using the symmetric P2P (mutual interactions) + * + * @return true + * @return false + */ auto mutual() const -> bool { return m_mutual; } + + /** + * @brief The accessor to specify if we are using the symmetric P2P (mutual interactions) + * + * @return true + * @return false + */ auto mutual() -> bool& { return m_mutual; } private: - matrix_kernel_type m_matrix_kernel; ///< the matrix kernel used in the near field - int m_separation_criterion{ - matrix_kernel_type::separation_criterion}; ///< the separation criterion; used int the near field - bool m_mutual{true}; ///< To specify if you use symmetric algorithm for the p2p (Mutual interactions) + /** + * @brief The matrix kernel used in the near field. + * + */ + matrix_kernel_type m_matrix_kernel; + + /** + * @brief The separation criterion used in the near field. + * + */ + int m_separation_criterion{matrix_kernel_type::separation_criterion}; + + /** + * @brief To specify if we use symmetric algorithm for the p2p (mutual interactions). + * + */ + bool m_mutual{true}; }; - /// - /// - /// \class far_field_operator - /// \brief The far_field_operator class - /// - /// This class concerns the far field operator for the fmm operators. - /// - /// @tparam APPROXIMATION_TYPE template for approximation method used to compute the far field - /// - /// The following example constructs the far field operator based on the uniform interpolation to compute - /// the potential associated to the potential 1/r - /// - /// \code - /// using matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; - /// using interpolation_type = scalfmm::interpolation::uniform_interpolator<double, dimension, - /// matrix_kernel_type>; - /// using far_field_type = scalfmm::operators::far_field_operator<interpolation_type>; - /// - /// \endcode - template<typename Approximation, bool ComputeGradient = false> + /** + * @brief + * + * This class concerns the far field operator for the fmm operators. + * + * The following example constructs the far field operator based on the uniform interpolation to compute + * the potential associated to the potential 1/r + * + * \code + * using matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; + * using interpolation_type = scalfmm::interpolation::uniform_interpolator<double, dimension, + * matrix_kernel_type>; + * using far_field_type = scalfmm::operators::far_field_operator<interpolation_type>; + * \endcode + * + * @tparam ApproximationType + * @tparam ComputeGradient + */ + template<typename ApproximationType, bool ComputeGradient = false> class far_field_operator { public: - using approximation_type = Approximation; + using approximation_type = ApproximationType; static constexpr bool compute_gradient = ComputeGradient; // precise the homogeneous type of the kernel static constexpr auto homogeneity_tag = approximation_type::homogeneity_tag; + /** + * @brief + * + */ far_field_operator() = delete; + + /** + * @brief Construct a new far field operator object + * + */ far_field_operator(far_field_operator const&) = delete; + + /** + * @brief Construct a new far field operator object + * + */ far_field_operator(far_field_operator&&) = delete; + + /** + * @brief + * + * @return far_field_operator& + */ auto operator=(far_field_operator const&) -> far_field_operator& = default; + + /** + * @brief + * + * @return far_field_operator& + */ auto operator=(far_field_operator&&) -> far_field_operator& = delete; - /// - /// \brief near_field_operator constructor - /// \param[in] matrix_kernel - the matrix kernel used int the near field - /// + + /** + * @brief Construct a new far field operator object + * + * @param approximation_method + */ far_field_operator(approximation_type const& approximation_method) : m_approximation(approximation_method) { } - /// - /// \brief approximation method accessor - /// @return the approximation method - /// + /** + * @brief + * + * @return approximation_type const& + */ auto approximation() const -> approximation_type const& { return m_approximation; } - /// - /// - /// \brief separation_criterion accessor - /// @return the separation criteria used in matrix kernel - /// + + /** + * @brief + * + * @return int + */ auto separation_criterion() const -> int { return m_separation_criterion; } + + /** + * @brief + * + * @return int& + */ auto separation_criterion() -> int& { return m_separation_criterion; } - /// + private: // Here, be careful with this reference with the lifetime of matrix_kernel. - approximation_type const& m_approximation; ///< the approximation method used in the near field + + /** + * @brief the approximation method used in the near field. + * + */ + approximation_type const& m_approximation; + + /** + * @brief + * + */ int m_separation_criterion{approximation_type::separation_criterion}; }; - /// - /// \class fmm_operators - /// \brief The fmm_operators class - /// - /// - /// @tparam NEAR_FIELD_TYPE template for the near field operator - /// @tparam FAR_FIELD_TYPE template for the far field operator - /// - /// The following example constructs the near field operator to compute the potential associated to the potential - /// 1/r - /// - /// \code - /// // Near field - /// using matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r; - /// using nar_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; - /// // Far field is the uniform interpolation - /// using far_field_type = scalfmm::interpolation::uniform_interpolator<double, dimension, matrix_kernel_type>; - /// using fmm_operator = scalfmm::operators::fmm_operators<near_field_type, far_field_type> ; - /// \endcode - /// - /// The following example constructs the near-field operator to calculate the potential and - /// the force associated with the potential 1/r. The far field considers the - /// one_over_r matrix kernel and calculates the force by deriving the interpolation polynomial - /// of the potential. - /// - /// \code - /// using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r; - /// using near_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; - /// // - /// using far_matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; - /// // Far field is the uniform interpolation - /// using far_field_type = scalfmm::interpolation::uniform_interpolator<double, dimension, - /// far_matrix_kernel_type>; using fmm_operator = scalfmm::operators::fmm_operators<near_field_type, - /// far_field_type> ; - /// \endcode - template<typename NearField, typename FarField> + + /** + * @brief + * + * The following example constructs the near field operator to compute the potential associated to the potential + * 1/r + * + * \code + * // Near field + * using matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r; + * using nar_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; + * // Far field is the uniform interpolation + * using far_field_type = scalfmm::interpolation::uniform_interpolator<double, dimension, matrix_kernel_type>; + * using fmm_operator = scalfmm::operators::fmm_operators<near_field_type, far_field_type> ; + * \endcode + * + * The following example constructs the near-field operator to calculate the potential and + * the force associated with the potential 1/r. The far field considers the + * one_over_r matrix kernel and calculates the force by deriving the interpolation polynomial + * of the potential. + * + * \code + * using near_matrix_kernel_type = scalfmm::matrix_kernels::laplace::val_grad_one_over_r; + * using near_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; + * // + * using far_matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; + * // Far field is the uniform interpolation + * using far_field_type = scalfmm::interpolation::uniform_interpolator<double, dimension, + * far_matrix_kernel_type>; using fmm_operator = scalfmm::operators::fmm_operators<near_field_type, + * far_field_type> ; + * \endcode + * + * @tparam NearFieldType + * @tparam FarFieldType + */ + template<typename NearFieldType, typename FarFieldType> class fmm_operators { public: - using near_field_type = NearField; - using far_field_type = FarField; + using near_field_type = NearFieldType; + using far_field_type = FarFieldType; + /** + * @brief + * + */ fmm_operators() = delete; + + /** + * @brief Construct a new fmm operators object + * + */ fmm_operators(fmm_operators&&) = delete; + + /** + * @brief Construct a new fmm operators object + * + * @param other + */ fmm_operators(fmm_operators const& other) = delete; + + /** + * @brief + * + * @return fmm_operators& + */ auto operator=(fmm_operators const&) -> fmm_operators& = delete; + + /** + * @brief + * + * @return fmm_operators& + */ auto operator=(fmm_operators&&) -> fmm_operators& = delete; + /** + * @brief Construct a new fmm operators object + * + * @param near_field + * @param far_field + */ fmm_operators(near_field_type const& near_field, far_field_type const& far_field) : m_near_field(near_field) , m_far_field(far_field) @@ -242,18 +375,32 @@ namespace scalfmm::operators } } - /// \brief near field accessor - /// @return the near field operator - /// + /** + * @brief + * + * @return near_field_type const& + */ auto near_field() const -> near_field_type const& { return m_near_field; } - /// \brief far field accessor - /// @return the ar field operator - /// + + /** + * @brief + * + * @return far_field_type const& + */ auto far_field() const -> far_field_type const& { return m_far_field; } private: - near_field_type const& m_near_field; ///< the near field used int the fmm operator - far_field_type const& m_far_field; ///< the far field used int the fmm operator + /** + * @brief the near field used in the fmm operator. + * + */ + near_field_type const& m_near_field; + + /** + * @brief the far field used in the fmm operator. + * + */ + far_field_type const& m_far_field; }; } // namespace scalfmm::operators diff --git a/include/scalfmm/operators/interpolation/l2p.hpp b/include/scalfmm/operators/interpolation/l2p.hpp index d4279a92fc4ddca3d758bfd6ab815b477404d655..5f7815260d7438cbc08eef9b636264ae59cf9ff1 100644 --- a/include/scalfmm/operators/interpolation/l2p.hpp +++ b/include/scalfmm/operators/interpolation/l2p.hpp @@ -1,19 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : kernels/interpolation/l2p.hpp +// File : scalfmm/operators/interpolation/l2p.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_INTERPOLATION_L2P_HPP #define SCALFMM_OPERATORS_INTERPOLATION_L2P_HPP -#include <algorithm> -#include <array> -#include <complex> -#include <cstddef> -#include <tuple> -#include <type_traits> -#include <vector> -#include <xsimd/xsimd.hpp> - #include "scalfmm/container/access.hpp" #include "scalfmm/container/particle.hpp" #include "scalfmm/container/particle_container.hpp" @@ -31,6 +22,14 @@ #include "xsimd/xsimd.hpp" +#include <algorithm> +#include <array> +#include <complex> +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <vector> + namespace scalfmm::operators { // ------------------------------------------------------------- @@ -40,25 +39,26 @@ namespace scalfmm::operators /** * @brief apply l2p when only the "potential" is evaluated * - * @tparam Far_field Far field type format is Far_field<Interpolator, false> - * @tparam Interpolator Interpolator type used in far field - * @tparam Cell Cell type - * @tparam Leaf Leaf type + * @tparam FarFieldType Far field type format is FarFieldType<InterpolatorType, false> + * @tparam InterpolatorType Interpolator type used in far field + * @tparam CellType Cell type + * @tparam LeafType Leaf type * @param far_field the far-field operator * @param source_cell the cell containing the local * @param target_leaf the leaf containing the particles - * @return std::enable_if_t<std::is_base_of_v<interpolation::impl::interpolator<Interpolator>, Interpolator>> */ - template<template<typename, bool> typename Far_field, typename Interpolator, typename Cell, typename Leaf> - inline auto apply_l2p(Far_field<Interpolator, false> const& far_field, Cell const& source_cell, Leaf& target_leaf) - -> std::enable_if_t<std::is_base_of_v<interpolation::impl::interpolator<Interpolator>, Interpolator>> + template<template<typename, bool> typename FarFieldType, typename InterpolatorType, typename CellType, + typename LeafType> + inline auto apply_l2p(FarFieldType<InterpolatorType, false> const& far_field, CellType const& source_cell, + LeafType& target_leaf) + -> std::enable_if_t<std::is_base_of_v<interpolation::impl::interpolator<InterpolatorType>, InterpolatorType>> { // Leaf type - using leaf_type = Leaf; + using leaf_type = LeafType; using particle_type = typename leaf_type::particle_type; using position_type = typename leaf_type::position_type; using outputs_type = typename particle_type::outputs_type; - using value_type = typename Leaf::value_type; + using value_type = typename leaf_type::value_type; using simd_outputs_type = decltype(meta::to_tuple(meta::replace_inner_tuple_type_t<xsimd::simd_type, outputs_type>{})); // position_type -> scalfmm::container::point<...> @@ -66,7 +66,7 @@ namespace scalfmm::operators static constexpr std::size_t dimension = position_type::dimension; // output_type -> std::tuple<...> // value_type -> inner position_type::value_type -> underlying scalar_type - using value_type = typename Leaf::value_type; + using value_type = typename LeafType::value_type; // simd_type -> xsimd::batch<scalar_type> using simd_type = xsimd::simd_type<value_type>; // simd_position_type -> container::point<xsimd::batch<scalar_type>> @@ -220,31 +220,34 @@ namespace scalfmm::operators ++outputs_iterator; } } + /** * @brief apply l2p when the potential is evaluated and we derivate it to obtain the forces * - * @tparam Far_field Far field type format is Far_field<Interpolator, true> - * @tparam Interpolator Interpolator type used in far field - * @tparam Cell Cell type - * @tparam Leaf Leaf type + * @tparam FarFieldType Far field type format is FarFieldType<InterpolatorType, true> + * @tparam InterpolatorType Interpolator type used in far field + * @tparam CellType Cell type + * @tparam LeafType Leaf type * @todo L2P error in the optimized version (shift grad) if we have several locals (both in simd and non simd * version) * @warning the optimized version works only if the matrix kernel involved in the far-field has only * one input * @param[in] far_field the interpolation far-field operation - * @param[in] Cell the cell containing the local - * @param[out] Leaf the leaf containing the particles + * @param[in] source_cell the cell containing the local + * @param[out] target_leaf the leaf containing the particles */ - template<template<typename, bool> typename Far_field, typename Interpolator, typename Cell, typename Leaf> - inline auto apply_l2p(Far_field<Interpolator, true> const& far_field, Cell const& source_cell, Leaf& target_leaf) - -> std::enable_if_t<std::is_base_of_v<interpolation::impl::interpolator<Interpolator>, Interpolator>> + template<template<typename, bool> typename FarFieldType, typename InterpolatorType, typename CellType, + typename LeafType> + inline auto apply_l2p(FarFieldType<InterpolatorType, true> const& far_field, CellType const& source_cell, + LeafType& target_leaf) + -> std::enable_if_t<std::is_base_of_v<interpolation::impl::interpolator<InterpolatorType>, InterpolatorType>> { // Leaf type - using leaf_type = Leaf; + using leaf_type = LeafType; using particle_type = typename leaf_type::particle_type; using position_type = typename leaf_type::position_type; using outputs_type = typename particle_type::outputs_type; - using value_type = typename Leaf::value_type; + using value_type = typename leaf_type::value_type; using simd_outputs_type = decltype(meta::to_tuple(meta::replace_inner_tuple_type_t<xsimd::simd_type, outputs_type>{})); // position_type -> scalfmm::container::point<...> @@ -253,7 +256,7 @@ namespace scalfmm::operators // output_type -> std::tuple<...> // value_type -> inner position_type::value_type -> underlying // scalar_type - using value_type = typename Leaf::value_type; + using value_type = typename LeafType::value_type; // simd_type -> xsimd::batch<scalar_type> using simd_type = xsimd::simd_type<value_type>; // simd_position_type -> container::point<xsimd::batch<scalar_type>> @@ -266,7 +269,7 @@ namespace scalfmm::operators container::particle_container<container::particle<value_type, dimension, value_type, 0, value_type, 0>>; ///// static constexpr auto alignment_tag = simd::unaligned{}; - ///// HERE + ///// auto const& interp = far_field.approximation(); const auto order{interp.order()}; static constexpr int number_of_output = particle_type::outputs_size; diff --git a/include/scalfmm/operators/interpolation/m2l.hpp b/include/scalfmm/operators/interpolation/m2l.hpp new file mode 100644 index 0000000000000000000000000000000000000000..da53b688346890a308533bb908f89a601814696e --- /dev/null +++ b/include/scalfmm/operators/interpolation/m2l.hpp @@ -0,0 +1,117 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/operators/interpolation/m2l.hpp +// -------------------------------- +#pragma once + +#include "scalfmm/container/access.hpp" +#include "scalfmm/container/particle.hpp" +#include "scalfmm/container/particle_container.hpp" +#include "scalfmm/container/point.hpp" +#include "scalfmm/container/variadic_adaptor.hpp" +#include "scalfmm/interpolation/interpolator.hpp" +#include "scalfmm/interpolation/mapping.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/operators/fmm_operators.hpp" +#include "scalfmm/operators/interpolation/utils.hpp" +#include "scalfmm/simd/memory.hpp" +#include "scalfmm/utils/math.hpp" + +#include "xsimd/xsimd.hpp" + +#include <algorithm> +#include <array> +#include <complex> +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <vector> + +namespace scalfmm::operators +{ + // ------------------------------------------------------------- + // naive M2L operator + // ------------------------------------------------------------- + + /** + * @brief + * + * @tparam ApproximationType + * @tparam CellType + * @param interp + * @param source_cell + * @param target_cell + */ + template<typename ApproximationType, typename CellType> + inline auto apply_naive_m2l(ApproximationType const& interp, CellType const& source_cell, + CellType& target_cell) -> void + { + using interpolator_type = ApproximationType; + using cell_type = CellType; + using matrix_kernel_type = typename interpolator_type::matrix_kernel_type; + using value_type = typename cell_type::value_type; + using position_type = typename cell_type::position_type; + using mapping_type = interpolation::map_loc_glob<position_type>; + + static constexpr std::size_t dimension = position_type::dimension; + static constexpr std::size_t inputs_size = matrix_kernel_type::km; + static constexpr std::size_t outputs_size = matrix_kernel_type::kn; + + // get approximation data + const auto order = interp.order(); + const auto& matrix_kernel = interp.matrix_kernel(); + + // mapping operator in scalar + const mapping_type source_mapping(source_cell.center(), position_type(source_cell.width())); + const mapping_type target_mapping(target_cell.center(), position_type(target_cell.width())); + + const std::size_t nnodes = math::pow(order, position_type::dimension); + + std::vector<position_type> source_root_grid(nnodes), target_root_grid(nnodes); + auto roots = interp.roots(); + + std::array<std::size_t, position_type::dimension> stops{}; + stops.fill(order); + + // generate grid of roots + std::size_t idx = 0; + + // lambda function for assembling S + auto construct_root_grids = [&roots, &source_root_grid, &target_root_grid, &source_mapping, &target_mapping, + &idx](auto... current_indices) + { + auto local_source_root_position = + position_type(utils::generate_root<dimension>(roots, {{current_indices...}})); + source_mapping(local_source_root_position, source_root_grid[idx]); + auto local_target_root_position = + position_type(utils::generate_root<dimension>(roots, {{current_indices...}})); + target_mapping(local_target_root_position, target_root_grid[idx]); + idx++; + }; + + // expand loops over ORDER + meta::looper<dimension>()(construct_root_grids, stops); + + auto locals_iterator = target_cell.locals_begin(); + + for(std::size_t n = 0; n < nnodes; ++n) + { + auto multipoles_iterator = source_cell.multipoles_begin(); + + for(std::size_t m = 0; m < nnodes; ++m) + { + auto kxy = matrix_kernel.evaluate(target_root_grid[n], source_root_grid[m]); + + for(std::size_t i = 0; i < outputs_size; ++i) + { + for(std::size_t j = 0; j < inputs_size; ++j) + { + (*locals_iterator[i]) += kxy.at(i * inputs_size + j) * (*multipoles_iterator[j]); + } + } + meta::repeat([](auto& it) { ++it; }, multipoles_iterator); + } + meta::repeat([](auto& it) { ++it; }, locals_iterator); + } + } +} // namespace scalfmm::operators diff --git a/include/scalfmm/operators/interpolation/m2m_l2l.hpp b/include/scalfmm/operators/interpolation/m2m_l2l.hpp index b402a0869ae587f01ce20c4249d73783dde5821e..9d1fdb9bfc455e60a47bce2b28252c0d3d7ca460 100644 --- a/include/scalfmm/operators/interpolation/m2m_l2l.hpp +++ b/include/scalfmm/operators/interpolation/m2m_l2l.hpp @@ -1,14 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/interpolation/m2m_l2l.hpp +// File : scalfmm/operators/interpolation/m2m_l2l.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_INTERPOLATION_M2M_L2L_HPP #define SCALFMM_OPERATORS_INTERPOLATION_M2M_L2L_HPP -#include <array> -#include <type_traits> - -#include <utility> #include <xtensor-blas/xblas.hpp> #include <xtensor-blas/xblas_utils.hpp> #include <xtensor/xarray.hpp> @@ -23,9 +19,17 @@ #include "scalfmm/utils/math.hpp" #include "scalfmm/utils/tensor.hpp" +#include <array> +#include <type_traits> +#include <utility> + namespace scalfmm::operators::impl { - // Compute whether it needs to transpose or not according to the operator + /** + * @brief Computes whether it needs to transpose or not according to the operator + * + * @tparam Tag + */ template<typename Tag> constexpr auto transpose(Tag /*tag*/) { @@ -51,11 +55,20 @@ namespace scalfmm::operators // ------------------------------------------------------------- // Compute : core optimized function for m2m and l2l // ------------------------------------------------------------- + + /** + * @brief + * + * @tparam I + * @tparam Interp + * @tparam TensorMultipole + * @tparam Tag + */ template<std::size_t I, typename Interp, typename TensorMultipole, typename Tag> - inline void compute_opt(Interp const& interp, TensorMultipole const& child_expansion, - std::size_t child_index, xt::xarray<typename TensorMultipole::value_type>& expansion_buffer, - xt::xarray<typename TensorMultipole::value_type>& perm_expansion_buffer, std::size_t order, - std::size_t tree_level, Tag /* tag */) + inline void compute_opt(Interp const& interp, TensorMultipole const& child_expansion, std::size_t child_index, + xt::xarray<typename TensorMultipole::value_type>& expansion_buffer, + xt::xarray<typename TensorMultipole::value_type>& perm_expansion_buffer, + std::size_t order, std::size_t tree_level, Tag /* tag */) { using value_type = typename TensorMultipole::value_type; std::size_t multipoles_dim = Interp::dimension; @@ -68,11 +81,9 @@ namespace scalfmm::operators // Call to blas if constexpr(I == 0) { - cxxblas::gemm(cxxblas::StorageOrder::RowMajor, transpose(Tag{}), cxxblas::NoTrans, order_int, - size, order_int, value_type(1.), - &interp_tensor.at(tree_level, child_index, 0, 0), order_int, - child_expansion.data(), size, value_type(0.), - perm_expansion_buffer.data(), size); + cxxblas::gemm(cxxblas::StorageOrder::RowMajor, transpose(Tag{}), cxxblas::NoTrans, order_int, size, + order_int, value_type(1.), &interp_tensor.at(tree_level, child_index, 0, 0), order_int, + child_expansion.data(), size, value_type(0.), perm_expansion_buffer.data(), size); } else { @@ -80,30 +91,37 @@ namespace scalfmm::operators const auto exp_buff_data = expansion_buffer.data(); const auto perm_exp_buff_data = perm_expansion_buffer.data(); //auto perm_1 = &grid_permutations.at(I-1,0); - auto perm_1 = perm_data + (I-1)*nnodes; + auto perm_1 = perm_data + (I - 1) * nnodes; //auto perm_2 = &grid_permutations.at(I,0); - auto perm_2 = perm_data + I*nnodes; + auto perm_2 = perm_data + I * nnodes; for(std::size_t n{0}; n < nnodes; ++n) { exp_buff_data[perm_1[n]] = perm_exp_buff_data[perm_2[n]]; //expansion_buffer.data()[perm_1[n]] = perm_expansion_buffer.data()[perm_2[n]]; } - cxxblas::gemm(xt::get_blas_storage_order(expansion_buffer), transpose(Tag{}), cxxblas::NoTrans, order_int, - size, order_int, value_type(1.), - &interp_tensor.at(tree_level, child_index, I, 0), order_int, - expansion_buffer.data(), size, value_type(0.), - perm_expansion_buffer.data(), size); + cxxblas::gemm(xt::get_blas_storage_order(expansion_buffer), transpose(Tag{}), cxxblas::NoTrans, + order_int, size, order_int, value_type(1.), + &interp_tensor.at(tree_level, child_index, I, 0), order_int, expansion_buffer.data(), + size, value_type(0.), perm_expansion_buffer.data(), size); } } // ------------------------------------------------------------- // Compute : core generic function for m2m and l2l // ------------------------------------------------------------- + + /** + * @brief + * + * @tparam Interp + * @tparam TensorMultipole + * @tparam Tag + */ template<typename Interp, typename TensorMultipole, typename Tag> - inline void compute(Interp const& interp, TensorMultipole const& child_expansion, - std::size_t child_index, xt::xarray<typename TensorMultipole::value_type>& expansion_buffer, - std::size_t order, std::size_t tree_level, std::size_t dimension, Tag /* tag */) + inline void compute(Interp const& interp, TensorMultipole const& child_expansion, std::size_t child_index, + xt::xarray<typename TensorMultipole::value_type>& expansion_buffer, std::size_t order, + std::size_t tree_level, std::size_t dimension, Tag /* tag */) { using value_type = typename TensorMultipole::value_type; std::size_t multipoles_dim = expansion_buffer.dimension(); @@ -123,6 +141,14 @@ namespace scalfmm::operators xt::eval(expansion_buffer = tensor::fold(buffer, dimension, expansion_buffer.shape())); } + /** + * @brief + * + * @tparam Interp + * @tparam TensorMultipole + * @tparam Tag + * @tparam Is + */ template<typename Interp, typename TensorMultipole, typename Tag, std::size_t... Is> inline void expand(Interp const& interp, TensorMultipole const& child_expansion, std::size_t child_index, TensorMultipole& parent_expansion, @@ -161,19 +187,26 @@ namespace scalfmm::operators // For compute il directly made form the child tensor expansion compute(interp, child_expansion, child_index, expansion_buffer, order, tree_level, 0, Tag{}); // Others are expand using the buffered result (dimension -1 calls) - meta::noop_t{((compute(interp, expansion_buffer, child_index, expansion_buffer, order, - tree_level, Is + 1, Tag{})), - 0)...}; + meta::noop_t{ + ((compute(interp, expansion_buffer, child_index, expansion_buffer, order, tree_level, Is + 1, Tag{})), + 0)...}; } // Updating parent tensorial expansion using value_type = typename TensorMultipole::value_type; - cxxblas::axpy(static_cast<int>(expansion_buffer.size()), value_type(1.), expansion_buffer.data(), static_cast<int>(1), - parent_expansion.data(), static_cast<int>(1)); + cxxblas::axpy(static_cast<int>(expansion_buffer.size()), value_type(1.), expansion_buffer.data(), + static_cast<int>(1), parent_expansion.data(), static_cast<int>(1)); } + /** + * @brief + * + * @tparam Interp + * @tparam TensorMultipole + * @tparam Tag + */ template<typename Interp, typename TensorMultipole, typename Tag> - inline void expand(Interp const& interp, TensorMultipole const& child_expansion, - std::size_t child_index, TensorMultipole& parent_expansion, + inline void expand(Interp const& interp, TensorMultipole const& child_expansion, std::size_t child_index, + TensorMultipole& parent_expansion, xt::xarray<typename TensorMultipole::value_type>& expansion_buffer, xt::xarray<typename TensorMultipole::value_type>& perm_expansion_buffer, std::size_t order, std::size_t tree_level, Tag /* tag */) @@ -191,11 +224,24 @@ namespace scalfmm::operators // ------------------------------------------------------------- // M2M operator // ------------------------------------------------------------- + + /** + * @brief + * + * @tparam D + * @tparam Cell + * @param interp + * @param child_cell + * @param child_index + * @param parent_cell + * @param tree_level + */ template<typename D, typename Cell> - inline auto apply_m2m(interpolation::impl::interpolator<D> const& interp, Cell const& child_cell, std::size_t child_index, - Cell& parent_cell, std::size_t tree_level) -> void + inline auto apply_m2m(interpolation::impl::interpolator<D> const& interp, Cell const& child_cell, + std::size_t child_index, Cell& parent_cell, std::size_t tree_level) -> void { - using tensor_multipole_type = typename memory::storage_traits<typename Cell::storage_type::multipoles_storage_type>::inner_type; + using tensor_multipole_type = + typename memory::storage_traits<typename Cell::storage_type::multipoles_storage_type>::inner_type; typename tensor_multipole_type::shape_type order_shape; auto order{interp.order()}; @@ -210,22 +256,37 @@ namespace scalfmm::operators // TODO : empty perm_expansion_buffer when dimension is > 3 auto perm_expansion_buffer = xt::xarray<typename tensor_multipole_type::value_type>::from_shape(order_shape); - // Expanding computation according to the number of multipole values auto tensor_size = child_expansion.size(); for(std::size_t km_{0}; km_ < tensor_size; ++km_) { - impl::expand(interp, child_expansion.at(km_), child_index, parent_expansion.at(km_), - expansion_buffer, perm_expansion_buffer, order, tree_level, impl::tag_m2m{}); + impl::expand(interp, child_expansion.at(km_), child_index, parent_expansion.at(km_), expansion_buffer, + perm_expansion_buffer, order, tree_level, impl::tag_m2m{}); } } + // ------------------------------------------------------------- + // L2L operator + // ------------------------------------------------------------- + + /** + * @brief + * + * @tparam D + * @tparam Cell + * @param interp + * @param parent_cell + * @param child_index + * @param child_cell + * @param tree_level + */ template<typename D, typename Cell> inline auto apply_l2l(interpolation::impl::interpolator<D> const& interp, Cell const& parent_cell, std::size_t child_index, Cell& child_cell, std::size_t tree_level) -> void { - using tensor_multipole_type = typename memory::storage_traits<typename Cell::storage_type::multipoles_storage_type>::inner_type; + using tensor_multipole_type = + typename memory::storage_traits<typename Cell::storage_type::multipoles_storage_type>::inner_type; typename tensor_multipole_type::shape_type order_shape; auto order{interp.order()}; @@ -245,8 +306,8 @@ namespace scalfmm::operators for(std::size_t kn_{0}; kn_ < tensor_size; ++kn_) { - impl::expand(interp, parent_expansion.at(kn_), child_index, child_expansion.at(kn_), - expansion_buffer, perm_expansion_buffer, order, tree_level, impl::tag_l2l{}); + impl::expand(interp, parent_expansion.at(kn_), child_index, child_expansion.at(kn_), expansion_buffer, + perm_expansion_buffer, order, tree_level, impl::tag_l2l{}); } } } // namespace scalfmm::operators diff --git a/include/scalfmm/operators/interpolation/m2p.hpp b/include/scalfmm/operators/interpolation/m2p.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d2654f3b687b4083a94f59311411f9fa84fade8e --- /dev/null +++ b/include/scalfmm/operators/interpolation/m2p.hpp @@ -0,0 +1,171 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/operators/interpolation/m2p.hpp +// -------------------------------- +#pragma once + +#include "scalfmm/container/access.hpp" +#include "scalfmm/container/particle.hpp" +#include "scalfmm/container/particle_container.hpp" +#include "scalfmm/container/point.hpp" +#include "scalfmm/container/variadic_adaptor.hpp" +#include "scalfmm/interpolation/interpolator.hpp" +#include "scalfmm/interpolation/mapping.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/operators/fmm_operators.hpp" +#include "scalfmm/operators/interpolation/utils.hpp" +#include "scalfmm/simd/memory.hpp" +#include "scalfmm/utils/math.hpp" + +#include "xsimd/xsimd.hpp" + +#include <algorithm> +#include <array> +#include <complex> +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <vector> + +namespace scalfmm::operators +{ + // ------------------------------------------------------------- + // M2P operator + // ------------------------------------------------------------- + + /** + * @brief + * + * @tparam FarFieldType + * @tparam InterpolatorType + * @tparam CellType + * @tparam LeafType + * @param far_field + * @param source_cell + * @param target_leaf + * @return std::enable_if_t<std::is_base_of_v<interpolation::impl::interpolator<InterpolatorType>, InterpolatorType>> + */ + template<template<typename, bool> typename FarFieldType, typename InterpolatorType, typename CellType, + typename LeafType> + inline auto apply_m2p(FarFieldType<InterpolatorType, false> const& far_field, CellType const& source_cell, + LeafType& target_leaf) + -> std::enable_if_t<std::is_base_of_v<interpolation::impl::interpolator<InterpolatorType>, InterpolatorType>> + { + // set-up (aliases and constants) + using leaf_type = LeafType; + using interpolator_type = InterpolatorType; + using matrix_kernel_type = typename interpolator_type::matrix_kernel_type; + using value_type = typename leaf_type::value_type; + using position_type = typename leaf_type::position_type; + using particle_type = typename leaf_type::particle_type; + using outputs_type = typename particle_type::outputs_type; + using mapping_type = interpolation::map_loc_glob<position_type>; + + static constexpr std::size_t dimension = position_type::dimension; + static constexpr std::size_t inputs_size = matrix_kernel_type::km; + static constexpr std::size_t outputs_size = matrix_kernel_type::kn; + + using simd_type = xsimd::simd_type<value_type>; + using simd_position_type = container::point<simd_type, position_type::dimension>; + using simd_outputs_type = std::array<simd_type, particle_type::outputs_size>; + + static constexpr std::size_t inc = simd_type::size; + const std::size_t leaf_size{target_leaf.size()}; + const std::size_t vec_size = leaf_size - leaf_size % inc; + + // get approximation data + auto const& interp = far_field.approximation(); + const auto order = interp.order(); + const auto& matrix_kernel = interp.matrix_kernel(); + + // mapping operator in scalar + const mapping_type mapping_part_position_scal(source_cell.center(), position_type(source_cell.width())); + + // iterator on the particle container -> std::tuple<iterators...> + auto position_iterator = container::position_begin(target_leaf.particles()); + auto outputs_iterator = container::outputs_begin(target_leaf.particles()); + + const std::size_t nnodes = math::pow(order, position_type::dimension); + + std::vector<position_type> root_grid(nnodes); + auto roots = interp.roots(); + + std::array<std::size_t, position_type::dimension> stops{}; + stops.fill(order); + + // generate grid of roots + std::size_t idx = 0; + + // lambda function for assembling S + auto construct_root_grid = [&roots, &root_grid, &mapping_part_position_scal, &idx](auto... current_indices) + { + auto local_root_position = position_type(utils::generate_root<dimension>(roots, {{current_indices...}})); + mapping_part_position_scal(local_root_position, root_grid[idx]); + idx++; + }; + + // expand loops over ORDER + meta::looper<dimension>()(construct_root_grid, stops); + + // Loop through the particles (vector) + for(std::size_t part = 0; part < vec_size; part += inc) + { + auto global_position = simd::load_position<simd_position_type>(position_iterator, simd::unaligned{}); + + simd_outputs_type outputs{meta::to_array(simd::load_tuple<simd_type>(outputs_iterator, simd::unaligned{}))}; + + auto multipoles_iterator = source_cell.multipoles_begin(); + + for(std::size_t m = 0; m < nnodes; ++m) + { + auto kxy = matrix_kernel.evaluate(global_position, simd_position_type(root_grid[m])); + + for(std::size_t i = 0; i < outputs_size; ++i) + { + for(std::size_t j = 0; j < inputs_size; ++j) + { + outputs.at(i) += kxy.at(i * inputs_size + j) * (*multipoles_iterator[j]); + } + } + + meta::repeat([](auto& it) { ++it; }, multipoles_iterator); + } + + simd::store_tuple<simd_type>(outputs_iterator, outputs, simd::unaligned{}); + + position_iterator += inc; + outputs_iterator += inc; + } + + // Loop through the particles (scalar) + for(std::size_t part = vec_size; part < leaf_size; ++part) + { + auto global_position = simd::load_position<position_type>(position_iterator); + + outputs_type outputs{}; + meta::for_each(outputs, [](auto& v) { v = value_type(0.); }); + + auto multipoles_iterator = source_cell.multipoles_begin(); + + for(std::size_t m = 0; m < nnodes; ++m) + { + auto kxy = matrix_kernel.evaluate(global_position, root_grid[m]); + + for(std::size_t i = 0; i < outputs_size; ++i) + { + for(std::size_t j = 0; j < inputs_size; ++j) + { + outputs.at(i) += kxy.at(i * inputs_size + j) * (*multipoles_iterator[j]); + } + } + + meta::repeat([](auto& it) { ++it; }, multipoles_iterator); + } + + meta::tuple_sum_update(*outputs_iterator, outputs); + + ++position_iterator; + ++outputs_iterator; + } + } +} // namespace scalfmm::operators diff --git a/include/scalfmm/operators/interpolation/p2l.hpp b/include/scalfmm/operators/interpolation/p2l.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5db5ce761f6a8b1b450b8d170cef626ac978bdee --- /dev/null +++ b/include/scalfmm/operators/interpolation/p2l.hpp @@ -0,0 +1,167 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/operators/interpolation/p2l.hpp +// -------------------------------- +#pragma once + +#include "scalfmm/container/access.hpp" +#include "scalfmm/container/particle.hpp" +#include "scalfmm/container/particle_container.hpp" +#include "scalfmm/container/point.hpp" +#include "scalfmm/container/variadic_adaptor.hpp" +#include "scalfmm/interpolation/interpolator.hpp" +#include "scalfmm/interpolation/mapping.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/operators/fmm_operators.hpp" +#include "scalfmm/operators/interpolation/utils.hpp" +#include "scalfmm/simd/memory.hpp" +#include "scalfmm/utils/math.hpp" + +#include "xsimd/xsimd.hpp" + +#include <algorithm> +#include <array> +#include <complex> +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <vector> + +namespace scalfmm::operators +{ + // ------------------------------------------------------------- + // L2P operator + // ------------------------------------------------------------- + + /** + * @brief + * + * @tparam FarFieldType + * @tparam InterpolatorType + * @tparam CellType + * @tparam LeafType + * @param far_field + * @param target_cell + * @param source_leaf + * @return std::enable_if_t<std::is_base_of_v<interpolation::impl::interpolator<InterpolatorType>, InterpolatorType>> + */ + template<template<typename, bool> typename FarFieldType, typename InterpolatorType, typename CellType, + typename LeafType> + inline auto apply_p2l(FarFieldType<InterpolatorType, false> const& far_field, CellType& target_cell, + LeafType const& source_leaf) + -> std::enable_if_t<std::is_base_of_v<interpolation::impl::interpolator<InterpolatorType>, InterpolatorType>> + { + // set-up (aliases and constants) + using leaf_type = LeafType; + using interpolator_type = InterpolatorType; + using matrix_kernel_type = typename interpolator_type::matrix_kernel_type; + using value_type = typename leaf_type::value_type; + using position_type = typename leaf_type::position_type; + using particle_type = typename leaf_type::particle_type; + using outputs_type = typename particle_type::outputs_type; + using mapping_type = interpolation::map_loc_glob<position_type>; + + static constexpr std::size_t dimension = position_type::dimension; + static constexpr std::size_t inputs_size = matrix_kernel_type::km; + static constexpr std::size_t outputs_size = matrix_kernel_type::kn; + + using simd_type = xsimd::simd_type<value_type>; + using simd_position_type = container::point<simd_type, position_type::dimension>; + using simd_outputs_type = std::array<simd_type, particle_type::outputs_size>; + + static constexpr std::size_t inc = simd_type::size; + const std::size_t leaf_size{source_leaf.size()}; + // const std::size_t vec_size = leaf_size - leaf_size % inc; + const std::size_t vec_size = 0; + + // get approximation data + auto const& interp = far_field.approximation(); + const auto order = interp.order(); + const auto& matrix_kernel = interp.matrix_kernel(); + + // mapping operator in scalar + const mapping_type mapping_part_position_scal(target_cell.center(), position_type(target_cell.width())); + + // iterator on the particle container -> std::tuple<iterators...> + auto position_iterator = container::position_begin(source_leaf.particles()); + auto inputs_iterator = container::inputs_begin(source_leaf.particles()); + + const std::size_t nnodes = math::pow(order, position_type::dimension); + + std::vector<position_type> root_grid(nnodes); + auto roots = interp.roots(); + + std::array<std::size_t, position_type::dimension> stops{}; + stops.fill(order); + + // generate grid of roots + std::size_t idx = 0; + + // lambda function for assembling S + auto construct_root_grid = [&roots, &root_grid, &mapping_part_position_scal, &idx](auto... current_indices) + { + auto local_root_position = position_type(utils::generate_root<dimension>(roots, {{current_indices...}})); + mapping_part_position_scal(local_root_position, root_grid[idx]); + idx++; + }; + + // expand loops over ORDER + meta::looper<dimension>()(construct_root_grid, stops); + + // Loop through the particles (vector) + // for(std::size_t part = 0; part < vec_size; part += inc) + // { + // auto global_position = simd::load_position<simd_position_type>(position_iterator, simd::unaligned{}); + // auto inputs_values = meta::to_array(simd::load_tuple<simd_type>(inputs_iterator, simd::unaligned{})); + + // auto locals_iterator = target_cell.locals_begin(); + // simd_locals_type simd_locals{simd::load_tuple<simd_type>(locals_iterator, simd)}; + + // for(std::size_t m = 0; m < nnodes; ++m) + // { + // auto kxy = matrix_kernel.evaluate(simd_position_type(root_grid[m]), global_position); + + // for(std::size_t i = 0; i < outputs_size; ++i) + // { + // for(std::size_t j = 0; j < inputs_size; ++j) + // { + // simd_locals[i] += kxy.at(i * inputs_size + j) * inputs_values[j]; + // } + // } + + // meta::it_sum_update(locals_iterator, simd_locals); + // meta::repeat([](auto& it) { ++it; }, locals_iterator); + // } + + // position_iterator += inc; + // inputs_iterator += inc; + // } + + // Loop through the particles (scalar) + for(std::size_t part = vec_size; part < leaf_size; ++part) + { + auto global_position = simd::load_position<position_type>(position_iterator); + auto inputs_values = meta::to_array(simd::load_tuple<value_type>(inputs_iterator)); + + auto locals_iterator = target_cell.locals_begin(); + + for(std::size_t m = 0; m < nnodes; ++m) + { + auto kxy = matrix_kernel.evaluate(root_grid[m], global_position); + + for(std::size_t i = 0; i < outputs_size; ++i) + { + for(std::size_t j = 0; j < inputs_size; ++j) + { + *locals_iterator[i] += kxy.at(i * inputs_size + j) * inputs_values[j]; + } + } + + meta::repeat([](auto& it) { ++it; }, locals_iterator); + } + + ++position_iterator; + ++inputs_iterator; + } + } +} // namespace scalfmm::operators diff --git a/include/scalfmm/operators/interpolation/p2m.hpp b/include/scalfmm/operators/interpolation/p2m.hpp index 445d1198d818f0177b2d6f6ecc77311fe34187aa..a5db0629bb20a7cd38ff0526a75b7a539b5070f1 100644 --- a/include/scalfmm/operators/interpolation/p2m.hpp +++ b/include/scalfmm/operators/interpolation/p2m.hpp @@ -1,16 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/interpolation/p2m.hpp +// File : scalfmm/operators/interpolation/p2m.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_INTERPOLATION_P2M_HPP #define SCALFMM_OPERATORS_INTERPOLATION_P2M_HPP -#include <array> -#include <cstdlib> -#include <tuple> -#include <vector> -#include <xsimd/xsimd.hpp> - #include "scalfmm/container/particle_container.hpp" #include "scalfmm/container/point.hpp" #include "scalfmm/interpolation/interpolator.hpp" @@ -19,27 +13,39 @@ #include "scalfmm/operators/interpolation/utils.hpp" #include "scalfmm/simd/memory.hpp" #include "scalfmm/utils/math.hpp" + #include "xsimd/xsimd.hpp" +#include <array> +#include <cstdlib> +#include <tuple> +#include <vector> + namespace scalfmm::operators { // ------------------------------------------------------------- // P2M operator // ------------------------------------------------------------- - // template<typename D, typename E, typename Leaf, typename Cell> - // inline auto apply_p2m(interpolation::impl::interpolator<D, E> const& interp, Leaf const& source_leaf, Cell& cell) - // -> void - template<typename far_field_operator, typename Cell, typename Leaf> - // inline auto apply_l2p(far_field_operator<Interpolator, true> const& far_field, Cell const& source_cell, - // Leaf& target_leaf) - inline auto apply_p2m(far_field_operator const& far_field, Leaf const& source_leaf, Cell& cell) -> void + + /** + * @brief + * + * @tparam FarFieldOperatorType + * @tparam CellType + * @tparam LeafType + * @param far_field + * @param source_leaf + * @param cell + */ + template<typename FarFieldOperatorType, typename CellType, typename LeafType> + inline auto apply_p2m(FarFieldOperatorType const& far_field, LeafType const& source_leaf, CellType& cell) -> void { // Leaf type - using leaf_type = Leaf; + using leaf_type = LeafType; // position_type -> scalfmm::container::point<...> using position_type = typename leaf_type::position_type; // value_type -> inner position_type::value_type -> underlying scalar_type - using value_type = typename Leaf::value_type; + using value_type = typename leaf_type::value_type; // simd_type -> xsimd::batch<scalar_type> using simd_type = xsimd::simd_type<value_type>; // simd_position_type -> container::point<xsimd::batch<scalar_type>> diff --git a/include/scalfmm/operators/interpolation/utils.hpp b/include/scalfmm/operators/interpolation/utils.hpp index 6854f922b7b465aa08c55af9bbbb9848cf4e17cc..e4c70cdbfefcd1f12a6594bd7cd122e94549155c 100644 --- a/include/scalfmm/operators/interpolation/utils.hpp +++ b/include/scalfmm/operators/interpolation/utils.hpp @@ -1,30 +1,44 @@ // -------------------------------- // See LICENCE file at project root -// File : kernels/interpolation/utils.hpp +// File : scalfmm/operators/interpolation/utils.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_INTERPOLATION_UTILS_HPP #define SCALFMM_OPERATORS_INTERPOLATION_UTILS_HPP +#include "scalfmm/container/point.hpp" +#include "scalfmm/meta/utils.hpp" + +#include "xsimd/xsimd.hpp" + #include <array> #include <cstddef> #include <type_traits> #include <utility> -#include <xsimd/xsimd.hpp> - -#include "scalfmm/container/point.hpp" -#include "scalfmm/meta/utils.hpp" namespace scalfmm::operators::utils { namespace impl { - + /** + * @brief + * + * @tparam Container + * @tparam ISs + */ template<typename Container, std::size_t... ISs> constexpr inline auto generate_s(std::index_sequence<ISs...> /*s*/, Container const& cont, std::array<std::size_t, sizeof...(ISs)> const& index) { return meta::multiply(std::get<ISs>(cont[index[ISs]])...); } + + /** + * @brief + * + * @tparam vec_size + * @tparam Container + * @tparam ISs + */ template<std::size_t vec_size, typename Container, std::size_t... ISs> constexpr inline auto generate_s_simd(std::index_sequence<ISs...> /*s*/, Container const& cont, std::array<std::size_t, sizeof...(ISs)> const& index) @@ -32,18 +46,37 @@ namespace scalfmm::operators::utils return meta::multiply(xsimd::load_aligned(&std::get<ISs>(cont)[index[ISs] * vec_size])...); } + /** + * @brief + * + * @tparam T + * @tparam Is + */ template<typename T, std::size_t... Is> inline constexpr auto multiply_der(T der, std::index_sequence<Is...> /*s*/) { return meta::multiply(meta::get<Is>(der)...); } + /** + * @brief + * + * @tparam T + * @param der + * @return constexpr auto + */ template<typename T> inline constexpr auto multiply_der(T der) { return multiply_der(der, std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>{}); } + /** + * @brief + * + * @tparam Container + * @tparam ISs + */ template<typename Container, std::size_t... ISs> constexpr inline auto generate_der_s(std::index_sequence<ISs...> /*s*/, Container const& pol, Container const& der, std::array<std::size_t, sizeof...(ISs)> const& index) @@ -72,6 +105,14 @@ namespace scalfmm::operators::utils meta::noop_t{(meta::get<ISs>(res) = multiply_der(to_folds[ISs]), 0)...}; return res; } + + /** + * @brief + * + * @tparam vec_size + * @tparam Container + * @tparam ISs + */ template<std::size_t vec_size, typename Container, std::size_t... ISs> constexpr inline auto generate_der_s_simd(std::index_sequence<ISs...> /*s*/, Container const& pol, Container const& der, @@ -119,6 +160,26 @@ namespace scalfmm::operators::utils erase(res, pol_val, der_val); return res; } + + /** + * @brief + * + * @tparam Container + * @tparam ISs + */ + template<typename Container, std::size_t... ISs> + constexpr inline auto generate_root(std::index_sequence<ISs...>, Container const& cont, + std::array<std::size_t, sizeof...(ISs)> const& index) + { + return std::array<typename Container::value_type, sizeof...(ISs)>{cont[index[ISs]]...}; + } + + /** + * @brief + * + * @tparam Container + * @tparam ISs + */ template<typename Container, std::size_t... ISs> constexpr inline auto multiply_components_at_indices(std::index_sequence<ISs...> /*s*/, Container const& cont, std::array<std::size_t, sizeof...(ISs)> const& index) @@ -128,11 +189,30 @@ namespace scalfmm::operators::utils } // namespace impl + /** + * @brief + * + * @tparam N + * @tparam Container + * @param cont + * @param index + * @return constexpr auto + */ template<std::size_t N, typename Container> constexpr inline auto generate_s(Container const& cont, std::array<std::size_t, N> const& index) { return impl::generate_s(std::make_index_sequence<N>{}, cont, index); } + + /** + * @brief + * + * @tparam vec_size + * @tparam Container + * @param cont + * @param index + * @return constexpr auto + */ template<std::size_t vec_size, typename Container> constexpr inline auto generate_s_simd(Container const& cont, @@ -142,15 +222,33 @@ namespace scalfmm::operators::utils std::make_index_sequence<meta::tuple_size_v<typename Container::value_type>>{}, cont, index); } - // Cont : the container storing S - // Der : the container storing the derivative - // Index : the current loop index + /** + * @brief + * + * @tparam N + * @tparam Container + * @param cont + * @param der + * @param index + * @return constexpr auto + */ template<std::size_t N, typename Container> constexpr inline auto generate_der_s(Container const& cont, Container const& der, std::array<std::size_t, N> const& index) { return impl::generate_der_s(std::make_index_sequence<N>{}, cont, der, index); } + + /** + * @brief + * + * @tparam vec_size + * @tparam Container + * @param cont + * @param der + * @param index + * @return constexpr auto + */ template<std::size_t vec_size, typename Container> constexpr inline auto generate_der_s_simd(Container const& cont, Container const& der, @@ -159,6 +257,31 @@ namespace scalfmm::operators::utils return impl::generate_der_s_simd<vec_size>( std::make_index_sequence<meta::tuple_size_v<typename Container::value_type>>{}, cont, der, index); } + + /** + * @brief + * + * @tparam N + * @tparam Container + * @param cont + * @param index + * @return constexpr auto + */ + template<std::size_t N, typename Container> + constexpr inline auto generate_root(Container const& cont, std::array<std::size_t, N> const& index) + { + return impl::generate_root(std::make_index_sequence<N>{}, cont, index); + } + + /** + * @brief + * + * @tparam N + * @tparam Container + * @param cont + * @param index + * @return constexpr auto + */ template<std::size_t N, typename Container> constexpr inline auto multiply_components_at_indices(Container const& cont, std::array<std::size_t, N> const& index) { diff --git a/include/scalfmm/operators/l2l.hpp b/include/scalfmm/operators/l2l.hpp index c7df5cf3ae869b979bc5af28002c1ab7f6f31e1b..1dbe18d9c3df5be52f9ef55f2d0583aa4c9f9655 100644 --- a/include/scalfmm/operators/l2l.hpp +++ b/include/scalfmm/operators/l2l.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/l2l.hpp +// File : scalfmm/operators/l2l.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_L2L_HPP #define SCALFMM_OPERATORS_L2L_HPP @@ -9,11 +9,22 @@ namespace scalfmm::operators { - template<typename Approximation, typename Cell> - inline void l2l(Approximation const& approximation, Cell const& parent_cell, std::size_t child_index, Cell& child_cell, - std::size_t tree_level = 2) + /** + * @brief + * + * @tparam ApproximationType + * @tparam CellType + * @param approximation + * @param parent_cell + * @param child_index + * @param child_cell + * @param tree_level + */ + template<typename ApproximationType, typename CellType> + inline void l2l(ApproximationType const& approximation, CellType const& parent_cell, std::size_t child_index, + CellType& child_cell, std::size_t tree_level = 2) { apply_l2l(approximation, parent_cell, child_index, child_cell, tree_level); } -} -#endif // SCALFMM_OPERATORS_M2M_HPP +} // namespace scalfmm::operators +#endif // SCALFMM_OPERATORS_M2M_HPP diff --git a/include/scalfmm/operators/l2p.hpp b/include/scalfmm/operators/l2p.hpp index 9b4a48b59281d326e8514142f1e636b0ce4aaec2..f81971da9a464d1deeb9eb8656bbd9c042bf2f3e 100644 --- a/include/scalfmm/operators/l2p.hpp +++ b/include/scalfmm/operators/l2p.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/l2p.hpp +// File : scalfmm/operators/l2p.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_L2P_HPP #define SCALFMM_OPERATORS_L2P_HPP @@ -9,8 +9,18 @@ namespace scalfmm::operators { - template<typename FarField, typename Cell, typename Leaf> - inline void l2p(FarField const& far_field, Cell const& source_cell, Leaf& target_leaf) + /** + * @brief + * + * @tparam FarFieldType + * @tparam CellType + * @tparam LeafType + * @param far_field + * @param source_cell + * @param target_leaf + */ + template<typename FarFieldType, typename CellType, typename LeafType> + inline void l2p(FarFieldType const& far_field, CellType const& source_cell, LeafType& target_leaf) { apply_l2p(far_field, source_cell, target_leaf); } diff --git a/include/scalfmm/operators/m2l.hpp b/include/scalfmm/operators/m2l.hpp index 5ec987f005dc4b315cb4be4bdc7d398a99b28b0b..643ba2be64fac988e7ef285d16c7eed8a1e7ba55 100644 --- a/include/scalfmm/operators/m2l.hpp +++ b/include/scalfmm/operators/m2l.hpp @@ -1,19 +1,21 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/m2l.hpp +// File : scalfmm/operators/m2l.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_M2L_HPP #define SCALFMM_OPERATORS_M2L_HPP #include <cstddef> +#include "scalfmm/operators/interpolation/m2l.hpp" + namespace scalfmm::operators { /** * @brief Compute M2L operator between two cells (only used in periodic case) * - * @tparam Approximation - * @tparam Cell + * @tparam ApproximationType + * @tparam CellType * @param approximation * @param source_cell * @param neighbor_idx @@ -21,20 +23,46 @@ namespace scalfmm::operators * @param current_tree_level * @param buffer */ - template<typename Approximation, typename Cell> - inline void m2l(Approximation const& approximation, Cell const& source_cell, std::size_t neighbor_idx, - Cell& target_cell, std::size_t current_tree_level, - [[maybe_unused]] typename Approximation::buffer_type& buffer) + template<typename ApproximationType, typename CellType> + inline auto m2l(ApproximationType const& approximation, CellType const& source_cell, std::size_t neighbor_idx, + CellType& target_cell, std::size_t current_tree_level, + [[maybe_unused]] typename ApproximationType::buffer_type& buffer) -> void { approximation.apply_m2l_single(source_cell, target_cell, neighbor_idx, current_tree_level, buffer); } - template<typename Approximation, typename Cell> - inline void m2l_loop(Approximation const& approximation, Cell& target_cell, std::size_t current_tree_level, - [[maybe_unused]] typename Approximation::buffer_type& buffer) + /** + * @brief + * + * @tparam ApproximationType + * @tparam CellType + * @param approximation + * @param target_cell + * @param current_tree_level + * @param buffer + */ + template<typename ApproximationType, typename CellType> + inline auto m2l_loop(ApproximationType const& approximation, CellType& target_cell, std::size_t current_tree_level, + [[maybe_unused]] typename ApproximationType::buffer_type& buffer) -> void { approximation.apply_m2l_loop(target_cell, current_tree_level, buffer); } + + /** + * @brief + * + * @tparam ApproximationType + * @tparam CellType + * @param approximation + * @param source_cell + * @param target_cell + */ + template<typename ApproximationType, typename CellType> + inline auto m2l_naive(ApproximationType const& approximation, CellType const& source_cell, + CellType& target_cell) -> void + { + apply_naive_m2l(approximation, source_cell, target_cell); + } } // namespace scalfmm::operators #endif // SCALFMM_OPERATORS_M2L_HPP diff --git a/include/scalfmm/operators/m2m.hpp b/include/scalfmm/operators/m2m.hpp index 220cfc372f7009fd21f9f4ec1a46fa735d6845be..f3181c34c34adf9a6156c339c0f55d7607f57887 100644 --- a/include/scalfmm/operators/m2m.hpp +++ b/include/scalfmm/operators/m2m.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/m2m.hpp +// File : scalfmm/operators/m2m.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_M2M_HPP #define SCALFMM_OPERATORS_M2M_HPP @@ -9,9 +9,20 @@ namespace scalfmm::operators { - template<typename Approximation, typename Cell> - inline void m2m(Approximation const& approximation, Cell const& child_cell, std::size_t child_index, Cell& parent_cell, - std::size_t tree_level = 2) + /** + * @brief + * + * @tparam ApproximationType + * @tparam CellType + * @param approximation + * @param child_cell + * @param child_index + * @param parent_cell + * @param tree_level + */ + template<typename ApproximationType, typename CellType> + inline void m2m(ApproximationType const& approximation, CellType const& child_cell, std::size_t child_index, + CellType& parent_cell, std::size_t tree_level = 2) { apply_m2m(approximation, child_cell, child_index, parent_cell, tree_level); } diff --git a/include/scalfmm/operators/m2p.hpp b/include/scalfmm/operators/m2p.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9c5af3b1e3457504dcc82b1182b72aac674608f0 --- /dev/null +++ b/include/scalfmm/operators/m2p.hpp @@ -0,0 +1,28 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/operators/m2p.hpp +// -------------------------------- +#ifndef SCALFMM_OPERATORS_M2P_HPP +#define SCALFMM_OPERATORS_M2P_HPP + +#include "scalfmm/operators/interpolation/m2p.hpp" + +namespace scalfmm::operators +{ + /** + * @brief + * + * @tparam FarFieldType + * @tparam CellType + * @tparam LeafType + * @param far_field + * @param source_cell + * @param target_leaf + */ + template<typename FarFieldType, typename CellType, typename LeafType> + inline auto m2p(FarFieldType const& far_field, CellType const& source_cell, LeafType& target_leaf) -> void + { + apply_m2p(far_field, source_cell, target_leaf); + } +} // namespace scalfmm::operators +#endif // SCALFMM_OPERATORS_M2P_HPP diff --git a/include/scalfmm/operators/mutual_apply.hpp b/include/scalfmm/operators/mutual_apply.hpp index 4f2695a13bdbd9cf2888770c4fa2ddc451a3b79a..96382727d2bc810bd507dad52d94d888f5d6ef1a 100644 --- a/include/scalfmm/operators/mutual_apply.hpp +++ b/include/scalfmm/operators/mutual_apply.hpp @@ -10,24 +10,23 @@ namespace scalfmm::operators { - /// @brief This function handles out-of-group interactions for the current group - /// and applies the p2p_full_mutual operator between the leaves of two different groups. - /// . maybe unused - /// - /// @tparam GroupType - /// @tparam MatrixKernel - /// @tparam ArrayT - /// @tparam ValueT - /// @param group The group on which we want to compute the interactions - /// @param matrix_kernel the matrix kernel you need to pass over to the operator - /// @param[in] pbc array of periodicity in each direction - /// @param[in] box_width the maximal width of simulation box - - template<typename GroupType, typename MatrixKernel, typename ArrayT, typename ValueT> - void apply_out_of_group_p2p(GroupType& group, MatrixKernel const& matrix_kernel, ArrayT const& pbc, - ValueT const& box_width) + /** + * @brief This function handles out-of-group interactions for the current group + * and applies the p2p_full_mutual operator between the leaves of two different groups. + * + * @tparam GroupType + * @tparam MatrixKernelType + * @tparam ArrayType + * @tparam ValueType + * @param group The group on which we want to compute the interactions + * @param matrix_kernel the matrix kernel you need to pass over to the operator + * @param[in] pbc array of periodicity in each direction + * @param[in] box_width the maximal width of simulation box + */ + template<typename GroupType, typename MatrixKernelType, typename ArrayType, typename ValueType> + void apply_out_of_group_p2p(GroupType& group, MatrixKernelType const& matrix_kernel, ArrayType const& pbc, + ValueType const& box_width) { - using operators::p2p_full_mutual; // Get interactions outside of the current group. const auto& outside_interactions = group.symbolics().outside_interactions; @@ -43,25 +42,26 @@ namespace scalfmm::operators } } - /// @brief This function applies some out-of-group interactions for the current group. - /// - /// The goal of this function is to apply the interactions only on 2 groups to split - /// the dependencies in OMP parallelization. - /// - /// @tparam GroupType - /// @tparam MatrixKernel - /// @tparam ArrayT - /// @tparam ValueT - /// @param[inout] group: The group on which we want to compute the interactions - /// @param[in] first_out the first iteration in the out-of-block iterations to treat - /// @param[in] last_out the last iteration in the out-of-block iterations to treat - /// @param[in] matrix_kernel the matrix kernel you need to pass over to the operator - /// @param[in] pbc array of periodicity in each direction - /// @param[in] box_width : the maximal width of simulation box - //The good method - template<typename GroupType, typename MatrixKernel, typename ArrayT, typename ValueT> + /** + * @brief This function applies some out-of-group interactions for the current group. + * + * The goal of this function is to apply the interactions only on 2 groups to split + * the dependencies in OMP parallelization. + * + * @tparam GroupType + * @tparam MatrixKernelType + * @tparam ArrayType + * @tparam ValueType + * @param[inout] group: The group on which we want to compute the interactions + * @param[in] first_out the first iteration in the out-of-block iterations to treat + * @param[in] last_out the last iteration in the out-of-block iterations to treat + * @param[in] matrix_kernel the matrix kernel you need to pass over to the operator + * @param[in] pbc array of periodicity in each direction + * @param[in] box_width : the maximal width of simulation box + */ + template<typename GroupType, typename MatrixKernelType, typename ArrayType, typename ValueType> void apply_out_of_group_p2p(GroupType& group, const int& first_out, const int& last_out, - MatrixKernel const& matrix_kernel, ArrayT const& pbc, ValueT const& box_width) + MatrixKernelType const& matrix_kernel, ArrayType const& pbc, ValueType const& box_width) { using operators::p2p_full_mutual; // constexpr std::size_t sizeNeig = (std::pow(static_cast<const int>(3), Dimension) - static_cast<const @@ -73,8 +73,8 @@ namespace scalfmm::operators constexpr std::size_t sizeNeig = (meta::Pow<3, Dimension>::value - 1); // Try to use a single call to perform p2p between leaves in the outer group with the same Morton index (inside) // as the leaf within the group. - auto current_pos = first_out ; - auto morton_index = outside_interactions[current_pos].inside_index; + auto current_pos = first_out; + auto morton_index = outside_interactions[current_pos].inside_index; std::array<typename GroupType::iterator_type, sizeNeig> neighbors_keep; // Start loop on the interactions inside the same group while(current_pos != last_out) @@ -103,7 +103,7 @@ namespace scalfmm::operators // } p2p_full_mutual(matrix_kernel, current_leaf, neighbors_keep, number_of_leaves, pbc, box_width); } - #else +#else for(int i = first_out; i < last_out; ++i) { @@ -117,30 +117,32 @@ namespace scalfmm::operators #endif } - /// @brief This function applies some out-of-group interactions for the current group. - /// - /// The goal of this function is to apply the interactions only on 2 groups to split - /// the dependencies in OMP parallelization. The interactions is between the group and - /// the group with Morton indexes between [first_morton_index, last_morton_index[ - /// - /// Maybe unused - /// - /// @tparam GroupType - /// @tparam MatrixKernel - /// @tparam MortonType - /// @tparam ArrayT - /// @tparam ValueT - /// @param[inout] group: The group on which we want to compute the interactions - /// @param[in] start th index to start the iterations inside the group - /// @param[in] first_morton_index the first morton index of the group to treat - /// @param[in] last_morton_index the last morton index of the group to treat - /// @param[in] matrix_kernel the matrix kernel you need to pass over to the operator - /// @param[in] pbc array of periodicity in each direction - /// @param[in] box_width : the maximal width of simulation box - template<typename GroupType, typename MatrixKernel, typename MortonType, typename ArrayT, typename ValueT> - void apply_out_of_group_p2p(GroupType& group, int& start, MortonType const& first_morton_index, - MortonType const& last_morton_index, MatrixKernel const& matrix_kernel, - ArrayT const& pbc, ValueT const& box_width) + /** + * @brief This function applies some out-of-group interactions for the current group. + * + * The goal of this function is to apply the interactions only on 2 groups to split + * the dependencies in OMP parallelization. The interactions is between the group and + * the group with Morton indexes between [first_morton_index, last_morton_index[ + * + * Maybe unused + * + * @tparam GroupType + * @tparam MatrixKernelType + * @tparam MortonType + * @tparam ArrayType + * @tparam ValueType + * @param[inout] group: The group on which we want to compute the interactions + * @param[in] start th index to start the iterations inside the group + * @param[in] first_morton_index the first morton index of the group to treat + * @param[in] last_morton_index the last morton index of the group to treat + * @param[in] matrix_kernel the matrix kernel you need to pass over to the operator + * @param[in] pbc array of periodicity in each direction + * @param[in] box_width : the maximal width of simulation box + */ + template<typename GroupType, typename MatrixKernelType, typename MortonType, typename ArrayType, typename ValueType> + void apply_out_of_group_p2p(GroupType& group, int& start, MortonType const& first_morton_index, + MortonType const& last_morton_index, MatrixKernelType const& matrix_kernel, + ArrayType const& pbc, ValueType const& box_width) { using operators::p2p_full_mutual; // Get interactions outside of the current group. diff --git a/include/scalfmm/operators/p2l.hpp b/include/scalfmm/operators/p2l.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5ea00e8d37b464d2786ff1eed832defa0f8af9c6 --- /dev/null +++ b/include/scalfmm/operators/p2l.hpp @@ -0,0 +1,28 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/operators/p2l.hpp +// -------------------------------- +#ifndef SCALFMM_OPERATORS_P2L_HPP +#define SCALFMM_OPERATORS_P2L_HPP + +#include "scalfmm/operators/interpolation/p2l.hpp" + +namespace scalfmm::operators +{ + /** + * @brief + * + * @tparam FarFieldType + * @tparam CellType + * @tparam LeafType + * @param far_field + * @param target_cell + * @param source_leaf + */ + template<typename FarFieldType, typename CellType, typename LeafType> + inline auto p2l(FarFieldType const& far_field, CellType const& target_cell, LeafType& source_leaf) -> void + { + apply_p2l(far_field, target_cell, source_leaf); + } +} // namespace scalfmm::operators +#endif // SCALFMM_OPERATORS_P2L_HPP diff --git a/include/scalfmm/operators/p2m.hpp b/include/scalfmm/operators/p2m.hpp index 95b510eb1b67dad45bbb0af5b94b52ba0c565ad8..3cfe711908ecb471dfbf6796d51f5ff94941501d 100644 --- a/include/scalfmm/operators/p2m.hpp +++ b/include/scalfmm/operators/p2m.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/p2m.hpp +// File : scalfmm/operators/p2m.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_P2M_HPP #define SCALFMM_OPERATORS_P2M_HPP @@ -12,12 +12,15 @@ namespace scalfmm::operators /** * @brief This function interpolates the inputs of the particle on the interpolation grid. * - * @param far_field the far-field operator - * @param leaf the source leaf - * @param cell the source cell + * @tparam FarFieldType + * @tparam LeafType + * @tparam CellType + * @param far_field the far-field operator + * @param leaf the source leaf + * @param cell the source cell */ - template<typename FarField, typename Leaf, typename Cell> - inline void p2m(FarField const& far_field, Leaf const& leaf, Cell& cell) + template<typename FarFieldType, typename LeafType, typename CellType> + inline void p2m(FarFieldType const& far_field, LeafType const& leaf, CellType& cell) { apply_p2m(far_field, leaf, cell); } diff --git a/include/scalfmm/operators/p2p.hpp b/include/scalfmm/operators/p2p.hpp index 63d74d23423a97d4815866dba1862f0f926cb9f4..21ed4de9e710bfa1628303921312ffd0cbfd7d41 100644 --- a/include/scalfmm/operators/p2p.hpp +++ b/include/scalfmm/operators/p2p.hpp @@ -1,12 +1,10 @@ -// See LICENCE file at project root -// File : operators/p2p.hpp +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/operators/p2p.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_P2P_HPP #define SCALFMM_OPERATORS_P2P_HPP -#include <array> -#include <numeric> - #include "scalfmm/container/particle.hpp" #include "scalfmm/container/point.hpp" #include "scalfmm/meta/type_pack.hpp" @@ -14,33 +12,41 @@ #include "scalfmm/simd/memory.hpp" #include "scalfmm/tree/utils.hpp" -#include <xsimd/xsimd.hpp> +#include "xsimd/xsimd.hpp" + +#include <array> +#include <numeric> namespace scalfmm::operators { - // Full mutual - // =========== - /// - /// \brief p2p_full_mutual compute in a mutual way the particle-to-particle interactions between the target and - /// the neighbors - /// - /// - /// For each particle, x, in target_leaf we compute for each leaf, neighbor_leaf, - /// in neighbors the interactions between particle y in - /// \f[ x.outputs = matrix_kernel(x,y) * y.inputs \f] - /// \f[ y.outputs = matrix_kernel(x,y) * x.inputs\f] - /// - /// \param[in] matrix_kernel the kernel used to compute the interaction - /// \param[inout] target_leaf the current leaf where we compute the p2p and the neighbors leafs with - /// morton index low - /// \param[inout] neighbors the set of neighboring leaves whose morton index is lower than that - /// of the target_leaf - template<typename MatrixKernel, typename Leaf, typename ContainerOfLeafIterator, typename ArrayT, typename ValueT> - inline void p2p_full_mutual(MatrixKernel const& matrix_kernel, Leaf& target_leaf, - ContainerOfLeafIterator const& neighbors, const int& size, - [[maybe_unused]] ArrayT const& pbc, ValueT const& box_width) + /** + * @brief Computes in a mutual way the particle-to-particle interactions between the target and the neighbors. + * + * For each particle, x, in target_leaf we compute for each leaf, neighbor_leaf, + * in neighbors the interactions between particle y in + * $\f[ x.outputs = matrix_kernel(x,y) * y.inputs \f]$ + * $\f[ y.outputs = matrix_kernel(x,y) * x.inputs\f]$ + * + * @tparam MatrixKernelType + * @tparam LeafType + * @tparam ContainerOfLeafIteratorType + * @tparam ArrayType + * @tparam ValueType + * @param matrix_kernel the kernel used to compute the interactions. + * @param target_leaf the current leaf where we compute the p2p + * @param neighbors all neighbouring leaves whose Morton indexes are lower than the target leaf's one. + * @param size + * @param pbc + * @param box_width + */ + template<typename MatrixKernelType, typename LeafType, typename ContainerOfLeafIteratorType, typename ArrayType, + typename ValueType> + inline void p2p_full_mutual(MatrixKernelType const& matrix_kernel, LeafType& target_leaf, + ContainerOfLeafIteratorType const& neighbors, const int& size, + [[maybe_unused]] ArrayType const& pbc, ValueType const& box_width) { - using leaf_type = Leaf; + using leaf_type = LeafType; + using matrix_kernel_type = MatrixKernelType; // leaf particle type using particle_type = typename leaf_type::particle_type; // position type @@ -48,7 +54,7 @@ namespace scalfmm::operators // outputs tuple type using outputs_type = typename particle_type::outputs_type; // value type - using value_type = typename Leaf::value_type; + using value_type = typename leaf_type::value_type; // simd batch type according to the value type using simd_type = xsimd::simd_type<value_type>; // simd point type @@ -58,9 +64,8 @@ namespace scalfmm::operators decltype(meta::to_tuple(meta::replace_inner_tuple_type_t<xsimd::simd_type, outputs_type>{})); // simd array style outputs type. using simd_outputs_type = std::array<simd_type, particle_type::outputs_size>; - // using near_field_type = typename interpolation::interpolator_traits<Interpolator>::near_field_type; - static constexpr std::size_t kn = MatrixKernel::kn; - static constexpr std::size_t km = MatrixKernel::km; + static constexpr std::size_t kn = matrix_kernel_type::kn; + static constexpr std::size_t km = matrix_kernel_type::km; // compute the center of the target_leaf auto const& target_center = target_leaf.center(); @@ -226,27 +231,27 @@ namespace scalfmm::operators * Most the computations are done using SIMD instructions. Assuming that there are N particles in the target leaf, * the total number of interactions to be computed is proportional to N x (N - 1) / 2. * - * @tparam MatrixKernel The type of the matrix kernel that is used in the p2p computation. - * @tparam Leaf The type of the leaf on which the operation will be performed. - * - * @param matrix_kernel A constant reference to the kernel object used to compute the interactions. - * @param target_leaf The current leaf where we compute the particle-to-particle interactions. + * @tparam MatrixKernelType The type of the matrix kernel that is used in the p2p computation. + * @tparam LeafType The type of the leaf on which the operation will be performed. * + * @param matrix_kernel a constant reference to the kernel object used to compute the interactions. + * @param target_leaf the current leaf where we compute the particle-to-particle interactions. */ template<typename MatrixKernelType, typename LeafType> inline void p2p_inner_mutual(MatrixKernelType const& matrix_kernel, LeafType& target_leaf) { using leaf_type = LeafType; + using matrix_kernel_type = MatrixKernelType; using particle_type = typename leaf_type::particle_type; using position_type = typename leaf_type::position_type; using outputs_type = typename particle_type::outputs_type; - using value_type = typename LeafType::value_type; + using value_type = typename leaf_type::value_type; using simd_type = xsimd::simd_type<value_type>; using simd_position_type = container::point<simd_type, particle_type::dimension>; using simd_outputs_type = std::array<simd_type, particle_type::outputs_size>; - // using near_field_type = typename interpolation::interpolator_traits<interpolator>::near_field_type; - static constexpr std::size_t kn = MatrixKernelType::kn; - static constexpr std::size_t km = MatrixKernelType::km; + + static constexpr std::size_t kn = matrix_kernel_type::kn; + static constexpr std::size_t km = matrix_kernel_type::km; constexpr std::size_t inc = simd_type::size; const std::size_t target_size{target_leaf.size()}; @@ -374,12 +379,11 @@ namespace scalfmm::operators * Most the computations are done using SIMD instructions. Assuming that there are N particles in the target leaf, * the total number of interactions to be computed is proportional to N x (N - 1). * - * @tparam MatrixKernel The type of the matrix kernel that is used in the p2p computation. - * @tparam Leaf The type of the leaf on which the operation will be performed. + * @tparam MatrixKernelType The type of the matrix kernel that is used in the p2p computation. + * @tparam LeafType The type of the leaf on which the operation will be performed. * * @param matrix_kernel A constant reference to the kernel object used to compute the interactions. * @param target_leaf The current leaf where we compute the particle-to-particle interactions. - * */ template<typename MatrixKernelType, typename LeafType> inline void p2p_inner_non_mutual(MatrixKernelType const& matrix_kernel, LeafType& target_leaf) @@ -393,7 +397,6 @@ namespace scalfmm::operators using simd_type = xsimd::simd_type<value_type>; using simd_position_type = container::point<simd_type, particle_type::dimension>; using simd_outputs_type = std::array<simd_type, particle_type::outputs_size>; - // using near_field_type = typename interpolation::interpolator_traits<Interpolator>::near_field_type; static constexpr std::size_t kn = matrix_kernel_type::kn; static constexpr std::size_t km = matrix_kernel_type::km; @@ -512,16 +515,15 @@ namespace scalfmm::operators * This function acts as an interface that either calls the mutual p2p inner operator or the non-mutual p2P inner * operator depending on the boolean parameter 'mutual'. * - * @tparam MatrixKernel The type of the matrix kernel that is used in the p2p computation. - * @tparam Leaf The type of the leaf on which the operation will be performed. + * @tparam MatrixKernelType The type of the matrix kernel that is used in the p2p computation. + * @tparam LeafType The type of the leaf on which the operation will be performed. * * @param matrix_kernel A constant reference to the kernel object used to compute the interactions. * @param target_leaf The current leaf where we compute the particle-to-particle interactions. * @param mutual An optional boolean flag that determines whether the computation is mutual or not. - * */ - template<typename MatrixKernel, typename Leaf> - inline void p2p_inner(MatrixKernel const& matrix_kernel, Leaf& target_leaf, bool mutual = true) + template<typename MatrixKernelType, typename LeafType> + inline void p2p_inner(MatrixKernelType const& matrix_kernel, LeafType& target_leaf, bool mutual = true) { if(mutual) { @@ -534,7 +536,7 @@ namespace scalfmm::operators } /** - * @brief compute the field due to the particles the source container on the target particles + * @brief Computes the field due to the particles the source container on the target particles * * compute the field due to the particles the source container on the target particles by * @@ -542,17 +544,21 @@ namespace scalfmm::operators * * We don't check if | pt - ps| =0 * + * @tparam MatrixKernelType + * @tparam TargetLeafType + * @tparam SourceLeafType + * @tparam ArrayType * @param matrix_kernel the kernel used to compute the interaction - * @param[inout] target_container the container of particles where the field is evaluated - * @param[in] source_container the container of particles which generate the field + * @param[inout] target_leaf the container of particles where the field is evaluated + * @param[in] source_leaf the container of particles which generate the field * @param[in] shift the shift to apply on the particle in periodic system. */ - template<typename MatrixKernel, typename TargetLeaf, typename SourceLeaf, typename ArrayT> - inline void p2p_outer(MatrixKernel const& matrix_kernel, TargetLeaf& target_leaf, SourceLeaf const& source_leaf, - [[maybe_unused]] ArrayT const& shift) + template<typename MatrixKernelType, typename TargetLeafType, typename SourceLeafType, typename ArrayType> + inline void p2p_outer(MatrixKernelType const& matrix_kernel, TargetLeafType& target_leaf, + SourceLeafType const& source_leaf, [[maybe_unused]] ArrayType const& shift) { #ifndef NDEBUG - if constexpr(std::is_same_v<TargetLeaf, SourceLeaf>) + if constexpr(std::is_same_v<TargetLeafType, SourceLeafType>) { if(&target_leaf.symbolics() == &source_leaf.symbolics()) { @@ -560,17 +566,20 @@ namespace scalfmm::operators } } #endif - using particle_type = typename TargetLeaf::particle_type; + using target_leaf_type = TargetLeafType; + using source_leaf_type = SourceLeafType; + using matrix_kernel_type = MatrixKernelType; + using particle_type = typename target_leaf_type::particle_type; using position_type = typename particle_type::position_type; using outputs_type = typename particle_type::outputs_type; - using inputs_value_type = typename SourceLeaf::particle_type::inputs_value_type; + using inputs_value_type = typename source_leaf_type::particle_type::inputs_value_type; using value_type = inputs_value_type; using simd_type = xsimd::simd_type<value_type>; using simd_position_type = container::point<simd_type, position_type::dimension>; using simd_outputs_type = std::array<simd_type, particle_type::outputs_size>; - static constexpr std::size_t kn = MatrixKernel::kn; - static constexpr std::size_t km = MatrixKernel::km; + static constexpr std::size_t kn = matrix_kernel_type::kn; + static constexpr std::size_t km = matrix_kernel_type::km; static constexpr std::size_t inc = simd_type::size; const std::size_t target_size{target_leaf.size()}; @@ -669,33 +678,39 @@ namespace scalfmm::operators } } - // - // Remote - // =========== - /// \brief p2p_outer compute the particle-to-particle interactions between the target leaf - /// and its neighbors. - /// - /// - /// For each particle, x, in target_leaf we compute for each leaf, neighbor_leaf, - /// in neighbors the interactions between particle y in - /// \f[ x.outputs = matrix_kernel(x,y) * y.inputs \f] - /// \warning{The neighbors are not modified.} - /// \todo{To be implemented} - /// \param[in] matrix_kernel the kernel used to compute the interaction - /// \param[inout] target_leaf the current leaf where we compute the p2p and the neighbors leafs with - /// morton index low - /// \param[inout] neighbors the set of neighboring leaves whose morton index is lower than that - /// of the target_leaf - /// \param[in] number_of_neighbors number of elements to treat in array neighbors - template<typename MatrixKernel, typename Leaf, typename ContainerOfLeafIterator, typename ArrayT, typename ValueT> - inline void p2p_outer(MatrixKernel const& matrix_kernel, Leaf& target_leaf, - ContainerOfLeafIterator const& neighbors, const int number_of_neighbors, - [[maybe_unused]] ArrayT const& pbc, ValueT const& box_width) + /** + * @brief p2p_outer compute the particle-to-particle interactions between the target leaf + * and its neighbors. + * + * For each particle, x, in target_leaf we compute for each leaf, neighbor_leaf, + * in neighbors the interactions between particle y in + * $\f[ x.outputs = matrix_kernel(x,y) * y.inputs \f]$ + * + * @warning{The neighbors are not modified.} + * @todo{To be implemented} + * + * @tparam MatrixKernelType + * @tparam LeafType + * @tparam ContainerOfLeafIteratorType + * @tparam ArrayType + * @tparam ValueType + * @param matrix_kernel the kernel used to compute the interactions. + * @param target_leaf the current leaf where we compute the p2p. + * @param neighbors all neighboring leaves. + * @param number_of_neighbors number of neighbors. + * @param pbc + * @param box_width + */ + template<typename MatrixKernelType, typename LeafType, typename ContainerOfLeafIteratorType, typename ArrayType, + typename ValueType> + inline void p2p_outer(MatrixKernelType const& matrix_kernel, LeafType& target_leaf, + ContainerOfLeafIteratorType const& neighbors, const int number_of_neighbors, + [[maybe_unused]] ArrayType const& pbc, ValueType const& box_width) { - using leaf_type = Leaf; + using leaf_type = LeafType; using position_type = typename leaf_type::position_type; // // value type - using value_type = typename Leaf::value_type; + using value_type = typename leaf_type::value_type; // // compute the center of the target_leaf auto const& target_center = target_leaf.center(); @@ -713,24 +728,26 @@ namespace scalfmm::operators }; std::for_each(std::begin(neighbors), std::begin(neighbors) + number_of_neighbors, apply_outer); } + /** * @brief P2P function between the target leaf to update and the list of neighbors. * - * @tparam MatrixKernel kernel type - * @tparam Leaf type of the leaf - * @tparam ContainerOfLeafIterator neighbor iterator container type - * @tparam ArrayT type of the pbc array - * @tparam ValueT - * @param[in] matrix_kernel The (kernel - * @param[inout] target_leaf the target leaf (the output field will be updated by the source leaf ) - * @param[in] neighbors the array of neighbors - * @param[in] pbc the array of periodic boundary condition - * @param[in] box_width the width of the simulation box + * @tparam MatrixKernelType + * @tparam LeafType + * @tparam ContainerOfLeafIteratorType + * @tparam ArrayType + * @tparam ValueType + * @param[in] matrix_kernel The kernel used to compute the interactions. + * @param[inout] target_leaf the target leaf (the output field will be updated by the source leaf). + * @param[in] neighbors the array of neighbors. + * @param[in] pbc the array of periodic boundary condition. + * @param[in] box_width the width of the simulation box. */ - template<typename MatrixKernel, typename Leaf, typename ContainerOfLeafIterator, typename ArrayT, typename ValueT> - inline void p2p_outer(MatrixKernel const& matrix_kernel, Leaf& target_leaf, - ContainerOfLeafIterator const& neighbors, [[maybe_unused]] ArrayT const& pbc, - ValueT const& box_width) + template<typename MatrixKernelType, typename LeafType, typename ContainerOfLeafIteratorType, typename ArrayType, + typename ValueType> + inline void p2p_outer(MatrixKernelType const& matrix_kernel, LeafType& target_leaf, + ContainerOfLeafIteratorType const& neighbors, [[maybe_unused]] ArrayType const& pbc, + ValueType const& box_width) { p2p_outer(matrix_kernel, target_leaf, neighbors, target_leaf.csymbolics().number_of_neighbors, pbc, box_width); } diff --git a/include/scalfmm/operators/spherical/l2p.hpp b/include/scalfmm/operators/spherical/l2p.hpp index f28d9cc8a18a3ec9e81a31c590847efdb8bc0fd6..d1ad9fda2c9bc5e341dafb0ee25309590e336777 100644 --- a/include/scalfmm/operators/spherical/l2p.hpp +++ b/include/scalfmm/operators/spherical/l2p.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/spherical/l2p.hpp +// File : scalfmm/operators/spherical/l2p.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_SPHERICAL_L2P_HPP #define SCALFMM_OPERATORS_SPHERICAL_L2P_HPP @@ -10,11 +10,24 @@ namespace scalfmm::operators { // ------------------------------------------------------------- - // l2p operator + // L2P operators // ------------------------------------------------------------- - template<typename T, typename Cell, typename Leaf> - inline auto apply_l2p(spherical::spherical<T> const& far_field, Cell const& source_cell, Leaf& target_leaf) -> void + + /** + * @brief + * + * @tparam T + * @tparam CellType + * @tparam LeafType + * @param far_field + * @param source_cell + * @param target_leaf + */ + template<typename T, typename CellType, typename LeafType> + inline auto apply_l2p(spherical::spherical<T> const& far_field, CellType const& source_cell, + LeafType& target_leaf) -> void { + throw("Spherical L2P not implemented yet !"); } } // namespace scalfmm::operators diff --git a/include/scalfmm/operators/spherical/m2m_l2l.hpp b/include/scalfmm/operators/spherical/m2m_l2l.hpp index a4130e3d38675b98bdfcec62aae6c163ae868ec5..989e77ae923637657c6df30a146e62a01be27846 100644 --- a/include/scalfmm/operators/spherical/m2m_l2l.hpp +++ b/include/scalfmm/operators/spherical/m2m_l2l.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/spherical/m2m_l2l.hpp +// File : scalfmm/operators/spherical/m2m_l2l.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_SPHERICAL_M2M_L2L_HPP #define SCALFMM_OPERATORS_SPHERICAL_M2M_L2L_HPP @@ -10,18 +10,43 @@ namespace scalfmm::operators { // ------------------------------------------------------------- - // m2m operator + // M2M operators // ------------------------------------------------------------- - template<typename T, typename Cell> - inline auto apply_m2m(spherical::spherical<T> const& far_field, Cell const& child_cell, Cell& parent_cell) -> void + + /** + * @brief + * + * @tparam T + * @tparam CellType + * @param far_field + * @param child_cell + * @param parent_cell + */ + template<typename T, typename CellType> + inline auto apply_m2m(spherical::spherical<T> const& far_field, CellType const& child_cell, + CellType& parent_cell) -> void { + throw("Spherical M2M not implemented yet !"); } + // ------------------------------------------------------------- - // l2l operator + // L2L operators // ------------------------------------------------------------- - template<typename T, typename Cell> - inline auto apply_l2l(spherical::spherical<T> const& far_field, Cell const& parent_cell, Cell& child_cell) -> void + + /** + * @brief + * + * @tparam T + * @tparam CellType + * @param far_field + * @param parent_cell + * @param child_cell + */ + template<typename T, typename CellType> + inline auto apply_l2l(spherical::spherical<T> const& far_field, CellType const& parent_cell, + CellType& child_cell) -> void { + throw("Spherical L2L not implemented yet !"); } } // namespace scalfmm::operators diff --git a/include/scalfmm/operators/spherical/p2m.hpp b/include/scalfmm/operators/spherical/p2m.hpp index 4675a046873bf3c938a5417f52ca8aa924166327..82cf1a400dce3953ba773e551aade52b5aa6a8b8 100644 --- a/include/scalfmm/operators/spherical/p2m.hpp +++ b/include/scalfmm/operators/spherical/p2m.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/spherical/p2m.hpp +// File : scalfmm/operators/spherical/p2m.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_SPHERICAL_P2M_HPP #define SCALFMM_OPERATORS_SPHERICAL_P2M_HPP @@ -10,11 +10,23 @@ namespace scalfmm::operators { // ------------------------------------------------------------- - // P2M operator + // P2M operators // ------------------------------------------------------------- - template<typename D, typename E, typename Leaf, typename Cell> - inline auto apply_p2m(spherical::spherical<T> const& interp, Leaf const& source_leaf, Cell& cell) -> void + + /** + * @brief + * + * @tparam T + * @tparam LeafType + * @tparam CellType + * @param interp + * @param source_leaf + * @param cell + */ + template<typename T, typename LeafType, typename CellType> + inline auto apply_p2m(spherical::spherical<T> const& interp, LeafType const& source_leaf, CellType& cell) -> void { + throw("Spherical P2M not implemented yet !"); } } // namespace scalfmm::operators diff --git a/include/scalfmm/operators/spherical/utils.hpp b/include/scalfmm/operators/spherical/utils.hpp index 7766f2d0b9c78715d8d54df1859b8e569b87500e..bb84ff71dfe4c7fa308363c7f8df837d44443bfa 100644 --- a/include/scalfmm/operators/spherical/utils.hpp +++ b/include/scalfmm/operators/spherical/utils.hpp @@ -1,20 +1,29 @@ // -------------------------------- // See LICENCE file at project root -// File : kernels/interpolation/utils.hpp +// File : scalfmm/operators/spherical/utils.hpp // -------------------------------- #ifndef SCALFMM_OPERATORS_INTERPOLATION_UTILS_HPP #define SCALFMM_OPERATORS_INTERPOLATION_UTILS_HPP +#include "scalfmm/meta/utils.hpp" + #include <array> #include <utility> -#include "scalfmm/meta/utils.hpp" - namespace scalfmm::operators::utils { namespace impl { - + /** + * @brief + * + * @tparam Container + * @tparam ISs + * @param s + * @param cont + * @param index + * @return constexpr auto + */ template<typename Container, std::size_t... ISs> constexpr inline auto generate_s(std::index_sequence<ISs...> s, Container const& cont, std::array<std::size_t, sizeof...(ISs)> const& index) @@ -22,15 +31,38 @@ namespace scalfmm::operators::utils return meta::multiply(std::get<ISs>(cont[index[ISs]])...); } - constexpr auto erase = [](auto pol, auto der, std::size_t i) constexpr { pol.at(i) = der.at(i); return pol; }; + /** + * @brief + * + */ + constexpr auto erase = [](auto pol, auto der, std::size_t i) constexpr + { + pol.at(i) = der.at(i); + return pol; + }; + /** + * @brief + * + * @tparam T + * @tparam Is + * @param der + * @param s + * @return constexpr auto + */ template<typename T, std::size_t... Is> - inline constexpr auto multiply_der(T&& der, - std::index_sequence<Is...> s) + inline constexpr auto multiply_der(T&& der, std::index_sequence<Is...> s) { return meta::multiply(meta::get<Is>(der)...); } + /** + * @brief + * + * @tparam T + * @param der + * @return constexpr auto + */ template<typename T> inline constexpr auto multiply_der(T&& der) { @@ -38,9 +70,20 @@ namespace scalfmm::operators::utils std::make_index_sequence<meta::tuple_size<std::decay_t<T>>::value>{}); } + /** + * @brief + * + * @tparam Container + * @tparam ISs + * @param s + * @param pol + * @param der + * @param index + * @return constexpr auto + */ template<typename Container, std::size_t... ISs> constexpr inline auto generate_der_s(std::index_sequence<ISs...> s, Container const& pol, Container const& der, - std::array<std::size_t, sizeof...(ISs)> const& index) + std::array<std::size_t, sizeof...(ISs)> const& index) { using value_type = typename Container::value_type; constexpr auto dimension = value_type::dimension; @@ -65,17 +108,34 @@ namespace scalfmm::operators::utils } // namespace impl + /** + * @brief + * + * @tparam N + * @tparam Container + * @param cont + * @param index + * @return constexpr auto + */ template<std::size_t N, typename Container> constexpr inline auto generate_s(Container const& cont, std::array<std::size_t, N> const& index) { return impl::generate_s(std::make_index_sequence<N>{}, cont, index); } - // Cont : the container storing S - // Der : the container storing the derivative - // Index : the current loop index + /** + * @brief + * + * @tparam N + * @tparam Container + * @param cont + * @param der + * @param index + * @return constexpr auto + */ template<std::size_t N, typename Container> - constexpr inline auto generate_der_s(Container const& cont, Container const& der, std::array<std::size_t, N> const& index) + constexpr inline auto generate_der_s(Container const& cont, Container const& der, + std::array<std::size_t, N> const& index) { return impl::generate_der_s(std::make_index_sequence<N>{}, cont, der, index); } diff --git a/include/scalfmm/operators/tags.hpp b/include/scalfmm/operators/tags.hpp index 5213690ed9e4f4235422792836197fedca834263..faeab8cafe69ccd2865685ce1886c3d372e8227b 100644 --- a/include/scalfmm/operators/tags.hpp +++ b/include/scalfmm/operators/tags.hpp @@ -1,38 +1,57 @@ // -------------------------------- // See LICENCE file at project root -// File : operators/tags.hpp +// File : scalfmm/operators/tags.hpp // -------------------------------- -#ifndef SCALFMM_OPERATORS_TAGS_HPP -#define SCALFMM_OPERATORS_TAGS_HPP - +#pragma once namespace scalfmm::operators::impl { - // Tag dispatching for op + /** + * @brief Tag dispatching for FMM operators. + */ + + // Common base type for tag dispatching struct tag { }; + + // Tag for the P2M operator + struct tag_p2m : tag + { + }; + + // Tag for the M2M operator struct tag_m2m : tag { }; - struct tag_l2l : tag + + // Tag for the M2L operator + struct tag_m2l : tag { }; - struct tag_m2p : tag + + // Tag for the L2L operator + struct tag_l2l : tag { }; - struct tag_p2m : tag + + // Tag for the L2P operator + struct tag_l2p : tag { }; - struct tag_m2l : tag + + // Tag for the M2P operator + struct tag_m2p : tag { }; - struct tag_p2p : tag + + // Tag for the P2L operator + struct tag_p2l : tag { }; - struct tag_l2p : tag + + // Tag for the P2P operator + struct tag_p2p : tag { }; } // namespace scalfmm::operators::impl - -#endif // SCALFMM_OPERATORSC_TAGS_HPP diff --git a/include/scalfmm/options/options.hpp b/include/scalfmm/options/options.hpp index b719ec5e8270b077bb87571ea89aa1d598b2eafd..96747683582919ffe0ad0899f8bfa8bd2a450db8 100644 --- a/include/scalfmm/options/options.hpp +++ b/include/scalfmm/options/options.hpp @@ -14,47 +14,97 @@ namespace scalfmm::options { + /** + * @brief Base template for defining a setting. + * + * This template provides a `value()` method to retrieve the string representation + * of the derived setting. It uses CRTP (Curiously Recurring Template Pattern) to + * delegate to the derived class's implementation. + * + * @tparam D The derived setting type. + */ template<typename D> struct setting { using type = setting<D>; using derived_type = D; + /** + * @brief Retrieves the string representation of the setting. + * + * Delegates to the `value()` method of the derived class. + * + * @return A string view representing the setting. + */ constexpr auto value() const noexcept -> std::string_view { return static_cast<derived_type>(this)->value(); } }; + /** + * @brief Represents a collection of multiple settings. + * + * This struct uses variadic inheritance to combine multiple `setting` instances + * into a single `settings` object. + * + * @tparam S Variadic template representing the types of the settings. + */ template<typename... S> struct settings : S... { using type = settings<S...>; + /** + * @brief Constructs a settings object. + * + * @param s Variadic arguments representing the settings. + */ template<typename... Setting> constexpr settings(Setting... s) { } }; + /** + * @brief Deduction guide for `settings`. + * + * Automatically deduces the types of the settings when `settings` is instantiated. + */ template<typename... S> settings(S... s) -> settings<typename S::type...>; + /** + * @brief A specific option for the 'dense' case (the M2L interaction matrices are fully stored). + */ struct dense_ : setting<dense_> { using type = dense_; static constexpr auto value() noexcept -> std::string_view { return "dense"; }; }; + /** + * @brief A specific option for the 'fft' case (this enable FFT-based optimization for the M2L pass). + */ struct fft_ : setting<fft_> { using type = fft_; static constexpr auto value() noexcept -> std::string_view { return "fft"; }; }; + /** + * @brief A specific option for the 'low-rank' case (the M2L interaction matrices are compressed). + */ struct low_rank_ : setting<low_rank_> { using type = low_rank_; static constexpr auto value() noexcept -> std::string_view { return "low_rank"; }; }; + /** + * @brief A specific compound option for the 'sequential' case that combines multiple settings. + * + * This option is formed by variadic inheritance of the specified settings. + * + * @tparam S Variadic template representing the types of the settings. + */ template<typename... S> struct seq_ : setting<seq_<S...>> @@ -65,6 +115,13 @@ namespace scalfmm::options static constexpr auto value() noexcept -> std::string_view { return "seq"; }; }; + /** + * @brief A specific compound option for the 'OpenMP-based' case that combines multiple settings. + * + * This option is formed by variadic inheritance of the specified settings. + * + * @tparam S Variadic template representing the types of the settings. + */ template<typename... S> struct omp_ : setting<omp_<S...>> @@ -75,55 +132,142 @@ namespace scalfmm::options static constexpr auto value() noexcept -> std::string_view { return "omp"; }; }; + /** + * @brief A specific option to time each pass of the FMM algorithm. + */ struct timit_ : setting<timit_> { using type = timit_; static constexpr auto value() noexcept -> std::string_view { return "timit"; }; }; + /** + * @brief A specific compound option for the 'Uniform interpolation' that extends another setting. + * + * This option is a combination of its own identity (Uniform interpolation) and another setting + * (e.g., dense_, low_rank_, fft_). Examples of possible combinations are 'Uniform + dense', + * 'Uniform + low-rank', 'Uniform + fft'. + * + * @tparam S The base setting type (default is `fft_`). + */ template<typename S = fft_> struct uniform_ - : setting<uniform_<S>> - , S + : setting<uniform_<S>> + , S { using type = uniform_<S>; static constexpr auto value() noexcept -> std::string_view { return "uniform_"; }; - // { - // std::stringstream ss; - // ss << "uniform_" << S().value(); - // return std::string(ss.str()); - // }; }; + /** + * @brief A specific compound option for the 'Chebyshev interpolation' that extends another setting. + * + * This option is a combination of its own identity (Chebyshev interpolation) and another setting + * (e.g., dense_, low_rank_, fft_). Examples of possible combinations are 'Chebyshev + dense', + * 'Chebyshev + low-rank'. + * + * @tparam S The base setting type (default is `low_rank_`). + */ template<typename S = low_rank_> struct chebyshev_ - : setting<chebyshev_<S>> - , S + : setting<chebyshev_<S>> + , S { using type = chebyshev_<S>; static constexpr auto value() noexcept -> std::string_view { return "chebyshev_"; }; - - // static auto value() noexcept -> std::string - // { - // std::stringstream ss; - // ss << "chebyshev_" << S().value(); - // return std::string(ss.str()); - // }; }; + /** + * @brief A specific compound option for the 'Barycentric interpolation' that extends another setting. + * + * This option is a combination of its own identity (Barycentric interpolation) and another setting + * (e.g., low_rank_). Examples of possible combinations are 'Barycentric + dense', + * 'Barycentric + low-rank'. + * + * @tparam S The base setting type (default is `low_rank_`). + */ + template<typename S = low_rank_> + struct barycentric_ + : setting<barycentric_<S>> + , S + { + using type = barycentric_<S>; + static constexpr auto value() noexcept -> std::string_view { return "barycentric_"; }; + }; + /** + * @brief Predefined static option for 'Uniform interpolation' with fully stored M2L interaction matrices. + */ static constexpr auto uniform_dense = uniform_<dense_>{}; - static constexpr auto uniform_fft = uniform_<fft_>{}; + + /** + * @brief Predefined static option for 'Uniform interpolation' with compressed M2L interaction matrices. + */ static constexpr auto uniform_low_rank = uniform_<low_rank_>{}; + + /** + * @brief Predefined static option for 'Uniform interpolation' with FFT-based optimization. + */ + static constexpr auto uniform_fft = uniform_<fft_>{}; + + /** + * @brief Predefined static option for 'Barycentric interpolation' with fully stored M2L interaction matrices. + */ + static constexpr auto barycentric_dense = barycentric_<dense_>{}; + + /** + * @brief Predefined static option for 'Barycentric interpolation' with compressed M2L interaction matrices. + */ + static constexpr auto barycentric_low_rank = barycentric_<low_rank_>{}; + + /** + * @brief Predefined static option for 'Chebyshev-based interpolation' with fully stored M2L interaction matrices. + */ static constexpr auto chebyshev_dense = chebyshev_<dense_>{}; + + /** + * @brief Predefined static option for 'Chebyshev-based interpolation' with compressed M2L interaction matrices. + */ static constexpr auto chebyshev_low_rank = chebyshev_<low_rank_>{}; + + /** + * @brief Predefined static option to fully store the M2L interaction matrices. + */ static constexpr auto dense = dense_{}; + + /** + * @brief Predefined static option to enable FFT-based optimization for the M2L pass. + */ static constexpr auto fft = fft_{}; + + /** + * @brief Predefined static option to compress the M2L interaction matrices. + */ static constexpr auto low_rank = low_rank_{}; + + /** + * @brief Predefined static option to use the OpenMP-based algorithm. + */ static constexpr auto omp = omp_{}; + + /** + * @brief Predefined static option to use the OpenMP-based algorithm with each pass is timed. + */ static constexpr auto omp_timit = omp_<timit_>{}; + + /** + * @brief Predefined static option to use the sequential algorithm. + */ static constexpr auto seq = seq_{}; + + /** + * @brief Predefined static option to use the sequential algorithm with each pass is timed. + */ static constexpr auto seq_timit = seq_<timit_>{}; + + /** + * @brief Predefined static option to time each pass of the algorithm. + */ static constexpr auto timit = timit_{}; /** @@ -137,10 +281,11 @@ namespace scalfmm::options * * @tparam S1 * @tparam S2 + * * @param s1 the first setting * @param s2 the second one - * @return true - * @return false + * + * @return */ template<typename... S1, typename... S2> static constexpr auto has(settings<S1...> s1, settings<S2...> s2) -> bool @@ -148,48 +293,128 @@ namespace scalfmm::options return (... || std::is_base_of_v<S2, decltype(s1)>); } + /** + * @brief + * + * @tparam S1 + * @tparam S + * + * @param s1 + * @param s + * + * @return + */ template<typename... S1, typename... S> static constexpr auto has(settings<S1...> s1, S... s) -> bool { return (... || std::is_base_of_v<S, decltype(s1)>); } + /** + * @brief + * + * @tparam S1 + * @tparam S2 + * + * @param s1 + * @param s2 + * + * @return + */ template<typename... S1, typename... S2> static constexpr auto match(settings<S1...> s1, settings<S2...> s2) -> bool { return (sizeof...(S1) == sizeof...(S2)) && (... && std::is_base_of_v<S2, decltype(s1)>); } + /** + * @brief + * + * @tparam S1 + * @tparam S + * + * @param s1 + * @param s + * + * @return + */ template<typename... S1, typename... S> static constexpr auto match(settings<S1...> s1, S... s) -> bool { return ((sizeof...(S1) == sizeof...(S)) && (... && std::is_base_of_v<S, decltype(s1)>)); } + /** + * @brief + * + * @tparam S1 + * @tparam S2 + * + * @param s1 + * @param s2 + * + * @return + */ template<typename... S1, typename... S2> static constexpr auto support(settings<S1...> s1, settings<S2...> s2) -> bool { return (... && std::is_base_of_v<S1, decltype(s2)>); } + /** + * @brief + * + * @tparam T + * + * @param t + * + * @return + */ template<typename... S> static constexpr auto is_settings(settings<S...> s) -> bool { return true; } + /** + * @brief + * + * @tparam T + * + * @param t + * + * @return + */ template<typename T> static constexpr auto is_settings(T t) -> bool { return false; } + /** + * @brief + * + * @tparam S + * + * @param s + * + * @return + */ template<typename... S> static constexpr auto _s(S... s) { return settings<S...>{}; } + /** + * @brief + * + * @tparam S + * + * @param s + * + * @return + */ template<typename... S> static constexpr auto _s(settings<S...> s) { @@ -197,28 +422,59 @@ namespace scalfmm::options } } // namespace scalfmm::options +/** + * @brief Macro to define an "optioned callee" struct with a functional interface. + * + * This macro generates a struct and a corresponding global instance that allows + * flexible invocation of functions in the `impl` namespace. The generated struct + * provides three interfaces: + * + * 1. **Static Call (`call`)**: + * A static method that forwards arguments to the corresponding `impl::<NAME>` function. + * + * 2. **Subscript Operator (`operator[]`)**: + * Allows binding of settings (`options::settings`) to create a callable lambda. + * + * 3. **Function Call Operator (`operator()`)**: + * Enables direct invocation of the `impl::<NAME>` function without explicitly binding settings. + * + * @param NAME The name of the callee, corresponding to a function in the `impl` namespace. + * + * ### Generated Components + * + * The macro expands into: + * - A struct named `<NAME>_` that encapsulates the interfaces for invocation. + * - A global constant instance named `<NAME>` for easy access. + * + * ### Example + * @code{.cpp} + * // Declare an optioned callee for the `process` function + * DECLARE_OPTIONED_CALLEE(fmm) + * + * // Usage + * fmm(tree, fmm_operator); // Direct invocation + * fmm[options::_s(options::seq)](tree, fmm_operator); // Invocation with settings + * @endcode + */ +#define DECLARE_OPTIONED_CALLEE(NAME) \ + struct NAME_ \ + { \ + template<typename Arg, typename... Args> \ + inline static constexpr auto call(Arg&& arg, Args&&... args) noexcept \ + { \ + return impl::NAME(std::forward<Arg>(arg), std::forward<Args>(args)...); \ + } \ + template<typename... S> \ + inline auto constexpr operator[](scalfmm::options::settings<S...> s) const noexcept \ + { \ + return [s](auto&&... args) { return NAME_::call(s, std::forward<decltype(args)>(args)...); }; \ + } \ + template<typename... Args> \ + inline auto constexpr operator()(Args&&... args) const noexcept \ + { \ + return NAME_::call(std::forward<Args>(args)...); \ + } \ + }; \ + inline const NAME_ NAME = {}; -#define DECLARE_OPTIONED_CALLEE(NAME) \ -struct NAME_ \ -{ \ - template<typename Arg, typename... Args> \ - inline static constexpr auto call(Arg&& arg, Args&&... args) noexcept \ - { \ - return impl::NAME(std::forward<Arg>(arg), std::forward<Args>(args)...); \ - } \ - template<typename... S> \ - inline auto constexpr operator[](scalfmm::options::settings<S...> s) const noexcept \ - { \ - return [s](auto&&... args) \ - { return NAME_::call(s, std::forward<decltype(args)>(args)...); }; \ - } \ - template<typename... Args> \ - inline auto constexpr operator()(Args&&... args) const noexcept \ - { \ - return NAME_::call(std::forward<Args>(args)...); \ - } \ -}; \ -inline const NAME_ NAME = {}; \ - - -#endif // SCALFMM_OPTIONS_OPTIONS_HPP +#endif // SCALFMM_OPTIONS_OPTIONS_HPP diff --git a/include/scalfmm/parallel/mpi/comm.hpp b/include/scalfmm/parallel/mpi/comm.hpp index 7d10c60275eef79360d1c3b1b3d93f53a51782c9..bf1219e99ec1c61513223ac3eae083f649748d0a 100644 --- a/include/scalfmm/parallel/mpi/comm.hpp +++ b/include/scalfmm/parallel/mpi/comm.hpp @@ -1,7 +1,9 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/parallel/mpi/comm.hpp +// -------------------------------- #pragma once -#include <vector> - #include "scalfmm/container/particle.hpp" #include "scalfmm/meta/utils.hpp" #include "scalfmm/parallel/utils.hpp" @@ -10,47 +12,49 @@ #include <mpi.h> +#include <vector> + namespace scalfmm::parallel::comm { using comm_type = cpp_tools::parallel_manager::mpi::communicator; /** * @brief Determines the Morton index vector to be received from processor p (STEP 1) - * + * * Determines the Morton index vector to be received from processor p? In addition, for each Morton index we - * store the cell, i.e. a pointer to its group and the index within the group (group_ptr, index). This will - * enable us to insert the multipoles received from processor p directly into the cell. - * - * leaf_to_receive_access: a vector of vector of pair (the iterator on the group ,the position of the cell in the group) - * leaf_to_receive_access[p] is the position vector of cells in groups whose Morton index comes from processor p - * leaf_to_receive_access[p][i] a pair (the iterator on the group ,the position of the cell in the group) - * vector of size nb_proc - * - nb_messages_to_receive: the number of morton indices to exchange with processor p - * - nb_messages_to_send: the number of morton indices to send tp processor p - * - morton_to_receive: the morton indices to exchange with processor p - * - * @tparam distribution_type - * @tparam iterator_type - * @tparam vector_vector_struct_type - * @tparam vector_vector_type + * store the cell, i.e. a pointer to its group and the index within the group (group_ptr, index). This will + * enable us to insert the multipoles received from processor p directly into the cell. + * + * leaf_to_receive_access: a vector of vector of pair (the iterator on the group ,the position of the cell in the group) + * leaf_to_receive_access[p] is the position vector of cells in groups whose Morton index comes from processor p + * leaf_to_receive_access[p][i] a pair (the iterator on the group ,the position of the cell in the group) + * vector of size nb_proc + * - nb_messages_to_receive: the number of morton indices to exchange with processor p + * - nb_messages_to_send: the number of morton indices to send tp processor p + * - morton_to_receive: the morton indices to exchange with processor p + * + * @tparam DistributionType + * @tparam IteratorType + * @tparam VectorOfVectorStructType + * @tparam VectorOfVectorType * @param[in] comm the mpi communicator * @param[in] begin_left_ghost The iterator of the first ghost on the left * @param[in] end_left_ghost The iterator of the last ghost on the left * @param[in] begin_right_ghost The iterator of the first ghost on the right * @param[in] end_right_ghost The iterator of the last ghost on the right - * @param[in] distrib the data distribution + * @param[in] distrib the data distribution * @param[out] nb_messages_to_receive the number of morton indices to exchange with processor p * @param[out] nb_messages_to_send the number of morton indices to send tp processor p * @param[out] leaf_to_receive_access For each component a direct access to it (iterator on group, position into the group) * @param[out] morton_to_receive for each process the vector of Morton indexes to receive - */ - template<typename distribution_type, typename iterator_type, typename vector_vector_struct_type, - typename vector_vector_type> - inline void start_step1(comm_type& comm, iterator_type begin_left_ghost, iterator_type end_left_ghost, - iterator_type begin_right_ghost, iterator_type end_right_ghost, - distribution_type const& distrib, std::vector<int>& nb_messages_to_receive, - std::vector<int>& nb_messages_to_send, vector_vector_struct_type& leaf_to_receive_access, - vector_vector_type& morton_to_receive) + */ + template<typename DistributionType, typename IteratorType, typename VectorOfVectorStructType, + typename VectorOfVectorType> + inline auto start_step1(comm_type& comm, IteratorType begin_left_ghost, IteratorType end_left_ghost, + IteratorType begin_right_ghost, IteratorType end_right_ghost, + DistributionType const& distrib, std::vector<int>& nb_messages_to_receive, + std::vector<int>& nb_messages_to_send, VectorOfVectorStructType& leaf_to_receive_access, + VectorOfVectorType& morton_to_receive) -> void { // We iterate on the ghosts @@ -69,7 +73,6 @@ namespace scalfmm::parallel::comm ++nb_messages_to_receive[i]; leaf_to_receive_access[i].push_back(std::make_pair(grp_ptr, idx)); morton_to_receive[i].push_back(morton); - // std:: cout << "step 1 " << idx << " " << *grp_ptr << " " << morton << std::endl; ++idx; } } @@ -84,54 +87,54 @@ namespace scalfmm::parallel::comm { build_receive(begin_right_ghost, end_right_ghost); } - // io::print("step nb_messages_to_receive[" + std::to_string(p) + "] ", nb_messages_to_receive.data[p]); - // Faut-il les trier ??? - int p{0}; - // io::print("step 1 nb_messages_to_receive ", nb_messages_to_receive); - // std::cout << " morton_to_receive.size() " << morton_to_receive.size() <<std::endl; - // for (auto & vec : morton_to_receive){ - // - // auto last = std::unique(vec.begin(), vec.end()); - // vec.erase(last, vec.end()); - // io::print("step 1 morton_to_receive[" + std::to_string(p++) + "] ", vec); - - // } - /* + // io::print("step nb_messages_to_receive[" + std::to_string(p) + "] ", nb_messages_to_receive.data[p]); + // Do we need to sort them ? + int p{0}; + // io::print("step 1 nb_messages_to_receive ", nb_messages_to_receive); + // std::cout << " morton_to_receive.size() " << morton_to_receive.size() <<std::endl; + // for (auto & vec : morton_to_receive){ + // + // auto last = std::unique(vec.begin(), vec.end()); + // vec.erase(last, vec.end()); + // io::print("step 1 morton_to_receive[" + std::to_string(p++) + "] ", vec); + + // } + /* p = 0 ; io::print("step 1 nb_messages_to_send ", nb_messages_to_send); - for (auto & vec : morton_to_send){ - + for (auto & vec : ){ + auto last = std::unique(vec.begin(), vec.end()); vec.erase(last, vec.end()); - io::print("step 1 morton_to_send[" + std::to_string(p++) + "] ", vec); - + io::print("step 1 [" + std::to_string(p++) + "] ", vec); + } */ //////////////////// /// Exchange the morton indexes with processor p auto mpi_int_type = cpp_tools::parallel_manager::mpi::get_datatype<int>(); comm.alltoall(nb_messages_to_receive.data(), 1, mpi_int_type, nb_messages_to_send.data(), 1, mpi_int_type); - }; + } /** * @brief We can now exchange the morton indices (STEP 2) - * + * * Morton's list of indices to send their data (mutipoles/particles) to proc p - * @tparam vector_vector_type + * @tparam VectorOfVectorType * @param[in] nb_proc number of mpi processes * @param[in] rank the mpi rank * @param[in] comm the communicator * @param[in] nb_messages_to_receive for each process the number of message to receive * @param[in] nb_messages_to_send for each process the number of message to send * @param[in] morton_to_receive for each process the vector of Morton indexes to receive - * @param[out] morton_to_send for each process the vector of Morton indexes to send + * @param[out] for each process the vector of Morton indexes to send */ - template<typename vector_vector_type> + template<typename VectorOfVectorType> inline void start_step2(int const& nb_proc, int const& rank, comm_type& comm, std::vector<int>& nb_messages_to_receive, std::vector<int>& nb_messages_to_send, - vector_vector_type& morton_to_receive, vector_vector_type& morton_to_send) + VectorOfVectorType& morton_to_receive, VectorOfVectorType&) { - using mortonIdx_type = typename vector_vector_type::value_type::value_type; + using mortonIdx_type = typename VectorOfVectorType::value_type::value_type; std::vector<cpp_tools::parallel_manager::mpi::request> tab_mpi_status; // auto mpi_morton_type = cpp_tools::parallel_manager::mpi::get_datatype<mortonIdx_type>(); @@ -144,21 +147,20 @@ namespace scalfmm::parallel::comm // send the morton indexes morton_to_receive if(nb_messages_to_send[p] != 0) { - morton_to_send[p].resize(nb_messages_to_send[p]); + [p].resize(nb_messages_to_send[p]); - // std::cout << "step 2 me " << rank << " send to " << p << " nb morton= " << nb_messages_to_receive[p] - // << std::endl; - // io::print("step 2 morton_to_receive[" + std::to_string(p) + "] ", morton_to_receive[p]); + // std::cout << "step 2 me " << rank << " send to " << p << " nb morton= " << nb_messages_to_receive[p] + // << std::endl; + // io::print("step 2 morton_to_receive[" + std::to_string(p) + "] ", morton_to_receive[p]); comm.isend(morton_to_receive[p].data(), nb_messages_to_receive[p], mpi_morton_type, p, 600); } if(nb_messages_to_receive[p] != 0) { - // std::cout << "step 2 me " << rank << " receive to " << p << " size= " << nb_messages_to_send[p] - // << std::endl; + // std::cout << "step 2 me " << rank << " receive to " << p << " size= " << nb_messages_to_send[p] + // << std::endl; - tab_mpi_status.push_back( - comm.irecv(morton_to_send[p].data(), nb_messages_to_send[p], mpi_morton_type, p, 600)); + tab_mpi_status.push_back(comm.irecv([p].data(), nb_messages_to_send[p], mpi_morton_type, p, 600)); } } if(tab_mpi_status.size() > 0) @@ -166,37 +168,36 @@ namespace scalfmm::parallel::comm cpp_tools::parallel_manager::mpi::request::waitall(tab_mpi_status.size(), tab_mpi_status.data()); } // // check - /* + /* for(auto p = 0; p < nb_proc; ++p) { - io::print("step 2 morton_to_send[" + std::to_string(p) + "] ", morton_to_send[p]); + io::print("step 2 [" + std::to_string(p) + "] ", morton_to_send[p]); } */ } + /** * @brief For the vector of Morton indices to be sent to processor p, we construct a direct access to the component - * - * @tparam iterator_type - * @tparam vector_vector_struct_type - * @tparam vector_vector_type + * + * @tparam IteratorType + * @tparam VectorOfVectorStructType + * @tparam VectorOfVectorType * @param nb_proc the number of processors * @param begin_grp the first iterator on the group * @param end_grp the last iterator on the group * @param component_access the access to the component (iterator on group, position into the group) - * @param morton_to_send for each processor the vector of Morton indexes to send + * @param for each processor the vector of Morton indexes to send */ - template<typename iterator_type, typename vector_vector_struct_type, typename vector_vector_type> - auto build_direct_access_to_leaf(const int nb_proc, iterator_type begin_grp, iterator_type end_grp, - vector_vector_struct_type& component_access, - vector_vector_type const& morton_to_send) -> void + template<typename IteratorType, typename VectorOfVectorStructType, typename VectorOfVectorType> + auto build_direct_access_to_leaf(const int nb_proc, IteratorType begin_grp, IteratorType end_grp, + VectorOfVectorStructType& component_access, VectorOfVectorType const&) -> void { - using access_type = typename vector_vector_struct_type::value_type; - using vector_morton_type = typename vector_vector_type::value_type; - auto build_index_grp = - [](auto begin_grp, auto end_grp, vector_morton_type const& morton_to_send_p, access_type& to_send_p) + using access_type = typename VectorOfVectorStructType::value_type; + using vector_morton_type = typename VectorOfVectorType::value_type; + auto build_index_grp = [](auto begin_grp, auto end_grp, vector_morton_type const& _p, access_type& to_send_p) { int idx{0}; - int max_idx = morton_to_send_p.size(); + int max_idx = _p.size(); to_send_p.resize(max_idx); // loop on the groups // auto it = std::begin(buffer); @@ -206,13 +207,13 @@ namespace scalfmm::parallel::comm int start_grp{0}; auto const& csymb = (*grp_ptr)->csymbolics(); // iterate on the cells - while(idx < max_idx and math::between(morton_to_send_p[idx], csymb.starting_index, csymb.ending_index)) + while(idx < max_idx and math::between(_p[idx], csymb.starting_index, csymb.ending_index)) { // find cell inside the group int pos{-1}; for(int i = start_grp; i < (*grp_ptr)->size(); ++i) { auto morton = (*grp_ptr)->component(i).csymbolics().morton_index; - if(morton_to_send_p[idx] == morton) + if(_p[idx] == morton) { pos = i; start_grp = i + 1; @@ -228,17 +229,17 @@ namespace scalfmm::parallel::comm for(auto p = 0; p < nb_proc; ++p) { - // io::print(" morton_to_send[" + std::to_string(p) + "] ", morton_to_send[p]); + // io::print(" [" + std::to_string(p) + "] ", morton_to_send[p]); - if(morton_to_send[p].size() != 0) + if([p].size() != 0) { - build_index_grp(begin_grp, end_grp, morton_to_send[p], component_access[p]); + build_index_grp(begin_grp, end_grp, [p], component_access[p]); auto const& elt = component_access[p]; // for(auto i = 0; i < elt.size(); ++i) // { // std::cout << " " << p << " " // << (*(elt[i].first))->component(elt[i].second).csymbolics().morton_index << " " - // << elt[i].second << " " << morton_to_send[p][i] << " nb part " + // << elt[i].second << " " << [p][i] << " nb part " // << (*(elt[i].first))->component(elt[i].second).size() << std::endl; // } } @@ -247,19 +248,22 @@ namespace scalfmm::parallel::comm /** * @brief Construct the MPI type of the particle according to leaf_to_access - * - * @tparam dimension - * @tparam vector_vector_struct_type + * + * @tparam dimension + * @tparam VectorOfVectorStructType * @param leaf_to_access For each processor the leaf to access (for receiving or sending) + * @param nb_inputs * @param mpi_position_type the MPI type of the coordinate of the points of the particles * @param mpi_input_type the MPI type of the inputs of the particles - * @return std::vector<MPI_Datatype> + * @return std::vector<MPI_Datatype> */ - template<std::size_t dimension, typename vector_vector_struct_type> - auto inline build_mpi_particles_type(vector_vector_struct_type const& leaf_to_access, int const nb_inputs, - MPI_Datatype mpi_position_type, MPI_Datatype mpi_input_type) - -> std::vector<MPI_Datatype> + template<std::size_t Dimension, typename VectorOfVectorStructType> + inline auto build_mpi_particles_type(VectorOfVectorStructType const& leaf_to_access, int const nb_inputs, + MPI_Datatype mpi_position_type, + MPI_Datatype mpi_input_type) -> std::vector<MPI_Datatype> { + static constexpr std::size_t dimension = Dimension; + const int nb_proc{int(leaf_to_access.size())}; std::vector<MPI_Datatype> newtype(nb_proc); @@ -304,16 +308,9 @@ namespace scalfmm::parallel::comm type[i + stride * nb_elt] = type[i + dimension * nb_elt]; MPI_Get_address(&(leaf[0].inputs(k)), &disp[i + stride * nb_elt]); } - // std::cout << p << " " << leaf.csymbolics().morton_index << " nb part " << leaf.size() << " *ptr_x " - // << proxy_position << " snd part " << *(ptr_x + 1) << " inputs0: " << leaf[0].inputs()[0] - // << " inputs1: " << *(&(leaf[0].inputs()[0]) + 1) << " ptr " << *(ptr_inputs_0 + 1) - // << std::endl; } // end loop on leaf_view - // std::cout << " create type " << std::endl; - // io::print(" " + std::to_string(p) + " disp", disp); MPI_Type_create_struct(nb_mpi_types, length.data(), disp.data(), type.data(), &newtype[p]); MPI_Type_commit(&newtype[p]); - // std::cout << " send to " << p << " size " << size_msg << std::endl; } } return newtype; diff --git a/include/scalfmm/parallel/mpi/utils.hpp b/include/scalfmm/parallel/mpi/utils.hpp index a038496659ae4a42506bc952ee2919c1765586c5..caf7833ffe8f46aba293b4265c7794c74f18514a 100644 --- a/include/scalfmm/parallel/mpi/utils.hpp +++ b/include/scalfmm/parallel/mpi/utils.hpp @@ -1,11 +1,10 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/parallel/mpi/utils.hpp +// -------------------------------- #ifndef _PARALLEL_MPI_UTILS_HPP_ #define _PARALLEL_MPI_UTILS_HPP_ -#include <algorithm> -#include <fstream> -#include <iostream> -#include <vector> - #include <cpp_tools/colors/colorized.hpp> #include <cpp_tools/parallel_manager/parallel_manager.hpp> @@ -16,19 +15,25 @@ #include <inria/algorithm/distributed/mpi.hpp> #include <inria/linear_tree/balance_tree.hpp> #endif + +#include <algorithm> +#include <fstream> +#include <iostream> +#include <vector> + namespace scalfmm::parallel::utils { /** * @brief print the distribution of components (cells/leaves) in a stream * - * @tparam Vector + * @tparam VectorType * @param out the stream * @param header the header to write * @param distrib */ - template<typename Vector> - auto inline print_distrib(std::ostream& out, std::string const& header, Vector const& distrib) -> void + template<typename VectorType> + inline auto print_distrib(std::ostream& out, std::string const& header, VectorType const& distrib) -> void { out << header; for(auto p: distrib) @@ -37,20 +42,22 @@ namespace scalfmm::parallel::utils } out << std::endl; } + /** * @brief print the distribution of components (cells/leaves) * - * @tparam Vector + * @tparam VectorType * @param header the header to write * @param rank the process id * @param distrib the vector of distribution */ - template<typename Vector> - auto inline print_distrib(std::string const& header, int rank, Vector const& distrib) -> void + template<typename VectorType> + inline auto print_distrib(std::string const& header, int rank, VectorType const& distrib) -> void { std::string new_header("rank(" + std::to_string(rank) + ") " + header); print_distrib(std::cout, new_header, distrib); } + /** * @brief construct the morton indexes at the parent level * @@ -62,30 +69,31 @@ namespace scalfmm::parallel::utils * @tparam VectorMortonIdx the type of the vector of Morton index * @param[inout] leafMortonIdx the vector of Morton index */ - template<int dimension, typename VectorMortonIdx> - auto inline move_index_to_upper_level(VectorMortonIdx& leafMortonIdx) -> void + template<int Dimension, typename VectorMortonIdx> + inline auto move_index_to_upper_level(VectorMortonIdx& leafMortonIdx) -> void { // Move leafMortonIdx to level level_shared for(auto& p: leafMortonIdx) { - p = p >> dimension; + p = p >> Dimension; } auto last = std::unique(leafMortonIdx.begin(), leafMortonIdx.end()); leafMortonIdx.erase(last, leafMortonIdx.end()); } - /// - /// \brief send_get_min_morton_idx send Morton index to the left and get value from the right - /// - /// \param[in] conf the mpi conf - /// \param[in] morton_idx the Morton index to send o send to processor p-1 - /// \return the Morton index coming from the right - /// - template<typename index_type> - [[nodiscard]] index_type send_get_min_morton_idx(cpp_tools::parallel_manager::parallel_manager& para, - index_type& morton_idx) + /** + * @brief send_get_min_morton_idx send Morton index to the left and get value from the right + * + * @tparam IndexType + * @param para + * @param[in] morton_idx the Morton index to send o send to processor p-1 + * @return the Morton index coming from the right + */ + template<typename IndexType> + [[nodiscard]] IndexType send_get_min_morton_idx(cpp_tools::parallel_manager::parallel_manager& para, + IndexType& morton_idx) { // Setting parameter - index_type buff_recev{0}; + IndexType buff_recev{0}; #ifdef SCALFMM_USE_MPI auto comm = para.get_communicator(); int nb_proc = comm.size(); @@ -94,7 +102,7 @@ namespace scalfmm::parallel::utils if(nb_proc != 1) { cpp_tools::parallel_manager::mpi::request tab_mpi_status; - auto mpi_type = cpp_tools::parallel_manager::mpi::get_datatype<index_type>(); + auto mpi_type = cpp_tools::parallel_manager::mpi::get_datatype<IndexType>(); const int sender = (my_rank + 1 == nb_proc) ? MPI_PROC_NULL : my_rank + 1; const int receiver = (my_rank == 0) ? MPI_PROC_NULL : my_rank - 1; @@ -114,24 +122,24 @@ namespace scalfmm::parallel::utils } #ifdef SCALFMM_USE_MPI - /// - /// \brief exchange_data_left_right to exchange data left and right between processor left and right - /// - /// The processor p send data_left to processor p-1 and receive from it data_right and - /// p send data_right to processor p+1 and receive from it data_left - /// \param[in] conf - /// \param[in] data_left data to send to processor left - /// \param[in] data_right data to send to processor right - /// - /// \return a tuple containing the value_right of processor on the left and the - /// value left coming from processor right - /// - template<typename data_type> - auto exchange_data_left_right(cpp_tools::parallel_manager::mpi_config& conf, data_type& data_left, - data_type& data_right) + /** + * @brief exchange_data_left_right to exchange data left and right between processor left and right + * + * The processor p send data_left to processor p-1 and receive from it data_right and + * p send data_right to processor p+1 and receive from it data_left + * + * @tparam DataType + * @param[in] conf + * @param[in] data_left data to send to processor left + * @param[in] data_right data to send to processor right + * @return a tuple containing the value_right of processor on the left and the value left coming from processor right + */ + template<typename DataType> + auto exchange_data_left_right(cpp_tools::parallel_manager::mpi_config& conf, DataType& data_left, + DataType& data_right) { // Setting parameter - data_type buff_p{0}, buff_n{0}; + DataType buff_p{0}, buff_n{0}; auto comm = conf.comm; int nb_proc = comm.size(); int my_rank = comm.rank(); @@ -140,7 +148,7 @@ namespace scalfmm::parallel::utils { // First exchange to the left cpp_tools::parallel_manager::mpi::request tab_mpi_status[2]; - // auto mpi_type = cpp_tools::parallel_manager::mpi::get_datatype<index_type>(); + // auto mpi_type = cpp_tools::parallel_manager::mpi::get_datatype<IndexType>(); // if i'm not the last proc const int right = (my_rank + 1 == nb_proc) ? MPI_PROC_NULL : my_rank + 1; const int left = (my_rank == 0) ? MPI_PROC_NULL : my_rank - 1; @@ -162,36 +170,28 @@ namespace scalfmm::parallel::utils } #endif - /// - /// \brief Distribute uniformly on the processes the leaves. - /// - /// Split in interval (semi-open) the leaves - /// The algorithm is - /// 1) we distribute the particles according to their Morton index. The - /// the leaves are split on the processor by their Morton index - /// - /// 2) balanced the Morton index by some criteria to define - /// - /// parameter[inout] mortonArray Morton index located on the processor - /// parameter[out] the distribution of leaves - - template<typename MortonArray_type> - auto balanced_leaves(cpp_tools::parallel_manager::parallel_manager& manager, MortonArray_type& mortonArray) + /** + * @brief Distribute uniformly on the processes the leaves. + * + * Split in interval (semi-open) the leaves + * The algorithm is + * 1) we distribute the particles according to their Morton index. The + * the leaves are split on the processor by their Morton index + * + * 2) balanced the Morton index by some criteria to define + * + * @tparam MortonArrayType + * @param manager + * @param mortonArray + * @return auto + */ + template<typename MortonArrayType> + auto balanced_leaves(cpp_tools::parallel_manager::parallel_manager& manager, MortonArrayType& mortonArray) { - // std::cout << cpp_tools::colors::green << " --> Begin distrib::balanced_leaves " << cpp_tools::colors::reset - // << std::endl; - // - using morton_type = typename MortonArray_type::value_type; - + using morton_type = typename MortonArrayType::value_type; + auto rank = manager.get_communicator().rank(); - - auto rank = manager.get_communicator().rank(); - // io::print("rank(" + std::to_string(rank) + ") leafMortonIdx: ", mortonArray); - // auto last = std::unique(mortonArray.begin(), mortonArray.end()); - // mortonArray.erase(last, mortonArray.end()); - // io::print("rank(" + std::to_string(rank) + ") leafMortonIdx U: ", mortonArray); - // // get max and min of the Morton index owned by current process // [min, max] On two consecutive processes we may have max[p] = min[p+1] // we remove such case @@ -215,7 +215,7 @@ namespace scalfmm::parallel::utils /// Construct a uniform distribution of the Morton index /// - MortonArray_type morton_distrib; + MortonArrayType morton_distrib; try { inria::mpi_config conf_tmp(manager.get_communicator().raw_comm); @@ -226,55 +226,40 @@ namespace scalfmm::parallel::utils { std::cerr << e.what() << '\n'; } - // print("rank(" + std::to_string(rank) + "morton_distrib ", morton_distrib); - // manager.comm.barrier(); - - // std::cout << "rank(" + std::to_string(rank) + ") Morton distrib [" << morton_distrib[0] << ", - // " - // << morton_distrib[morton_distrib.size() - 1] << "]\n"; - - //print("rank(" + std::to_string(rank) + ") Distrib cells Index: ", morton_distrib); cell_distrib.resize(manager.get_num_processes(), {0, 0}); - std::array<morton_type, 2> local{morton_distrib[0], morton_distrib[morton_distrib.size() - 1] }; + std::array<morton_type, 2> local{morton_distrib[0], morton_distrib[morton_distrib.size() - 1]}; cell_distrib[0] = local; - // print("rank(" + std::to_string(rank) + ") local: ", local); /// share the distribution on all processors manager.get_communicator().allgather(local.data(), sizeof(local), MPI_CHAR, cell_distrib.data(), sizeof(local), MPI_CHAR /*, 0*/); #endif - // std::cout << cpp_tools::colors::red; - // io::print("rank(" + std::to_string(rank) + ") cell_distrib: ", cell_distrib); - - // std::cout << cpp_tools::colors::green << " --> End distrib::balanced_leaves " << cpp_tools::colors::reset - // << std::endl; return cell_distrib; } - /// - /// \brief balanced_particles compute a balanced particle distribution - /// - /// 1) we distribute the particles according to their Morton index. The - /// the leaves - /// are split on the processor by their Morton index - /// 2) balanced the Morton index by some criteria to define - /// - /// input[in] tha parallel manager - /// input[in] partArray the vector of particles own by the processor - /// input[inout] mortonArray the morton index located on the processor - /// input[in] number_of_particles the total number of particles on all processes - /// - /// \return the distribution in terms of Morton index (std::vector<std::array<Morton_type,2>>) - template<typename ParticleArray_type, typename MortonArray_type> - auto balanced_particles(cpp_tools::parallel_manager::parallel_manager& manager, ParticleArray_type& partArray, - const MortonArray_type& morton_array, const std::size_t& number_of_particles) + + /** + * @brief balanced_particles compute a balanced particle distribution + * + * 1) we distribute the particles according to their Morton index. The + * the leaves + * are split on the processor by their Morton index + * 2) balanced the Morton index by some criteria to define + * + * @tparam ParticleArrayType + * @tparam MortonArrayType + * @param manager the parallel manager. + * @param partArray the vector of particles own by the processor. + * @param morton_array the morton index located on the processor. + * @param number_of_particles the total number of particles on all processes. + * @return the distribution in terms of Morton index. + */ + template<typename ParticleArrayType, typename MortonArrayType> + auto balanced_particles(cpp_tools::parallel_manager::parallel_manager& manager, ParticleArrayType& partArray, + const MortonArrayType& morton_array, const std::size_t& number_of_particles) { - // std::cout << cpp_tools::colors::green << " --> Begin distrib::balanced_particles " << - // cpp_tools::colors::reset - // << std::endl; - // - using Morton_type = typename MortonArray_type::value_type; - using MortonDistrib_type = std::array<int, 2>; + using Morton_type = typename MortonArrayType::value_type; + using MortonDistribType = std::array<int, 2>; auto rank = manager.get_process_id(); auto nb_proc = manager.get_num_processes(); @@ -287,32 +272,23 @@ namespace scalfmm::parallel::utils LeafMortonIndex.erase(last, LeafMortonIndex.end()); /// LeafMortonIndex has the size of the number of leaves /// weight = ({Morton index, number of particles}) for each leaf - std::vector<MortonDistrib_type> weight(LeafMortonIndex.size(), {bad_index, 0}); + std::vector<MortonDistribType> weight(LeafMortonIndex.size(), {bad_index, 0}); std::size_t pos = 0; weight[pos][0] = LeafMortonIndex[pos]; - // std::cout << cpp_tools::colors::red << "leaf size: " << LeafMortonIndex.size() << std::endl; { // loop on the number of particles for(std::size_t part = 0; part < morton_array.size(); ++part) { - // std::cout << "part " << part << " " << - // tmp[part] << " pos " << pos << " " << - // leafMortonIdx[pos] - // << " " << weight[pos] << - // std::endl; while(morton_array[part] != LeafMortonIndex[pos]) { - // std::cout << " new pos " << pos << - // std::endl; pos++; } weight[pos][1] += 1; weight[pos][0] = LeafMortonIndex[pos]; } - /// io::print("rank(" + std::to_string(rank) + ") weight: ", weight); } - // get max and min of the Morton index owned by current process - // [min, max] On two consecutive processes we may have max[p] = min[p+1] + // get max and min of the Morton index owned by current process + // [min, max] On two consecutive processes we may have max[p] = min[p+1] // // we remove such case /// Find the minimal and maximal index for the current process /// this index should be unique (check with the neighbors left and right) @@ -323,13 +299,12 @@ namespace scalfmm::parallel::utils morton_distrib[0] = {minIndex[0], maxIndex[0]}; // return morton_distrib; } - // io::print("rank(" + std::to_string(rank) + ") weight initial: ", weight); #ifdef SCALFMM_USE_MPI cpp_tools::parallel_manager::mpi_config conf(manager.get_communicator()); - MortonDistrib_type weight_prev, weight_next; + MortonDistribType weight_prev, weight_next; std::tie(weight_prev, weight_next) = exchange_data_left_right(conf, minIndex, maxIndex); if(maxIndex[0] == weight_next[0]) @@ -341,7 +316,6 @@ namespace scalfmm::parallel::utils weight[0][1] += weight_prev[1]; } - // io::print("rank(" + std::to_string(rank) + ") weight final: ", weight); /// /// compute the number of particles in the leaves int nb_part = 0; @@ -358,20 +332,15 @@ namespace scalfmm::parallel::utils { block = number_of_particles - rank * block; } - // std::cout << "rank(" << rank << ") N particles: " << nb_part << " block " << block << std::endl; std::array<Morton_type, 3> local{weight[0][0], weight[weight.size() - 1][0], nb_part}; std::vector<std::array<Morton_type, 3>> part_distrib(nb_proc); part_distrib[0] = local; - // io::print("rank(" + std::to_string(rank) + ") 0 Distrib cells Index: ", part_distrib); - // std::cout << "rank(" << rank << ") local: " <<local[0]<<" " <<local[1]<<" " <<local[2] <<std::endl; - /// share the distribution on all processors auto nb_elt = sizeof(local); conf.comm.allgather(local.data(), nb_elt, MPI_CHAR, part_distrib[0].data(), nb_elt, MPI_CHAR /*, 0*/); - // io::print("rank(" + std::to_string(rank) + ") Distrib cells Index: ", part_distrib); /// /// Try to have the same number of particles on a processor /// @@ -387,9 +356,7 @@ namespace scalfmm::parallel::utils numberLeaves[i] = part_distrib[i][1] - part_distrib[i][0] + 1; maxLeaves = std::max(numberLeaves[i], maxLeaves); } - // io::print("rank(" + std::to_string(rank) + ") numberLeaves: ", numberLeaves); - // std::cout << "rank(" + std::to_string(rank) + ") initial tomove: " << maxLeaves << std::endl; /// Prevent to have 0 cell on a processor. if(maxLeaves > 1) { @@ -398,8 +365,6 @@ namespace scalfmm::parallel::utils tomove[i] = part_distrib[i][2] - block; } } - // io::print("rank(" + std::to_string(rank) + ") initial tomove: ", tomove); - // if(rank == 0) for(int i = 0; i < nb_proc - 1; ++i) { @@ -415,27 +380,9 @@ namespace scalfmm::parallel::utils tomove[i] = 0; tomove[i + 1] += tomove[i]; } - // print(" end (" + std::to_string(i) + ") - // tomove: ", tomove); print(" end (" + - // std::to_string(i) + ") tosendR: ", - // tosendR); print(" end (" + - // std::to_string(i) + ") tosendL: ", - // tosendL); } tosendR[nb_proc - 1] = 0; - // io::print("rank(" + std::to_string(rank) + ") tomove: ", tomove); - // io::print("rank(" + std::to_string(rank) + ") tosendR: ", tosendR); - // io::print("rank(" + std::to_string(rank) + ") tosendRL: ", tosendL); - /// - // std::cout << "tosendL(" + std::to_string(rank) + "): " << tosendL[rank] << std::endl; - // std::cout << "tosendR(" + std::to_string(rank) + "): " << tosendR[rank] << std::endl; - // if(rank > 0) - // std::cout << "toReceivL(" + std::to_string(rank) + "): " << tosendR[rank - 1] << - // std::endl; - // if(rank < nb_proc - 1) - // std::cout << "toReceivR(" + std::to_string(rank) + "): " << tosendL[rank + 1] << - // std::endl; int toReceivL, toReceivR; toReceivL = tosendR[rank - 1] > 0 ? 1 : 0; @@ -445,73 +392,48 @@ namespace scalfmm::parallel::utils /// int nb_leaf_to_left{0}, nb_leaf_to_right{0}, nb_part_to_left{0}, nb_part_to_right{0}; Morton_type morton_to_left{0}, morton_to_right{0}; - MortonDistrib_type MortonPart_to_left{{0, 0}}, MortonPart_to_right{{0, 0}}; - // std::cout << rank << " Morton [ " << MortonPart_to_left<< ", " << MortonPart_to_right << "]" << std::endl; + MortonDistribType MortonPart_to_left{{0, 0}}, MortonPart_to_right{{0, 0}}; if(tosendL[rank] > 0) { int leaf_idx = 0; nb_part_to_left = weight[leaf_idx][1]; - // std::cout << " tosendL leaf_idx " << leaf_idx << " " << nb_part_to_left << std::endl; while(nb_part_to_left <= tosendL[rank]) { leaf_idx++; nb_part_to_left += weight[leaf_idx][1]; - // std::cout << " tosendL new pos " << leaf_idx << " " << nb_part_to_left << std::endl; } nb_leaf_to_left = leaf_idx + 1; morton_to_left = weight[leaf_idx][0]; MortonPart_to_left = {weight[leaf_idx][0], nb_leaf_to_left}; // New starting Morton index for the local distribution local[0] = weight[leaf_idx + 1][0]; - - // std::cout << rank << "send morton_to_left" << morton_to_left << std::endl; } if(tosendR[rank] > 0) { int leaf_idx = weight.size() - 1; nb_part_to_right = weight[leaf_idx][1]; - // std::cout << "tosendR leaf_idx " << leaf_idx << " " << nb_part_to_right << std::endl; while(nb_part_to_right <= tosendL[rank]) { leaf_idx--; nb_part_to_right += weight[leaf_idx][1]; - // std::cout << " - tosendR new pos " << leaf_idx << " " << nb_part_to_right << - // std::endl; } nb_leaf_to_right = leaf_idx + 1; morton_to_right = weight[leaf_idx][0]; MortonPart_to_right = {weight[leaf_idx][0], nb_leaf_to_left}; // New starting Morton index for the local distribution local[1] = weight[leaf_idx][0]; - - // std::cout << rank << " send " << nb_leaf_to_right << " leaf to right - nb - // part " - // << nb_part_to_right << " " << MortonPart_to_right[0] << std::endl; - // std::cout << rank << "send morton_to_right " << morton_to_right << std::endl; } local[3] = 0; - // std::cout << rank << " local partition [ " << local[0] << ", " << local[1] << "]" << std::endl; /// Send the number /// send to left and right - // int nb_elt_from_left{0}, nb_elt_from_right{0}; - // Morton_type min_idx{part_distrib[rank][0]}, max_idx{part_distrib[rank][1]}; Morton_type morton_from_left{local[0]}, morton_from_right{local[1]}; /// receive from left right - // auto exchange_val = [&manager, &rank, - // &nb_proc, &tosendL, &tosendR, &toReceivL, - // &toReceivR](const auto& - // nb_part_to_left, const - // auto& nb_part_to_right, - // auto& - // nb_elt_from_left, - // auto& - // nb_elt_from_right) { // compute the buffer size @@ -546,50 +468,38 @@ namespace scalfmm::parallel::utils { cpp_tools::parallel_manager::mpi::request::waitall(toReceivL + toReceivR, tab_mpi_status); } - - // std::cout << rank << " Morton Left: " << morton_from_left << " Morton right: " << - // morton_from_right - // << std::endl; } - - // exchange_val(nb_part_to_left, nb_part_to_right, - // nb_elt_from_left, nb_elt_from_right); - - // std::array<Morton_type, 3> local{weight[0][0], weight[weight.size() - 1][0], nb_part}; - // io::print("rank(" + std::to_string(rank) + ") 00 Distrib cells Index: ", part_distrib); - - std::array<Morton_type, 2> local1 = {std::max(morton_from_left,part_distrib[rank][0]), std::min(morton_from_right,part_distrib[rank][1])}; - - // std::cout << rank << " final local 1 [ " << local1[0] << ", " << local1[1] << "]" << std::endl; + std::array<Morton_type, 2> local1 = {std::max(morton_from_left, part_distrib[rank][0]), + std::min(morton_from_right, part_distrib[rank][1])}; morton_distrib[0] = local1; - // print("rank(" + std::to_string(rank) + ") Distrib cells Index: ", part_distrib); - // std::cout << "rank(" << rank << ") Distrib Leaf Index: " << - // nb_part << std::endl; - /// share the distribution on all processors nb_elt = sizeof(local1); conf.comm.allgather(local1.data(), nb_elt, MPI_CHAR, morton_distrib[0].data(), nb_elt, MPI_CHAR /*, 0*/); - // io::print("rank(" + std::to_string(rank) + ") Morton distrib final: ", morton_distrib); #endif - // std::cout << cpp_tools::colors::green << " --> End distrib::balanced_particles " << cpp_tools::colors::reset - // << std::endl; return morton_distrib; } - template<typename ParticlesArray_type, typename MortonArray_type, typename MortonDistrib_type> - auto compute_communications(int my_rank, ParticlesArray_type& particles, const MortonArray_type& morton_array, - const MortonDistrib_type& morton_dist) + /** + * @brief + * + * @tparam ParticleArrayType + * @tparam MortonArrayType + * @tparam MortonDistribType + * @param my_rank + * @param particles + * @param morton_array + * @param morton_dist + * @return auto + */ + template<typename ParticleArrayType, typename MortonArrayType, typename MortonDistribType> + auto compute_communications(int my_rank, ParticleArrayType& particles, const MortonArrayType& morton_array, + const MortonDistribType& morton_dist) { - // std::cout << cpp_tools::colors::green << " --> Begin distrib::compute_communications " - // << cpp_tools::colors::reset << std::endl; + using Morton_type = typename MortonArrayType::value_type; - using Morton_type = typename MortonArray_type::value_type; - // auto between = [](const Morton_type& m, const Morton_type& mmin, const Morton_type& mmax) { - // return (mmin <= m) && (m <= mmax); - // }; std::vector<int> message(morton_dist.size()); std::vector<std::array<int, 2>> details_partL(morton_dist.size(), {0, 0}), details_partR(morton_dist.size(), {0, 0}); @@ -598,11 +508,7 @@ namespace scalfmm::parallel::utils /// Compute on the left int pos = 0; bool new_start = true; - // std::string beg("rank(" + std::to_string(my_rank) + ")"); - // if(my_rank == 2) { - // io::print(beg + " morton_dist: ", morton_dist); - for(std::size_t i = 0; i < particles.size(); ++i) { if(morton_array[i] >= mortonMin) @@ -625,30 +531,14 @@ namespace scalfmm::parallel::utils new_start = false; } details_partL[pos][1] += 1; - // std::cout << beg << i << " L m_i " << morton_array[i] << " min " << - // mortonMin << " rank " - // << morton_dist[pos] << " " << std::boolalpha - // << between(morton_array[i], morton_dist[pos][0], - // morton_dist[pos][1]) << " pos " << pos - // << std::endl; } } /// Compute on the right - // print("rank(" + std::to_string(my_rank) + ") message: ", message); - // print("rank(" + std::to_string(my_rank) + ") details_part: ", details_partL); { - // print(beg + " morton_dist (Right): ", morton_dist); pos = morton_dist.size() - 1; - // my_rank + 1; for(std::size_t i = particles.size() - 1; i > 0; --i) { - // std::cout << beg << i << " R m_i " << morton_array[i] << " max " << - // mortonMax << " rank " - // << morton_dist[pos] << " " << std::boolalpha - // << between(morton_array[i], morton_dist[pos][0], - // morton_dist[pos][1]) << " pos " << pos - // << std::endl; if(morton_array[i] <= mortonMax) { break; @@ -669,23 +559,18 @@ namespace scalfmm::parallel::utils new_start = false; } details_partR[pos][1] += 1; - // std::cout << beg << i << " R m_i " << morton_array[i] << " max " << - // mortonMax << " rank " - // << morton_dist[pos] << " " << std::boolalpha - // << between(morton_array[i], morton_dist[pos][0], - // morton_dist[pos][1]) << " pos " << pos - // << std::endl; } - // print("rank(" + std::to_string(my_rank) + ") message: ", message); - // print("rank(" + std::to_string(my_rank) + ") details_part R: ", details_partR); } - // std::cout << cpp_tools::colors::green << " --> End distrib::compute_communications " - // << cpp_tools::colors::reset << std::endl; return std::make_tuple(message, details_partL, details_partR); } + /** * @brief * + * @tparam ParticleArrayType + * @tparam MortonArrayType + * @tparam MortonDistribType + * @tparam BoxType * @param manager * @param particles * @param morton_array @@ -694,30 +579,21 @@ namespace scalfmm::parallel::utils * @param leaf_level * @param total_num_particles */ - template<typename ParticlesArray_type, typename MortonArray_type, typename MortonDistrib_type, typename Box_type> - void fit_particles_in_distrib(cpp_tools::parallel_manager::parallel_manager& manager, - ParticlesArray_type& particles, const MortonArray_type& morton_array, - const MortonDistrib_type& morton_dist, const Box_type& box, const int& leaf_level, - const int& total_num_particles) + template<typename ParticleArrayType, typename MortonArrayType, typename MortonDistribType, typename BoxType> + auto fit_particles_in_distrib(cpp_tools::parallel_manager::parallel_manager& manager, ParticleArrayType& particles, + const MortonArrayType& morton_array, const MortonDistribType& morton_dist, + const BoxType& box, const int& leaf_level, const int& total_num_particles) -> void { - // std::cout << cpp_tools::colors::green << " --> Begin distrib::fit_particles_in_distrib " - // << cpp_tools::colors::reset << std::endl; int my_rank = manager.get_process_id(); int nb_proc = manager.get_num_processes(); - // std::cout << " (" << my_rank << ") size " << particles.size() << " " - // << morton_array.size() << std::endl; #ifdef SCALFMM_USE_MPI auto comm = manager.get_communicator(); - // std::cout << "\n------------- fit_particles_in_distrib -------------" << std::endl; - // io::print("rank(" + std::to_string(my_rank) + ") morton_array: ", morton_array); // get the min and the max morton index of the particles own by the // process // send the number of communication we will receive - // auto mortonMin = morton_dist[my_rank][0]; - // auto mortonMax = morton_dist[my_rank][1]; auto to_comm = std::move(compute_communications(my_rank, particles, morton_array, morton_dist)); - // std::cout << " (" << my_rank << ") " << std::get<0>(to_comm) << std::endl; + // Send these numbers auto nb_message = std::get<0>(to_comm); auto nb_length_left = std::get<1>(to_comm); @@ -726,63 +602,39 @@ namespace scalfmm::parallel::utils comm.allreduce(nb_message.data(), message_to_receiv.data(), nb_proc, MPI_INT, MPI_SUM); - // print("rank(" + std::to_string(my_rank) + ") final message: ", message_to_receiv); - - // - // int nb_message_to_receiv = - // message_to_receiv[my_rank]; int buffer_size_left{0}, buffer_size_right{0}; int nb_left = my_rank > 0 ? nb_length_left[my_rank - 1][1] : 0; int nb_right = my_rank + 1 != nb_proc ? nb_length_right[my_rank + 1][1] : 0; cpp_tools::parallel_manager::mpi_config conf(comm); std::tie(buffer_size_left, buffer_size_right) = exchange_data_left_right(conf, nb_left, nb_right); - // std::cout << "rank(" + std::to_string(my_rank) + ") nb_left: " << nb_left << std::endl; - // std::cout << "rank(" + std::to_string(my_rank) + ") nb_right: " << nb_right << std::endl; - // std::cout << "rank(" + std::to_string(my_rank) + ") buffer_size_left: " << - // buffer_size_left - // << std::endl; std::cout << "rank(" + std::to_string(my_rank) + ") buffer_size_right: " << - // buffer_size_right << std::endl; - /// + /// Send the particles /// if nb_left >0 we send a communication on the left /// if nb_right >0 we send a communication on the right /// if buffer_size_left >0 we receive a communication on the left /// if buffer_size_right >0 we receive a communication on the right - using particle_type = typename ParticlesArray_type::value_type; + using particle_type = typename ParticleArrayType::value_type; particle_type *buffer_left{nullptr}, *buffer_right{nullptr}; const int to_right = (my_rank + 1 == nb_proc) ? MPI_PROC_NULL : my_rank + 1; const int to_left = (my_rank == 0) ? MPI_PROC_NULL : my_rank - 1; if(nb_left > 0) { - // std::cout << my_rank << " send first part to " << to_left << " nb val= " << - // nb_left << " first p " - // << particles[0] << std::endl; - conf.comm.isend(particles.data(), nb_left * sizeof(particle_type), MPI_CHAR, to_left, 100); } if(nb_right > 0) { int start = particles.size() - nb_right; - // std::cout << my_rank << " send last part to " << to_right << " nb val= " << - // nb_right - // << " first p " - // << particles[start] << std::endl; conf.comm.isend(&(particles[start]), nb_right * sizeof(particle_type), MPI_CHAR, to_right, 100); } /// int nb_commL{(buffer_size_left > 0) ? 1 : 0}, nb_commR{(buffer_size_right > 0) ? 1 : 0}; std::vector<cpp_tools::parallel_manager::mpi::request> tab_mpi_status; - // buffer_right = new particle_type[buffer_size_right]; if(nb_commL > 0) { buffer_left = new particle_type[buffer_size_left]; - // std::cout << my_rank << " post a receiv on left " << to_left << " b " << - // buffer_left - // << " size " - // << buffer_size_left << std::endl; tab_mpi_status.push_back( conf.comm.irecv(buffer_left, buffer_size_left * sizeof(particle_type), MPI_CHAR, to_left, 100)); @@ -791,10 +643,6 @@ namespace scalfmm::parallel::utils { buffer_right = new particle_type[buffer_size_right]; - // std::cout << my_rank << " post a receiv on right " << to_right << " b " << - // buffer_right << " size " - // << buffer_size_right << " " << std::endl; - tab_mpi_status.push_back( conf.comm.irecv(buffer_right, buffer_size_right * sizeof(particle_type), MPI_CHAR, to_right, 100)); } @@ -802,36 +650,25 @@ namespace scalfmm::parallel::utils // Prepare the copy during the communications // int new_part_size = particles.size() - nb_left - nb_right + buffer_size_left + buffer_size_right; - // std::cout << my_rank << " old size " << particles.size() << " new size " << new_part_size << - // std::endl; - ParticlesArray_type newArray(new_part_size); + ParticleArrayType newArray(new_part_size); /// Here we copy in the right place the particles that do not move auto start = particles.begin() + nb_left /*std::advance(particles.begin(), nb_left)*/; auto end = particles.end() - nb_right /* std::advance(std::begin(particles), particles.size() - nb_right)*/; auto start_out = newArray.begin() + buffer_size_left /*std::advance(std::begin(newArray), buffer_size_left)*/; std::copy(start, end, start_out); - // conf.comm.barrier(); - // std::cout << my_rank << " status size " << tab_mpi_status.size() << std::endl; if(tab_mpi_status.size() > 0) { - // std::cout << my_rank << " I'm waiting " << tab_mpi_status.size() << " " << - // buffer_left << " " - // << buffer_right << std::endl; for(int j = 0; j < tab_mpi_status.size(); ++j) { cpp_tools::parallel_manager::mpi::status status; tab_mpi_status[j].get_status(status); - // std::cout << my_rank << " request " << j << " count " << - // status.get_count(MPI_CHAR) << " source " - // << status.source() << " tag " << status.tag() << std::endl; } cpp_tools::parallel_manager::mpi::request::waitall(tab_mpi_status.size(), tab_mpi_status.data()); } conf.comm.barrier(); - // std::cout << my_rank << " ---------- End Redistribution ----------" << std::endl; if(buffer_left) { /// Here we copy in the right place the particles that do not move @@ -854,7 +691,7 @@ namespace scalfmm::parallel::utils int_length local_num_particles{static_cast<int_length>(particles.size())}; auto mpi_type = cpp_tools::parallel_manager::mpi::get_datatype<int_length>(); conf.comm.allreduce(&local_num_particles, &new_num_particles, 1, mpi_type, MPI_SUM); - // + // if(new_num_particles - total_num_particles != 0) { std::cerr << " Total number of particles: (new) " << new_num_particles << " (old) " << total_num_particles @@ -864,22 +701,23 @@ namespace scalfmm::parallel::utils std::exit(EXIT_FAILURE); } #else - new_num_particles = particles.size() ; + new_num_particles = particles.size(); #endif - - // std::cout << cpp_tools::colors::green << " --> End distrib::fit_particles_in_distrib " - // << cpp_tools::colors::reset << std::endl; } - /// - /// \brief Build the cell distribution at one level upper - /// \param[in] para the parallel manager - /// \param[in] dimension of the problem - /// \param[inout] mortonCellIndex the index cell at level+1 (in) and we - /// construct the parent cells (out) - /// \param[in] level current level to construct the cell distribution - /// \param[in] cellDistrib at level + 1 - /// \return the cell distribution at level - /// + + /** + * @brief Build the cell distribution at one level upper + * + * @tparam VectorMortonIdx + * @tparam MortonDistribution + * @param[in] para the parallel manager + * @param[in] dimension dimension of the problem. + * @param[in] level current level to construct the cell distribution + * @param[inout] mortonCellIndex the index cell at level+1 (in) and we construct the parent cells (out). + * @param ghost_l2l + * @param[in] cells_distrib at level + 1 + * @return the cell distribution at level + */ template<typename VectorMortonIdx, typename MortonDistribution> inline auto build_upper_distribution(cpp_tools::parallel_manager::parallel_manager& para, const std::size_t dimension, const int& level, @@ -887,14 +725,9 @@ namespace scalfmm::parallel::utils const MortonDistribution& cells_distrib) -> MortonDistribution { using morton_type = typename VectorMortonIdx::value_type; - // std::cout << cpp_tools::colors::blue << " --> Begin distrib::build_upper_distribution at level " << level - // << cpp_tools::colors::reset << std::endl; - // std::cout << std::endl; + MortonDistribution parent_distrib(cells_distrib); auto rank = para.get_process_id(); - // std::int64_t ghost_parent{-1}; - // io::print("rank(" + std::to_string(rank) + ") cells_distrib: ", cells_distrib); - // io::print("rank(" + std::to_string(rank) + ") mortonCellIndex: ", mortonCellIndex); // get the parent distribution for(auto& p: parent_distrib) @@ -995,7 +828,6 @@ namespace scalfmm::parallel::utils mortonCellIndex.erase(last, mortonCellIndex.end()); } - // io::print("rank(" + std::to_string(rank) + ") mortonCellIndex1: ", mortonCellIndex); parent_distrib[0][0] = parent_distrib[rank][0]; parent_distrib[0][1] = parent_distrib[rank][1]; auto mpi_type = cpp_tools::parallel_manager::mpi::get_datatype<morton_type>(); @@ -1003,51 +835,45 @@ namespace scalfmm::parallel::utils /// share the distribution on all processors para.get_communicator().allgather(parent_distrib.data(), 2, mpi_type); - // print_distrib("parent_distrib(allgather):", rank, parent_distrib); - - // std::cout << cpp_tools::colors::blue << " --> End distrib::build_upper_distribution at level " << level - // << cpp_tools::colors::reset << std::endl; return parent_distrib; } - /// - /// \brief merge two sorted vectors - /// - /// Elements appear only once - /// - /// \param[in] v1 first vector to merge - /// \param[in] v2 vector to merge - /// - /// \return the merged vector - /// to the first vector - /// + /** + * @brief Merges two sorted vectors. + * + * Elements appear only once. + * + * @tparam VectorMortonIdx + * @param v1 first vector to merge. + * @param v2 vector to merge. + * @return the merged vector to the first vector. + */ template<typename VectorMortonIdx> - inline VectorMortonIdx merge_unique(VectorMortonIdx& v1, const VectorMortonIdx& v2) + inline auto merge_unique(VectorMortonIdx& v1, const VectorMortonIdx& v2) -> VectorMortonIdx { - /* std::cout << cpp_tools::colors::green << " --> Begin let::merge_unique " << - cpp_tools::colors::reset - << std::endl*/ VectorMortonIdx dst; std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst)); auto last = std::unique(dst.begin(), dst.end()); - // std::cout << " last " << *last <<std::endl; dst.erase(last, dst.end()); - // std::cout << cpp_tools::colors::green << " --> End let::merge_unique " << - // cpp_tools::colors::reset - // << std::endl; - // io::print(" merge uniq dst", dst); + return dst; } + /** + * @brief + * + * @tparam VectorMortonIdx + * @tparam Iterator + * @param a_beg + * @param a_end + * @param b_beg + * @param b_end + * @return VectorMortonIdx + */ template<typename VectorMortonIdx, typename Iterator> - VectorMortonIdx merge_unique_fast(const Iterator a_beg, const Iterator a_end, const Iterator b_beg, - const Iterator b_end) + auto merge_unique_fast(const Iterator a_beg, const Iterator a_end, const Iterator b_beg, + const Iterator b_end) -> VectorMortonIdx { - - // io::print(std::cout, " merge uniq v1", a_beg, a_end); - // std::cout << std::endl; - // io::print(std::cout, " merge uniq v2", b_beg, b_end); - // std::cout << std::endl; int j{0}; int n = std::distance(b_beg, b_end); std::vector<int> add(n); @@ -1105,19 +931,22 @@ namespace scalfmm::parallel::utils } } std::copy(it_a, a_end, it); - // io::print("merged ", merged); + // io::print("merged ", merged); return merged; } - /// - /// \brief find if the index exists owning the index - /// \param[in] index - /// \param[in] distrib the index distribution - /// \param[in] start [optional] position to start in the distribution - /// vector \return the process number (if -1 index not in my distribution) - /// + /** + * @brief find if the index exists owning the index + * + * @tparam MortonIdx + * @tparam VectorMortonIdx + * @param index + * @param my_index + * @param[in] start [optional] position to start in the distribution vector + * @return the process number (if -1 index not in my distribution) + */ template<typename MortonIdx, typename VectorMortonIdx> - inline std::int64_t find_index(const MortonIdx& index, const VectorMortonIdx& my_index, std::size_t& start) + inline auto find_index(const MortonIdx& index, const VectorMortonIdx& my_index, std::size_t& start) -> std::int64_t { for(std::size_t i = start; i < my_index.size(); ++i) { @@ -1139,8 +968,19 @@ namespace scalfmm::parallel::utils } return -1; } + + /** + * @brief + * + * @tparam MortonIdx + * @tparam LeafInfo + * @param index + * @param my_leaves + * @param start + * @return std::int64_t + */ template<typename MortonIdx, typename LeafInfo> - inline std::int64_t find_index2(const MortonIdx& index, const LeafInfo& my_leaves, std::size_t& start) + inline auto find_index2(const MortonIdx& index, const LeafInfo& my_leaves, std::size_t& start) -> std::int64_t { for(std::size_t i = start; i < my_leaves.size(); ++i) { @@ -1162,18 +1002,22 @@ namespace scalfmm::parallel::utils } return -1; } - /// - /// \brief check if the morton index used in the vector of indexes exist - /// - /// This step needs communication - /// \param para the parallel manager - /// \param needed_idx the index to check if they exits in the other processors - /// \param distrib the index distribution on all processors - /// \param local_morton_idx My local morton index - /// + + /** + * @brief check if the morton index used in the vector of indexes exist + * + * This step needs communication + * + * @tparam VectorMortonIdx + * @tparam MortonDistribution + * @param para the parallel manager + * @param needed_idx the index to check if they exits in the other processors + * @param distrib the index distribution on all processors + * @param local_morton_idx My local morton index + */ template<typename VectorMortonIdx, typename MortonDistribution> - void check_if_morton_index_exist(cpp_tools::parallel_manager::parallel_manager& para, VectorMortonIdx& needed_idx, - const MortonDistribution& distrib, const VectorMortonIdx& local_morton_idx) + auto check_if_morton_index_exist(cpp_tools::parallel_manager::parallel_manager& para, VectorMortonIdx& needed_idx, + const MortonDistribution& distrib, const VectorMortonIdx& local_morton_idx) -> void { auto rank = para.get_process_id(); auto nb_proc = para.get_num_processes(); @@ -1326,7 +1170,7 @@ namespace scalfmm::parallel::utils { cpp_tools::parallel_manager::mpi::request::waitall(tab_mpi_status.size(), tab_mpi_status.data()); } - // io::print("rank(" + std::to_string(rank) + ") needed_idx : ", needed_idx); + // io::print("rank(" + std::to_string(rank) + ") needed_idx : ", needed_idx); for(std::size_t i = 0; i < nb_messages_to_receive.size(); ++i) { @@ -1340,31 +1184,38 @@ namespace scalfmm::parallel::utils // We remove the bad_index in order to have only the existing components (leaf/cell) std::sort(needed_idx.begin(), needed_idx.end()); auto last = std::unique(needed_idx.begin(), needed_idx.end()); - // io::print("rank(" + std::to_string(rank) + ") uniq needed_idx : ", needed_idx); + if(*(last - 1) == bad_index) { last = last - 1; } + needed_idx.erase(last, needed_idx.end()); - // // io::print("rank(" + std::to_string(rank) + ") needed_idx : ", needed_idx); - // std::cout << cpp_tools::colors::green << " (" << rank << ") --> End distrib::check_if_morton_index_exist - // " - // << cpp_tools::colors::reset << std::endl - // << std::flush; } + /** + * @brief + * + * @tparam VectorMortonIdx + * @tparam leafInfo + * @tparam MortonDistribution + * @param para + * @param needed_idx + * @param distrib + * @param leaf_info + */ template<typename VectorMortonIdx, typename leafInfo, typename MortonDistribution> - void check_if_leaf_morton_index_exist(cpp_tools::parallel_manager::parallel_manager& para, + auto check_if_leaf_morton_index_exist(cpp_tools::parallel_manager::parallel_manager& para, VectorMortonIdx& needed_idx, const MortonDistribution& distrib, - const leafInfo& leaf_info) + const leafInfo& leaf_info) -> void { auto rank = para.get_process_id(); auto nb_proc = para.get_num_processes(); - // std::cout << cpp_tools::colors::green << " (" << rank - // << ")--> Begin distrib::check_if_leaf_morton_index_exist " << cpp_tools::colors::reset - // << std::endl; - // io::print("rank(" + std::to_string(rank) + ") needed_idx(p2p) : ", needed_idx); + // std::cout << cpp_tools::colors::green << " (" << rank + // << ")--> Begin distrib::check_if_leaf_morton_index_exist " << cpp_tools::colors::reset + // << std::endl; + // io::print("rank(" + std::to_string(rank) + ") needed_idx(p2p) : ", needed_idx); #ifdef SCALFMM_USE_MPI using mortonIdx_type = typename VectorMortonIdx::value_type; @@ -1396,16 +1247,16 @@ namespace scalfmm::parallel::utils } } start[rank] = start[rank + 1]; - // io::print("rank(" + std::to_string(rank) + ") start : ", start); - // io::print("rank(" + std::to_string(rank) + ") needed_idx : ", needed_idx); - // io::print("rank(" + std::to_string(rank) + ") Nb msg send : ", nb_messages_to_send); + // io::print("rank(" + std::to_string(rank) + ") start : ", start); + // io::print("rank(" + std::to_string(rank) + ") needed_idx : ", needed_idx); + // io::print("rank(" + std::to_string(rank) + ") Nb msg send : ", nb_messages_to_send); // exchange the vector all to all to know which process send us a // message auto comm = para.get_communicator(); auto mpi_type = cpp_tools::parallel_manager::mpi::get_datatype<int>(); comm.alltoall(nb_messages_to_send.data(), 1, mpi_type, nb_messages_to_receive.data(), 1, mpi_type); - // io::print("rank(" + std::to_string(rank) + ") Nb msg receiv : ", nb_messages_to_receive); + // io::print("rank(" + std::to_string(rank) + ") Nb msg receiv : ", nb_messages_to_receive); // _to_send // check if the morton index exist locally // bad_index the largest morton index in the whole indexes + 1 @@ -1530,24 +1381,21 @@ namespace scalfmm::parallel::utils } #endif - - // io::print("rank(" + std::to_string(rank) + ") needed_idx : ", needed_idx); - // std::cout << cpp_tools::colors::green << " (" << rank - // << ") --> End distrib::check_if_leaf_morton_index_exist " << cpp_tools::colors::reset << - // std::endl - // << std::flush; } - /// - /// \brief find the group owning the index - /// - /// \param[in] index the index - /// \param[in] begin iterator to start search - /// \param[in] end iterator to complete the search - /// vector \return the process number - /// - template<typename MortonIdx, typename Group_iterator_t> - inline Group_iterator_t find_group_for_index(Group_iterator_t begin, Group_iterator_t end, const MortonIdx& index) + /** + * @brief find the group owning the index + * + * @tparam MortonIdx + * @tparam GroupIteratorType + * @param[in] begin iterator to start search + * @param[in] end iterator to complete the search vector + * @param[in] index the index + * @return GroupIteratorType + */ + template<typename MortonIdx, typename GroupIteratorType> + inline auto find_group_for_index(GroupIteratorType begin, GroupIteratorType end, + const MortonIdx& index) -> GroupIteratorType { for(auto grp_ptr = begin; grp_ptr != end; ++grp_ptr) { @@ -1560,9 +1408,22 @@ namespace scalfmm::parallel::utils } return end; } - template<typename Group_iterator_t, typename MortonIdxVector_t, typename Dependencies_t> - void build_dependencies_from_morton_vector(Group_iterator_t begin, Group_iterator_t end, - const MortonIdxVector_t& morton_to_send, Dependencies_t& deps) + + /** + * @brief + * + * @tparam GroupIteratorType + * @tparam MortonIdxVectorType + * @tparam Dependencies_t + * @param begin + * @param end + * @param morton_to_send + * @param deps + */ + template<typename GroupIteratorType, typename MortonIdxVectorType, typename Dependencies_t> + inline auto build_dependencies_from_morton_vector(GroupIteratorType begin, GroupIteratorType end, + const MortonIdxVectorType& morton_to_send, + Dependencies_t& deps) -> void { const int max_idx = morton_to_send.size(); // loop on the groups // Find the group containing the first index @@ -1587,9 +1448,20 @@ namespace scalfmm::parallel::utils } } } - template<typename Group_iterator_t, typename MortonIdxVector_t> - auto serialise(std::pair<Group_iterator_t, Group_iterator_t> first, - std::pair<Group_iterator_t, Group_iterator_t> second, const MortonIdxVector_t& mortons) + + /** + * @brief + * + * @tparam GroupIteratorType + * @tparam MortonIdxVectorType + * @param first + * @param second + * @param mortons + * @return auto + */ + template<typename GroupIteratorType, typename MortonIdxVectorType> + auto serialise(std::pair<GroupIteratorType, GroupIteratorType> first, + std::pair<GroupIteratorType, GroupIteratorType> second, const MortonIdxVectorType& mortons) { } diff --git a/include/scalfmm/parallel/utils.hpp b/include/scalfmm/parallel/utils.hpp index b90519b02d00cc94af9976105e2ae82837c7157f..3d495280aa34469a957b1ca99b386603c44ebb13 100644 --- a/include/scalfmm/parallel/utils.hpp +++ b/include/scalfmm/parallel/utils.hpp @@ -1,18 +1,26 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/parallel/utils.hpp +// -------------------------------- #pragma once #include "scalfmm/utils/math.hpp" namespace scalfmm::parallel::utils { - /// - /// \brief find the processor owning the index - /// \param[in] index - /// \param[in] distrib the index distribution - /// \param[in] start [optional]position to start in the distribution - /// vector \return the process number - /// + /** + * @brief find the processor owning the index + * + * @tparam MortonIdx + * @tparam MortonDistribution + * @param[in] index + * @param[in] distrib the index distribution + * @param[in] start [optional]position to start in the distribution vector + * @return the process number + */ template<typename MortonIdx, typename MortonDistribution> - inline int find_proc_for_index(const MortonIdx& index, const MortonDistribution& distrib, std::size_t start = 0) + inline auto find_proc_for_index(const MortonIdx& index, const MortonDistribution& distrib, + std::size_t start = 0) -> int { for(std::size_t i = start; i < distrib.size(); ++i) { @@ -23,21 +31,21 @@ namespace scalfmm::parallel::utils } return -1; } - /// - /// \brief get theoretical p2p interaction list outside me - /// - /// We return the list of indexes of cells involved in P2P interaction that we do - /// not have locally. The cells on other processors may not exist. - /// - /// \param[in] para the parallel manager - /// \param tree the tree used to compute the interaction - /// \param local_morton_vect the vector of local morton indexes on my node - /// \param leaves_distrib the leaves distribution on the processes - /// \return the list of indexes on other processes - /// + /** + * @brief get theoretical p2p interaction list outside me + * + * We return the list of indexes of cells involved in P2P interaction that we do + * not have locally. The cells on other processors may not exist. + * + * @tparam MortonIdx + * @tparam MortonDistribution + * @param morton_idx + * @param leaves_distrib the leaves distribution on the processes. + * @return the list of indexes on other processes + */ template<typename MortonIdx, typename MortonDistribution> - inline bool is_inside_distrib(MortonIdx morton_idx, MortonDistribution const& leaves_distrib) + inline auto is_inside_distrib(MortonIdx morton_idx, MortonDistribution const& leaves_distrib) -> bool { for(auto const& interval: leaves_distrib) { @@ -56,6 +64,7 @@ namespace scalfmm::parallel::utils } return false; } + /** * @brief Check if morton index is inside the vector of distribution starting at start +1 * @@ -67,8 +76,8 @@ namespace scalfmm::parallel::utils * @return true if morton_ids is inside otherwise false */ template<typename MortonIdx, typename MortonDistribution> - inline bool is_inside_distrib_right(MortonIdx morton_idx, int const& start, - MortonDistribution const& component_distrib) + inline auto is_inside_distrib_right(MortonIdx morton_idx, int const& start, + MortonDistribution const& component_distrib) -> bool { if(find_proc_for_index(morton_idx, component_distrib, start + 1) > 0) { @@ -79,6 +88,7 @@ namespace scalfmm::parallel::utils return false; } } + /** * @brief Check if morton index is inside the vector of distribution between 0 and end - 1 * @@ -90,8 +100,8 @@ namespace scalfmm::parallel::utils * @return true if morton_ids is inside otherwise false */ template<typename MortonIdx, typename MortonDistribution> - inline bool is_inside_distrib_left(MortonIdx morton_idx, int const& end, - MortonDistribution const& component_distrib) + inline auto is_inside_distrib_left(MortonIdx morton_idx, int const& end, + MortonDistribution const& component_distrib) -> bool { for(int i = end - 1; i >= 0; --i) { @@ -107,17 +117,18 @@ namespace scalfmm::parallel::utils } return false; } + /** * @brief Get the proc id of the morton index * - * @tparam Morton_type + * @tparam MortonType * @tparam VectorDistrib * @param morton_idx mordon index * @param distrib a vector of distribution (number of processes size) * @return the process id containing the Morton index */ - template<typename Morton_type, typename VectorDistrib> - inline auto get_proc_id(Morton_type const& morton_idx, VectorDistrib const& distrib) -> int + template<typename MortonType, typename VectorDistrib> + inline auto get_proc_id(MortonType const& morton_idx, VectorDistrib const& distrib) -> int { int idx_proc{0}; for(auto dist: distrib) @@ -131,9 +142,19 @@ namespace scalfmm::parallel::utils return -1; } - template<typename vector_struct_type, typename vector_type> - auto inline set_data_in_leaf(int dim, vector_struct_type const& leaf_to_receive_access, vector_type const& buffer) - -> void + + /** + * @brief Set the data in leaf object + * + * @tparam VectorStructType + * @tparam VectorType + * @param dim + * @param leaf_to_receive_access + * @param buffer + */ + template<typename VectorStructType, typename VectorType> + inline auto set_data_in_leaf(int dim, VectorStructType const& leaf_to_receive_access, + VectorType const& buffer) -> void { auto const& elt = leaf_to_receive_access; int nb_elt{0}; // size of the buffer for one coordinate diff --git a/include/scalfmm/simd/memory.hpp b/include/scalfmm/simd/memory.hpp index f23e7d1abb704bd7175d213fb45ca51829b2bb56..d5ff2934e707a783ba969a14401fa2f2396ad46f 100644 --- a/include/scalfmm/simd/memory.hpp +++ b/include/scalfmm/simd/memory.hpp @@ -1,46 +1,94 @@ // -------------------------------- // See LICENCE file at project root -// File : simd/memeory.hpp +// File : scalfmm/simd/memory.hpp // -------------------------------- #ifndef SCALFMM_SIMD_MEMORY_HPP #define SCALFMM_SIMD_MEMORY_HPP -#include <xsimd/xsimd.hpp> -#include <scalfmm/meta/traits.hpp> -#include <scalfmm/meta/utils.hpp> +#include "scalfmm/meta/traits.hpp" +#include "scalfmm/meta/utils.hpp" + +#include "xsimd/xsimd.hpp" + +#include <cstddef> +#include <functional> #include <tuple> #include <type_traits> #include <utility> -#include <cstddef> -#include <functional> - namespace scalfmm::simd { + /** + * @brief + * + */ struct aligned { }; + + /** + * @brief + * + */ struct unaligned { }; + + /** + * @brief + * + */ struct splated { }; namespace impl { + /** + * @brief + * + * @tparam SimdType + * @tparam Tuple + * @tparam Is + * @param values_to_splat + * @param s + * @return constexpr auto + */ template<typename SimdType, typename Tuple, std::size_t... Is> [[nodiscard]] constexpr inline auto load_splat_value(Tuple const& values_to_splat, std::index_sequence<Is...> s) { return std::make_tuple(SimdType(meta::get<Is>(values_to_splat))...); } + /** + * @brief + * + * @tparam SimdType + * @tparam TupleOfIt + * @tparam Is + * @param values_to_splat + * @param s + * @return constexpr auto + */ template<typename SimdType, typename TupleOfIt, std::size_t... Is> - [[nodiscard]] constexpr inline auto load_splat_value_from_it(TupleOfIt const& values_to_splat, std::index_sequence<Is...> s) + [[nodiscard]] constexpr inline auto load_splat_value_from_it(TupleOfIt const& values_to_splat, + std::index_sequence<Is...> s) { return std::make_tuple(SimdType(*meta::get<Is>(values_to_splat))...); } + /** + * @brief + * + * @tparam Position + * @tparam TuplePtr + * @tparam Aligned + * @tparam Is + * @param particle_ptrs + * @param a + * @param s + * @return constexpr auto + */ template<typename Position, typename TuplePtr, typename Aligned, std::size_t... Is> [[nodiscard]] constexpr inline auto load_position(TuplePtr const& particle_ptrs, Aligned a, std::index_sequence<Is...> s) @@ -67,6 +115,19 @@ namespace scalfmm::simd } } + /** + * @brief + * + * @tparam StoredType + * @tparam Tuple + * @tparam TuplePtr + * @tparam Aligned + * @tparam Is + * @param variadic_adaptor_iterator + * @param src + * @param a + * @param s + */ template<typename StoredType, typename Tuple, typename TuplePtr, typename Aligned, std::size_t... Is> constexpr inline void store_tuple(TuplePtr& variadic_adaptor_iterator, Tuple const& src, Aligned a, std::index_sequence<Is...> s) @@ -90,6 +151,18 @@ namespace scalfmm::simd } } + /** + * @brief + * + * @tparam LoadedType + * @tparam TuplePtr + * @tparam Aligned + * @tparam Is + * @param particle_ptrs + * @param a + * @param s + * @return constexpr auto + */ template<typename LoadedType, typename TuplePtr, typename Aligned, std::size_t... Is> [[nodiscard]] constexpr inline auto load_tuple(TuplePtr const& particle_ptrs, Aligned a, std::index_sequence<Is...> s) @@ -115,15 +188,19 @@ namespace scalfmm::simd } } - //template<typename T, typename Type, typename TT, typename Container, typename... Types, std::size_t... Is> - //[[nodiscard]] constexpr inline auto apply_f(std::index_sequence<Is...> s, Type T::*f, TT&& tt, - // Container&& input, Types&&... ts) - //{ - // using return_type = typename std::decay_t<Container>; - // return return_type{ - // {std::invoke(f, tt, meta::get<Is>(std::forward<Container>(input)), std::forward<Types>(ts)...)...}}; - //} - + /** + * @brief + * + * @tparam F + * @tparam Container + * @tparam Types + * @tparam Is + * @param s + * @param f + * @param input + * @param ts + * @return constexpr auto + */ template<typename F, typename Container, typename... Types, std::size_t... Is> [[nodiscard]] constexpr inline auto apply_f(std::index_sequence<Is...> s, F&& f, Container&& input, Types&&... ts) @@ -135,24 +212,61 @@ namespace scalfmm::simd } // namespace impl + /** + * @brief + * + * @tparam SimdType + * @tparam Tuple + * @param values_to_splat + * @return constexpr auto + */ template<typename SimdType, typename Tuple> [[nodiscard]] constexpr inline auto load_splat_value(Tuple const& values_to_splat) { return impl::load_splat_value<SimdType>(values_to_splat, std::make_index_sequence<meta::tuple_size_v<Tuple>>{}); } + /** + * @brief + * + * @tparam SimdType + * @tparam TupleOfIt + * @param values_to_splat + * @return constexpr auto + */ template<typename SimdType, typename TupleOfIt> [[nodiscard]] constexpr inline auto load_splat_value_from_it(TupleOfIt const& values_to_splat) { - return impl::load_splat_value_from_it<SimdType>(values_to_splat, std::make_index_sequence<meta::tuple_size_v<TupleOfIt>>{}); + return impl::load_splat_value_from_it<SimdType>(values_to_splat, + std::make_index_sequence<meta::tuple_size_v<TupleOfIt>>{}); } + /** + * @brief + * + * @tparam Position + * @tparam TuplePtr + * @tparam Aligned + * @param particle_ptrs + * @param a + * @return constexpr auto + */ template<typename Position, typename TuplePtr, typename Aligned = aligned> [[nodiscard]] constexpr inline auto load_position(TuplePtr const& particle_ptrs, Aligned a = Aligned{}) { return impl::load_position<Position>(particle_ptrs, a, std::make_index_sequence<Position::dimension>{}); } + /** + * @brief + * + * @tparam LoadedType + * @tparam TuplePtr + * @tparam Aligned + * @param variadic_adaptor_iterator + * @param a + * @return constexpr auto + */ template<typename LoadedType, typename TuplePtr, typename Aligned = aligned> [[nodiscard]] constexpr inline auto load_tuple(TuplePtr const& variadic_adaptor_iterator, Aligned a = Aligned{}) { @@ -161,20 +275,16 @@ namespace scalfmm::simd std::make_index_sequence<meta::tuple_size_v<decltype(std::declval<TuplePtr>().operator*())>>{}); } - // template<typename Particle, typename LoadedType, typename TuplePtr, typename Aligned = aligned> - //[[nodiscard]] constexpr inline auto load_attributes(TuplePtr const& particle_ptrs, Aligned a = Aligned{}) - //{ - // return impl::load_attributes<LoadedType>( - // particle_ptrs, a, - // meta::add_to_sequence<Particle::dimension>(std::make_index_sequence<Particle::attributes_size>{})); - //} - - // template<typename Position, typename TuplePtr, typename Aligned = aligned> - //[[nodiscard]] constexpr inline auto load_forces(TuplePtr const& particle_ptrs, Aligned a = Aligned{}) - //{ - // return impl::load_position<Position>(particle_ptrs, a, std::make_index_sequence<Position::dimension>{}); - //} - + /** + * @brief + * + * @tparam Position + * @tparam TuplePtr + * @tparam Aligned + * @param particle_ptrs + * @param src + * @param a + */ template<typename Position, typename TuplePtr, typename Aligned = aligned> constexpr inline void store_position(TuplePtr& particle_ptrs, Position const& src, Aligned a = Aligned{}) { @@ -182,6 +292,17 @@ namespace scalfmm::simd std::make_index_sequence<Position::dimension>{}); } + /** + * @brief + * + * @tparam StoredType + * @tparam TupleSrc + * @tparam TuplePtr + * @tparam Aligned + * @param variadic_adaptor_iterator + * @param src + * @param a + */ template<typename StoredType, typename TupleSrc, typename TuplePtr, typename Aligned = aligned> constexpr inline void store_tuple(TuplePtr& variadic_adaptor_iterator, TupleSrc const& src, Aligned a = Aligned{}) { @@ -189,14 +310,18 @@ namespace scalfmm::simd std::make_index_sequence<meta::tuple_size_v<TupleSrc>>{}); } - // template<typename StoredType, typename TupleAttributes, typename TuplePtr, typename Aligned = aligned> - // constexpr inline void store_attributes(TuplePtr& attributes_ptrs, TupleAttributes const& src, Aligned a = - // Aligned{}) - //{ - // impl::store_tuple<StoredType>(attributes_ptrs, src, a, - // std::make_index_sequence<meta::tuple_size<TupleAttributes>::value>{}); - //} - + /** + * @brief + * + * @tparam Size + * @tparam F + * @tparam Container + * @tparam Types + * @param f + * @param input + * @param ts + * @return constexpr auto + */ template<std::size_t Size, typename F, typename Container, typename... Types> [[nodiscard]] constexpr inline auto apply_f(F&& f, Container&& input, Types&&... ts) { @@ -204,13 +329,6 @@ namespace scalfmm::simd std::forward<Types>(ts)...); } - //template<std::size_t Size, typename F, typename TT, typename Container, typename... Types> - //[[nodiscard]] constexpr inline auto apply_f(F&& f, TT& tt, Container&& input, Types&&... ts) - //{ - // return impl::apply_f(std::make_index_sequence<Size>{}, std::forward<F>(f), std::forward<TT>(tt), - // std::forward<Container>(input), std::forward<Types>(ts)...); - //} - } // namespace scalfmm::simd #endif // SCALFMM_SIMD_MEMORY_HPP diff --git a/include/scalfmm/simd/utils.hpp b/include/scalfmm/simd/utils.hpp index a6f5ba5e9023c657c83b590122443ff2d130b948..ecaaab8d476d0b3bdf6dd25173465e4a0e766a05 100644 --- a/include/scalfmm/simd/utils.hpp +++ b/include/scalfmm/simd/utils.hpp @@ -1,23 +1,37 @@ // -------------------------------- // See LICENCE file at project root -// File : simd/utils.hpp +// File : scalfmm/simd/utils.hpp // -------------------------------- #ifndef SCALFMM_SIMD_UTILS_HPP #define SCALFMM_SIMD_UTILS_HPP -#include <xsimd/xsimd.hpp> +#include "xsimd/xsimd.hpp" + #include <cmath> -#include <type_traits> #include <cstddef> +#include <type_traits> namespace xsimd { + /** + * @brief + * + * @tparam T + * @tparam A + */ template<class T, class A> class batch; } // namespace xsimd namespace scalfmm::simd { + /** + * @brief + * + * @tparam T + * @tparam A + * @param x + */ template<typename T, class A> inline void compare(xsimd::batch<T, A>& x) { @@ -31,6 +45,13 @@ namespace scalfmm::simd } } + /** + * @brief + * + * @tparam T + * @param x + * @return std::enable_if_t<std::is_scalar<T>::value> + */ template<typename T> inline auto compare(T& x) -> std::enable_if_t<std::is_scalar<T>::value> { diff --git a/include/scalfmm/spherical/sperical.hpp b/include/scalfmm/spherical/sperical.hpp index 0aafc5bd4716fe288bd974658e056152fb128fc4..6e896a22b94b7b71d6d8e943845ade2f3119c436 100644 --- a/include/scalfmm/spherical/sperical.hpp +++ b/include/scalfmm/spherical/sperical.hpp @@ -1,14 +1,18 @@ // -------------------------------- // See LICENCE file at project root -// File : spherical/spherical.hpp +// File : scalfmm/spherical/sperical.hpp // -------------------------------- -#ifndef SCALFMM_SPHERICAL_SPHERICAL_HPP -#define SCALFMM_SPHERICAL_SPHERICAL_HPP +#pragma once namespace scalfmm::spherical { - + /** + * @brief + * + * @tparam T + */ template<typename T> - class spherical{}; -} -#endif // SCALFMM_SPHERICAL_SPHERICAL_HPP + class spherical + { + }; +} // namespace scalfmm::spherical diff --git a/include/scalfmm/tags/tags.hpp b/include/scalfmm/tags/tags.hpp index 7a737a9194f3e4c26a31b707770c989e52de41ff..a5a91c4e89cff950593540ebabdc74be6473d3d6 100644 --- a/include/scalfmm/tags/tags.hpp +++ b/include/scalfmm/tags/tags.hpp @@ -1,9 +1,8 @@ // -------------------------------- // See LICENCE file at project root -// File : interpolation/tags.hpp +// File : scalfmm/tags/tags.hpp // -------------------------------- -#ifndef SCALFMM_TAGS_TAGS_HPP -#define SCALFMM_TAGS_TAGS_HPP +#pragma once namespace scalfmm { @@ -49,6 +48,4 @@ namespace scalfmm { }; } // namespace models::tags -} // namespace scalfmm - -#endif // SCALFMM_INTERPOLATION_TAGS_HPP +} // namespace scalfmm \ No newline at end of file diff --git a/include/scalfmm/tools/bench.hpp b/include/scalfmm/tools/bench.hpp index d88061d709d8c96076487cc156f584075b430af6..387815faee2c9f6b9c74fe89815c434563cf5f8d 100644 --- a/include/scalfmm/tools/bench.hpp +++ b/include/scalfmm/tools/bench.hpp @@ -1,13 +1,14 @@ // -------------------------------- // See LICENCE file at project root -// File : algorithm/utils/bench.hpp +// File : scalfmm/tools/bench.hpp // -------------------------------- #pragma once +#include <cpp_tools/timers/simple_timer.hpp> + #include <algorithm> #include <cctype> #include <chrono> -#include <cpp_tools/timers/simple_timer.hpp> #include <filesystem> #include <fstream> #include <iostream> @@ -18,6 +19,13 @@ namespace scalfmm::bench { + /** + * @brief + * + * @tparam DurationType + * @param timers + * @return auto + */ template<typename DurationType> inline auto compute(std::unordered_map<std::string, cpp_tools::timers::timer<DurationType>> timers) { @@ -41,6 +49,13 @@ namespace scalfmm::bench return std::make_tuple(fartime, neartime, overall, ratio); } + /** + * @brief + * + * @tparam DurationType + * @param timers + * @return auto + */ template<typename DurationType> inline auto print(std::unordered_map<std::string, cpp_tools::timers::timer<DurationType>> timers) { @@ -70,6 +85,16 @@ namespace scalfmm::bench return std::make_tuple(fartime, neartime, overall, ratio); } + /** + * @brief + * + * @tparam String + * @tparam Strings + * @param file_name + * @param header + * @param arg + * @param args + */ template<typename String, typename... Strings> inline auto dump_csv(std::string file_name, std::string header, String arg, Strings... args) -> void { diff --git a/include/scalfmm/tools/data_generate.hpp b/include/scalfmm/tools/data_generate.hpp index a34a3c1c7c59bcb4840b78b1598e1cbe509accc6..6850ef8dd8b4b08ce47b24b129e2d89c01f117dd 100644 --- a/include/scalfmm/tools/data_generate.hpp +++ b/include/scalfmm/tools/data_generate.hpp @@ -1,37 +1,35 @@ // -------------------------------- // See LICENCE file at project root -// File : tools/generate.hpp +// File : scalfmm/tools/data_generate.hpp // -------------------------------- #ifndef SCALFMM_TOOLS_DATA_GENERATE_HPP #define SCALFMM_TOOLS_DATA_GENERATE_HPP -#include <math.h> #include <array> +#include <cstdlib> #include <iostream> +#include <math.h> #include <random> #include <vector> -#include <cstdlib> -// #include <numbers> // Checker f numbers exists namespace scalfmm::tools { /** - * \brief Generate points uniformly inside a cuboid [0,width[0]]x..x[0,width[d]] + * @brief Generate points uniformly inside a cuboid [0,width[0]]x..x[0,width[d]] * * the number of particle is data.size() / stride * - * \tparam VALUE_T Floating point type - * \tparam CONTAINER_Tthe container of particle. method size should exist - * - * \param dimension (int) the space dimension - * \param stride the stride between two points in the container - * \param data array of size stride*N and stores data as follow x,y,z,0-0,x,y,z,0-0... - * \param width a vector of size dimension containig the length of the cuboid + * @tparam ContainerType the container of particle. method size should exist + * @tparam ValueType Floating point type + * @param dimension (int) the space dimension + * @param stride the stride between two points in the container + * @param data array of size stride*N and stores data as follow x,y,z,0-0,x,y,z,0-0... + * @param width a vector of size dimension containig the length of the cuboid */ - template<class CONTAINER_T, typename VALUE_T> - void uniform_points_in_cuboid(const int dimension, const int stride, CONTAINER_T& data, - const std::vector<VALUE_T> width) + template<class ContainerType, typename ValueType> + auto uniform_points_in_cuboid(const int dimension, const int stride, ContainerType& data, + const std::vector<ValueType> width) -> void { std::cout << " call uniform_points_in_cuboid " << std::endl; const auto seed{33}; @@ -47,30 +45,30 @@ namespace scalfmm::tools } /** - * \brief Generate points uniformly inside a ball of radius R + * @brief Generate points uniformly inside a ball of radius R * * The Rejection Method is used to generate uniform points unsid a ball. The * method is independent of tehe dimension. * - * \tparam VALUE_T Floating point type - * \tparam CONTAINER_T array for the data - * - * \param dimension (int) the space dimension - * \param stride the stride between two points in the container - * \param R (VALUE_T) the ball radius - * \param data array of size stride*N and stores data as follow x,y,z,0-0,x,y,z,0-0... + * @tparam ContainerType array for the data + * @tparam ValueType Floating point type + * @param dimension (int) the space dimension + * @param stride the stride between two points in the container + * @param data array of size stride*N and stores data as follow x,y,z,0-0,x,y,z,0-0... + * @param R (ValueType) the ball radius */ - template<class CONTAINER_T, typename VALUE_T> - void uniform_points_ball(const int dimension, const int stride, CONTAINER_T& data, const VALUE_T R) + template<class ContainerType, typename ValueType> + auto uniform_points_ball(const int dimension, const int stride, ContainerType& data, const ValueType R) -> void { std::cout << " call unifRandomPointsInBall with the R= " << R << std::endl; - using point_type = typename CONTAINER_T::value_type; + using point_type = typename ContainerType::value_type; const auto seed{33}; std::mt19937_64 gen(seed); - std::uniform_real_distribution<VALUE_T> dist(-R, R); + std::uniform_real_distribution<ValueType> dist(-R, R); - auto is_in_sphere = [&R, &dimension](point_type* p) { - VALUE_T norm = 0.0; + auto is_in_sphere = [&R, &dimension](point_type* p) + { + ValueType norm = 0.0; for(int i = 0; i < dimension; ++i) { norm += p[i] * p[i]; @@ -89,27 +87,27 @@ namespace scalfmm::tools } while(!is_in_sphere(&(data[i]))); } } + /** - * \brief Generate N points uniformly distributed on the cercle of radius R + * @brief Generate N points uniformly distributed on the cercle of radius R * * We use the polar method do biild the distribution. The number of points is data.size()/stride * - * \tparam VALUE_T Floating point type - * \tparam CONTAINER_T array for the data - * - * \param stride the stride between two points in the container - * \param R the radius of the sphere - * \param points array of size stride*N and stores data as follow x,y,z,0-0,x,y,z,0-0... + * @tparam ContainerType array for the data + * @tparam ValueType Floating point type + * @param stride the stride between two points in the container + * @param data array of size stride*N and stores data as follow x,y,z,0-0,x,y,z,0-0... + * @param R the radius of the sphere */ - template<class CONTAINER_T, typename VALUE_T> - void uniform_points_on_cercle(const int stride, CONTAINER_T& data, const VALUE_T R) + template<class ContainerType, typename ValueType> + auto uniform_points_on_cercle(const int stride, ContainerType& data, const ValueType R) -> void { std::cout << " call unifRandomPointsOnSphere with the R= " << R << std::endl; const auto seed{33}; std::mt19937_64 gen(seed); - VALUE_T u{}, theta{}, twoPi{/*std::numbers::pi_v<VALUE_T>*/ M_PI * 2.0}; - std::uniform_real_distribution<VALUE_T> dist(0.0, twoPi); + ValueType u{}, theta{}, twoPi{/*std::numbers::pi_v<ValueType>*/ M_PI * 2.0}; + std::uniform_real_distribution<ValueType> dist(0.0, twoPi); for(std::size_t i = 0; i < data.size(); i += stride) { u = dist(gen); @@ -117,28 +115,28 @@ namespace scalfmm::tools data[i] = std::cos(theta) * R; data[i + 1] = std::sin(theta) * R; } - }; + } + /** - * \brief Generate N points uniformly distributed on the sphere of radius R + * @brief Generate N points uniformly distributed on the sphere of radius R * * We use the polar method do biild the distribution. The number of points is data.size()/stride * - * \tparam VALUE_T Floating point type - * \tparam CONTAINER_T array for the data - * - * \param stride the stride between two points in the container - * \param R the radius of the sphere - * \param points array of size stride*N and stores data as follow x,y,z,0-0,x,y,z,0-0... + * @tparam ContainerType array for the data + * @tparam ValueType Floating point type + * @param stride the stride between two points in the container + * @param data array of size stride*N and stores data as follow x,y,z,0-0,x,y,z,0-0... + * @param R the radius of the sphere */ - template<class CONTAINER_T, typename VALUE_T> - void uniform_points_on_sphere(const int stride, CONTAINER_T& data, const VALUE_T R) + template<class ContainerType, typename ValueType> + auto uniform_points_on_sphere(const int stride, ContainerType& data, const ValueType R) -> void { std::cout << " call unifRandomPointsOnSphere with the R= " << R << std::endl; const auto seed{33}; std::mt19937_64 gen(seed); - std::uniform_real_distribution<VALUE_T> dist(0.0, 1.0); - VALUE_T u, v, theta, phi, sinPhi, twoPi = /*std::numbers::pi_v<VALUE_T>*/ M_PI * 2.0; + std::uniform_real_distribution<ValueType> dist(0.0, 1.0); + ValueType u, v, theta, phi, sinPhi, twoPi = /*std::numbers::pi_v<ValueType>*/ M_PI * 2.0; for(std::size_t i = 0; i < data.size(); i += stride) { u = dist(gen); @@ -151,20 +149,21 @@ namespace scalfmm::tools data[i + 1] = std::sin(theta) * sinPhi * R; data[i + 2] = (2.0 * v - 1.0) * R; } - }; + } + /** - * \brief Generate N points uniformly distributed on the sphere of radius R - * - * \tparam VALUE_T Floating point type - * \tparam CONTAINER_T array for the data + * @brief Generate N points uniformly distributed on the sphere of radius R * - * \param stride the stride between two points in the container - * \param R the radius of the sphere - * \param points array of size stride*N and stores data as follow - * x,y,z,0-0,x,y,z,0-0... + * @tparam ContainerType array for the data + * @tparam ValueType Floating point type + * @param dimension + * @param stride the stride between two points in the container + * @param data array of size stride*N and stores data as follow x,y,z,-1-0,x,y,z,0-0... + * @param R the radius of the sphere */ - template<class CONTAINER_T, typename VALUE_T> - void uniform_points_on_d_sphere(const int dimension, const int stride, CONTAINER_T& data, const VALUE_T R) + template<class ContainerType, typename ValueType> + auto uniform_points_on_d_sphere(const int dimension, const int stride, ContainerType& data, + const ValueType R) -> void { if(dimension == 2) { @@ -180,39 +179,39 @@ namespace scalfmm::tools std::exit(EXIT_FAILURE); } } + /** - * \brief Generate N points uniformly distributed on the ellipsoid of aspect ratio a:a:c - * - * \tparam VALUE_T Floating point type - * \tparam CONTAINER_T array for the data + * @brief Generate N points uniformly distributed on the ellipsoid of aspect ratio a:a:c * - * \param stride the stride between two points in the container - * \param radius (a,c) the x semi-axe length and the z semi-axe length - * \param vector of data of size stride*N and stores data as follow + * @tparam ContainerType array for the data + * @tparam ValueType Floating point type + * @param stride the stride between two points in the container + * @param radius (a,c) the x semi-axe length and the z semi-axe length + * @param points of data of size stride*N and stores data as follow * points(dim), values(nb) .... * stride = dim + nb */ - template<class CONTAINER_T, typename VALUE_T> - void uniform_points_on_prolate(const int& stride, std::array<VALUE_T, 2>& radius, CONTAINER_T& points) + template<class ContainerType, typename ValueType> + auto uniform_points_on_prolate(const int& stride, std::array<ValueType, 2>& radius, ContainerType& points) -> void { // Number of particles const auto N = points.size() / stride; auto a = radius[0]; auto c = radius[1]; // - VALUE_T u, w, v; - VALUE_T e = (a * a * a * a) / (c * c * c * c); + ValueType u, w, v; + ValueType e = (a * a * a * a) / (c * c * c * c); std::size_t cpt = 0; const int NN = 20; std::vector<int> bin(NN, 0); - VALUE_T h = 2 * c / NN; - VALUE_T twoPi = /*std::numbers::pi_v<VALUE_T>*/ M_PI * 2.0; - VALUE_T pi = /*std::numbers::pi_v<VALUE_T>*/ M_PI; + ValueType h = 2 * c / NN; + ValueType twoPi = /*std::numbers::pi_v<ValueType>*/ M_PI * 2.0; + ValueType pi = /*std::numbers::pi_v<ValueType>*/ M_PI; std::cout << " call unifRandomPointsOnProlate with the a= " << a << " and c= " << c << std::endl; // const auto seed{33}; std::mt19937_64 gen(seed); - std::uniform_real_distribution<VALUE_T> dist(0.0, 1.0); + std::uniform_real_distribution<ValueType> dist(0.0, 1.0); // bool isgood = false; for(std::size_t i = 0, j = 0; i < N; ++i, j += stride) @@ -230,7 +229,7 @@ namespace scalfmm::tools points[j + 2] = c * u; // Accept the position ? if x*x +y*y +e *z*z > a^2 kxi ( see hen and // Glotzer) - VALUE_T ksi = dist(gen) / a; + ValueType ksi = dist(gen) / a; isgood = (points[j] * points[j] + points[j + 1] * points[j + 1] + e * points[j + 2] * points[j + 2]) < ksi * ksi; } while(isgood); @@ -246,7 +245,7 @@ namespace scalfmm::tools } std::cout.precision(4); std::cout << "Total tested points: " << cpt - << " % of rejected points: " << 100 * static_cast<VALUE_T>(cpt - N) / static_cast<VALUE_T>(cpt) + << " % of rejected points: " << 100 * static_cast<ValueType>(cpt - N) / static_cast<ValueType>(cpt) << " %" << std::endl; std::cout << " h " << h << std::endl; // std::cout << " [ " ; @@ -254,7 +253,7 @@ namespace scalfmm::tools // std::cout << bin[k]<< " , " ; // } // std::cout << bin[bin.size() -1]<< " ] "<< std::endl; - VALUE_T x1, x2, surf; + ValueType x1, x2, surf; // We approximate the arc of the ellipsoide by as straight line (Conical // Frustum) see http://mathworld.wolfram.com/ConicalFrustum.html std::cout << " z-density - bins: [ "; @@ -272,25 +271,25 @@ namespace scalfmm::tools } std::cout << " ] " << std::endl; } + /** - * \brief Generate N points uniformly distributed on the ellipsoid of aspect ratio a:b:c + * @brief Generate N points uniformly distributed on the ellipsoid of aspect ratio a:b:c * - * - * See Chen, T., & Glotzer, S. C. (2007). Simulation studies of a phenomenological - * model for elongated virus capsid formation. Physical Review. E, - * Statistical, Nonlinear, and Soft Matter Physics, 75, 1–25. + * See Chen, T., & Glotzer, S. C. (2007). Simulation studies of a phenomenological + * model for elongated virus capsid formation. Physical Review. E, + * Statistical, Nonlinear, and Soft Matter Physics, 75, 1–25. * http://www.biomedsearch.com/nih/Simulation-studies-phenomenological-model-elongated/17677070.html - * \tparam VALUE_T Floating point type - * \tparam CONTAINER_T array for the data * - * \param stride the stride between two points in the container - * \param radius (a, b, c) the x semi-axe length, y and the z semi-axe length - * \param vector of data of size stride*N and stores data as follow + * @tparam ContainerType array for the data + * @tparam ValueType Floating point type + * @param stride the stride between two points in the container + * @param radius (a, b, c) the x semi-axe length, y and the z semi-axe length + * @param points of data of size stride*N and stores data as follow * points(dim), values(nb) .... * stride = dim + nb */ - template<class CONTAINER_T, typename VALUE_T> - void uniform_points_on_ellipsoid(const int& stride, std::array<VALUE_T, 3>& radius, CONTAINER_T& points) + template<class ContainerType, typename ValueType> + auto uniform_points_on_ellipsoid(const int& stride, std::array<ValueType, 3>& radius, ContainerType& points) -> void { // Number of particles const auto N = points.size() / stride; @@ -298,20 +297,20 @@ namespace scalfmm::tools auto b = radius[1]; auto c = radius[2]; // - VALUE_T u, w, v; - VALUE_T e1 = (a * a * a * a) / (b * b * b * b); - VALUE_T e2 = (a * a * a * a) / (c * c * c * c); + ValueType u, w, v; + ValueType e1 = (a * a * a * a) / (b * b * b * b); + ValueType e2 = (a * a * a * a) / (c * c * c * c); std::size_t cpt = 0; const int NN = 20; std::vector<int> bin(NN, 0); - VALUE_T h = 2 * c / NN; - VALUE_T twoPi = /*std::numbers::pi_v<VALUE_T>*/ M_PI * 2.0; - VALUE_T pi = /*std::numbers::pi_v<VALUE_T>*/ M_PI; + ValueType h = 2 * c / NN; + ValueType twoPi = /*std::numbers::pi_v<ValueType>*/ M_PI * 2.0; + ValueType pi = /*std::numbers::pi_v<ValueType>*/ M_PI; std::cout << " call unifRandomPointsOnProlate with the a= " << a << " and c= " << c << std::endl; // const auto seed{33}; std::mt19937_64 gen(seed); - std::uniform_real_distribution<VALUE_T> dist(0.0, 1.0); + std::uniform_real_distribution<ValueType> dist(0.0, 1.0); // bool isgood = false; for(std::size_t i = 0, j = 0; i < N; ++i, j += stride) @@ -329,7 +328,7 @@ namespace scalfmm::tools points[j + 2] = c * u; // Accept the position ? if x*x + e2*y*y +e2 *z*z > a^2 kxi ( see hen and // Glotzer) - VALUE_T ksi = dist(gen) / a; + ValueType ksi = dist(gen) / a; isgood = (points[j] * points[j] + e1 * points[j + 1] * points[j + 1] + e2 * points[j + 2] * points[j + 2]) < ksi * ksi; } while(isgood); @@ -345,7 +344,7 @@ namespace scalfmm::tools } std::cout.precision(4); std::cout << "Total tested points: " << cpt - << " % of rejected points: " << 100 * static_cast<VALUE_T>(cpt - N) / static_cast<VALUE_T>(cpt) + << " % of rejected points: " << 100 * static_cast<ValueType>(cpt - N) / static_cast<ValueType>(cpt) << " %" << std::endl; std::cout << " h " << h << std::endl; // std::cout << " [ " ; @@ -353,7 +352,7 @@ namespace scalfmm::tools // std::cout << bin[k]<< " , " ; // } // std::cout << bin[bin.size() -1]<< " ] "<< std::endl; - VALUE_T x1, x2, surf; + ValueType x1, x2, surf; // We approximate the arc of the ellipsoide by as straight line (Conical // Frustum) see http://mathworld.wolfram.com/ConicalFrustum.html std::cout << " z-density - bins: [ "; @@ -371,34 +370,36 @@ namespace scalfmm::tools } std::cout << " ] " << std::endl; } + /** - * \brief Generate N points non uniformly distributed on the ellipsoid of + * @brief Generate N points non uniformly distributed on the ellipsoid of * aspect ratio a:a:c * * f(x,y,z) = (x^2+y^2)/a^2 + z^2/c^2 - * \tparam VALUE_T Floating point type - * \tparam CONTAINER_T array for the data * - * - * \param stride the stride between two points in the container - * \param radius (a,b,c) the x,y and semi-axe length - * \param vector of data of size stride*N with N the number of points. + * @tparam ContainerType array for the data + * @tparam ValueType Floating point type + * @param stride the stride between two points in the container + * @param radius (a,b,c) the x,y and semi-axe length + * @param density + * @param points of data of size stride*N with N the number of points. */ - template<class CONTAINER_T, class VALUE_T> - void nonuniform_point_on_prolate(const int stride, std::array<VALUE_T, 2>& radius, const double& density, CONTAINER_T& points) + template<class ContainerType, class ValueType> + auto nonuniform_point_on_prolate(const int stride, std::array<ValueType, 2>& radius, const double& density, + ContainerType& points) -> void { - VALUE_T twoPi = /*std::numbers::pi_v<VALUE_T>*/ M_PI * 2.0; - VALUE_T pi = /*std::numbers::pi_v<VALUE_T>*/ M_PI; + ValueType twoPi = /*std::numbers::pi_v<ValueType>*/ M_PI * 2.0; + ValueType pi = /*std::numbers::pi_v<ValueType>*/ M_PI; const auto N = points.size() / stride; auto a = radius[0]; auto b = radius[1]; - // auto c = radius[2]; + // auto c = radius[2]; - VALUE_T rotationMatrix[3][3]; - VALUE_T alpha = pi / 8.0; - VALUE_T omega = pi / 4.0; + ValueType rotationMatrix[3][3]; + ValueType alpha = pi / 8.0; + ValueType omega = pi / 4.0; - VALUE_T yrotation[3][3]; + ValueType yrotation[3][3]; yrotation[0][0] = std::cos(alpha); yrotation[0][1] = 0.0; yrotation[0][2] = std::sin(alpha); @@ -409,7 +410,7 @@ namespace scalfmm::tools yrotation[2][1] = 0.0; yrotation[2][2] = std::cos(alpha); - VALUE_T zrotation[3][3]; + ValueType zrotation[3][3]; zrotation[0][0] = std::cos(omega); zrotation[0][1] = -std::sin(omega); zrotation[0][2] = 0.0; @@ -424,7 +425,7 @@ namespace scalfmm::tools { for(int j = 0; j < 3; ++j) { - VALUE_T sum = 0.0; + ValueType sum = 0.0; for(int k = 0; k < 3; ++k) { sum += zrotation[i][k] * yrotation[k][j]; @@ -434,19 +435,19 @@ namespace scalfmm::tools } const auto seed{33}; std::mt19937_64 gen(seed); - std::uniform_real_distribution<VALUE_T> dist(0.0, 1.0); + std::uniform_real_distribution<ValueType> dist(0.0, 1.0); - const VALUE_T MaxDensity = density; - std::cout << "MaxDensity: "<< MaxDensity << std::endl; + const ValueType MaxDensity = density; + std::cout << "MaxDensity: " << MaxDensity << std::endl; for(std::size_t i = 0, j = 0; i < N; ++i, j += stride) { - const VALUE_T maxPerimeter = twoPi * a; + const ValueType maxPerimeter = twoPi * a; - VALUE_T px = 0; + ValueType px = 0; // rayon du cercle pour ce x - VALUE_T subr = 0; - VALUE_T coef = 1.0; + ValueType subr = 0; + ValueType coef = 1.0; // the ellipsoid is generated by the rotation of an ellipse around one of its axes // px^2/a^2 + z^2/b^2 = 1 @@ -461,9 +462,9 @@ namespace scalfmm::tools // on genere un angle for the rotation omega = dist(gen) * twoPi; // on recupere py et pz sur le cercle - const VALUE_T py = std::cos(omega) * subr; - const VALUE_T pz = std::sin(omega) * subr; - //std::cout << j << " " << px*px +py*py + pz*pz + const ValueType py = std::cos(omega) * subr; + const ValueType pz = std::sin(omega) * subr; + //std::cout << j << " " << px*px +py*py + pz*pz // inParticle.setPosition(px,py,pz); points[j] = px * rotationMatrix[0][0] + py * rotationMatrix[0][1] + pz * rotationMatrix[0][2]; points[j + 1] = px * rotationMatrix[1][0] + py * rotationMatrix[1][1] + pz * rotationMatrix[1][2]; @@ -472,38 +473,27 @@ namespace scalfmm::tools } /** - * \Brief Radial Plummer distribution - * - * \tparam FReal Floating point type - * - * \param cpt counter to know how many random selections we need to obtain a - * radius less than R \param R radius of the sphere that contains the - * particles \return The radius according to the Plummer distribution - */ - - /** - * \brief Build N points following the Plummer like distribution + * @brief Build N points following the Plummer like distribution * * First we construct N points uniformly distributed on the unit sphere. * Then the radius in construct according to the Plummer like distribution. * - * \tparam VALUE_T Floating point type - * \tparam CONTAINER_T type of container containig the points (typically std::vector) - * - * \param stride the number of values between two point positions - * \param radius_max the maximal radius of the sphere that contains all the points - * \param points array of size stride*N and stores data as follow + * @tparam ContainerType type of container containig the points (typically std::vector) + * @tparam ValueType Floating point type + * @param stride the number of values between two point positions + * @param radius_max the maximal radius of the sphere that contains all the points + * @param points array of size stride*N and stores data as follow * x,y,z,0-0,x,y,z,0-0....... The size of the array is N*stride */ - template<class CONTAINER_T, class VALUE_T> - void plummer_distrib(const int stride, const VALUE_T radius_max, CONTAINER_T& points) + template<class ContainerType, class ValueType> + auto plummer_distrib(const int stride, const ValueType radius_max, ContainerType& points) -> void { - VALUE_T r = 1.0; + ValueType r = 1.0; uniform_points_on_sphere(stride, points, r); const auto seed{44}; std::mt19937_64 gen(seed); - std::uniform_real_distribution<VALUE_T> dist(0.0, 1.0); + std::uniform_real_distribution<ValueType> dist(0.0, 1.0); std::size_t cpt = 0; for(std::size_t j = 0; j < points.size(); j += stride) @@ -512,7 +502,7 @@ namespace scalfmm::tools { // r = dist(gen); - VALUE_T u = std::pow(r, 2.0 / 3.0); + ValueType u = std::pow(r, 2.0 / 3.0); r = std::sqrt(u / (1.0 - u)); } while(r >= radius_max); @@ -523,29 +513,31 @@ namespace scalfmm::tools } auto N = points.size() / stride; std::cout << "Total tested points: " << cpt - << " % of rejected points: " << 100 * static_cast<VALUE_T>(cpt - N) / static_cast<VALUE_T>(cpt) + << " % of rejected points: " << 100 * static_cast<ValueType>(cpt - N) / static_cast<ValueType>(cpt) << " %" << std::endl; } - /// - /// \brief generate_input_values generate random input values for particles - /// - /// \param data array contains the positions of the particles and the input values - /// \param nb_input_values number of physical values to generate for each particle - /// \param stride the stride between two particle position = dim + nb_input_values - /// \param charge the data distribution between (-1,1) - /// \param zeromean boolean to center each physical values - /// - /// - template<class CONTAINER_T, typename VALUE_T> - void generate_input_values(CONTAINER_T& data, const int nb_input_values, const int stride, - std::vector<std::array<VALUE_T, 2>>& interval, const bool zeromean) + + /** + * @brief generate_input_values generate random input values for particles + * + * @tparam ContainerType + * @tparam ValueType + * @param data array contains the positions of the particles and the input values + * @param nb_input_values number of physical values to generate for each particle + * @param stride the stride between two particle position = dim + nb_input_values + * @param interval + * @param zeromean boolean to center each physical values + */ + template<class ContainerType, typename ValueType> + auto generate_input_values(ContainerType& data, const int nb_input_values, const int stride, + std::vector<std::array<ValueType, 2>>& interval, const bool zeromean) -> void { - // using VALUE_T = typename CONTAINER_T::value_type; + // using ValueType = typename ContainerType::value_type; const auto seed{33}; std::mt19937_64 gen(seed); - VALUE_T* mean = new VALUE_T[nb_input_values]{}; - std::uniform_real_distribution<VALUE_T> dist(0.0, 1.0); + ValueType* mean = new ValueType[nb_input_values]{}; + std::uniform_real_distribution<ValueType> dist(0.0, 1.0); const int start = stride - nb_input_values; std::size_t pos = 0; for(std::size_t i = 0; i < data.size(); i += stride) @@ -558,12 +550,12 @@ namespace scalfmm::tools } } const auto nb_particles = data.size() / stride; - double cor = 1.0 / static_cast<VALUE_T>(nb_particles); + double cor = 1.0 / static_cast<ValueType>(nb_particles); if(zeromean) { for(int j = 0; j < nb_input_values; ++j) { - mean[j] /= static_cast<VALUE_T>(nb_particles); + mean[j] /= static_cast<ValueType>(nb_particles); std::cout << " Mean for variables " << j << " is " << mean[j] << std::endl; } for(std::size_t i = 0; i < data.size(); i += stride) diff --git a/include/scalfmm/tools/fma_dist_loader.hpp b/include/scalfmm/tools/fma_dist_loader.hpp index f1bc94b2bd1c702cd83521974cd7e7f869088c64..7fadf8c4a6679f259b654ae49720e16938005932 100644 --- a/include/scalfmm/tools/fma_dist_loader.hpp +++ b/include/scalfmm/tools/fma_dist_loader.hpp @@ -1,25 +1,29 @@ // -------------------------------- // See LICENCE file at project root -// File : tools/fma_loader.hpp +// File : scalfmm/tools/fma_dist_loader.hpp // -------------------------------- #ifndef SCALFMM_TOOLS_FMA_MPI_LOADER_HPP #define SCALFMM_TOOLS_FMA_MPI_LOADER_HPP + +#include "scalfmm/tools/fma_loader.hpp" + #include <cpp_tools/parallel_manager/parallel_manager.hpp> -#include <cstdlib> -#include <vector> #include <array> +#include <cstdlib> #include <iostream> #include <limits> #include <string> - -#include "scalfmm/tools/fma_loader.hpp" +#include <vector> namespace scalfmm::io { - /// - /// \brief The FMpiFmaGenericLoader class - /// + /** + * @brief + * + * @tparam FReal + * @tparam Dimension + */ template<class FReal, int Dimension = 3> class DistFmaGenericLoader : public FFmaGenericLoader<FReal, Dimension> { @@ -30,14 +34,46 @@ namespace scalfmm::io using FFmaGenericLoader<FReal, Dimension>::m_verbose; using MPI_Offset = std::size_t; - std::size_t m_local_number_of_particles; ///< Number of particles that the calling process will manage - MPI_Offset m_idxParticles; // - std::size_t m_start; ///< number of my first parts in file + /** + * @brief Number of particles that the calling process will manage. + * + */ + std::size_t m_local_number_of_particles; + + /** + * @brief + * + */ + MPI_Offset m_idxParticles; + + /** + * @brief number of my first parts in file. + * + */ + std::size_t m_start; + + /** + * @brief + * + */ size_t m_headerSize; + + /** + * @brief + * + */ const cpp_tools::parallel_manager::parallel_manager* m_parallelManager; public: - DistFmaGenericLoader(const std::string inFilename, const cpp_tools::parallel_manager::parallel_manager& para, const bool verbose = false) + /** + * @brief Construct a new Dist Fma Generic Loader object + * + * @param inFilename + * @param para + * @param verbose + */ + DistFmaGenericLoader(const std::string inFilename, const cpp_tools::parallel_manager::parallel_manager& para, + const bool verbose = false) : FFmaGenericLoader<FReal, Dimension>(inFilename, verbose) , m_local_number_of_particles(0) , m_idxParticles(0) @@ -53,8 +89,9 @@ namespace scalfmm::io } // Determine the number of particles to read auto rank = m_parallelManager->get_process_id(); - std::size_t startPart = bloc * rank ; - std::size_t endPart = (rank+1 == m_parallelManager->get_num_processes()) ? m_nbParticles : startPart + bloc; + std::size_t startPart = bloc * rank; + std::size_t endPart = + (rank + 1 == m_parallelManager->get_num_processes()) ? m_nbParticles : startPart + bloc; this->m_start = startPart; this->m_local_number_of_particles = endPart - startPart; @@ -86,58 +123,108 @@ namespace scalfmm::io } } + /** + * @brief Destroy the Dist Fma Generic Loader object + * + */ ~DistFmaGenericLoader() {} - auto getMyNumberOfParticles() const { return m_local_number_of_particles; } + /** + * @brief Get the My Number Of Particles object + * + * @return auto + */ + inline auto getMyNumberOfParticles() const { return m_local_number_of_particles; } - auto getStart() const { return m_start; } + /** + * @brief Get the Start object + * + * @return auto + */ + inline auto getStart() const { return m_start; } /** - * Given an index, get the one particle from this index + * @brief Given an index, get the one particle from this index. + * + * @param data + * @param indexInFile */ - void fill1Particle(FReal* data, std::size_t indexInFile) + inline auto fill1Particle(FReal* data, std::size_t indexInFile) -> void { m_file->seekg(m_headerSize + (int(indexInFile) * FFmaGenericLoader<FReal>::getNbRecordPerline() * sizeof(FReal))); m_file->read((char*)data, FFmaGenericLoader<FReal>::getNbRecordPerline() * sizeof(FReal)); } }; + /** - * - * \brief Writes a set of distributed particles to an FMA formated file. + * @brief Writes a set of distributed particles to an FMA formated file. * * The file may be in ASCII or binary mode. The example below shows how to use the class. * - * \code + * @code * // Instanciate the writer with a binary fma file (extension .bfma). - * \endcode + * @endcode * ---------------------------------------- * FMA is a simple format to store particles in a file. It is organized as follow. * * file + * + * @tparam FReal */ template<class FReal> class DistFmaGenericWriter : public FFmaGenericWriter<FReal> { protected: + /** + * @brief + * + */ const cpp_tools::parallel_manager::parallel_manager* m_parallelManager; + + /** + * @brief + * + */ bool _writeDone; + + /** + * @brief + * + */ int m_headerSize; - int _nbDataTowritePerRecord; //< number of data to write for one particle - std::size_t _numberOfParticles; //< number of particle (global) to write in the file - // + + /** + * @brief number of data to write for one particle. + * + */ + int _nbDataTowritePerRecord; + + /** + * @brief number of particle (global) to write in the file. + * + */ + std::size_t _numberOfParticles; + using FFmaGenericWriter<FReal>::m_file; #ifdef SCALFMM_USE_MPI - MPI_File _mpiFile; //< MPI pointer on data file (write mode) + /** + * @brief MPI pointer on data file (write mode). + * + */ + MPI_File _mpiFile; #endif public: /** + * @brief Construct a new Dist Fma Generic Writer object + * * This constructor opens a file to be written to. * * - The opening mode is guessed from the file extension : `.fma` will open * in ASCII mode, `.bfma` will open in binary mode. * - * @param filename the name of the file to open. + * @param inFilename the name of the file to open. + * @param para */ DistFmaGenericWriter(const std::string inFilename, const cpp_tools::parallel_manager::parallel_manager& para) : FFmaGenericWriter<FReal>(inFilename) @@ -166,21 +253,25 @@ namespace scalfmm::io } #endif } + /** - * Writes the header of FMA file. + * @brief Writes the header of FMA file. * * Should be used if we write the particles with writeArrayOfReal method * - * @param centerOfBox The center of the Box (POINT_T class) + * @tparam PointType + * @param centerOfBox The center of the Box (PointType class) * @param boxWidth The width of the box * @param nbParticles Number of particles in the box (or to save) * @param dataType Size of the data type of the values in particle * @param nbDataPerRecord Number of record/value per particle + * @param dimension + * @param nb_input_values */ - template<class POINT_T> - void writeHeader(const POINT_T& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, + template<class PointType> + auto writeHeader(const PointType& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, const unsigned int dataType, const unsigned int nbDataPerRecord, const unsigned int dimension, - const unsigned int nb_input_values) + const unsigned int nb_input_values) -> void { // * \code // * DatatypeSize Number_of_record_per_line @@ -223,15 +314,15 @@ namespace scalfmm::io MPI_Type_size(mpiInt64, &sizeType); m_headerSize += sizeType * 1; - // std::array<FReal, 1 + POINT_T::dimension> boxSim{}; + // std::array<FReal, 1 + PointType::dimension> boxSim{}; // boxSim[0] = boxWidth; - // for(int d = 0; d < POINT_T::dimension; ++d) + // for(int d = 0; d < PointType::dimension; ++d) // { // boxSim[d + 1] = centerOfBox[d]; // } // ierr = MPI_File_write_at(_mpiFile, m_headerSize, &boxSim[0], 4, mpiReal, MPI_STATUS_IGNORE); MPI_Type_size(mpiReal, &sizeType); - m_headerSize += sizeType * (1 + POINT_T::dimension); + m_headerSize += sizeType * (1 + PointType::dimension); // Build the header offset std::cout << " headerSize " << m_headerSize << std::endl; FFmaGenericWriter<FReal>::close(); @@ -247,22 +338,22 @@ namespace scalfmm::io // MPI_File_close(&_mpiFile); } - ~DistFmaGenericWriter() - { /* MPI_File_close(&_mpiFile);*/ - } /** - * Write all for all particles the position, physical values, potential and forces + * @brief Destroy the Dist Fma Generic Writer object * - * @param myOctree the octree - * @param nbParticlesnumber of particles - * @param mortonLeafDistribution the morton distribution of the leaves (this is a vecor of size 2* the - number of - * MPI processes + */ + ~DistFmaGenericWriter() { /* MPI_File_close(&_mpiFile);*/ } + + /** + * @brief Write all for all particles the position, physical values, potential and forces. * + * @tparam TreeType + * @param myOctree the octree + * @param nbParticles number of particles. */ - template<typename OCTREECLASS> - void writeFromTree(const OCTREECLASS& myOctree, const std::size_t& nbParticles) + template<typename TreeType> + auto writeFromTree(const TreeType& myOctree, const std::size_t& nbParticles) -> void { // // // // Write the header @@ -310,11 +401,11 @@ namespace scalfmm::io // std::size_t nbLocalParticles = 0, maxPartLeaf = 0; // MortonIndex starIndex = mortonLeafDistribution[2 * myRank], // endIndex = mortonLeafDistribution[2 * myRank + 1]; - // myOctree.template forEachCellLeaf<typename OCTREECLASS::LeafClass_T>( - // [&](typename OCTREECLASS::GroupSymbolCellClass_T* gsymb, - // typename OCTREECLASS::GroupCellUpClass_T* /* gmul */, - // typename OCTREECLASS::GroupCellDownClass_T* /* gloc */, - // typename OCTREECLASS::LeafClass_T* leafTarget) { + // myOctree.template forEachCellLeaf<typename TreeType::LeafClass_T>( + // [&](typename TreeType::GroupSymbolCellClass_T* gsymb, + // typename TreeType::GroupCellUpClass_T* /* gmul */, + // typename TreeType::GroupCellDownClass_T* /* gloc */, + // typename TreeType::LeafClass_T* leafTarget) { // if(!(gsymb->getMortonIndex() < starIndex || gsymb->getMortonIndex() > endIndex)) // { // auto n = leafTarget->getNbParticles(); @@ -330,11 +421,11 @@ namespace scalfmm::io // _headerSize + sizeType * _nbDataTowritePerRecord * before; // // // // Write particles in file - // myOctree.template forEachCellLeaf<typename OCTREECLASS::LeafClass_T>( - // [&](typename OCTREECLASS::GroupSymbolCellClass_T* gsymb, - // typename OCTREECLASS::GroupCellUpClass_T* /* gmul */, - // typename OCTREECLASS::GroupCellDownClass_T* /* gloc */, - // typename OCTREECLASS::LeafClass_T* leafTarget) { + // myOctree.template forEachCellLeaf<typename TreeType::LeafClass_T>( + // [&](typename TreeType::GroupSymbolCellClass_T* gsymb, + // typename TreeType::GroupCellUpClass_T* /* gmul */, + // typename TreeType::GroupCellDownClass_T* /* gloc */, + // typename TreeType::LeafClass_T* leafTarget) { // if(!(gsymb->getMortonIndex() < starIndex || gsymb->getMortonIndex() > endIndex)) // { // const std::size_t nbPartsInLeaf = leafTarget->getNbParticles(); @@ -380,8 +471,8 @@ namespace scalfmm::io // * of MPI processes // * // */ - // template<class OCTREECLASS> - // void writeDistributionOfParticlesFromOctree(OCTREECLASS& myOctree, const std::size_t& nbParticles, + // template<class TreeType> + // void writeDistributionOfParticlesFromOctree(TreeType& myOctree, const std::size_t& nbParticles, // const std::size_t& nbLocalParticles, // const std::vector<MortonIndex>& mortonLeafDistribution) // { @@ -432,9 +523,9 @@ namespace scalfmm::io // std::size_t maxPartLeaf = 0, nn = 0; // MortonIndex starIndex = mortonLeafDistribution[2 * myRank], // endIndex = mortonLeafDistribution[2 * myRank + 1]; - // // myOctree.template forEachCellLeaf<typename OCTREECLASS::LeafClass_T >( + // // myOctree.template forEachCellLeaf<typename TreeType::LeafClass_T >( // myOctree.forEachCellLeaf( - // [&](typename OCTREECLASS::CellClassType* cell, typename OCTREECLASS::LeafClass_T* leaf) { + // [&](typename TreeType::CellClassType* cell, typename TreeType::LeafClass_T* leaf) { // if(!(cell->getMortonIndex() < starIndex || cell->getMortonIndex() > endIndex)) // { // auto n = leaf->getSrc()->getNbParticles(); @@ -453,7 +544,7 @@ namespace scalfmm::io // // // // Write particles in file // myOctree.forEachCellLeaf( - // [&](typename OCTREECLASS::CellClassType* cell, typename OCTREECLASS::LeafClass_T* leaf) { + // [&](typename TreeType::CellClassType* cell, typename TreeType::LeafClass_T* leaf) { // if(!(cell->getMortonIndex() < starIndex || cell->getMortonIndex() > endIndex)) // { // const std::size_t nbPartsInLeaf = leaf->getTargets()->getNbParticles(); diff --git a/include/scalfmm/tools/fma_loader.hpp b/include/scalfmm/tools/fma_loader.hpp index 85be6a06d741400c88799c3557a1bede29a93d89..9647c684e6bce872956134b3ca4c3b16b7bb4413 100644 --- a/include/scalfmm/tools/fma_loader.hpp +++ b/include/scalfmm/tools/fma_loader.hpp @@ -1,10 +1,17 @@ // -------------------------------- // See LICENCE file at project root -// File : tools/fma_loader.hpp +// File : scalfmm/tools/fma_loader.hpp // -------------------------------- #ifndef SCALFMM_TOOLS_FMA_LOADER_HPP #define SCALFMM_TOOLS_FMA_LOADER_HPP +#include "scalfmm/container/particle.hpp" +#include "scalfmm/container/particle_container.hpp" +#include "scalfmm/container/point.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/tree/box.hpp" +#include "scalfmm/tree/for_each.hpp" + #include <array> #include <cstdlib> #include <errno.h> @@ -17,27 +24,19 @@ #include <string> #include <vector> -#include "scalfmm/container/particle.hpp" -#include "scalfmm/container/particle_container.hpp" -#include "scalfmm/container/point.hpp" -#include "scalfmm/meta/utils.hpp" -#include "scalfmm/tree/box.hpp" -#include "scalfmm/tree/for_each.hpp" - namespace scalfmm::io { - /**\class FFmaGenericLoader + /** * @author Olivier Coulaud (olivier coulaud@inria.fr) - * \warning This class only works in shared memory (doesn't work with MPI). + * @warning This class only works in shared memory (doesn't work with MPI). * - * \brief Reads an FMA formatted particle file. + * @brief Reads an FMA formatted particle file. * * The file may be in ascii or binary mode. There are several overloads of the * fillParticle(FPoint<FReal>*, FReal*) member function to read data from a file. The * example below shows how to use the loader to read from a file. * - * - * \code + * @code * // Instanciate the loader with the particle file. * FFmaGenericLoader<FReal> loader("../Data/unitCubeXYZQ20k.fma"); // extension fma -> ascii format * // Retrieve the number of particles @@ -51,15 +50,15 @@ namespace scalfmm::io * for(std::size_t idx = 0 ; idx < nbParticles ; ++idx){ * loader.fillParticle(particles[idx]); * } - * \endcode + * @endcode * ---------------------------------------- * FMA is a simple format to store particles in a file. It is organized as follow. * - * \code + * @code * DatatypeSize Number_of_record_per_line dimension Number_of_input_data * NB_particles half_Box_width Center (dim values) * Particle_values - * \endcode + * @endcode * * `DatatypeSize` can have one of two values: * - 4, float ; @@ -70,28 +69,89 @@ namespace scalfmm::io * - 4, the particle values are X Y Z Q; * - 8, the particle values are X Y Z Q P FX FY FZ<br> * + * @tparam FReal + * @tparam Dimension */ template<class FReal, int Dimension = 3> class FFmaGenericLoader { protected: - std::fstream* m_file; ///< the stream used to read the file - bool m_binaryFile; ///< if true the file to read is in binary mode - container::point<FReal, Dimension> m_centerOfBox; ///< The center of box (read from file) - std::vector<FReal> m_center{}; ///< The center of box (read from file) - FReal m_boxWidth; ///< the box width (read from file) - std::size_t m_nbParticles; ///< the number of particles (read from file) - std::array<unsigned int, 4> m_typeData; ///< Size of the data to read, number of data on 1 line, dimension - ///< of space and number of input values - std::string m_filename; ///< file name containung the data - bool m_verbose; ///< Verbose mode + /** + * @brief the stream used to read the file. + * + */ + std::fstream* m_file; + + /** + * @brief if true the file to read is in binary mode. + * + */ + bool m_binaryFile; + + /** + * @brief the center of box (read from file). + * + */ + container::point<FReal, Dimension> m_centerOfBox; + + /** + * @brief the center of box (read from file). + * + */ + std::vector<FReal> m_center{}; + + /** + * @brief the box width (read from file). + * + */ + FReal m_boxWidth; + + /** + * @brief the number of particles (read from file) + * + */ + std::size_t m_nbParticles; + + /** + * @brief Size of the data to read, number of data on 1 line, + * dimension of space and number of input values + * + */ + std::array<unsigned int, 4> m_typeData; + + /** + * @brief file name containung the data. + * + */ + std::string m_filename; + + /** + * @brief Verbose mode. + * + */ + bool m_verbose; private: - FReal* m_tmpVal; ///< Temporary array to read data - /// Count of other data pieces to read in a particle record after the 4 first ones. + /** + * @brief Temporary array to read data. + * + */ + FReal* m_tmpVal; + + /** + * @brief Count of other data pieces to read in a particle record after the 4 first ones. + * + */ unsigned int m_otherDataToRead; - void open_file(const std::string filename, const bool binary, const bool verbose = true) + /** + * @brief + * + * @param filename + * @param binary + * @param verbose + */ + auto open_file(const std::string filename, const bool binary, const bool verbose = true) -> void { m_filename = filename; m_verbose = verbose; @@ -119,6 +179,8 @@ namespace scalfmm::io using dataType = FReal; /** + * @brief Construct a new FFmaGenericLoader object + * * This constructor opens a file and reads its header. The file will be * kept opened until destruction of the object. * @@ -128,6 +190,8 @@ namespace scalfmm::io * - To test if the file has successfully been opened, call hasNotFinished(). * * @param filename the name of the file to open. Must end with `.fma` or `.bfma`. + * @param verbose + * @param old_format */ FFmaGenericLoader(const std::string& filename, bool verbose, bool old_format = false) : m_file(nullptr) @@ -159,14 +223,21 @@ namespace scalfmm::io this->open_file(filename, m_binaryFile, m_verbose); this->readHeader(old_format); } - void close() + + /** + * @brief + * + */ + inline auto close() -> void { m_file->close(); delete m_file; m_file = nullptr; } + /** - * Default destructor, closes the file + * @brief Destroy the FFmaGenericLoader object and close the file. + * */ virtual ~FFmaGenericLoader() { @@ -178,76 +249,97 @@ namespace scalfmm::io } /** - * To know if file is open and ready to read - * @return true if loader can work + * @brief To know if the file is open and ready to read. + * + * @return true + * @return false */ - bool isOpen() const { return this->m_file->is_open() && !this->m_file->eof(); } + inline auto isOpen() const -> bool { return this->m_file->is_open() && !this->m_file->eof(); } /** - * To get the number of particles from this loader + * @brief To get the number of particles from this loader + * + * @return std::size_t */ - std::size_t getNumberOfParticles() const { return this->getParticleCount(); } + inline auto getNumberOfParticles() const -> std::size_t { return this->getParticleCount(); } /** - * The center of the box from the simulation file opened by the loader - * @return box center + * @brief To get the center of the box from the simulation file opened by the loader. + * */ - auto /*container::point<FReal, Dimension> */ getCenterOfBox() const -> container::point<FReal, Dimension> - { - return this->getBoxCenter(); - } /** - * The center of the box from the simulation file opened by the loader - * @return box center - */ - /// - /// \brief getPointerCenterOfBox return a pointer on the element of the Box center - /// - auto getPointerCenterOfBox() const -> FReal* { return this->m_center.data(); } + inline auto getCenterOfBox() const -> container::point<FReal, Dimension> { return this->getBoxCenter(); } + + /** + * @brief Returns a pointer on the element of the Box center. + * + * @return FReal* + */ + inline auto getPointerCenterOfBox() const -> FReal* { return this->m_center.data(); } /** - * \brief Get the distribution particle count - * \return The distribution particle count + * @brief Get the distribution particle count + * + * @return The distribution particle count */ - std::size_t getParticleCount() const { return this->m_nbParticles; } + inline auto getParticleCount() const -> std::size_t { return this->m_nbParticles; } /** - * \brief Get the center of the box contining the particles + * @brief Get the center of the box contining the particles * - * \return A point (ontainer::point<FReal>) representing the box center + * @return A point (ontainer::point<FReal>) representing the box center */ - auto getBoxCenter() const { return this->m_centerOfBox; } + inline auto getBoxCenter() const { return this->m_centerOfBox; } /** - * The box width from the simulation file opened by the loader + * @brief box width from the simulation file opened by the loader + * * @return box width */ - FReal getBoxWidth() const { return this->m_boxWidth; } + inline auto getBoxWidth() const -> FReal { return this->m_boxWidth; } + /** - * The box width from the simulation file opened by the loader + * @brief The box width from the simulation file opened by the loader + * * @return the number of data per record (Particle) */ - unsigned int getNbRecordPerline() { return m_typeData[1]; } + inline auto getNbRecordPerline() -> unsigned int { return m_typeData[1]; } + /** - * The box width from the simulation file opened by the loader + * @brief The box width from the simulation file opened by the loader + * * @return the Dimension space */ - unsigned int get_dimension() { return m_typeData[2]; } + inline auto get_dimension() -> unsigned int { return m_typeData[2]; } + /** - * The box width from the simulation file opened by the loader + * @brief The box width from the simulation file opened by the loader + * * @return the number of input data per record (Particle) */ - unsigned int get_number_of_input_per_record() { return m_typeData[3]; } + inline auto get_number_of_input_per_record() -> unsigned int { return m_typeData[3]; } + /** - * The box width from the simulation file opened by the loader + * @brief The box width from the simulation file opened by the loader + * * @return the number of ioutput data per record (Particle) */ - unsigned int get_number_of_output_per_record() { return m_typeData[1] - m_typeData[2] - m_typeData[3]; } + inline auto get_number_of_output_per_record() -> unsigned int + { + return m_typeData[1] - m_typeData[2] - m_typeData[3]; + } + /** - * To know if the data are in float or in double type + * @brief To know if the data are in float or in double type + * * @return the type of the values float (4) or double (8) */ - unsigned int getDataType() { return m_typeData[0]; } + inline auto getDataType() -> unsigned int { return m_typeData[0]; } + /** + * @brief Get the header size object + * + * @return auto + */ auto get_header_size() { return m_typeData.size() * sizeof(unsigned int) + sizeof(std::size_t) + sizeof(m_boxWidth) + @@ -255,14 +347,13 @@ namespace scalfmm::io } /** - * \brief Fill a particle set from the current position in the file. + * @brief Fill a particle set from the current position in the file. * * @param dataToRead array of type FReal. It contains all the values of a * particles (for instance X,Y,Z,Q, ..) - * * @param nbDataToRead number of value to read (I.e. size of the array) */ - void fillParticle(FReal* dataToRead, const unsigned int nbDataToRead) + auto fillParticle(FReal* dataToRead, const unsigned int nbDataToRead) -> void { if(m_binaryFile) { @@ -293,19 +384,17 @@ namespace scalfmm::io } /** - * Fill a set of particles form the current position in the file. + * @brief Fills a set of particles form the current position in the file. * * If the file is a binary file and we read all record per particle then we * read and fill the array in one instruction. * - * @tparam dataPart the particle class. It must implement the members - * getPtrFirstData(), getReadDataNumber(). See FmaRWParticle. - * + * @tparam VectorType * @param dataToRead the array of particles to fill. * @param N the number of particles. */ - template<class vector_type> - void fillParticles(vector_type& dataToRead, const std::size_t N) + template<class VectorType> + auto fillParticles(VectorType& dataToRead, const std::size_t N) -> void { if(dataToRead.size() != m_typeData[1] * N) { @@ -333,7 +422,12 @@ namespace scalfmm::io } private: - void readHeader(const bool old_format = true) + /** + * @brief + * + * @param old_format + */ + auto readHeader(const bool old_format = true) -> void { int nbval = 4; if(old_format) @@ -371,7 +465,13 @@ namespace scalfmm::io std::cout << this->m_centerOfBox[m_typeData[2] - 1] << " ]" << std::endl; } } - void readAscciHeader(const int nbVal) + + /** + * @brief + * + * @param nbVal + */ + auto readAscciHeader(const int nbVal) -> void { if(m_verbose) { @@ -403,8 +503,14 @@ namespace scalfmm::io this->m_boxWidth *= 2; m_otherDataToRead = m_typeData[1] - m_typeData[2] - m_typeData[3]; // output variables - }; - void readBinaryHeader(const int nbVal) + } + + /** + * @brief + * + * @param nbVal + */ + auto readBinaryHeader(const int nbVal) -> void { if(m_verbose) { @@ -454,13 +560,13 @@ namespace scalfmm::io }; /** - * \warning This class only works in shared memory (doesn't work with MPI). + * @warning This class only works in shared memory (doesn't work with MPI). * - * \brief Writes a set of particles to an FMA formated file. + * @brief Writes a set of particles to an FMA formated file. * * The file may be in ASCII or binary mode. The example below shows how to use the class. * - * \code + * @code * // Instantiate the writer with a binary fma file (extension .bfma). * FFmaGenericWriter<FReal> writer ("data.bfma"); * @@ -469,15 +575,15 @@ namespace scalfmm::io * * // Write the data. Here particles is an array and a particle has nbData values. * writer.writeArrayOfReal(particles, nbData, NbPoints); - * \endcode + * @endcode * ---------------------------------------- * FMA is a simple format to store particles in a file. It is organized as follow. * - * \code + * @code * DatatypeSize Number_of_record_per_line dimension Number_of_input_data * NB_particles half_Box_width Center (dim values) * Particle_values - * \endcode + * @endcode * * `DatatypeSize` can have one of two values: * - 4, float; @@ -487,16 +593,29 @@ namespace scalfmm::io * the `Particle_values`. For example : * - 4, the particle values are `X Y Z Q`; * - 8, the particle values are `X Y Z Q P FX FY FZ`. + * + * @tparam FReal */ template<class FReal> class FFmaGenericWriter { protected: - std::fstream* m_file; ///< the stream used to write the file - bool m_binaryFile; ///< if true the file is in binary mode + /** + * @brief the stream used to write the file. + * + */ + std::fstream* m_file; + + /** + * @brief if true the file is in binary mode. + * + */ + bool m_binaryFile; public: /** + * @brief Construct a new FFmaGenericWriter object + * * This constructor opens a file to be written to. * * - The opening mode is guessed from the file extension : `.fma` will open @@ -537,6 +656,8 @@ namespace scalfmm::io } /** + * @brief Construct a new FFmaGenericWriter object + * * This constructor opens a file to be written to. * * @param filename the name of the file to open. @@ -563,13 +684,21 @@ namespace scalfmm::io } std::cout << "FFmaGenericWriter file " << filename << " opened" << std::endl; } - void close() + + /** + * @brief + * + */ + inline auto close() -> void { m_file->close(); delete m_file; m_file = nullptr; } + /** + * @brief Destroy the FFmaGenericWriter object. + * * Default destructor, closes the file. */ virtual ~FFmaGenericWriter() @@ -581,32 +710,39 @@ namespace scalfmm::io } /** - * To know if file is open and ready to read - * @return true if loader can work + * @brief To know if file is open and ready to read. + * + * @return true + * @return false */ - bool isOpen() const { return this->m_file->is_open() && !this->m_file->eof(); } + inline auto isOpen() const -> bool { return this->m_file->is_open() && !this->m_file->eof(); } /** - * To know if opened file is in binary mode - * @return true if opened file is in binary mode + * @brief To know if opened file is in binary mode. + * + * @return true + * @return false */ - bool isBinary() const { return this->m_binaryFile; } + inline auto isBinary() const -> bool { return this->m_binaryFile; } /** - * Writes the header of FMA file. + * @brief Writes the header of FMA file. * * Should be used if we write the particles with writeArrayOfReal method * + * @tparam PointType * @param centerOfBox The center of the Box (FPoint<FReal> class) * @param boxWidth The width of the box * @param nbParticles Number of particles in the box (or to save) * @param dataType Size of the data type of the values in particle * @param nbDataPerRecord Number of record/value per particle + * @param dimension + * @param nb_input_values */ - template<class POINT_T> - void writeHeader(const POINT_T& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, + template<class PointType> + auto writeHeader(const PointType& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, const unsigned int dataType, const unsigned int nbDataPerRecord, const unsigned int dimension, - const unsigned int nb_input_values) + const unsigned int nb_input_values) -> void { std::array<unsigned int, 4> typeFReal = {dataType, nbDataPerRecord, dimension, nb_input_values}; FReal x = boxWidth * FReal(0.5); @@ -633,10 +769,23 @@ namespace scalfmm::io } std::cout << std::endl; } - template<class POINT_T> - void writeHeaderOld(const POINT_T& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, + + /** + * @brief + * + * @tparam PointType + * @param centerOfBox + * @param boxWidth + * @param nbParticles + * @param dataType + * @param nbDataPerRecord + * @param dimension + * @param nb_input_values + */ + template<class PointType> + auto writeHeaderOld(const PointType& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, const unsigned int dataType, const unsigned int nbDataPerRecord, - const unsigned int dimension, const unsigned int nb_input_values) + const unsigned int dimension, const unsigned int nb_input_values) -> void { std::array<unsigned int, 4> typeFReal = {dataType, nbDataPerRecord, dimension, nb_input_values}; FReal x = boxWidth * FReal(0.5); @@ -660,16 +809,16 @@ namespace scalfmm::io * The size of the array is N*nbData * * example - * \code + * @code * FmaRParticle * const particles = new FmaRParticle[nbParticles]; * memset(particles, 0, sizeof(FmaRParticle) * nbParticles) ; * ... * FFmaGenericWriter<FReal> writer(filenameOut) ; * Fwriter.writeHeader(Centre,BoxWith, nbParticles,*particles) ; * Fwriter.writeArrayOfReal(particles, nbParticles); - * \endcode + * @endcode */ - void writeArrayOfReal(const FReal* dataToWrite, const int nbData, const std::size_t N) + auto writeArrayOfReal(const FReal* dataToWrite, const int nbData, const std::size_t N) -> void { if(m_binaryFile) { @@ -696,32 +845,34 @@ namespace scalfmm::io } /** - * Write all particles (position, input values, output values) inside the octree in fma format into a file. - * - * @param[in] tree Octree that contains the particles in the leaves - * @param[in] number_particles number of particles + * @brief Write all particles (position, input values, output values) + * inside the octree in fma format into a file. * * example - * \code + * @code * group_tree_type tree(TreeHeight, SubTreeHeight, BoxWidth, CenterOfBox); * ... * FFmaGenericWriter<FReal> writer(filenameOut) ; * Fwriter.writeDataFromOctree(&tree, nbParticles); - * \endcode + * @endcode + * + * @tparam TreeType + * @param[in] tree Octree that contains the particles in the leaves + * @param[in] number_particles number of particles */ - template<class TREE_T> - void writeDataFromTree(const TREE_T& tree, const std::size_t& number_particles) + template<class TreeType> + auto writeDataFromTree(const TreeType& tree, const std::size_t& number_particles) -> void { - constexpr int dimension = TREE_T::leaf_type::dimension; - constexpr int nb_input_elements = TREE_T::leaf_type::particle_type::inputs_size; - constexpr int nb_output_elements = TREE_T::leaf_type::particle_type::outputs_size; + constexpr int dimension = TreeType::leaf_type::dimension; + constexpr int nb_input_elements = TreeType::leaf_type::particle_type::inputs_size; + constexpr int nb_output_elements = TreeType::leaf_type::particle_type::outputs_size; constexpr int nb_elt_per_par = dimension + nb_input_elements + nb_output_elements; std::cout << "Dimension: " << dimension << std::endl; std::cout << "Number of input values: " << nb_input_elements << std::endl; std::cout << "Number of output values: " << nb_output_elements << std::endl; std::cout << "nb_elt_per_par " << nb_elt_per_par << std::endl; // - using value_type = typename TREE_T::leaf_type::value_type; + using value_type = typename TreeType::leaf_type::value_type; using particles_t = std::array<value_type, nb_elt_per_par>; std::vector<particles_t> particles(number_particles); // @@ -732,7 +883,7 @@ namespace scalfmm::io for(auto const& it_p: leaf) { auto& particles_elem = particles[pos++]; - const auto& p = typename TREE_T::leaf_type::particle_type(it_p); + const auto& p = typename TreeType::leaf_type::particle_type(it_p); // int i = 0; const auto points = p.position(); @@ -759,16 +910,28 @@ namespace scalfmm::io centre.dimension, nb_input_elements); this->writeArrayOfReal(particles.data()->data(), nb_elt_per_par, number_particles); } - /// - /// writeDataFrom write all data from the container in fma format - /// - /// How to get automatically double from container - template<class CONTAINER_T, class POINT_T, typename VALUE_T> - void writeDataFrom(CONTAINER_T& values, const int& number_particles, const POINT_T& center, - const VALUE_T box_width) + + /** + * @brief + * + * writeDataFrom write all data from the container in fma format + * + * How to get automatically double from container + * + * @tparam ContainerType + * @tparam PointType + * @tparam ValueType + * @param values + * @param number_particles + * @param center + * @param box_width + */ + template<class ContainerType, class PointType, typename ValueType> + auto writeDataFrom(ContainerType& values, const int& number_particles, const PointType& center, + const ValueType box_width) -> void { // get the number of elements per particles in the container build with tuples. - using particle_type = typename CONTAINER_T::value_type; + using particle_type = typename ContainerType::value_type; constexpr int nb_elt_per_par = meta::tuple_size_v<particle_type>; // Not good output_values are put in input_values constexpr int nb_input_per_par = particle_type::inputs_size; @@ -797,9 +960,19 @@ namespace scalfmm::io } protected: - template<class POINT_T> - void writerAscciHeader(const POINT_T& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, - const unsigned int* typeFReal, const unsigned int nbVal) + /** + * @brief + * + * @tparam PointType + * @param centerOfBox + * @param boxWidth + * @param nbParticles + * @param typeFReal + * @param nbVal + */ + template<class PointType> + auto writerAscciHeader(const PointType& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, + const unsigned int* typeFReal, const unsigned int nbVal) -> void { this->m_file->precision(std::numeric_limits<FReal>::digits10); // Line 1 @@ -819,9 +992,20 @@ namespace scalfmm::io (*this->m_file) << '\n'; } - template<class POINT_T> - void writerBinaryHeader(const POINT_T& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, - const unsigned int* typeFReal, const unsigned int nbVal) + + /** + * @brief + * + * @tparam PointType + * @param centerOfBox + * @param boxWidth + * @param nbParticles + * @param typeFReal + * @param nbVal + */ + template<class PointType> + auto writerBinaryHeader(const PointType& centerOfBox, const FReal& boxWidth, const std::size_t& nbParticles, + const unsigned int* typeFReal, const unsigned int nbVal) -> void { m_file->seekg(std::ios::beg); m_file->write((const char*)typeFReal, nbVal * sizeof(unsigned int)); diff --git a/include/scalfmm/tools/laplace_tools.hpp b/include/scalfmm/tools/laplace_tools.hpp index cf3debc75c015da6f70a24a270d50f68dc9e762e..51c13997f050768d97f82968ba7f6c07bde6cade 100644 --- a/include/scalfmm/tools/laplace_tools.hpp +++ b/include/scalfmm/tools/laplace_tools.hpp @@ -6,9 +6,9 @@ #define SCALFMM_EXAMPLES_POST_PROCESSING_LAPLACE_HPP #include <tuple> +#include "scalfmm/matrix_kernels/laplace.hpp" +#include "scalfmm/tools/fma_loader.hpp" #include "scalfmm/tree/group_tree_view.hpp" -#include <scalfmm/matrix_kernels/laplace.hpp> -#include <scalfmm/tools/fma_loader.hpp> #include <cpp_tools/cl_parser/cl_parser.hpp> #include <cpp_tools/colors/colorized.hpp> @@ -17,14 +17,23 @@ namespace laplace { namespace args { + /** + * @brief + * + */ struct matrix_kernel { cpp_tools::cl_parser::str_vec flags = {"--kernel", "--k"}; - const char* description = "Matrix kernels: \n 0) 1/r, 1) grad(1/r), 2) p & grad(1/r) 3) shift(1/r)-> grad 4) shift(1/r)-> p & grad. "; + const char* description = "Matrix kernels: \n 0) 1/r, 1) grad(1/r), 2) p & grad(1/r) 3) shift(1/r)-> " + "grad 4) shift(1/r)-> p & grad. "; using type = int; type def = 0; }; + /** + * @brief + * + */ struct post_traitement { /// Unused type, mandatory per interface specification @@ -39,22 +48,34 @@ namespace laplace }; } // namespace args - template<int dimension, typename CONTAINER_T, typename POINT_T, typename VALUE_T> - void read_data(const std::string& filename, CONTAINER_T*& container, POINT_T& Centre, VALUE_T& width) + /** + * @brief + * + * @tparam Dimension + * @tparam ContainerType + * @tparam PointType + * @tparam ValueType + * @param filename + * @param container + * @param Centre + * @param width + */ + template<std::size_t Dimension, typename ContainerType, typename PointType, typename ValueType> + auto read_data(const std::string& filename, ContainerType*& container, PointType& Centre, ValueType& width) -> void { // std::cout << "READ DATA " << std::endl << std::flush; - using particle_type = typename CONTAINER_T::particle_type; + using particle_type = typename ContainerType::particle_type; bool verbose = true; - scalfmm::io::FFmaGenericLoader<VALUE_T, dimension> loader(filename, verbose); + scalfmm::io::FFmaGenericLoader<ValueType, Dimension> loader(filename, verbose); const auto number_of_particles = loader.getNumberOfParticles(); width = loader.getBoxWidth(); Centre = loader.getBoxCenter(); auto nb_val_to_red_per_part = loader.get_dimension() + loader.get_number_of_input_per_record(); - VALUE_T* values_to_read = new VALUE_T[nb_val_to_red_per_part]{}; - container = new CONTAINER_T(number_of_particles); + ValueType* values_to_read = new ValueType[nb_val_to_red_per_part]{}; + container = new ContainerType(number_of_particles); std::cout << "number_of_particles " << number_of_particles << std::endl; for(std::size_t idx = 0; idx < number_of_particles; ++idx) { @@ -78,18 +99,27 @@ namespace laplace loader.close(); } - template<class MATRIX_KERNEL_T, class LEAF_T> - auto compute_energy_tuple(MATRIX_KERNEL_T const& mat, LEAF_T const& leaf) + /** + * @brief + * + * @tparam MatrixKernelType + * @tparam LeafType + * @param mat + * @param leaf + * @return auto + */ + template<class MatrixKernelType, class LeafType> + auto compute_energy_tuple(MatrixKernelType const& mat, LeafType const& leaf) { - using outputs_type = typename LEAF_T::particle_type::outputs_type; - using inputs_type = typename LEAF_T::particle_type::inputs_type; + using outputs_type = typename LeafType::particle_type::outputs_type; + using inputs_type = typename LeafType::particle_type::inputs_type; // outputs_type energy{}; for(auto& e: energy) { e = 0.; }; - // std::array<value_type, MATRIX_KERNEL_T::km> + // std::array<value_type, MatrixKernelType::km> inputs_type total_physical_value{}; for(auto& e: total_physical_value) { @@ -97,7 +127,7 @@ namespace laplace }; for(auto const p_tuple_ref: leaf) { - const auto p = typename LEAF_T::const_proxy_type(p_tuple_ref); + const auto p = typename LeafType::const_proxy_type(p_tuple_ref); // } // for(std::size_t i = 0; i < leaf.size(); ++i) // { @@ -107,12 +137,12 @@ namespace laplace // get forces auto out = p.outputs(); // update - for(std::size_t j = 0; j < MATRIX_KERNEL_T::kn; ++j) + for(std::size_t j = 0; j < MatrixKernelType::kn; ++j) { energy[j] += q[j] * out[j]; std::cout << out[j] << '\n'; } - for(std::size_t j = 0; j < MATRIX_KERNEL_T::km; ++j) + for(std::size_t j = 0; j < MatrixKernelType::km; ++j) { total_physical_value[j] += q[j]; } @@ -120,16 +150,16 @@ namespace laplace return std::make_tuple(energy, total_physical_value); } - /// - /// - /// \brief multiply_force_by_q Compute the force for the simulation F = q \nabla mk - /// - /// \param begin_force the first position of the force in the outputs - /// \param end_force the las position of the force in the outputs - /// \param container the container of particles - /// - template<class CONTAINER_T> - void multiply_force_by_q(const int begin_force, const int end_force, CONTAINER_T& container) + /** + * @brief multiply_force_by_q Compute the force for the simulation F = q \nabla mk + * + * @tparam ContainerType + * @param begin_force the first position of the force in the outputs + * @param end_force the las position of the force in the outputs + * @param container the container of particles + */ + template<class ContainerType> + auto multiply_force_by_q(const int begin_force, const int end_force, ContainerType& container) -> void { // const std::size_t nb_part = container.size(); const auto nb_part = std::distance(std::begin(container), std::end(container)); @@ -146,81 +176,103 @@ namespace laplace } } } - /// - /// \brief post_traitement specialization function for one_over_r - /// - /// Here we compute and print the energy - /// \param mat The kernel matrix - /// \param container The contaier of particles - /// - template<class CONTAINER_T> - void post_traitement(scalfmm::matrix_kernels::laplace::like_mrhs&, CONTAINER_T&) + + /** + * @brief post_traitement specialization function for one_over_r. + * + * @tparam ContainerType + * @param mat the matrix kernel. + * @param container the container of particles. + */ + template<class ContainerType> + auto post_traitement(scalfmm::matrix_kernels::laplace::like_mrhs& mat, ContainerType& container) -> void { std::cout << "From post_traitement like_mrhs " << std::endl; //compute_energy(mat, container); } - /// - /// \brief post_traitement specialization function for val_tgrad_one_over_r - /// - /// Here we compute the forces ont the particles Q \nabla 1/r - /// given by inputs[0] * outputs[1-3]. - /// \param mat The kernel matrix - /// \param container The contaier of particles - /// - template<class CONTAINER_T, std::size_t dim> - void post_traitement(scalfmm::matrix_kernels::laplace::val_grad_one_over_r<dim>&, CONTAINER_T& container) + + /** + * @brief post_traitement specialization function for val_tgrad_one_over_r + * + * Here we compute the forces ont the particles Q \nabla 1/r + * given by inputs[0] * outputs[1-3]. + * + * @tparam ContainerType + * @tparam Dimension + * @param mat The kernel matrix + * @param container The contaier of particles + */ + template<class ContainerType, std::size_t Dimension> + auto post_traitement(scalfmm::matrix_kernels::laplace::val_grad_one_over_r<Dimension>& mat, + ContainerType& container) -> void { std::cout << "From post_traitement val_tgrad_one_over_r" << std::endl; multiply_force_by_q(1, 4, container); - // compute_energy(mat, container); + // compute_energy(mat, container); } - /// - /// \brief post_traitement specialization function for grad_one_over_r - /// - /// Here we compute the forces ont the particles Q \nabla 1/r - /// given by inputs[0] * outputs[0-2]. - /// \param mat The kernel matrix - /// \param container The container of particles - /// - template<class MATRIX_KERNEL_T, class CONTAINER_T, std::size_t dim> - void post_traitement(scalfmm::matrix_kernels::laplace::grad_one_over_r<dim>& mat, CONTAINER_T& container) + + /** + * @brief post_traitement specialization function for grad_one_over_r + * + * Here we compute the forces ont the particles Q \nabla 1/r + * given by inputs[0] * outputs[0-2]. + * + * @tparam MatrixKernelType + * @tparam ContainerType + * @tparam Dimension + * @param mat The kernel matrix + * @param container The container of particles + */ + template<class MatrixKernelType, class ContainerType, std::size_t Dimension> + auto post_traitement(scalfmm::matrix_kernels::laplace::grad_one_over_r<Dimension>& mat, + ContainerType& container) -> void { std::cout << "From post_traitement grad_one_over_r " << std::endl; multiply_force_by_q(0, 3, container); } - template<class CONTAINER_T> - /// - /// \brief post_traitement specialization function for one_over_r - /// - /// Here we compute and print the energy - /// \param mat The kernel matrix - /// \param container The contaier of particles - /// - void post_traitement(scalfmm::matrix_kernels::laplace::one_over_r&, CONTAINER_T&) + + /** + * @brief post_traitement specialization function for one_over_r + * + * Here we compute and print the energy + * + * @tparam ContainerType + * @param mat The kernel matrix + * @param container The contaier of particles + */ + template<class ContainerType> + auto post_traitement(scalfmm::matrix_kernels::laplace::one_over_r& mat, ContainerType& container) -> void { std::cout << "From post_traitement one_over_r " << std::endl; - // compute_energy(mat, container); + // compute_energy(mat, container); } - // - /// - /// \brief post_traitement The generic function. Nothing is done. - /// \param mat The kernel matrix - /// \param tree The tree of particles - /// - template<class MATRIX_KERNEL_T, class CELL_T, class LEAF_T, class BOX_T> - void post_traitement(MATRIX_KERNEL_T& mat, scalfmm::component::group_tree_view<CELL_T, LEAF_T, BOX_T>* tree) + /** + * @brief post_traitement The generic function. Nothing is done. + * + * @tparam MatrixKernelType + * @tparam CellType + * @tparam LeafType + * @tparam BoxType + * @param mat The kernel matrix + * @param tree The tree of particles + */ + template<class MatrixKernelType, class CellType, class LeafType, class BoxType> + void post_traitement(MatrixKernelType& mat, scalfmm::component::group_tree_view<CellType, LeafType, BoxType>* tree) { std::cout << "Generic nothing to do tree \n"; } - // - template<class MATRIX_KERNEL_T, class CONTAINER_T> - /// - /// \brief post_traitement The generic function. Nothing is done. - /// \param mat The kernel matrix - /// \param container The contaier of particles - /// - void post_traitement(MATRIX_KERNEL_T&, CONTAINER_T&) + + /** + * @brief post_traitement The generic function. Nothing is done. + * + * @tparam MatrixKernelType + * @tparam ContainerType + * @param mat The kernel matrix + * @param container The contaier of particles + */ + template<class MatrixKernelType, class ContainerType> + auto post_traitement(MatrixKernelType& mat, ContainerType& container) -> void { std::cout << "Generic nothing to do \n"; } diff --git a/include/scalfmm/tools/progress_bar.hpp b/include/scalfmm/tools/progress_bar.hpp index d251a3d3c388d34016430712c8bf586a5b344f9c..e7324ef15e6cd8f6ea49eb7a3caa98af383acfe0 100644 --- a/include/scalfmm/tools/progress_bar.hpp +++ b/include/scalfmm/tools/progress_bar.hpp @@ -1,47 +1,84 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/tools/progress_bar.hpp +// -------------------------------- #ifndef SCALFMM_TOOLS_PROGRESS_BAR_HPP #define SCALFMM_TOOLS_PROGRESS_BAR_HPP +#include "scalfmm/tools/colorized.hpp" + #include <iostream> #include <memory> -#include <scalfmm/tools/colorized.hpp> #include <sstream> #include <thread> namespace scalfmm::tools { + /** + * @brief + * + * @tparam Object + * @param obj + * @return int + */ template<typename Object> int progress(Object& obj); + /** + * @brief + * + */ struct progress_bar { + /** + * @brief + * + */ std::stringstream sstr{}; + + /** + * @brief + * + */ std::thread t; + /** + * @brief + * + * @tparam Object + * @param obj + */ template<typename Object> void follow(Object& obj) { - this->t = std::thread([this, &obj]() { - bool run = true; - while(run) - { - sstr.str(""); - sstr.clear(); - sstr.precision(4); - int p = progress(obj); - std::cout << p << std::endl; - sstr << "["; - for(int i = 0; i < 100; ++i) - { - sstr << (i < p ? "\u2038" : " "); - } - sstr << "] " << p << "% "; - std::cout << '\r' << sstr.str() << std::flush; - run = p < 100; - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - } - }); + this->t = std::thread( + [this, &obj]() + { + bool run = true; + while(run) + { + sstr.str(""); + sstr.clear(); + sstr.precision(4); + int p = progress(obj); + std::cout << p << std::endl; + sstr << "["; + for(int i = 0; i < 100; ++i) + { + sstr << (i < p ? "\u2038" : " "); + } + sstr << "] " << p << "% "; + std::cout << '\r' << sstr.str() << std::flush; + run = p < 100; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + }); } + /** + * @brief + * + */ void finish() { this->t.join(); } }; } // namespace scalfmm::tools diff --git a/include/scalfmm/tools/tikz_writer.hpp b/include/scalfmm/tools/tikz_writer.hpp index 12df81335dde60e732a41937486f70b487243d31..94fc2db3f69d8246b55b850425bc181c86d9b3ed 100644 --- a/include/scalfmm/tools/tikz_writer.hpp +++ b/include/scalfmm/tools/tikz_writer.hpp @@ -1,136 +1,143 @@ // -------------------------------- // See LICENCE file at project root -// File : tools/vtk_writer.hpp +// File : scalfmm/tools/tikz_writer.hpp // -------------------------------- #ifndef SCALFMM_TOOLS_TIKZ_WRITER_HPP #define SCALFMM_TOOLS_TIKZ_WRITER_HPP -#include <fstream> -#include <iostream> -#include <string> - -#include "scalfmm/tree/box.hpp" -#include "scalfmm/tree/for_each.hpp" #include "scalfmm/container/access.hpp" #include "scalfmm/meta/utils.hpp" +#include "scalfmm/tree/box.hpp" +#include "scalfmm/tree/for_each.hpp" -namespace scalfmm::tools::io { -template <class TREE> -/// -/// \brief export in TIKZ the leaves and their morton index -/// -/// Generate a tikz pictures od the non empty leaves and the -/// grid full parent grid -/// -/// \param filename name to store the tikz command -/// \param tree to draw the leaf level -/// -void exportTIKZ(const std::string& filename, const TREE& tree, bool const displayParticles, std::string const & color, - const bool plot_parent = false) { - std::cout << "Write tikz in " << filename << std::endl; - std::ofstream out(filename); +#include <fstream> +#include <iostream> +#include <string> - auto width = tree.box_width() * 0.5; - auto scale = 0.5 / tree.leaf_width(0); - width *= scale; - auto center = scale * tree.box_center(); - auto corner_l = center - width; - auto corner_u = center + width; - auto shift(corner_l) ; - std::cout << "center " << center << std::endl; - std::cout << "corner_l " << corner_l << " corner_u " << corner_u << std::endl; +namespace scalfmm::tools::io +{ + /** + * @brief export in TIKZ the leaves and their morton index. + * + * Generate a tikz pictures od the non empty leaves and the + * grid full parent grid. + * + * @tparam TreeType + * @param filename name to store the tikz command. + * @param tree to draw the leaf level. + * @param displayParticles + * @param color + * @param plot_parent + */ + template<class TreeType> + auto exportTIKZ(const std::string& filename, const TreeType& tree, bool const displayParticles, + std::string const& color, const bool plot_parent = false) -> void + { + std::cout << "Write tikz in " << filename << std::endl; + std::ofstream out(filename); - shift *= -1.0; - std::cout << "shift " << shift << " leaf size " << scale * tree.leaf_width() << " scale " << scale << std::endl; - out << "\\begin{tikzpicture}[help lines/.style={blue!50,very thin}] " << std::endl; - auto half_width = scale * tree.leaf_width() / 2.0; - // std::cout << "shift " << shift << "leaf size " << scale * tree.leaf_width() << " half_width " << half_width - // << std::endl; + auto width = tree.box_width() * 0.5; + auto scale = 0.5 / tree.leaf_width(0); + width *= scale; + auto center = scale * tree.box_center(); + auto corner_l = center - width; + auto corner_u = center + width; + auto shift(corner_l); + std::cout << "center " << center << std::endl; + std::cout << "corner_l " << corner_l << " corner_u " << corner_u << std::endl; - component::for_each(std::get<0>(tree.begin()), std::get<0>(tree.end()), - [&scale, &half_width, &out, &shift, &color, &displayParticles](auto& group) - { - // std::size_t index_in_group{0}; - component::for_each( - std::begin(*group), std::end(*group), - [&scale, &half_width, &out, &shift, &color, &displayParticles](auto& leaf) - { - auto center = scale * leaf.center(); - auto corner_l = center - half_width; - auto corner_u = center + half_width; - out << "\\filldraw[fill=black!30!white] (" << shift[0] + corner_l[0] << "," - << shift[1] + corner_l[1] << ") rectangle (" << shift[0] + corner_u[0] << "," - << shift[1] + corner_u[1] << " );" << std::endl; + shift *= -1.0; + std::cout << "shift " << shift << " leaf size " << scale * tree.leaf_width() << " scale " << scale << std::endl; + out << "\\begin{tikzpicture}[help lines/.style={blue!50,very thin}] " << std::endl; + auto half_width = scale * tree.leaf_width() / 2.0; + // std::cout << "shift " << shift << "leaf size " << scale * tree.leaf_width() << " half_width " << half_width + // << std::endl; - out << "\\node[scale=0.8,color=" << color << "] at (" << shift[0] + center[0] << "," - << shift[1] + center[1] << ") {\\textbf{" << leaf.index() << "}};" << std::endl; - // - // Plot particles - if(displayParticles) - { - std::cout << " leaf " << leaf.index() << " nb part: " << leaf.size() << " " - << std::endl; - // #ifdef USE_VIEW - using proxy_type = typename TREE::leaf_type::particle_type::proxy_type; - for(auto const& p: leaf) - { - auto pos = scale * proxy_type(p).position(); - out << "\\node[scale=0.4,color=" << color << "] at (" << shift[0] + pos[0] - << "," << shift[1] + pos[1] << ") {x};" << std::endl; - } - // #else - // const auto container = leaf.cparticles() ; + component::for_each( + std::get<0>(tree.begin()), std::get<0>(tree.end()), + [&scale, &half_width, &out, &shift, &color, &displayParticles](auto& group) + { + // std::size_t index_in_group{0}; + component::for_each( + std::begin(*group), std::end(*group), + [&scale, &half_width, &out, &shift, &color, &displayParticles](auto& leaf) + { + auto center = scale * leaf.center(); + auto corner_l = center - half_width; + auto corner_u = center + half_width; + out << "\\filldraw[fill=black!30!white] (" << shift[0] + corner_l[0] << "," + << shift[1] + corner_l[1] << ") rectangle (" << shift[0] + corner_u[0] << "," + << shift[1] + corner_u[1] << " );" << std::endl; - // for (int i{0}; i < leaf.size() ; ++i) - // { - // auto pos = - // container.particle(i).position(); out << - // "\\node[scale=0.4,color=" << color << "] - // at (" << shift[0] + pos[0] - // << "," << shift[1] + pos[1] << ") - // {x};" << std::endl; - // } - // #endif - } - }); - }); - out << "\\draw[ thick, black, step=" << scale * tree.leaf_width() << "] (" << shift[0] + corner_l[0] << "," - << shift[1] + corner_l[1] << ") grid (" << shift[0] + corner_u[0] << "," << shift[1] + corner_u[1] << " );" - << std::endl; - if (plot_parent) { - /// - auto cell_level_it = std::get<1>(tree.begin()) + tree.leaf_level() - 1; - auto group_of_cell_begin = std::begin(*cell_level_it); - auto group_of_cell_end = std::end(*cell_level_it); - half_width *= 2; - component::for_each(group_of_cell_begin, group_of_cell_end, - [&out, &shift, &scale, &half_width](auto& group) + out << "\\node[scale=0.8,color=" << color << "] at (" << shift[0] + center[0] << "," + << shift[1] + center[1] << ") {\\textbf{" << leaf.index() << "}};" << std::endl; + // + // Plot particles + if(displayParticles) + { + std::cout << " leaf " << leaf.index() << " nb part: " << leaf.size() << " " << std::endl; + // #ifdef USE_VIEW + using proxy_type = typename TreeType::leaf_type::particle_type::proxy_type; + for(auto const& p: leaf) { - component::for_each(std::begin(*group), std::end(*group), - [&out, &shift, &scale, &half_width](auto& cell) - { - auto center = scale * cell.center(); - auto corner_l = center - half_width; - auto corner_u = center + half_width; - out << "\\draw[thick, blue] (" << shift[0] + corner_l[0] << "," - << shift[1] + corner_l[1] << ") rectangle (" - << shift[0] + corner_u[0] << "," << shift[1] + corner_u[1] - << " );" << std::endl; + auto pos = scale * proxy_type(p).position(); + out << "\\node[scale=0.4,color=" << color << "] at (" << shift[0] + pos[0] << "," + << shift[1] + pos[1] << ") {x};" << std::endl; + } + // #else + // const auto container = leaf.cparticles() ; - out << "\\node[scale=1.1, blue] at (" << shift[0] + center[0] << "," - << shift[1] + center[1] << ") {\\textbf{" << cell.index() - << "}};" << std::endl; - }); - }); - } - std::cout << "corner_l " << corner_l << " corner_u " << corner_u << std::endl; - std::cout << "C1 " << shift + corner_l << " C2 " << shift + corner_u << std::endl; + // for (int i{0}; i < leaf.size() ; ++i) + // { + // auto pos = + // container.particle(i).position(); out << + // "\\node[scale=0.4,color=" << color << "] + // at (" << shift[0] + pos[0] + // << "," << shift[1] + pos[1] << ") + // {x};" << std::endl; + // } + // #endif + } + }); + }); + out << "\\draw[ thick, black, step=" << scale * tree.leaf_width() << "] (" << shift[0] + corner_l[0] << "," + << shift[1] + corner_l[1] << ") grid (" << shift[0] + corner_u[0] << "," << shift[1] + corner_u[1] + << " );" << std::endl; + if(plot_parent) + { + /// + auto cell_level_it = std::get<1>(tree.begin()) + tree.leaf_level() - 1; + auto group_of_cell_begin = std::begin(*cell_level_it); + auto group_of_cell_end = std::end(*cell_level_it); + half_width *= 2; + component::for_each(group_of_cell_begin, group_of_cell_end, + [&out, &shift, &scale, &half_width](auto& group) + { + component::for_each(std::begin(*group), std::end(*group), + [&out, &shift, &scale, &half_width](auto& cell) + { + auto center = scale * cell.center(); + auto corner_l = center - half_width; + auto corner_u = center + half_width; + out << "\\draw[thick, blue] (" << shift[0] + corner_l[0] + << "," << shift[1] + corner_l[1] << ") rectangle (" + << shift[0] + corner_u[0] << "," + << shift[1] + corner_u[1] << " );" << std::endl; - out << "\\end{tikzpicture}" << std::endl; + out << "\\node[scale=1.1, blue] at (" + << shift[0] + center[0] << "," << shift[1] + center[1] + << ") {\\textbf{" << cell.index() << "}};" + << std::endl; + }); + }); + } + std::cout << "corner_l " << corner_l << " corner_u " << corner_u << std::endl; + std::cout << "C1 " << shift + corner_l << " C2 " << shift + corner_u << std::endl; - out.close(); -} + out << "\\end{tikzpicture}" << std::endl; + out.close(); + } -} +} // namespace scalfmm::tools::io #endif diff --git a/include/scalfmm/tools/tree_io.hpp b/include/scalfmm/tools/tree_io.hpp index a425de4c0ad2e2ea2d938372bd557ff988f8a80e..2fd7054b720e519a76b51e627e06d646200413c2 100644 --- a/include/scalfmm/tools/tree_io.hpp +++ b/include/scalfmm/tools/tree_io.hpp @@ -1,10 +1,9 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/tools/tree_io.hpp +// -------------------------------- #pragma once -#include <array> -#include <fstream> -#include <functional> -#include <string> - #include "scalfmm/tree/box.hpp" #include "scalfmm/tree/group_tree_view.hpp" #include "scalfmm/tree/utils.hpp" @@ -14,22 +13,30 @@ #include "scalfmm/tree/dist_group_tree.hpp" #include <cpp_tools/parallel_manager/parallel_manager.hpp> #endif + +#include <array> +#include <fstream> +#include <functional> +#include <string> + namespace scalfmm::tools::io { /** - * @brief write the header of the tree in binary file + * @brief write the header of the tree in binary file. * + * @tparam TreeType * @param out[inout] the writing stream * @param tree[in] the tree to write * @param header[in] the comments to write + * @return auto */ - template<typename Tree> - inline auto write_binary_header(std::fstream& out, Tree const& tree, std::string const& header) + template<typename TreeType> + inline auto write_binary_header(std::fstream& out, TreeType const& tree, std::string const& header) -> void { - static constexpr std::size_t dimension = Tree::dimension; - static constexpr std::size_t inputs_size = Tree::cell_type::storage_type::inputs_size; - static constexpr std::size_t outputs_size = Tree::cell_type::storage_type::outputs_size; - using value_type = typename Tree::position_value_type; + static constexpr std::size_t dimension = TreeType::dimension; + static constexpr std::size_t inputs_size = TreeType::cell_type::storage_type::inputs_size; + static constexpr std::size_t outputs_size = TreeType::cell_type::storage_type::outputs_size; + using value_type = typename TreeType::position_value_type; // std::cout << " inputs_size1 " << inputs_size << " outputs_size " << outputs_size << std::endl; std::int32_t l = header.size(); out.write(reinterpret_cast<char*>(&l), sizeof(std::int32_t)); @@ -52,13 +59,23 @@ namespace scalfmm::tools::io out.write(reinterpret_cast<char*>(&c1[0]), dimension * sizeof(value_type)); out.write(reinterpret_cast<char*>(&c2[0]), dimension * sizeof(value_type)); } - template<typename Tree> - inline auto write_txt_header(std::fstream& out, Tree const& tree, std::string const& header) + + /** + * @brief + * + * @tparam TreeType + * @param out + * @param tree + * @param header + * @return auto + */ + template<typename TreeType> + inline auto write_txt_header(std::fstream& out, TreeType const& tree, std::string const& header) -> void { - static constexpr std::size_t dimension = Tree::dimension; - static constexpr std::size_t inputs_size = Tree::cell_type::storage_type::inputs_size; - static constexpr std::size_t outputs_size = Tree::cell_type::storage_type::outputs_size; - using value_type = typename Tree::position_value_type; + static constexpr std::size_t dimension = TreeType::dimension; + static constexpr std::size_t inputs_size = TreeType::cell_type::storage_type::inputs_size; + static constexpr std::size_t outputs_size = TreeType::cell_type::storage_type::outputs_size; + using value_type = typename TreeType::position_value_type; // std::cout << " inputs_size1 " << inputs_size << " outputs_size " << outputs_size << std::endl; out << header << std::endl; out << "h: " << tree.height() << std::endl; @@ -69,21 +86,23 @@ namespace scalfmm::tools::io out << tree.box().c1() << std::endl; out << tree.box().c2() << std::endl; } + /** * @brief read the header of the tree and empty tree (without the hierarchy and the components) * + * @tparam TreeType * @param[in] input the reading stream - * @return Tree the empty tree + * @return TreeType */ - template<typename Tree> - inline auto read_binary_header(std::fstream& input) -> Tree + template<typename TreeType> + inline auto read_binary_header(std::fstream& input) -> TreeType { - static constexpr int dimension = Tree::dimension; - static constexpr std::size_t inputs_size = Tree::cell_type::storage_type::inputs_size; - static constexpr std::size_t outputs_size = Tree::cell_type::storage_type::outputs_size; - using value_type = typename Tree::position_value_type; - using position_type = typename Tree::position_type; - using box_type = typename Tree::box_type; + static constexpr int dimension = TreeType::dimension; + static constexpr std::size_t inputs_size = TreeType::cell_type::storage_type::inputs_size; + static constexpr std::size_t outputs_size = TreeType::cell_type::storage_type::outputs_size; + using value_type = typename TreeType::position_value_type; + using position_type = typename TreeType::position_type; + using box_type = typename TreeType::box_type; std::cout << "dimension : " << dimension << std::endl; std::int32_t l; input.read(reinterpret_cast<char*>(&l), sizeof(std::int32_t)); @@ -124,19 +143,24 @@ namespace scalfmm::tools::io } box_type box(c1, c2); std::cout << "box: " << box << std::endl; - return Tree(in[0], in[1], in[2], in[3], box); + return TreeType(in[0], in[1], in[2], in[3], box); } + /** - * @brief save a tree in binary format in a file (only the cells) + * @brief save a tree in binary format in a file (only the cells). * + * @tparam CellType + * @tparam LeafType + * @tparam BoxType * @param filename[in] name of the file * @param tree[in] tree to save * @param header[in] string that represents a comment (useful to know what tree we read) * @param options[in] (not use yet) */ - template<typename Cell, typename Leaf, typename Box> - inline auto save_bin(std::string const& filename, component::group_tree_view<Cell, Leaf, Box> const& tree, - std::string const& header, const int options = 0) -> void + template<typename CellType, typename LeafType, typename BoxType> + inline auto save_bin(std::string const& filename, + component::group_tree_view<CellType, LeafType, BoxType> const& tree, std::string const& header, + const int options = 0) -> void { std::cout << "save tree (binary mode) in filename " << filename << std::endl; std::fstream out(filename, std::ifstream::out | std::ios::binary); @@ -171,7 +195,7 @@ namespace scalfmm::tools::io } } // loop on the groups - using value_type = typename Cell::value_type; + using value_type = typename CellType::value_type; for(std::size_t level = tree.leaf_level(); level >= tree.top_level(); --level) { // std::cout << "level " << level << std::endl; @@ -204,17 +228,22 @@ namespace scalfmm::tools::io } out.close(); } + /** - * @brief save a tree in text format in a file (only the cells) + * @brief save a tree in text format in a file (only the cells). * + * @tparam CellType + * @tparam LeafType + * @tparam BoxType * @param filename[in] name of the file * @param tree[in] tree to save * @param header[in] string that represents a comment (useful to know what tree we read) * @param options[in] (not use yet) */ - template<typename Cell, typename Leaf, typename Box> - inline auto save_txt(std::string const& filename, component::group_tree_view<Cell, Leaf, Box> const& tree, - std::string const& header, const int options = 0) -> void + template<typename CellType, typename LeafType, typename BoxType> + inline auto save_txt(std::string const& filename, + component::group_tree_view<CellType, LeafType, BoxType> const& tree, std::string const& header, + const int options = 0) -> void { std::cout << "save tree (ascii mode) in filename " << filename << std::endl; @@ -252,7 +281,7 @@ namespace scalfmm::tools::io out << std::endl; } // loop on the groups - using value_type = typename Cell::value_type; + using value_type = typename CellType::value_type; for(std::size_t level = tree.leaf_level(); level >= tree.top_level(); --level) { // std::cout << "level " << level << std::endl; @@ -285,16 +314,20 @@ namespace scalfmm::tools::io } out.close(); } + /** - * @brief save a tree in either text or binary format in a file (only the cells) + * @brief save a tree in either text or binary format in a file (only the cells). * + * @tparam CellType + * @tparam LeafType + * @tparam BoxType * @param filename[in] name of the file with extention .bib or .txt * @param tree[in] tree to save * @param header[in] string that represents a comment (useful to know what tree we read) * @param options[in] (not use yet) */ - template<typename Cell, typename Leaf, typename Box> - inline auto save(std::string const& filename, component::group_tree_view<Cell, Leaf, Box> const& tree, + template<typename CellType, typename LeafType, typename BoxType> + inline auto save(std::string const& filename, component::group_tree_view<CellType, LeafType, BoxType> const& tree, std::string const& header, const int options = 0) -> void { if(filename.find(".bin") != std::string::npos) @@ -312,21 +345,23 @@ namespace scalfmm::tools::io std::exit(EXIT_FAILURE); } } + /** * @brief Read a tree (only the cells) and set a tree from a file * + * @tparam TreeType * @param[in] filename name of the file containing the ree * @param[in] options (unused) - * @return Tree return the tree + * @return TreeType */ - template<typename Tree> - inline auto read(std::string const& filename, const int options = 0) -> Tree + template<typename TreeType> + inline auto read(std::string const& filename, const int options = 0) -> TreeType { using morton_type = std::size_t; // int64_t; std::cout << " Read from file " << filename << std::endl; std::fstream input(filename, std::ifstream::in | std::ios::binary); - auto tree(read_binary_header<Tree>(input)); + auto tree(read_binary_header<TreeType>(input)); std::vector<std::int64_t> number_of_cells(tree.height(), std::int64_t(0)); input.read(reinterpret_cast<char*>(number_of_cells.data()), number_of_cells.size() * sizeof(std::int64_t)); @@ -338,7 +373,7 @@ namespace scalfmm::tools::io tree.construct(vector_of_mortons); // // Fill tree cells - using value_type = typename Tree::cell_type::value_type; + using value_type = typename TreeType::cell_type::value_type; for(std::size_t level = tree.leaf_level(); level >= tree.top_level(); --level) { @@ -378,24 +413,28 @@ namespace scalfmm::tools::io } #ifdef SCALFMM_USE_MPI + /** - * @brief save a tree in binary format in a file (only the cells) + * @brief save a tree in binary format in a file (only the cells). * + * @tparam CellType + * @tparam LeafType + * @tparam BoxType * @param[in] para the parallel manager * @param[in] filename name of teh file * @param[out] tree tree to save * @param[in] header string that represents a comment (useful to know what tree we read) * @param[in] options (not use yet) */ - template<typename Cell, typename Leaf, typename Box> + template<typename CellType, typename LeafType, typename BoxType> inline auto save(cpp_tools::parallel_manager::parallel_manager& para, std::string const& filename, - component::dist_group_tree<Cell, Leaf, Box> const& tree, std::string const& header, + component::dist_group_tree<CellType, LeafType, BoxType> const& tree, std::string const& header, const int options = 0) -> void { static constexpr std::size_t inputs_size = - component::dist_group_tree<Cell, Leaf, Box>::cell_type::storage_type::inputs_size; + component::dist_group_tree<CellType, LeafType, BoxType>::cell_type::storage_type::inputs_size; static constexpr std::size_t outputs_size = - component::dist_group_tree<Cell, Leaf, Box>::cell_type::storage_type::outputs_size; + component::dist_group_tree<CellType, LeafType, BoxType>::cell_type::storage_type::outputs_size; if(para.master()) { std::fstream out(filename, std::ifstream::out | std::ios::binary); @@ -480,7 +519,7 @@ namespace scalfmm::tools::io } pos_start += sizeof(morton_type) * glob_number_of_cells[leaf_level]; { - using value_type = typename Cell::value_type; + using value_type = typename CellType::value_type; // loop on the groups // nb = size of the multipoles and the locals in a cell @@ -535,4 +574,3 @@ namespace scalfmm::tools::io #endif } // namespace scalfmm::tools::io - diff --git a/include/scalfmm/tools/tree_is_nan.hpp b/include/scalfmm/tools/tree_is_nan.hpp new file mode 100644 index 0000000000000000000000000000000000000000..162615f747f3c849ca45df15e151051409080da7 --- /dev/null +++ b/include/scalfmm/tools/tree_is_nan.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include <array> +#include <fstream> +#include <string> + +#include "scalfmm/tree/box.hpp" +// #include "scalfmm/tree/dist_group_tree.hpp" +#include "scalfmm/tree/group_tree_view.hpp" +#include "scalfmm/tree/utils.hpp" +#include "scalfmm/utils/io_helpers.hpp" +#include <cpp_tools/parallel_manager/parallel_manager.hpp> + +#include <cpp_tools/colors/colorized.hpp> + +namespace scalfmm::tools::io +{ + ///////////////////////////////// + /// + /** + * @brief check if there is any NaN in the multipoles are the locals of a tree + * + * For all levels, depending on the option, we check whether the multipole and local tensors contain NaN values. The + * output (error output) is colored for ease of reading. + * + * option 1 only the multipoles + * option 2 only the locals + * otherwise both multipoles and locals + * + * @param tree the tree (tree view) to check + * @param option int (1,2 ... ) - the option described above + * @return the test value + */ + template<typename TreeType> + inline auto check_nan_in_tree(TreeType const& tree, int option = -1) -> bool + { + bool check_mult{true}, check_loc{true}; + + // loop through all the levels of the tree + for(std::size_t level = tree.leaf_level(); level >= tree.top_level(); --level) + { + std::cout << cpp_tools::colors::magenta << "\n\tLEVEL = " << level << cpp_tools::colors::reset << std::endl; + auto start = tree.begin_mine_cells(level); + + // loop through all the group of the current level + for(auto grp = start; grp != tree.end_mine_cells(level); ++grp) + { + // loop over all the cells of the current group + for(std::size_t index = 0; index < (*grp)->size(); ++index) + { + auto const& cell = (*grp)->ccomponent(index); + + // multipoles + if(option != 2) + { + auto mults = cell.cmultipoles(); + auto number_of_arrays = mults.size(); + for(std::size_t l = 0; l < number_of_arrays; ++l) + { + auto& mult = mults.at(l); + std::cerr << "level " << level << " - Cell morton " << cell.csymbolics().morton_index + << std::endl; + if(xt::any(xt::isnan(mult))) + { + check_mult = false; + std::cerr << cpp_tools::colors::red; + } + else + { + std::cerr << cpp_tools::colors::green; + } + std::cerr << "mult(" << l << ")\n" << mult << std::endl; + std::cerr << cpp_tools::colors::reset; + } + } + + // locals + if(option != 1) + { + auto const& locals = cell.clocals(); + auto number_of_arrays = locals.size(); + for(std::size_t l = 0; l < number_of_arrays; ++l) + { + auto const& local = locals.at(l); + std::cerr << "level " << level << " - Cell morton " << cell.csymbolics().morton_index + << std::endl; + if(xt::any(xt::isnan(local))) + { + check_loc = false; + std::cerr << cpp_tools::colors::red; + } + else + { + std::cerr << cpp_tools::colors::cyan; + } + std::cerr << "local(" << l << ")\n" << local << std::endl; + std::cerr << cpp_tools::colors::reset; + } + } + } + } + } + + return check_mult && check_loc; + } +}; // namespace scalfmm::tools::io diff --git a/include/scalfmm/tools/vtk_writer.hpp b/include/scalfmm/tools/vtk_writer.hpp index 3375a43cc0c6c47706421277a8bc113deee4f7dc..d662c7627c8f2c363a4335499b6bb1628f704c57 100644 --- a/include/scalfmm/tools/vtk_writer.hpp +++ b/include/scalfmm/tools/vtk_writer.hpp @@ -1,22 +1,29 @@ // -------------------------------- // See LICENCE file at project root -// File : tools/vtk_writer.hpp +// File : scalfmm/tools/vtk_writer.hpp // -------------------------------- #ifndef SCALFMM_TOOLS_VTK_WRITER_HPP #define SCALFMM_TOOLS_VTK_WRITER_HPP +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/tree/for_each.hpp" + +#include <cstddef> #include <fstream> #include <iostream> -#include <string> -#include <cstddef> #include <iterator> +#include <string> #include <vector> -#include "scalfmm/meta/utils.hpp" -#include "scalfmm/tree/for_each.hpp" - namespace scalfmm::tools::io { + /** + * @brief + * + * @param nb_val + * @param name + * @return std::string + */ std::string writeHeader(const int nb_val, const std::string& name) { std::string header; @@ -32,6 +39,14 @@ namespace scalfmm::tools::io return header; } + /** + * @brief + * + * @tparam TreeType + * @param filename + * @param tree + * @param npart + */ template<class TreeType> void exportVTKxml(std::string const& filename, TreeType const& tree, std::size_t npart) { @@ -42,36 +57,38 @@ namespace scalfmm::tools::io std::vector<particle_type> particles(npart); std::size_t pos{0}; - scalfmm::component::for_each_leaf(std::cbegin(tree), std::cend(tree), [&pos,&particles](auto& leaf) { - const auto container = leaf.cparticles(); + scalfmm::component::for_each_leaf(std::cbegin(tree), std::cend(tree), + [&pos, &particles](auto& leaf) + { + const auto container = leaf.cparticles(); - // for(std::size_t idx = 0; idx < leaf.cparticles().size(); ++idx) - // { - // particles[pos++] = leaf.cparticles().particle(idx); - // } - for(auto const& it_p: leaf) - { - // particle_type& particles_elem = particles[pos++]; - particles[pos++] = typename TreeType::leaf_type::particle_type(it_p); - // - // int i = 0; - // const auto points = p.position(); - // for(int k = 0; k < dimension; ++k, ++i) - // { - // particles_elem[i] = points[k]; - // } - // // get inputs - // for(int k = 0; k < nb_input_elements; ++k, ++i) - // { - // particles_elem[i] = p.inputs(k); - // } - // // get outputs - // for(int k = 0; k < nb_output_elements; ++k, ++i) - // { - // particles_elem[i] = p.outputs(k); - // } - } - }); + // for(std::size_t idx = 0; idx < leaf.cparticles().size(); ++idx) + // { + // particles[pos++] = leaf.cparticles().particle(idx); + // } + for(auto const& it_p: leaf) + { + // particle_type& particles_elem = particles[pos++]; + particles[pos++] = typename TreeType::leaf_type::particle_type(it_p); + // + // int i = 0; + // const auto points = p.position(); + // for(int k = 0; k < dimension; ++k, ++i) + // { + // particles_elem[i] = points[k]; + // } + // // get inputs + // for(int k = 0; k < nb_input_elements; ++k, ++i) + // { + // particles_elem[i] = p.inputs(k); + // } + // // get outputs + // for(int k = 0; k < nb_output_elements; ++k, ++i) + // { + // particles_elem[i] = p.outputs(k); + // } + } + }); if(TreeType::dimension > 3) { @@ -186,23 +203,24 @@ namespace scalfmm::tools::io << "</PolyData>" << std::endl << "</VTKFile>" << std::endl; } - //! \fn void exportVTKxml(std::ofstream& file, const FReal particles, const - //! std::size_t N ) - //! \brief Export particles in xml polydata VTK Format - //! - //! Export particles in the xml polydata VTK Format. - //! A particle is composed of k fields pos (dim real) (size/N-dim) values - //! It is useful to plot the distribution with paraView - //! - //! @param filename (string) file to save the vtk file. - //! @param particles vector of particles of type Real (float or double) - //! @param dimension dimension of the space (2 or 3) - //! @param nb_input_values number of input values per particles - //! @param N number of particles - template<class VECTOR_T> - void exportVTKxml(std::string& filename, const VECTOR_T& particles, const int dimension, const int nb_input_values, - const std::size_t N) + /** + * @brief Export particles in xml polydata VTK Format + * + * Export particles in the xml polydata VTK Format. + * A particle is composed of k fields pos (dim real) (size/N-dim) values + * It is useful to plot the distribution with paraView + * + * @tparam VectorType + * @param filename (string) file to save the vtk file. + * @param particles vector of particles of type Real (float or double) + * @param dimension dimension of the space (2 or 3) + * @param nb_input_values number of input values per particles + * @param N number of particles + */ + template<class VectorType> + void exportVTKxml(std::string& filename, const VectorType& particles, const int dimension, + const int nb_input_values, const std::size_t N) { if(dimension > 3) { @@ -220,10 +238,11 @@ namespace scalfmm::tools::io std::cout << " nb_input_values " << nb_input_values << std::endl; std::cout << " nb_output_values " << nb_output_values << std::endl; std::cout << " N " << N << std::endl; - if(nb_output_values <0 ){ + if(nb_output_values < 0) + { std::cerr << "nb_output_values <0, the dimension maybe wrong\n"; } - std::cout << "Write vtk format in " << filename << std::endl; + std::cout << "Write vtk format in " << filename << std::endl; std::cout << " dim=" << dimension << " stride =" << stride << " nb_input_values " << nb_input_values << " nb_output_values " << nb_output_values << " N " << N << std::endl; VTKfile << "<?xml version=\"1.0\"?>" << std::endl @@ -266,7 +285,7 @@ namespace scalfmm::tools::io } header += "\" "; } - VTKfile << "<PointData " << header << " > " << std::endl; + VTKfile << "<PointData " << header << " > " << std::endl; for(int k = 0; k < nb_input_values; ++k) { VTKfile << "<DataArray type=\"Float64\" Name=\"inputValue" + std::to_string(k) + @@ -297,8 +316,8 @@ namespace scalfmm::tools::io << " <CellData>" << " </CellData>" << std::endl; } - else{ - + else + { } VTKfile << " <Verts>" << std::endl << " <DataArray type=\"Int32\" Name=\"connectivity\" " diff --git a/include/scalfmm/tree/box.hpp b/include/scalfmm/tree/box.hpp index ec9917cfd1f68cbdd8009ba0757975b92115b182..e100f3e1d493b1b6e71478cae8832485926a7697 100644 --- a/include/scalfmm/tree/box.hpp +++ b/include/scalfmm/tree/box.hpp @@ -1,31 +1,31 @@ -// See LICENCE file at project root -// File : box.hpp +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/tree/box.hpp // -------------------------------- #ifndef SCALFMM_TREE_BOX_HPP #define SCALFMM_TREE_BOX_HPP +#include "scalfmm/tree/morton_curve.hpp" + #include <array> #include <cmath> #include <cstddef> #include <ostream> -#include <scalfmm/tree/morton_curve.hpp> - -/* Implements a N dimensions box - * - * \author Quentin Khan <quentin.khan@inria.fr> - * - * The box is described by two opposite corners : the maximum and - * the minimum one. All the class transformations maintain this - * predicate. - * - * \tparam value_type Floating number representation. - * \tparam dimension Space dimension count. - * \tparam SpaceFillingCurve A templatize implementation of a space filling curve - * - */ namespace scalfmm::component { + /** + * @brief Implements a N dimensions box + * + * @author Quentin Khan <quentin.khan@inria.fr> + * + * The box is described by two opposite corners : the maximum and + * the minimum one. All the class transformations maintain this + * predicate. + * + * @tparam Position + * @tparam SpaceFillingCurve A templatize implementation of a space filling curve + */ template<class Position, template<std::size_t> class SpaceFillingCurve = z_curve> class box { @@ -40,15 +40,47 @@ namespace scalfmm::component using space_filling_curve_t = SpaceFillingCurve<dimension>; private: - position_type m_c1; ///< Minimum corner - position_type m_c2; ///< Maximum corner - position_type m_center; ///< Center + /** + * @brief Minimum corner. + * + */ + position_type m_c1; + + /** + * @brief Maximum corner. + * + */ + position_type m_c2; + + /** + * @brief Center. + * + */ + position_type m_center; + + /** + * @brief + * + */ space_filling_curve_t m_space_filling_curve; - std::array<bool, dimension> m_periodicity; ///< get the periodicity per direction - bool m_is_periodic; ///< True if a direction is periodic - /** Rearranges the corners to ensure the maximum-minimum predicate */ - void rearrange_corners() + /** + * @brief get the periodicity per direction. + * + */ + std::array<bool, dimension> m_periodicity; + + /** + * @brief Boolean indicating if a direction is periodic. + * + */ + bool m_is_periodic; + + /** + * @brief Rearranges the corners to ensure the maximum-minimum predicate. + * + */ + auto rearrange_corners() -> void { for(std::size_t i = 0; i < dimension; ++i) { @@ -61,34 +93,73 @@ namespace scalfmm::component } public: - /// Accessor for the minimum corner - [[nodiscard]] auto c1() const noexcept -> position_type const& { return m_c1; } - /// Accessor for the maximum corner - [[nodiscard]] auto c2() const noexcept -> position_type const& { return m_c2; } + /** + * @brief Accessor for the minimum corner. + * + * @return position_type const& + */ + [[nodiscard]] inline auto c1() const noexcept -> position_type const& { return m_c1; } - /** Builds an empty box at the origin */ + /** + * @brief Accessor for the maximum corner. + * + * @return position_type const& + */ + [[nodiscard]] inline auto c2() const noexcept -> position_type const& { return m_c2; } + + /** + * @brief Construct a new box object + * + * Builds an empty box at the origin + * + */ box() = default; - /** Copies an existing box */ + /** + * @brief Construct a new box object + * + * Copies an existing box + * + */ box(const box&) = default; - /** Copies an other box */ + /** + * @brief + * + * @param other + * @return box& + */ auto operator=(const box& other) -> box& = default; - /** Move constructor */ + /** + * @brief Construct a new box object + * + * Move constructor + */ box(box&&) noexcept = default; - /** Move assignment */ + /** + * @brief + * + * Move assignment + * + * @param other + * @return box& + */ auto operator=(box&& other) noexcept -> box& = default; - /** Destructor */ + /** + * @brief Destroy the box object + * + */ ~box() = default; - /** Builds a cube from the lower corner and its side length + /** + * @brief Builds a cube from the lower corner and its side length. * - * \param min_corner The lowest corner - * \param side_length The cube's side length - **/ + * @param min_corner + * @param side_length + */ [[deprecated]] box(const position_type& min_corner, value_type side_length) : m_c1(min_corner) , m_c2(min_corner) @@ -111,10 +182,13 @@ namespace scalfmm::component m_center = (m_c1 + m_c2) * 0.5; } - /** Builds a cube using its center and width + /** + * @brief Construct a new box object + * + * Builds a cube using its center and width. * - * \param width Cube width - * \param box_center Cube center + * @param width Cube width. + * @param box_center Cube center. */ box(value_type width, position_type const& box_center) : m_c1(box_center) @@ -144,12 +218,13 @@ namespace scalfmm::component } } - /** Builds a box from two corners + /** + * @brief Builds a box from two corners. * * The maximum and minimum corners are deduced from the given corners. * - * \param corner_1 The first corner. - * \param corner_2 The second corner. + * @param corner_1 + * @param corner_2 */ box(const position_type& corner_1, const position_type& corner_2) : m_c1(corner_1) @@ -165,14 +240,15 @@ namespace scalfmm::component rearrange_corners(); } - /** Changes the box corners + /** + * @brief Changes the box corners. * * The maximum and minimum corners are deduced from the given corners. * - * \param corner_1 The first corner. - * \param corner_2 The second corner. + * @param corner_1 + * @param corner_2 */ - void set(const position_type& corner_1, const position_type& corner_2) + auto set(const position_type& corner_1, const position_type& corner_2) -> void { m_c1 = corner_1; m_c2 = corner_2; @@ -180,9 +256,12 @@ namespace scalfmm::component rearrange_corners(); } - /** Checks whether a position is within the box bounds + /** + * @brief Checks whether a position is within the box bounds. * - * \param p The position to check. + * @param p The position to check. + * @return true + * @return false */ [[nodiscard]] auto contains(const position_type& p) const -> bool { @@ -196,23 +275,36 @@ namespace scalfmm::component return true; } - /** Checks whether an object's position is within the box bounds + /** + * @brief Checks whether an object's position is within the box bounds. * - * The object must implement a `position_type position() const;` method. + * The object must implement a 'position_type position() const;' method. * - * \tparam T The object type. - * \param obj The object which position to check. + * @tparam T The object type. + * @param obj The object which position to check. + * @return true + * @return false */ template<class T> - [[nodiscard]] auto contains(const T& obj) const -> bool + [[nodiscard]] inline auto contains(const T& obj) const -> bool { return contains(obj.position()); } - /** Accessor for the box center */ - [[nodiscard]] auto center() const -> position_type const& { return m_center; } + /** + * @brief Accessor for the box center. + * + * @return position_type const& + */ + [[nodiscard]] inline auto center() const -> position_type const& { return m_center; } - /** Accessor for the box center */ + /** + * @brief Access for the box center. + * + * @tparam Int + * @param added_levels + * @return position_type const + */ template<typename Int> [[nodiscard]] auto extended_center(Int added_levels) const -> position_type const { @@ -229,12 +321,13 @@ namespace scalfmm::component } } - /** Accessor for the box corners + /** + * @brief Accessor for the box corners. * * The corners are numbered using a space filling curve. * - * \param idx The corner index. - * \return The idx'th corner. + * @param idx The corner index. + * @return position_type The idx'th corner. */ [[nodiscard]] auto corner(std::size_t idx) const -> position_type { @@ -248,15 +341,16 @@ namespace scalfmm::component return c; } - /** Setter for the corners + /** + * @brief Setter for the corners. * * Moves a corner to a new position and modifies the relevant other * ones. The corners are numbered using a space filling curve. * - * \param idx The moved corner index. - * \param pos The new corner position. + * @param idx The moved corner index. + * @param pos The new corner position. */ - void corner(std::size_t idx, const position_type& pos) + auto corner(std::size_t idx, const position_type& pos) -> void { std::size_t i = 0; for(bool choice: m_space_filling_curve.position(idx)) @@ -274,46 +368,66 @@ namespace scalfmm::component rearrange_corners(); } #ifdef scalfmm_BUILD_PBC - - /// - /// \brief set the periodicity in the direction dir - /// \param dir direction - /// \param per true if the direction dir is periodic otherwise false - /// - void set_periodicity(int dir, bool per) + + /** + * @brief Set the periodicity in the direction dir. + * + * @param dir direction. + * @param per true if the direction dir is periodic otherwise false. + */ + auto set_periodicity(int dir, bool per) -> void { m_periodicity[dir] = per; m_is_periodic = m_is_periodic || per; } - /// - /// \brief set the periodicity in all directions - /// \param pbl a vector of boolean that specifies if the direction is periodic or not - /// + /** + * @brief Set the periodicity in all directions. + * + * @tparam Vector + * @param pbl a vector of boolean that specifies whether the direction is periodic or not. + */ template<typename Vector> - void set_periodicity(const Vector& pbl) + auto set_periodicity(const Vector& pbl) -> void { for(std::size_t d = 0; d < pbl.size(); ++d) set_periodicity(d, pbl[d]); } #endif - /// - /// \brief get_periodicity return the array of periodic direction - /// - /// - auto get_periodicity() const -> std::array<bool, dimension> { return m_periodicity; } - /// - /// \brief is_periodic tell if there is a periodic direction - /// - /// - auto is_periodic() const noexcept -> bool { return m_is_periodic; } - - /** Returns the width for given dimension */ - [[nodiscard]] auto width(std::size_t dim) const noexcept -> decltype(std::abs(m_c2[dim] - m_c1[dim])) + + /** + * @brief Returns the array of periodic direction. + * + * @return std::array<bool, dimension> + */ + inline auto get_periodicity() const -> std::array<bool, dimension> { return m_periodicity; } + + /** + * @brief Tells whether there is a periodic direction. + * + * @return true + * @return false + */ + inline auto is_periodic() const noexcept -> bool { return m_is_periodic; } + + /** + * @brief Returns the width for given dimension. + * + * @param dim + * @return decltype(std::abs(m_c2[dim] - m_c1[dim])) + */ + [[nodiscard]] inline auto width(std::size_t dim) const noexcept -> decltype(std::abs(m_c2[dim] - m_c1[dim])) { return std::abs(m_c2[dim] - m_c1[dim]); } - /** Returns the extended width for periodic simulation for added_levels n*/ + + /** + * @brief Returns the extended width for periodic simulation for added_levels. + * + * @tparam Int + * @param added_levels + * @return value_type + */ template<typename Int> [[nodiscard]] auto extended_width(Int added_levels) const noexcept -> value_type { @@ -329,14 +443,40 @@ namespace scalfmm::component } return width; } - /** Sums the corners of two boxes */ - auto operator+(const box& other) const -> box { return box(m_c1 + other.m_c1, m_c2 + other.m_c2); } - /** Tests two boxes equality */ - auto operator==(const box& other) const -> bool { return c1() == other.c1() && c2() == other.c2(); } - /** Tests two boxes inequality */ - auto operator!=(const box& other) const -> bool { return !this->operator==(other); } + /** + * @brief Sums the corners of two boxes + * + * @param other + * @return box + */ + inline auto operator+(const box& other) const -> box { return box(m_c1 + other.m_c1, m_c2 + other.m_c2); } + + /** + * @brief Tests two boxes equality + * + * @param other + * @return true + * @return false + */ + inline auto operator==(const box& other) const -> bool { return c1() == other.c1() && c2() == other.c2(); } + /** + * @brief Tests two boxes inequality + * + * @param other + * @return true + * @return false + */ + inline auto operator!=(const box& other) const -> bool { return !this->operator==(other); } + + /** + * @brief + * + * @param os + * @param box + * @return std::ostream& + */ friend auto operator<<(std::ostream& os, const box& box) -> std::ostream& { os << " [" << box.c1() << "," << box.c2() << "] ; periodicity: "; diff --git a/include/scalfmm/tree/cell.hpp b/include/scalfmm/tree/cell.hpp index 9e81dfab1eef414a40b0525dda83a7ab9c4612b2..cae9742f62687a13b27b07c4ed0600066689281e 100644 --- a/include/scalfmm/tree/cell.hpp +++ b/include/scalfmm/tree/cell.hpp @@ -1,37 +1,41 @@ // -------------------------------- // See LICENCE file at project root -// File : tree/cell.hpp +// File : scalfmm/tree/cell.hpp // -------------------------------- #ifndef SCALFMM_TREE_CELL_HPP #define SCALFMM_TREE_CELL_HPP -#include <array> -#include <cstddef> -#include <cstdint> -#include <type_traits> -#include <utility> -#include <vector> -#include <xtensor/xarray.hpp> -#include <xtensor/xfixed.hpp> -#include <xtensor/xtensor.hpp> -#include <xtensor/xtensor_forward.hpp> - #include "scalfmm/container/point.hpp" #include "scalfmm/container/variadic_adaptor.hpp" +#include "scalfmm/memory/storage.hpp" #include "scalfmm/meta/traits.hpp" #include "scalfmm/tree/group.hpp" #include "scalfmm/tree/header.hpp" #include "scalfmm/utils/math.hpp" #include "scalfmm/utils/tensor.hpp" -#include "scalfmm/memory/storage.hpp" + +#include "xtensor/xarray.hpp" +#include "xtensor/xfixed.hpp" +#include "xtensor/xtensor.hpp" #include "xtensor/xtensor_config.hpp" +#include "xtensor/xtensor_forward.hpp" + +#include <array> +#include <cstddef> +#include <cstdint> +#include <type_traits> +#include <utility> +#include <vector> namespace scalfmm::component { - /// @brief The cell type stores the multipoles and the local expansions - /// - /// @tparam Storage : the storage type that gives the interface the storage of the cell. - template<typename Storage, typename D=void> + /** + * @brief The cell type stores the multipoles and the local expansions + * + * @tparam Storage the storage type that gives the interface the storage of the cell. + * @tparam D + */ + template<typename Storage, typename D = void> class alignas(XTENSOR_FIXED_ALIGN) cell : public Storage { public: @@ -43,18 +47,52 @@ namespace scalfmm::component using position_type = typename symbolics_type::position_type; // Constructors generated + + /** + * @brief Construct a new cell object + * + */ cell() = default; + + /** + * @brief Construct a new cell object + * + */ cell(cell const&) = default; + + /** + * @brief Construct a new cell object + * + */ cell(cell&&) noexcept = default; + + /** + * @brief + * + * @return cell& + */ inline auto operator=(cell const&) -> cell& = default; + + /** + * @brief + * + * @return cell& + */ inline auto operator=(cell&&) noexcept -> cell& = default; + + /** + * @brief Destroy the cell object + * + */ ~cell() = default; - /// @brief Constructor - /// - /// @param center : the center of the cell - /// @param width : the width of the cell - /// @param order : the order of the approximation + /** + * @brief Construct a new cell object + * + * @param center : the center of the cell + * @param width : the width of the cell + * @param order : the order of the approximation + */ explicit cell(position_type const& center, value_type width, std::size_t order) : storage_type(order) , m_center(center) @@ -63,14 +101,16 @@ namespace scalfmm::component { } - /// @brief Constructor - /// - /// @param center : the center of the cell - /// @param width : the width of the cell - /// @param order : the order of the approximation - /// @param level : the level of the cell - /// @param morton_index : the morton index of the cell - /// @param coordinate_in_tree : the coordinate of the cell in the tree + /** + * @brief Construct a new cell object + * + * @param center : the center of the cell + * @param width : the width of the cell + * @param order : the order of the approximation + * @param level : the level of the cell + * @param morton_index : the morton index of the cell + * @param coordinate_in_tree : the coordinate of the cell in the tree + */ explicit cell(position_type const& center, value_type width, std::size_t order, std::size_t level, std::size_t morton_index, coordinate_type const& coordinate_in_tree) : storage_type(order) @@ -81,60 +121,100 @@ namespace scalfmm::component { } - /// @brief Access to the symbolic type - /// - /// @return a symbolics_type reference + /** + * @brief Access to the symbolic type + * + * @return a symbolics_type reference + */ [[nodiscard]] inline auto symbolics() -> symbolics_type& { return m_symbolics; } - /// @brief Access to the symbolic type - /// - /// @return a const symbolics_type reference + + /** + * @brief Access to the symbolic type + * + * @return a const symbolics_type reference + */ [[nodiscard]] inline auto symbolics() const -> symbolics_type const& { return m_symbolics; } - /// @brief Access to the symbolic type - /// - /// @return a const symbolics_type reference + + /** + * @brief Access to the symbolic type + * + * @return a const symbolics_type reference + */ [[nodiscard]] inline auto csymbolics() const -> symbolics_type const& { return m_symbolics; } - /// @brief Returns the width of the leaf - /// - /// @return value_type + /** + * @brief Returns the width of the leaf + * + * @return value_type + */ [[nodiscard]] inline auto width() const noexcept -> value_type { return m_width; } - /// - ///@brief Set the width object - /// - /// @param in_width teh size of teh cell - /// + + /** + * @brief Set the width object + * + * @param in_width teh size of teh cell + */ inline auto set_width(value_type in_width) -> void { m_width = in_width; } - /// @brief Returns the center of the leaf - /// - /// @return position_type + + /** + * @brief Returns the center of the leaf + * + * @return position_type + */ [[nodiscard]] inline auto center() const noexcept -> position_type const& { return m_center; } - /// @brief Returns the order of the approximation - /// - /// @return std::size_t + + /** + * @brief Returns the order of the approximation + * + * @return std::size_t + */ [[nodiscard]] inline auto order() const noexcept -> std::size_t { return m_order; } - /// @brief Returns the morton index of the cell - /// - /// @return std::size_t + /** + * @brief Returns the morton index of the cell + * + * @return std::size_t + */ [[nodiscard]] inline auto index() const noexcept -> std::size_t { return m_symbolics.morton_index; } private: + /** + * @brief + * + */ position_type m_center{}; + + /** + * @brief + * + */ value_type m_width{}; + + /** + * @brief + * + */ std::size_t m_order{}; + + /** + * @brief + * + */ symbolics_type m_symbolics{}; }; - /// @brief The symbolics type stores information about the cell - /// It represents a generic that also exists on the leaves - /// - /// @tparam P + /** + * @brief The symbolics type stores information about the cell + * It represents a generic that also exists on the leaves + * + * @tparam S + */ template<typename S> struct symbolics_data<cell<S>> { // the storage type using storage_type = S; - // the cell type + // the cell type using component_type = cell<S>; // the group type using group_type = group<component_type>; @@ -176,16 +256,36 @@ namespace scalfmm::component // existing number of neighbors std::size_t existing_neighbors{0}; - void set(int counter, std::size_t const &idx, const iterator_type & cell_iter) { - interaction_positions.at(counter) = interaction_positions.at(idx) ; - interaction_iterators.at(counter) = cell_iter ; + /** + * @brief + * + * @param counter + * @param idx + * @param cell_iter + */ + auto set(int counter, std::size_t const& idx, const iterator_type& cell_iter) -> void + { + interaction_positions.at(counter) = interaction_positions.at(idx); + interaction_iterators.at(counter) = cell_iter; } - void finalize(bool done, std::size_t const &counter_existing_component) { + /** + * @brief + * + * @param done + * @param counter_existing_component + */ + auto finalize(bool done, std::size_t const& counter_existing_component) -> void + { existing_neighbors = counter_existing_component; } }; + /** + * @brief + * + * @tparam S + */ template<typename S> struct symbolics_data<group<cell<S>>> { @@ -195,16 +295,37 @@ namespace scalfmm::component using self_type = symbolics_data<group<component_type>>; using morton_type = std::size_t; // using morton_type = typename symbolics_data<component_type>::morton_type; - // the starting morton index in the group + + /** + * @brief the starting morton index in the group. + * + */ morton_type starting_index{0}; - // the ending morton index in the group + + /** + * @brief the ending morton index in the group. + * + */ morton_type ending_index{0}; - // number of cells in the group + + /** + * @brief number of cells in the group. + * + */ std::size_t number_of_component_in_group{0}; - // Global index of the group in the Octree at each level + + /** + * @brief Global index of the group in the Octree at each level + * + */ std::size_t idx_global{0}; + /** + * @brief + * + */ bool is_mine{false}; + // debug std::size_t idx_global_tree{0}; #if _OPENMP diff --git a/include/scalfmm/tree/dist_group_tree.hpp b/include/scalfmm/tree/dist_group_tree.hpp index f53067ad90415d8f69bcebf6ecbe3de11c66cd5d..e7d1b40af66b9c07e08a06b5bfc6693f77b92c86 100644 --- a/include/scalfmm/tree/dist_group_tree.hpp +++ b/include/scalfmm/tree/dist_group_tree.hpp @@ -1,9 +1,18 @@ // -------------------------------- // See LICENCE file at project root -// File : group_tree.hpp +// File : scalfmm/tree/dist_group_tree.hpp // -------------------------------- + #ifndef SCALFMM_TREE_DIST_GROUP_TREE_HPP #define SCALFMM_TREE_DIST_GROUP_TREE_HPP + +#include "scalfmm/tree/box.hpp" +#include "scalfmm/tree/group_let.hpp" +#include "scalfmm/tree/group_tree_view.hpp" +#include "scalfmm/utils/io_helpers.hpp" + +#include <cpp_tools/colors/colorized.hpp> + #include <array> #include <fstream> #include <iostream> @@ -12,15 +21,15 @@ #include <utility> #include <vector> -#include "scalfmm/tree/box.hpp" -#include <scalfmm/tree/group_let.hpp> -#include <scalfmm/tree/group_tree_view.hpp> -#include <scalfmm/utils/io_helpers.hpp> - -#include <cpp_tools/colors/colorized.hpp> - namespace scalfmm::component { + /** + * @brief + * + * @tparam Cell + * @tparam Leaf + * @tparam box<typename Leaf::position_type> + */ template<typename Cell, typename Leaf, typename Box = box<typename Leaf::position_type>> class dist_group_tree : public group_tree_view<Cell, Leaf, Box> { @@ -34,7 +43,18 @@ namespace scalfmm::component using cell_group_level_iterator_type = typename base_type::cell_group_level_type::iterator; using iterator_type = typename base_type::iterator_type; using const_iterator_type = typename base_type::const_iterator_type; - /// Constructor + + /** + * @brief Construct a new dist group tree object + * + * @param parallel_manager + * @param tree_height + * @param level_shared + * @param order + * @param size_leaf_blocking + * @param size_cell_blocking + * @param box + */ explicit dist_group_tree(cpp_tools::parallel_manager::parallel_manager& parallel_manager, std::size_t tree_height, const int level_shared, std::size_t order, std::size_t size_leaf_blocking, std::size_t size_cell_blocking, Box const& box) @@ -45,6 +65,7 @@ namespace scalfmm::component { m_cell_distrib.resize(tree_height); } + // template<typename ParticleContainer> // explicit dist_group_tree(std::size_t tree_height, const int level_shared, std::size_t order, Box const& box, // std::size_t size_leaf_blocking, std::size_t size_cell_blocking, @@ -67,45 +88,53 @@ namespace scalfmm::component // , m_level_shared{level_shared} // { // } + /** * @brief Set the leaf distribution object * * @param in_leaf_distrib The leaf distribution */ - void set_leaf_distribution(const data_distrib_type& in_leaf_distrib) { m_leaf_distrib = in_leaf_distrib; } + inline auto set_leaf_distribution(const data_distrib_type& in_leaf_distrib) -> void + { + m_leaf_distrib = in_leaf_distrib; + } + /** * @brief Get the leaf distribution object * * @param in_leaf_distrib The leaf distribution */ - auto inline get_leaf_distribution() -> data_distrib_type const& { return m_leaf_distrib; } + inline auto get_leaf_distribution() -> data_distrib_type const& { return m_leaf_distrib; } + /** * @brief Set the cell distribution object * * @param in_level level * @param in_cell_distrib the cell distribution at in_level */ - void set_cell_distribution(const int in_level, const data_distrib_type& in_cell_distrib) + inline auto set_cell_distribution(const int in_level, const data_distrib_type& in_cell_distrib) -> void { m_cell_distrib.at(in_level) = in_cell_distrib; } + /** * @brief Get the cell distribution object at level in_level * * @param in_level the level * @return data_distrib_type const& */ - auto inline get_cell_distribution(const int in_level) -> data_distrib_type const& + inline auto get_cell_distribution(const int in_level) -> data_distrib_type const& { return m_cell_distrib.at(in_level); } + /** * @brief print the distribution of leaves and cells * * @param out the stream where we display * @param verbose to display explanations of what we print */ - void print_distrib(std::ostream& out, bool verbose = true) + auto print_distrib(std::ostream& out, bool verbose = true) -> void { if(m_cell_distrib.size() > 0) { @@ -142,12 +171,13 @@ namespace scalfmm::component std::cout << " m_cell_distrib.size() == 0 !!!!!\n"; } } + /** * @brief ghost cell level display on output stream * * @param out the stream where we display */ - auto inline print_morton_ghosts(std::ostream& out) -> void + auto print_morton_ghosts(std::ostream& out) -> void { out << "The ghost leaves " << std::endl << std::flush; @@ -196,6 +226,11 @@ namespace scalfmm::component out << std::endl << std::flush; } } + + /** + * @brief Destroy the dist group tree object + * + */ ~dist_group_tree() { // std::cout << cpp_tools::colors::red; @@ -203,7 +238,14 @@ namespace scalfmm::component // std::cout << cpp_tools::colors::reset; // std::cout << " end ~dist_group_tree() " << std::endl; } - auto inline start_duplicated_level() -> const int { return m_level_shared; } + + /** + * @brief + * + * @return const int + */ + inline auto start_duplicated_level() -> const int { return m_level_shared; } + /** * @brief Create groups of cells at level object * @@ -212,6 +254,7 @@ namespace scalfmm::component * operator on the right. the Then we create separately the three set of groups (2 for the ghosts and one * for my cells) * + * @tparam VectorMortonIndexType * @param level the level where we construct the groups of cells * @param mortonIdx the index cells I own * @param ghosts_m2l the ghost cells needed in the m2l operator (VectorMortonIndexType) @@ -220,13 +263,13 @@ namespace scalfmm::component * @param cell_distrib the cell distribution */ template<typename VectorMortonIndexType> - void create_cells_at_level(const int level, VectorMortonIndexType const& mortonIdx, + auto create_cells_at_level(const int level, VectorMortonIndexType const& mortonIdx, VectorMortonIndexType const& ghosts_m2l, VectorMortonIndexType const& ghosts_m2m, - const std::int64_t& ghost_l2l, data_distrib_value_type const& cell_distrib) + const std::int64_t& ghost_l2l, data_distrib_value_type const& cell_distrib) -> void { - //io::print("create_cells_at_level mortonIdx", mortonIdx); - //io::print("ghosts_m2l", ghosts_m2l); - //io::print("ghosts_m2m", ghosts_m2m); + //io::print("create_cells_at_level mortonIdx", mortonIdx); + //io::print("ghosts_m2l", ghosts_m2l); + //io::print("ghosts_m2m", ghosts_m2m); // construct group of cells at leaf level auto first_index = cell_distrib[0]; @@ -244,19 +287,18 @@ namespace scalfmm::component { ghost_left_mortonIdx.back() = ghost_l2l; } - - //io::print("create_from_leaf : ghost_left_mortonIdx ", ghost_left_mortonIdx); + + //io::print("create_from_leaf : ghost_left_mortonIdx ", ghost_left_mortonIdx); this->build_groups_of_cells_at_level(ghost_left_mortonIdx, level, false); this->build_cells_in_groups_at_level(ghost_left_mortonIdx, base_type::m_box, level); - //io::print("ghost_left_mortonIdx ", ghost_left_mortonIdx); - + //io::print("ghost_left_mortonIdx ", ghost_left_mortonIdx); auto left_block_cells = std::move(base_type::m_group_of_cell_per_level.at(level)); auto ghost_right_mortonIdx = scalfmm::parallel::utils::merge_unique_fast<VectorMortonIndexType>( last, ghosts_m2l.end(), ghosts_m2m.begin(), ghosts_m2m.end()); - //io::print("create_from_leaf : ghost_right_mortonIdx ", ghost_right_mortonIdx); + //io::print("create_from_leaf : ghost_right_mortonIdx ", ghost_right_mortonIdx); this->build_groups_of_cells_at_level(ghost_right_mortonIdx, level, false); this->build_cells_in_groups_at_level(ghost_right_mortonIdx, base_type::m_box, level); @@ -284,6 +326,7 @@ namespace scalfmm::component it->get()->symbolics().idx_global = idx; } } + /** * @brief Create groups of cells and leaves from the leaf level * @@ -300,10 +343,10 @@ namespace scalfmm::component * @param cell_distrib the cell distribution */ template<typename VectorLeafInfoType, typename VectorMortonIndexType> - void create_from_leaf_level(VectorLeafInfoType& localLeaves, VectorLeafInfoType& ghosts_p2p, + auto create_from_leaf_level(VectorLeafInfoType& localLeaves, VectorLeafInfoType& ghosts_p2p, VectorMortonIndexType const& ghosts_m2l, VectorMortonIndexType const& ghosts_m2m, data_distrib_value_type const& leaf_distrib, - data_distrib_value_type const& cell_distrib) + data_distrib_value_type const& cell_distrib) -> void { using morton_type = typename VectorLeafInfoType::value_type::morton_type; // @@ -316,7 +359,7 @@ namespace scalfmm::component // io::print("create_from_leaf :nbpart ", number_of_part); this->build_groups_of_leaves(mortonIdx, number_of_part, base_type::m_box); - auto localBlocks = std::move(base_type::m_group_of_leaf); + auto localBlocks = std::move(base_type::m_group_of_leaves); // Build group on the left auto first_index = leaf_distrib[0]; @@ -330,7 +373,7 @@ namespace scalfmm::component // io::print("create_from_leaf : left nbpart ", ghost_left_number_of_part); this->build_groups_of_leaves(ghost_left_mortonIdx, ghost_left_number_of_part, base_type::m_box, false); - auto ghost_left_Blocks = std::move(base_type::m_group_of_leaf); + auto ghost_left_Blocks = std::move(base_type::m_group_of_leaves); std::vector<morton_type> ghost_right_mortonIdx; std::vector<std::size_t> ghost_right_number_of_part; std::tie(ghost_right_mortonIdx, ghost_right_number_of_part) = @@ -340,14 +383,14 @@ namespace scalfmm::component // io::print("create_from_leaf : right nbpart ", ghost_right_number_of_part); this->build_groups_of_leaves(ghost_right_mortonIdx, ghost_right_number_of_part, base_type::m_box, false); - auto ghost_right_Blocks = std::move(base_type::m_group_of_leaf); + auto ghost_right_Blocks = std::move(base_type::m_group_of_leaves); // Merge the three block structure auto all_blocks = scalfmm::tree::let::merge_blocs(ghost_left_Blocks, localBlocks, ghost_right_Blocks); - base_type::m_group_of_leaf = std::move(all_blocks); + base_type::m_group_of_leaves = std::move(all_blocks); int idx{0}; - for(auto it = std::begin(base_type::m_group_of_leaf); it != std::end(base_type::m_group_of_leaf); + for(auto it = std::begin(base_type::m_group_of_leaves); it != std::end(base_type::m_group_of_leaves); ++it, ++idx) { it->get()->symbolics().idx_global = idx; @@ -368,20 +411,24 @@ namespace scalfmm::component this->create_cells_at_level(leaf_level, mortonIdx, ghosts_m2l, ghosts_m2m, ghost_l2l_cell, cell_distrib); // } + /** * @brief Construct the cells at levels in [level_shared, top_level] * * The data are duplicated on all processors for level between level_shared and top_level. * The bounds are included. + * + * @tparam MortonDistVector + * @tparam MortonIdxVector * @param para the manager of the parallelism * @param level_dist the vector of distribution on all levels * @param leafMortonIdx the morton indexes of the existing cells at level level_shared * @param level_shared the level where the cells are duplicated on the first levels */ template<typename MortonDistVector, typename MortonIdxVector> - void build_other_shared_levels(cpp_tools::parallel_manager::parallel_manager& para, + auto build_other_shared_levels(cpp_tools::parallel_manager::parallel_manager& para, MortonDistVector& level_dist, MortonIdxVector& leafMortonIdx, - int const& level_shared) + int const& level_shared) -> void { // Get all cells on all process const int nproc = para.get_num_processes(); @@ -435,20 +482,21 @@ namespace scalfmm::component parallel::utils::move_index_to_upper_level<base_type::dimension>(shared_cell_index); } } + /** * @brief Set the valid begin and end iterators on cell and leaf group * * Here the iterators begin_mine_{cells,leaves} and begin_{cells,leaves} may be different * */ - void set_valid_iterators(bool verbose = false) + auto set_valid_iterators(bool verbose = false) -> void { // set iterators for leaf // begin_mine_leaf - auto& vectG = base_type::m_group_of_leaf; + auto& vectG = base_type::m_group_of_leaves; // - base_type::m_view_on_my_leaf_groups[0] = std::end(base_type::m_group_of_leaf); - for(auto it = std::begin(base_type::m_group_of_leaf); it != std::end(base_type::m_group_of_leaf); ++it) + base_type::m_view_on_my_leaf_groups[0] = std::end(base_type::m_group_of_leaves); + for(auto it = std::begin(base_type::m_group_of_leaves); it != std::end(base_type::m_group_of_leaves); ++it) { if(it->get()->csymbolics().is_mine) { @@ -457,10 +505,10 @@ namespace scalfmm::component } } // set iterator end_mine_leaf - auto it_group = std::end(base_type::m_group_of_leaf); + auto it_group = std::end(base_type::m_group_of_leaves); base_type::m_view_on_my_leaf_groups[1] = it_group; --it_group; // to have a valid iterator - for(auto it = it_group; it != --(std::begin(base_type::m_group_of_leaf)); --it) + for(auto it = it_group; it != --(std::begin(base_type::m_group_of_leaves)); --it) { if(it->get()->csymbolics().is_mine) { @@ -542,6 +590,13 @@ namespace scalfmm::component --cell_level_it; } } + + /** + * @brief + * + * @tparam ParticleContainer + * @param particle_container + */ template<typename ParticleContainer> auto fill_leaves_with_particles(ParticleContainer const& particle_container) -> void { @@ -613,20 +668,40 @@ namespace scalfmm::component // std::clog << " ---------------------------------------------------\n"; // #endif } - auto inline get_parallel_manager() -> cpp_tools::parallel_manager::parallel_manager& + + /** + * @brief Get the parallel manager object + * + * @return cpp_tools::parallel_manager::parallel_manager& + */ + inline auto get_parallel_manager() -> cpp_tools::parallel_manager::parallel_manager& { return m_parallel_manager; } private: - /// a reference on the parallel manager + /** + * @brief a reference on the parallel manager + * + */ cpp_tools::parallel_manager::parallel_manager& m_parallel_manager; - /// Distribution of leaves at different level. The interval is a range (open on the right) + /** + * @brief Distribution of leaves at different level. The interval is a range (open on the right) + * + */ data_distrib_type m_leaf_distrib; - /// Distribution of cells at different level + + /** + * @brief Distribution of cells at different level + * + */ std::vector<data_distrib_type> m_cell_distrib; - /// The level at which cells are duplicated on processors. If the level is negative, nothing is duplicated. + + /** + * @brief The level at which cells are duplicated on processors. If the level is negative, nothing is duplicated. + * + */ int m_level_shared; }; } // namespace scalfmm::component diff --git a/include/scalfmm/tree/for_each.hpp b/include/scalfmm/tree/for_each.hpp index 52ff4e29e72da8f331b55c25262736a3eb73b5f5..449e4da07e3641d38191c56ba753846c9e8ff9b5 100644 --- a/include/scalfmm/tree/for_each.hpp +++ b/include/scalfmm/tree/for_each.hpp @@ -1,12 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : scalfmm/tree/interaction_list.hpp +// File : scalfmm/tree/for_each.hpp // -------------------------------- #ifndef SCALFMM_TREE_FOR_EACH_HPP #define SCALFMM_TREE_FOR_EACH_HPP -// #include <bits/utility.h> -// #include <cstddef> #include <algorithm> #include <functional> #include <type_traits> @@ -41,7 +39,7 @@ namespace scalfmm::component * @return auto */ template<typename GroupIterator, typename ComponentIterator> - auto inline generate_linear_iterator(int gs, GroupIterator& it_group, ComponentIterator& it_cell) + inline auto generate_linear_iterator(int gs, GroupIterator& it_group, ComponentIterator& it_cell) { return [&it_group, &it_cell, gs, n = int(1)]() mutable { @@ -61,8 +59,18 @@ namespace scalfmm::component }; } + /** + * @brief + * + * @tparam ComponentIterator + * @tparam F + * @param begin + * @param end + * @param f + * @return auto + */ template<typename ComponentIterator, typename F> - auto for_each(ComponentIterator begin, ComponentIterator end, F&& f) + inline auto for_each(ComponentIterator begin, ComponentIterator end, F&& f) { std::for_each(begin, end, std::forward<F>(f)); // TODO: parallel version @@ -73,26 +81,25 @@ namespace scalfmm::component // } return begin; } + /** * @brief * + * code example to print leaf info + * @code + * scalfmm::component::for_each_leaf(std::cbegin(tree), std::cend(tree), + * [&tree](auto& leaf) { scalfmm::io::print_leaf(leaf); }); + * @endcode + * * @tparam InputTreeIterator * @tparam UnaryFunction * @param begin begin iterator of the group tree * @param end end iterator of the group tree * @param f lambda function * @return UnaryFunction - * - * code example to print leaf info - * @code - * scalfmm::component::for_each_leaf(std::cbegin(tree), std::cend(tree), - * [&tree](auto& leaf) { scalfmm::io::print_leaf(leaf); }); - * - * @endcode */ template<typename InputTreeIterator, typename UnaryFunction> - inline auto for_each_leaf( - InputTreeIterator begin, InputTreeIterator end, UnaryFunction f) -> UnaryFunction + inline auto for_each_leaf(InputTreeIterator begin, InputTreeIterator end, UnaryFunction f) -> UnaryFunction { auto group_leaf_iterator_begin = std::get<0>(begin); auto group_leaf_iterator_end = std::get<0>(end); @@ -107,6 +114,17 @@ namespace scalfmm::component return f; } + + /** + * @brief + * + * @tparam InputTreeIterator + * @tparam UnaryFunction + * @param begin + * @param end + * @param f + * @return UnaryFunction + */ template<typename InputTreeIterator, typename UnaryFunction> inline auto for_each_mine_leaf(InputTreeIterator begin, InputTreeIterator end, UnaryFunction f) -> UnaryFunction { @@ -120,6 +138,7 @@ namespace scalfmm::component return f; } + /** * @brief iterate en two (same) leaf struture (same groupe size) * @@ -158,6 +177,13 @@ namespace scalfmm::component /** * @brief * + * code example to print cell info at level 3 + * @code + * calfmm::component::for_each_cell(tree.begin(), tree.end(), 3, + * [&tree](auto& cell) { scalfmm::io::print_cell(cell); }); + * + * @endcode + * * @tparam InputTreeIterator * @tparam UnaryFunction * @param begin begin iterator of the group tree @@ -165,17 +191,10 @@ namespace scalfmm::component * @param level level of the tree to iterate * @param f lambda function * @return UnaryFunction - * - * code example to print cell info at level 3 - * @code - * calfmm::component::for_each_cell(tree.begin(), tree.end(), 3, - * [&tree](auto& cell) { scalfmm::io::print_cell(cell); }); - * - * @endcode */ template<typename InputTreeIterator, typename UnaryFunction> - inline auto for_each_cell(InputTreeIterator begin, InputTreeIterator end, std::size_t level, UnaryFunction f) - -> UnaryFunction + inline auto for_each_cell(InputTreeIterator begin, InputTreeIterator end, std::size_t level, + UnaryFunction f) -> UnaryFunction { using iterator_type = std::decay_t<decltype(std::get<1>(begin))>; using difference_type = typename iterator_type::difference_type; @@ -194,6 +213,7 @@ namespace scalfmm::component return f; } + /** * @brief Iterate both en leaves and cells at leaf level * diff --git a/include/scalfmm/tree/group.hpp b/include/scalfmm/tree/group.hpp index cec79ee5e32c9809d76a73157ce1366b7daa3f99..c6bba98850a4d991a1ceb8d7a932b1939d2dcb8c 100644 --- a/include/scalfmm/tree/group.hpp +++ b/include/scalfmm/tree/group.hpp @@ -1,16 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : group_tree.hpp +// File : scalfmm/tree/group.hpp // -------------------------------- #ifndef SCALFMM_TREE_GROUP_HPP #define SCALFMM_TREE_GROUP_HPP -#include <algorithm> -#include <cstddef> -#include <iterator> -#include <type_traits> -#include <vector> - #include "scalfmm/meta/traits.hpp" #include "scalfmm/tree/header.hpp" #include "scalfmm/utils/massert.hpp" @@ -18,11 +12,18 @@ #include <cpp_tools/colors/colorized.hpp> +#include <algorithm> +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <vector> + namespace scalfmm::component { /** * @brief Group class class manages cells or leaves in block allocation * + * @tparam Component */ template<typename Component> struct group @@ -36,13 +37,53 @@ namespace scalfmm::component using group_source_type = group; using iterator_source_type = iterator_type; + /** + * @brief Construct a new group object + * + */ group() = default; + + /** + * @brief Construct a new group object + * + */ group(group const&) = default; + + /** + * @brief Construct a new group object + * + */ group(group&&) noexcept = default; + + /** + * @brief + * + * @return group& + */ inline auto operator=(group const&) -> group& = default; + + /** + * @brief + * + * @return group& + */ inline auto operator=(group&&) noexcept -> group& = default; + + /** + * @brief Destroy the group object + * + */ ~group() = default; + /** + * @brief Construct a new group object + * + * @param starting_morton_idx + * @param ending_morton_idx + * @param number_of_component + * @param index_global + * @param is_mine + */ explicit group(std::size_t starting_morton_idx, std::size_t ending_morton_idx, std::size_t number_of_component, std::size_t index_global = 0, bool is_mine = true) : m_vector_of_component(number_of_component) @@ -51,13 +92,16 @@ namespace scalfmm::component { } + /** + * @brief + * + * @tparam S + */ template<typename S = symbolics_type> explicit group(std::size_t starting_morton_idx, std::size_t ending_morton_idx, std::size_t number_of_component, std::size_t number_of_particles_in_group, std::size_t index_global = 0, bool is_mine = true, typename std::enable_if_t<meta::is_leaf_group_symbolics<S>::value>* /*unused*/ = nullptr) : m_vector_of_component(number_of_component) - // , m_symbolics{starting_morton_idx, ending_morton_idx, number_of_component, - // number_of_particles_in_group, index_global, is_mine} , m_number_of_component{number_of_component} { m_symbolics.starting_index = starting_morton_idx; @@ -67,12 +111,16 @@ namespace scalfmm::component m_symbolics.is_mine = is_mine; } + /** + * @brief + * + * @tparam S + */ template<typename S = symbolics_type> explicit group(std::size_t starting_morton_idx, std::size_t ending_morton_idx, std::size_t number_of_component, std::size_t index_global = 0, bool is_mine = true, typename std::enable_if_t<meta::is_leaf_group_symbolics<S>::value>* /*unused*/ = nullptr) : m_vector_of_component(number_of_component) - // , m_symbolics(starting_morton_idx, ending_morton_idx, number_of_component, 0, index_global, is_mine) , m_number_of_component{number_of_component} { m_symbolics.starting_index = starting_morton_idx; @@ -81,6 +129,7 @@ namespace scalfmm::component m_symbolics.idx_global = index_global; m_symbolics.is_mine = is_mine; } + /** * @brief Display the elements of the group (views and the storage) * @@ -93,28 +142,98 @@ namespace scalfmm::component grp.print(os); return os; } + /** * @brief return the symbolic structure of the group * * @return symbolics_type& */ [[nodiscard]] inline auto symbolics() -> symbolics_type& { return m_symbolics; } + + /** + * @brief return the symbolic structure of the group + * + * @return symbolics_type& + */ [[nodiscard]] inline auto symbolics() const -> symbolics_type const& { return m_symbolics; } + + /** + * @brief return the symbolic structure of the group + * + * @return symbolics_type& + */ [[nodiscard]] inline auto csymbolics() const -> symbolics_type const& { return m_symbolics; } + /** + * @brief + * + * @return block_type& + */ [[nodiscard]] inline auto components() -> block_type& { return m_vector_of_component; } + + /** + * @brief + * + * @return block_type& + */ [[nodiscard]] inline auto components() const -> block_type const& { return m_vector_of_component; } + + /** + * @brief + * + * @return block_type& + */ [[nodiscard]] inline auto ccomponents() const -> block_type const& { return m_vector_of_component; } + /** + * @brief + * + * @return iterator_type + */ [[nodiscard]] inline auto begin() -> iterator_type { return std::begin(m_vector_of_component); } + + /** + * @brief + * + * @return iterator_type + */ [[nodiscard]] inline auto begin() const -> const_iterator_type { return std::cbegin(m_vector_of_component); } + + /** + * @brief + * + * @return iterator_type + */ [[nodiscard]] inline auto cbegin() const -> const_iterator_type { return std::cbegin(m_vector_of_component); } + /** + * @brief + * + * @return iterator_type + */ [[nodiscard]] inline auto end() -> iterator_type { return std::end(m_vector_of_component); } + + /** + * @brief + * + * @return const_iterator_type + */ [[nodiscard]] inline auto end() const -> const_iterator_type { return std::cend(m_vector_of_component); } + + /** + * @brief + * + * @return const_iterator_type + */ [[nodiscard]] inline auto cend() const -> const_iterator_type { return std::cend(m_vector_of_component); } + /** + * @brief + * + * @return auto + */ [[nodiscard]] inline auto size() const noexcept { return m_number_of_component; } + /** * @brief Check if morton_index is inside the range of morton indexes of the group * @@ -126,6 +245,7 @@ namespace scalfmm::component { return ((morton_index >= m_symbolics.starting_index) && (morton_index < m_symbolics.ending_index)); } + /** * @brief Check if morton_index is below the last morton index of the group * @@ -137,6 +257,7 @@ namespace scalfmm::component { return ((morton_index < m_symbolics.ending_index)); } + /** * @brief Check if morton_index exists inside the group * @@ -149,24 +270,48 @@ namespace scalfmm::component return is_inside(morton_index) && (component_index(morton_index) != -1); } + /** + * @brief + * + * @param component_index + * @return component_type& + */ [[nodiscard]] inline auto component(std::size_t component_index) -> component_type& { assertm(component_index < std::size(m_vector_of_component), "Out of range in group."); return m_vector_of_component.at(component_index); } + /** + * @brief + * + * @param component_index + * @return component_type const& + */ [[nodiscard]] inline auto component(std::size_t component_index) const -> component_type const& { assertm(component_index < std::size(m_vector_of_component), "Out of range in group."); return m_vector_of_component.at(component_index); } + /** + * @brief + * + * @param component_index + * @return component_type const& + */ [[nodiscard]] inline auto ccomponent(std::size_t component_index) const -> component_type const& { assertm(component_index < std::size(m_vector_of_component), "Out of range in group."); return m_vector_of_component.at(component_index); } + /** + * @brief + * + * @param cell_index + * @return iterator_type + */ [[nodiscard]] inline auto component_iterator(std::size_t cell_index) -> iterator_type { assertm(cell_index < std::size(m_vector_of_component), "Out of range in group."); @@ -175,6 +320,12 @@ namespace scalfmm::component return it; } + /** + * @brief + * + * @param cell_index + * @return const_iterator_type + */ [[nodiscard]] inline auto component_iterator(std::size_t cell_index) const -> const_iterator_type { assertm(cell_index < std::size(m_vector_of_component), "Out of range in group."); @@ -183,6 +334,12 @@ namespace scalfmm::component return it; } + /** + * @brief + * + * @param cell_index + * @return const_iterator_type + */ [[nodiscard]] inline auto ccomponent_iterator(std::size_t cell_index) const -> const_iterator_type { assertm(cell_index < std::size(m_vector_of_component), "Out of range in group."); @@ -191,6 +348,13 @@ namespace scalfmm::component return it; } + /** + * @brief + * + * @tparam MortonIndex + * @param morton_index + * @return int + */ template<typename MortonIndex = std::size_t> [[nodiscard]] inline auto component_index(MortonIndex morton_index) const -> int { @@ -216,6 +380,15 @@ namespace scalfmm::component return -1; } + /** + * @brief + * + * @tparam MortonIndex + * @param morton_index + * @param idx_left + * @param idx_right + * @return int + */ template<typename MortonIndex = std::size_t> [[nodiscard]] inline auto component_index(MortonIndex morton_index, int idx_left, int idx_right) const -> int { @@ -238,20 +411,41 @@ namespace scalfmm::component } return -1; } - void print(std::ostream& os) const + + /** + * @brief + * + * @param os + */ + inline auto print(std::ostream& os) const -> void { os << "group: idx=" << m_symbolics.idx_global << " range [" << m_symbolics.starting_index << ", " << m_symbolics.ending_index << "[ is_mine " << std::boolalpha << m_symbolics.is_mine << "\n"; } - void print() const { this->print(std::cout); } + + /** + * @brief + * + */ + inline auto print() const -> void { this->print(std::cout); } private: - //< Vector of (component (cells or leaves) inside the group + /** + * @brief Vector of (component (cells or leaves) inside the group + * + */ block_type m_vector_of_component{}; - //< the symbolic information of the group + + /** + * @brief the symbolic information of the group + * + */ symbolics_type m_symbolics{}; - //< The number of component in the block same as m_vector_of_component.size() @todo to remove + /** + * @brief The number of component in the block same as m_vector_of_component.size() @todo to remove. + * + */ const std::size_t m_number_of_component{}; }; diff --git a/include/scalfmm/tree/group_let.hpp b/include/scalfmm/tree/group_let.hpp index 91e10d06907750bb34fb2192a651f30b208b3a1c..aa52cfc47b87263389a93a20bed6e0c149a96674 100644 --- a/include/scalfmm/tree/group_let.hpp +++ b/include/scalfmm/tree/group_let.hpp @@ -1,21 +1,9 @@ -#ifndef SCALFMM_TREE_LET_HPP +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/tree/group_let.hpp +// -------------------------------- +#ifndef SCALFMM_TREE_LET_HPP #define SCALFMM_TREE_LET_HPP -#include <algorithm> -#include <array> -#include <fstream> -#include <iostream> -#include <iterator> -#include <string> -#include <tuple> -#include <utility> -#include <vector> - -#include <cpp_tools/parallel_manager/parallel_manager.hpp> -#include <cpp_tools/colors/colorized.hpp> - -#include <scalfmm/tree/utils.hpp> -#include <scalfmm/utils/io_helpers.hpp> // for io::print -#include <scalfmm/utils/math.hpp> #include "scalfmm/container/particle_container.hpp" #include "scalfmm/lists/sequential.hpp" @@ -24,8 +12,11 @@ #include "scalfmm/parallel/mpi/utils.hpp" #include "scalfmm/parallel/utils.hpp" #include "scalfmm/tree/for_each.hpp" -#ifdef SCALFMM_USE_MPI +#include "scalfmm/tree/utils.hpp" +#include "scalfmm/utils/io_helpers.hpp" +#include "scalfmm/utils/math.hpp" +#ifdef SCALFMM_USE_MPI #include <inria/algorithm/distributed/distribute.hpp> #include <inria/algorithm/distributed/mpi.hpp> #include <inria/algorithm/distributed/sort.hpp> @@ -33,15 +24,33 @@ #include <mpi.h> #endif +#include <cpp_tools/colors/colorized.hpp> +#include <cpp_tools/parallel_manager/parallel_manager.hpp> + +#include <algorithm> +#include <array> +#include <fstream> +#include <iostream> +#include <iterator> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + namespace scalfmm::tree { using morton_type = std::int64_t; // typename Tree_type:: - template<typename MortonIdx> + /** + * @brief + * + * @tparam MortonIdxType + */ + template<typename MortonIdxType> struct leaf_info_type { - using morton_type = MortonIdx; - MortonIdx morton{}; + using morton_type = MortonIdxType; + MortonIdxType morton{}; std::size_t number_of_particles{}; friend std::ostream& operator<<(std::ostream& os, const leaf_info_type& w) { @@ -53,11 +62,18 @@ namespace scalfmm::tree namespace let { - template<typename Box, typename VectorLeafInfo, typename MortonDistribution> - inline /*std::vector<morton_type>*/ VectorLeafInfo - get_ghosts_p2p_interaction(cpp_tools::parallel_manager::parallel_manager& para, Box const& box, - std::size_t const& level, int const& separation, VectorLeafInfo const& leaf_info, - MortonDistribution const& leaves_distrib) + /** + * @brief + * + * @tparam BoxType + * @tparam VectorLeafInfoType + * @tparam MortonDistributionType + */ + template<typename BoxType, typename VectorLeafInfoType, typename MortonDistributionType> + inline /*std::vector<morton_type>*/ VectorLeafInfoType + get_ghosts_p2p_interaction(cpp_tools::parallel_manager::parallel_manager& para, BoxType const& box, + std::size_t const& level, int const& separation, VectorLeafInfoType const& leaf_info, + MortonDistributionType const& leaves_distrib) { std::vector<morton_type> ghost_to_add; auto const& period = box.get_periodicity(); @@ -67,17 +83,14 @@ namespace scalfmm::tree for(auto const& info: leaf_info) { auto const& morton_index = info.morton; - auto coordinate{index::get_coordinate_from_morton_index<Box::dimension>(morton_index)}; + auto coordinate{index::get_coordinate_from_morton_index<BoxType::dimension>(morton_index)}; auto interaction_neighbors = index::get_neighbors(coordinate, level, period, separation); auto& list = std::get<0>(interaction_neighbors); auto nb = std::get<1>(interaction_neighbors); int it{0}; - //io::print("rank(" + std::to_string(rank) + ") list idx(p2p) : ", list); while(list[it] < my_distrib[0]) { - // std::cout << "INSIDE left idx " << list[it] << " " << std::boolalpha - // << parallel::utils::is_inside_distrib(list[it], leaves_distrib) << std::endl; if(parallel::utils::is_inside_distrib_left(list[it], rank, leaves_distrib)) { ghost_to_add.push_back(list[it]); @@ -87,8 +100,6 @@ namespace scalfmm::tree it = nb - 1; while(list[it] >= my_distrib[1]) { - // std::cout << "INSIDE right idx " << list[it] << " " << std::boolalpha - // << parallel::utils::is_inside_distrib(list[it], leaves_distrib) << std::endl; if(parallel::utils::is_inside_distrib_right(list[it], rank, leaves_distrib)) { ghost_to_add.push_back(list[it]); @@ -99,7 +110,7 @@ namespace scalfmm::tree std::sort(ghost_to_add.begin(), ghost_to_add.end()); auto last = std::unique(ghost_to_add.begin(), ghost_to_add.end()); ghost_to_add.erase(last, ghost_to_add.end()); - VectorLeafInfo ghost_leaf_to_add(ghost_to_add.size()); + VectorLeafInfoType ghost_leaf_to_add(ghost_to_add.size()); for(int i = 0; i < ghost_to_add.size(); ++i) { ghost_leaf_to_add[i] = {ghost_to_add[i], 0}; @@ -107,25 +118,32 @@ namespace scalfmm::tree return ghost_leaf_to_add; } - /// - /// \brief get theoretical m2l interaction list outside me - /// - /// We return the list of indexes of cells involved in P2P interaction that we do - /// not have locally. The cells on other processors may not exist. - /// - /// \param[in] para the parallel manager - /// \param tree the tree used to compute the interaction - /// \param local_morton_idx the local morton index of the cells - /// \param cell_distrib the cells distribution on the processes - /// \return the list of indexes on tother processes - /// - template<typename Box, typename VectorMortonIdx, typename MortonDistribution> - inline VectorMortonIdx - get_ghosts_m2l_interaction(cpp_tools::parallel_manager::parallel_manager& para, Box const& box, - const std::size_t& level, int const& separation, - VectorMortonIdx const& local_morton_vect, MortonDistribution const& cell_distrib) + + /** + * @brief get theoretical m2l interaction list outside me + * + * We return the list of indexes of cells involved in P2P interaction that we do + * not have locally. The cells on other processors may not exist. + * + * @tparam BoxType + * @tparam VectorMortonIdxType + * @tparam MortonDistributionType + * @param[in] para the parallel manager + * @param box + * @param level + * @param separation + * @param local_morton_vect + * @param cell_distrib the cells distribution on the processes + * @return the list of indexes on tother processes + */ + template<typename BoxType, typename VectorMortonIdxType, typename MortonDistributionType> + inline VectorMortonIdxType get_ghosts_m2l_interaction(cpp_tools::parallel_manager::parallel_manager& para, + BoxType const& box, const std::size_t& level, + int const& separation, + VectorMortonIdxType const& local_morton_vect, + MortonDistributionType const& cell_distrib) { - VectorMortonIdx ghost_to_add; + VectorMortonIdxType ghost_to_add; auto const& period = box.get_periodicity(); const auto rank = para.get_process_id(); auto const my_distrib = cell_distrib[rank]; @@ -134,64 +152,36 @@ namespace scalfmm::tree for(auto morton_index: local_morton_vect) { // for each index in the vector of cells in local_morton_vect we compute the m2l interactions - auto coordinate{index::get_coordinate_from_morton_index<Box::dimension>(morton_index)}; + auto coordinate{index::get_coordinate_from_morton_index<BoxType::dimension>(morton_index)}; auto interaction_m2l_list = index::get_m2l_list(coordinate, level, period, separation); auto& list = std::get<0>(interaction_m2l_list); auto nb = std::get<2>(interaction_m2l_list); - // - // io::print("rank(" + std::to_string(rank) + ") list idx(m2l) : ", list); - // io::print("rank(" + std::to_string(rank) + ") my_distrib : ", my_distrib); int it{0}; // We check if the cells are in the distribution for(auto it = 0; it < nb; ++it) { - // if(list[it] > my_distrib[0]) - // std::cout << list[it] << " " << std::boolalpha - // << math::between(list[it], my_distrib[0], my_distrib[1]) << std::endl; - if(math::between(list[it], my_distrib[0], my_distrib[1])) { break; } bool check{false}; - // for(int i = 0; i < rank; ++i) for(int i = rank - 1; i >= 0; i--) { auto const& interval = cell_distrib[i]; - // // if(rank == 2) - // { - // std::cout << "parallel::utils::is_inside_distrib_left list[it]: " << interval[0] << " < " - // << list[it] - // << " < " << interval[1] << std::endl; - // } check = math::between(list[it], interval[0], interval[1]); if(check) { break; } } - // std::cout << " " << list[it] << " " << std::boolalpha << check << std::endl; if(check) // parallel::utils::is_inside_distrib_left(list[it], rank, cell_distrib)) { ghost_to_add.push_back(list[it]); } } - // while(list[it] < my_distrib[0]) - // { - // std::cout << it << " INSIDE left idx " << list[it] << " " << std::boolalpha - // << parallel::utils::is_inside_distrib(list[it], cell_distrib) << std::endl; - // if(parallel::utils::is_inside_distrib_left(list[it], rank, cell_distrib)) - // { - // ghost_to_add.push_back(list[it]); - // } - // ++it; - // if(it > nb) - // { - // break; - // } - // } + it = nb - 1; if(not last_proc) // No ghost on the right on last process { @@ -204,20 +194,22 @@ namespace scalfmm::tree --it; } } - // if(rank == 2) - // { - // io::print("rank(" + std::to_string(rank) + ") tmp ghost_to_add(m2l) : ", ghost_to_add); - // } } std::sort(ghost_to_add.begin(), ghost_to_add.end()); auto last = std::unique(ghost_to_add.begin(), ghost_to_add.end()); ghost_to_add.erase(last, ghost_to_add.end()); - // io::print("rank(" + std::to_string(rank) + ") cell_distrib: ", cell_distrib); - // io::print("rank(" + std::to_string(rank) + ") ghost_to_add(m2l): ", ghost_to_add); return ghost_to_add; } + /** + * @brief + * + * @tparam VectorLeafInfoType + * @param localLeaves + * @param ghosts + * @return auto + */ template<typename VectorLeafInfoType> auto merge_split_structure(VectorLeafInfoType const& localLeaves, VectorLeafInfoType const& ghosts) { @@ -265,6 +257,7 @@ namespace scalfmm::tree return std::make_tuple(morton, number_of_particles); } + /** * @brief Split the LeafInfo structure in two vectors (Morton, number_of_particles) * @@ -289,6 +282,15 @@ namespace scalfmm::tree } return std::make_tuple(morton, number_of_particles); } + + /** + * @brief + * + * @tparam VectorLeafInfoIteratorType + * @param begin + * @param end + * @return auto + */ template<typename VectorLeafInfoIteratorType> auto split_structure(const VectorLeafInfoIteratorType begin, const VectorLeafInfoIteratorType end) { @@ -317,8 +319,8 @@ namespace scalfmm::tree * @return the vector ot the three blocs */ template<typename VectorBlockType> - VectorBlockType merge_blocs(VectorBlockType const& bloc1, VectorBlockType const& bloc2, - VectorBlockType const& bloc3) + auto merge_blocs(VectorBlockType const& bloc1, VectorBlockType const& bloc2, + VectorBlockType const& bloc3) -> VectorBlockType { // Merge the three block structure auto size = bloc1.size() + bloc2.size() + bloc3.size(); @@ -339,33 +341,33 @@ namespace scalfmm::tree } return all_blocks; } + /** * @brief Construct the M2M ghost for the current level * * The routine check if there is ghosts during the M2M operation. * If yes, we exchange the ghost indexes - * @tparam Box - * @tparam VectorMortonIdx - * @tparam MortonDistribution + * @tparam BoxType + * @tparam VectorMortonIdxType + * @tparam MortonDistributionType * @param para the parallel manager * @param box the simulation box * @param level the current level * @param local_morton_vect * @param cells_distrib teh distribution of cells * @param top if top is true nothing is down - * @return VectorMortonIdx + * @return VectorMortonIdxType */ - template<typename Box, typename VectorMortonIdx, typename MortonDistribution> - [[nodiscard]] auto build_ghost_m2m_let_at_level(cpp_tools::parallel_manager::parallel_manager& para, Box& box, - const int& level, const VectorMortonIdx& local_morton_vect, - const MortonDistribution& cells_distrib, bool top = false) - -> VectorMortonIdx + template<typename BoxType, typename VectorMortonIdxType, typename MortonDistributionType> + [[nodiscard]] auto build_ghost_m2m_let_at_level(cpp_tools::parallel_manager::parallel_manager& para, + BoxType& box, const int& level, + const VectorMortonIdxType& local_morton_vect, + const MortonDistributionType& cells_distrib, + bool top = false) -> VectorMortonIdxType { - using morton_type = typename VectorMortonIdx::value_type; - static constexpr int nb_children = math::pow(2, Box::dimension); - VectorMortonIdx ghosts; - // std::cout << " begin build_ghost_m2m_let_at_level " << level << std::endl; - // io::print("local_morton_vect: ",local_morton_vect); + using morton_type = typename VectorMortonIdxType::value_type; + static constexpr int nb_children = math::pow(2, BoxType::dimension); + VectorMortonIdxType ghosts; if(top) return ghosts; const auto rank = para.get_process_id(); @@ -380,26 +382,23 @@ namespace scalfmm::tree // the children belong on two processes std::array<morton_type, nb_children> send{}, recv{}; // - // parallel::utils::print_distrib("level_dist[leaf_level]): ", rank, cells_distrib); + // parallel::utils::print_distrib("level_dist[leaf_level]): ", rank, cells_distrib); bool comm_left{false}, comm_right{false}; // Check on left if(rank > 0) { auto first_index = local_morton_vect[0]; - auto parent_first = first_index >> Box::dimension; - auto last_parent_previous_proc = cells_distrib[rank - 1][1] >> Box::dimension; - // std::cout << "index : " << first_index << " Parent ! " << parent_first << " " << last_parent_previous_proc << std::endl; + auto parent_first = first_index >> BoxType::dimension; + auto last_parent_previous_proc = cells_distrib[rank - 1][1] >> BoxType::dimension; if(parent_first == last_parent_previous_proc) { comm_left = true; - // std::cout << "Need to exchange between " << rank << " and " << rank - 1 << std::endl; int idx{1}; send[idx] = local_morton_vect[0]; - for(int i = 1; i < std::min(nb_children,int(local_morton_vect.size())); ++i) + for(int i = 1; i < std::min(nb_children, int(local_morton_vect.size())); ++i) { - auto parent_index = local_morton_vect[i] >> Box::dimension; - // std::cout << "index : " << local_morton_vect[i] << " Parent ! " << parent_first << " " << last_parent_previous_proc << std::endl; + auto parent_index = local_morton_vect[i] >> BoxType::dimension; if(parent_index == last_parent_previous_proc) { ++idx; @@ -410,29 +409,28 @@ namespace scalfmm::tree break; } } - // std::cout << "nbindex to send " <<idx <<std::endl; + // std::cout << "nbindex to send " <<idx <<std::endl; send[0] = idx; - // io::print(" index to send ", send); + // io::print(" index to send ", send); comm.isend(send.data(), nb_children, mpi_type, rank - 1, tag); } } - // std::cout << "check right\n "; + // std::cout << "check right\n "; auto last_index = local_morton_vect[local_morton_vect.size() - 1]; - auto parent_last = last_index >> Box::dimension; - // std::cout << "last_index " << last_index << " parent_last " << parent_last <<std::endl; + auto parent_last = last_index >> BoxType::dimension; + // std::cout << "last_index " << last_index << " parent_last " << parent_last <<std::endl; ghosts.resize(0); if(rank < proc - 1) { // check on left - auto first_parent_next_proc = cells_distrib[rank + 1][0] >> Box::dimension; - // std::cout << "Parent ! " << parent_last << " " << first_parent_next_proc << std::endl; + auto first_parent_next_proc = cells_distrib[rank + 1][0] >> BoxType::dimension; + // std::cout << "Parent ! " << parent_last << " " << first_parent_next_proc << std::endl; if(parent_last == first_parent_next_proc) { comm_right = true; - // std::cout << "Need to exchange between " << rank << " and " << rank + 1 << std::endl; + // std::cout << "Need to exchange between " << rank << " and " << rank + 1 << std::endl; /*mpi_status_right =*/comm.recv(recv.data(), nb_children, mpi_type, rank + 1, tag); - // cpp_tools::parallel_manager::mpi::request::waitall(1, &mpi_status_right); - // io::print("recv ",recv ); + ghosts.resize(recv[0]); for(int i = 0; i < ghosts.size(); ++i) { @@ -440,43 +438,42 @@ namespace scalfmm::tree } } } - // io::print("m2m ghosts ", ghosts); - // std::cout << " end build_ghost_m2m_let_at_level" << std::endl; + return ghosts; } - /// - /// \brief construct the local essential tree (LET) at the level. - /// - /// We start from a given Morton index distribution and we compute all - /// interactions needed - /// in the algorithm steps. - /// At the leaf level it corresponds to the interactions coming from the - /// direct pass (P2P operators) - /// and in the transfer pass (M2L operator). For the other levels we - /// consider only the M2L interactions. - /// The leaves_distrib and the cells_distrib might be different - /// At the end the let has also all the interaction list computed - /// - /// \param[inout] tree the tree to compute the let. - /// \param[in] local_morton_idx the morton index of the particles in the - /// processors. - /// - /// - /// \param[in] cells_distrib the morton index distribution for - /// the cells at the leaf level. - /// - /// \param[in] level the level to construct the let - /// - template<typename Box, typename VectorMortonIdx, typename MortonDistribution> - [[nodiscard]] auto build_let_at_level(cpp_tools::parallel_manager::parallel_manager& para, Box& box, - const int& level, const VectorMortonIdx& local_morton_vect, - const MortonDistribution& cells_distrib, const int& separation) - -> VectorMortonIdx + + /** + * @brief construct the local essential tree (LET) at the level. + * + * We start from a given Morton index distribution and we compute all + * interactions needed + * in the algorithm steps. + * At the leaf level it corresponds to the interactions coming from the + * direct pass (P2P operators) + * and in the transfer pass (M2L operator). For the other levels we + * consider only the M2L interactions. + * The leaves_distrib and the cells_distrib might be different + * At the end the let has also all the interaction list computed + * + * @tparam BoxType + * @tparam VectorMortonIdxType + * @tparam MortonDistributionType + * @param para + * @param box + * @param[in] level the level to construct the let + * @param local_morton_vect + * @param[in] cells_distrib the morton index distribution for + * the cells at the leaf level. + * @param separation + * @return VectorMortonIdxType + */ + template<typename BoxType, typename VectorMortonIdxType, typename MortonDistributionType> + [[nodiscard]] auto build_let_at_level(cpp_tools::parallel_manager::parallel_manager& para, BoxType& box, + const int& level, const VectorMortonIdxType& local_morton_vect, + const MortonDistributionType& cells_distrib, + const int& separation) -> VectorMortonIdxType { const auto my_rank = para.get_process_id(); - // std::cout << cpp_tools::colors::red << " --> Begin let::build_let_at_level() at level = " << level - // << "dist: " << cells_distrib[my_rank] << cpp_tools::colors::reset << std::endl; - // io::print("rank(" + std::to_string(my_rank) + ") local_morton_vect : ", local_morton_vect); // we compute the cells needed in the M2L operator @@ -488,15 +485,10 @@ namespace scalfmm::tree std::cout << std::flush; /// Look if the morton index really exists in the distributed tree parallel::utils::check_if_morton_index_exist(para, needed_idx, cells_distrib, local_morton_vect); - /// - // io::print("rank(" + std::to_string(my_rank) + ") check_if_morton_index_exist(m2l) : ", needed_idx); - // - // std::cout << cpp_tools::colors::red - // << "rank(" + std::to_string(my_rank) + ")-- > End let::build_let_at_level() " - // << cpp_tools::colors::reset << std::endl; return needed_idx; - } + } + // template<typename OctreeTree, typename VectorMortonIdx, typename MortonDistribution> // void build_let_at_level(cpp_tools::parallel_manager::parallel_manager& para, OctreeTree& tree, // const VectorMortonIdx& local_morton_idx, const MortonDistribution& cells_distrib, @@ -524,12 +516,13 @@ namespace scalfmm::tree // std::cout << cpp_tools::colors::green << " --> End let::build_let_at_level() at level = " << level // << cpp_tools::colors::reset << std::endl; // } + /** * @brief * - * @tparam Box - * @tparam VectorMortonIdx - * @tparam MortonDistribution + * @tparam BoxType + * @tparam VectorMortonIdxType + * @tparam MortonDistributionType * @param para * @param box * @param level @@ -537,19 +530,15 @@ namespace scalfmm::tree * @param leaves_distrib * @param separation */ - template<typename Box, typename VectorLeafInfo, typename MortonDistribution> - [[nodiscard]] auto build_let_leaves(cpp_tools::parallel_manager::parallel_manager& para, Box const& box, + template<typename BoxType, typename VectorLeafInfo, typename MortonDistributionType> + [[nodiscard]] auto build_let_leaves(cpp_tools::parallel_manager::parallel_manager& para, BoxType const& box, const std::size_t& level, const VectorLeafInfo& leaf_info /*local_morton_vect*/, - MortonDistribution const& leaves_distrib, const int& separation) + MortonDistributionType const& leaves_distrib, const int& separation) -> VectorLeafInfo { - auto my_rank = para.get_process_id(); - // std::cout << cpp_tools::colors::green - // << "rank(" + std::to_string(my_rank) + ") --> Begin let::build_let_leaves() " - // << cpp_tools::colors::reset << std::endl; - // io::print("rank(" + std::to_string(my_rank) + ") leaf_info : ", leaf_info); + auto my_rank = para.get_process_id(); // we compute the leaves involved in the P2P operators auto leaf_info_to_add = @@ -565,10 +554,8 @@ namespace scalfmm::tree /// needed_idx input contains the Morton index of leaf /// output contains the number of particles in the leaf - // io::print("rank(" + std::to_string(my_rank) + ") 1 leaf_info_to_add(p2p) : ", leaf_info_to_add); - parallel::utils::check_if_leaf_morton_index_exist(para, needed_idx, leaves_distrib, leaf_info); - // io::print("rank(" + std::to_string(my_rank) + ") check needed_idx.size : ", needed_idx); - int idx{0}; + parallel::utils::check_if_leaf_morton_index_exist(para, needed_idx, leaves_distrib, leaf_info); + int idx{0}; for(int i = 0; i < needed_idx.size(); ++i) { @@ -584,54 +571,49 @@ namespace scalfmm::tree auto last = leaf_info_to_add.cbegin() + idx; leaf_info_to_add.erase(last, leaf_info_to_add.end()); } - /// - // io::print("rank(" + std::to_string(my_rank) + ") final leaf_info_to_add(p2p) : ", leaf_info_to_add); - // std::cout << cpp_tools::colors::green - // << "rank(" + std::to_string(my_rank) + ")-- > End let::build_let_leaves() " - // << cpp_tools::colors::reset << std::endl; + return leaf_info_to_add; } - /// - /// \brief buildLetTree Build the let of the tree and the leaves and cells distributions - /// - /// The algorithm has 5 steps: - /// 1) We sort the particles according to their Morton Index (leaf level) - /// 2) Build the leaf morton vector of my local particles and construct either - /// the leaves distribution or the cell distribution according to parameter - /// use_leaf_distribution or use_particle_distribution - /// 3) Fit the particles inside the use_leaf_distribution - /// 4) Construct the tree according to my particles and build the leaf - /// morton vector of my local particles - /// 5) Constructing the let level by level - /// - /// \param[in] manager the parallel manager - /// \param[in] number_of_particles total number of particles in the simulation - /// \param[in] particle_container vector of particles on my node. On output the - /// array is sorted and correspond to teh distribution built - /// \param[in] box size of the simulation box - /// \param[in] leaf_level level of the leaf in the tree - /// \param[in] level_shared the level at which cells are duplicated on processors. If the level is negative, - /// nothing is duplicated. - /// \param[in] groupSizeLeaves blocking parameter for the leaves (particles) - /// \param[in] groupSizeCells blocking parameter for the cells - /// @param[in] order order of the approximation to build the tree - /// @param[in] use_leaf_distribution to say if you consider the leaf distribution - /// @param[in] use_particle_distribution to say if you consider the particle distribution - /// @return localGroupTree the LET of the octree - - /// processors - template<typename Tree_type, typename Vector_type, typename Box_type> - Tree_type - buildLetTree(cpp_tools::parallel_manager::parallel_manager& manager, const std::size_t& number_of_particles, - Vector_type& particle_container, const Box_type& box, const int& leaf_level, - const int& level_shared, const int groupSizeLeaves, const int groupSizeCells, const int order, - const int separation, const bool use_leaf_distribution, const bool use_particle_distribution) + /** + * @brief buildLetTree Build the let of the tree and the leaves and cells distributions + * + * The algorithm has 5 steps: + * 1) We sort the particles according to their Morton Index (leaf level) + * 2) Build the leaf morton vector of my local particles and construct either + * the leaves distribution or the cell distribution according to parameter + * use_leaf_distribution or use_particle_distribution + * 3) Fit the particles inside the use_leaf_distribution + * 4) Construct the tree according to my particles and build the leaf + * morton vector of my local particles + * 5) Constructing the let level by level + * + * @tparam TreeType + * @tparam VectorType + * @tparam BoxType + * @param[in] manager the parallel manager + * @param[in] number_of_particles total number of particles in the simulation + * @param[in] particle_container vector of particles on my node. On output the array is sorted and correspond to teh distribution built + * @param[in] box size of the simulation box + * @param[in] leaf_level level of the leaf in the tree + * @param[in] level_shared the level at which cells are duplicated on processors. If the level is negative, + * nothing is duplicated. + * @param[in] groupSizeLeaves blocking parameter for the leaves (particles) + * @param[in] groupSizeCells blocking parameter for the cells + * @param[in] order order of the approximation to build the tree + * @param[in] use_leaf_distribution to say if you consider the leaf distribution + * @param[in] use_particle_distribution to say if you consider the particle distribution + * @return localGroupTree the LET of the octree processors + */ + template<typename TreeType, typename VectorType, typename BoxType> + auto buildLetTree(cpp_tools::parallel_manager::parallel_manager& manager, + const std::size_t& number_of_particles, VectorType& particle_container, const BoxType& box, + const int& leaf_level, const int& level_shared, const int groupSizeLeaves, + const int groupSizeCells, const int order, const int separation, + const bool use_leaf_distribution, const bool use_particle_distribution) -> TreeType { - // std::cout << cpp_tools::colors::green << " --> Begin let::group_let() " << cpp_tools::colors::reset - // << std::endl; // - static constexpr std::size_t dimension = Vector_type::value_type::dimension; + static constexpr std::size_t dimension = VectorType::value_type::dimension; const auto rank = manager.get_process_id(); //////////////////////////////////////////////////////////////////////////// /// Sort the particles at the leaf level according to their Morton index @@ -647,7 +629,8 @@ namespace scalfmm::tree }); #else std::sort(particle_container.begin(), particle_container.end(), - [&box, &leaf_level](const auto& p1, const auto& p2) { + [&box, &leaf_level](const auto& p1, const auto& p2) + { auto m1 = scalfmm::index::get_morton_index(p1.position(), box, leaf_level); auto m2 = scalfmm::index::get_morton_index(p2.position(), box, leaf_level); return m1 < m2; @@ -663,21 +646,21 @@ namespace scalfmm::tree { particleMortonIndex[part] = scalfmm::index::get_morton_index(particle_container[part].position(), box, leaf_level); - // std::cout << part << " m " << particleMortonIndex[part] << particle_container[part] << std::endl; + // std::cout << part << " m " << particleMortonIndex[part] << particle_container[part] << std::endl; } auto leafMortonIdx(particleMortonIndex); // delete duplicate indexes auto last = std::unique(leafMortonIdx.begin(), leafMortonIdx.end()); leafMortonIdx.erase(last, leafMortonIdx.end()); /////////////////////////////////////////////////////////////////////////////////// - io::print("rank(" + std::to_string(rank) + ") --> init leafMortonIdx: ", leafMortonIdx); + io::print("rank(" + std::to_string(rank) + ") --> init leafMortonIdx: ", leafMortonIdx); /// //////////////////////////////////////////////////////////////////////////////////////////// //// Construct a uniform distribution for the leaves/cells at the leaves level /// /// A morton index should be own by only one process /// - using morton_distrib_type = typename Tree_type::data_distrib_type; + using morton_distrib_type = typename TreeType::data_distrib_type; /// /// Build a uniform distribution of the leaves/cells @@ -692,7 +675,6 @@ namespace scalfmm::tree interval[1] += 1; } } - // io::print("rank(" + std::to_string(rank) + ") --> leaves_distrib: ", leaves_distrib); //// End //////////////////////////////////////////////////////////////////////////////////////////// /// @@ -722,7 +704,7 @@ namespace scalfmm::tree { particles_distrib = leaves_distrib; } - if(manager.io_master()) + if(manager.io_master()) { std::cout << cpp_tools::colors::red; parallel::utils::print_distrib(" --> particles_distrib:", rank, particles_distrib); @@ -739,14 +721,13 @@ namespace scalfmm::tree /// parallel::utils::fit_particles_in_distrib(manager, particle_container, particleMortonIndex, particles_distrib, box, leaf_level, number_of_particles); - // io::print("rank(" + std::to_string(rank) + ") --> particle_container: ", particle_container); /// All the particles are located on the good process //////////////////////////////////////////////////////////////////////////////////////////// /// /// Construct the local tree based on our set of particles // Build and empty tree - Tree_type localGroupTree(manager, static_cast<std::size_t>(leaf_level + 1), level_shared, order, - groupSizeLeaves, groupSizeCells, box); + TreeType localGroupTree(manager, static_cast<std::size_t>(leaf_level + 1), level_shared, order, + groupSizeLeaves, groupSizeCells, box); /// Set true because the particles are already sorted /// In fact we have all the leaves to add in leafMortonIdx - could be used to construct /// the tree !!! @@ -754,9 +735,6 @@ namespace scalfmm::tree #ifdef SCALFMM_USE_MPI - // std::cout << cpp_tools::colors::red; - // io::print("rank(" + std::to_string(rank) + ") leafMortonIdx: ", leafMortonIdx); - // std::cout << cpp_tools::colors::reset << std::endl; /// End //////////////////////////////////////////////////////////////////////////////////////////// /// @@ -768,7 +746,6 @@ namespace scalfmm::tree { leafMortonIdx[i] = scalfmm::index::get_morton_index(particle_container[i].position(), box, leaf_level); } - // io::print("rank(" + std::to_string(rank) + ") --> leafMortonIdx: ", leafMortonIdx); // localLeafInfo contains information on leaves (morton, number of particles) own by th current process std::vector<tree::leaf_info_type<morton_type>> localLeafInfo(leafMortonIdx.size()); @@ -793,8 +770,6 @@ namespace scalfmm::tree } leafMortonIdx.resize(idx + 1); localLeafInfo.resize(leafMortonIdx.size()); - // io::print("rank(" + std::to_string(rank) + ") --> localLeafInfo: ", localLeafInfo); - // io::print("rank(" + std::to_string(rank) + ") --> leafMortonIdx: ", leafMortonIdx); //////////////////////////////////////////////////////////////////////////////////////// // Build the pointer of the tree with all parameters @@ -808,17 +783,9 @@ namespace scalfmm::tree auto ghostP2P_leafInfo = build_let_leaves(manager, box, leaf_level, localLeafInfo, particles_distrib, separation); - // io::print("rank(" + std::to_string(rank) + ") --> final ghostP2P_leafInfo: ", - // ghostP2P_leafInfo); io::print("rank(" + std::to_string(rank) + ") --> final localLeafInfo: ", - // localLeafInfo); localGroupTree.set_leaf_distribution(particles_distrib); - // std::cout << std::flush; - // std::cout << cpp_tools::colors::red; - // std::cout << "END LEAF LEVEL " << std::endl; - // std::cout << cpp_tools::colors::reset; - /// If the distribution is not the same for the leaf and the cell we redistribute the /// morton index according to the uniform distribution of morton index /// @@ -850,82 +817,57 @@ namespace scalfmm::tree auto ghost_m2l_cells = build_let_at_level(manager, box, leaf_level, leafMortonIdx, level_dist[leaf_level], separation); - // io::print("rank(" + std::to_string(rank) + ") --> final ghost_cells(m2l): ", ghost_m2l_cells); auto ghost_m2m_cells = build_ghost_m2m_let_at_level(manager, box, leaf_level, leafMortonIdx, level_dist[leaf_level]); - // io::print("rank(" + std::to_string(rank) + ") --> ghost_cells(m2m): ", ghost_m2m_cells); // distribution, particles - // std::cout << " $$$$$$$$$$$$$$$$$$$$$$$$$ leaf level " << leaf_level << " $$$$$$$$$$$$$$$$$$$$$$$$$ " - // << std::endl; localGroupTree.create_from_leaf_level(localLeafInfo, ghostP2P_leafInfo, ghost_m2l_cells, ghost_m2m_cells, particles_distrib[rank], level_dist[leaf_level][rank]); - // std::cout << " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ leaf level $$$$$$$$$$$$$$$$$$$$$$$$$$ " - // << std::endl; - // parallel::utils::print_distrib("leaf_cell distribution ", rank, level_dist[leaf_level]); // build all leaves between leaf_level - 1 and level_shared -1. // we use the maximum because if we don't share certain levels this number is <0 - // std::cout << "std::max(level_shared, int(localGroupTree.top_level())) " - // << std::max(level_shared, int(localGroupTree.top_level())) << std::endl; - // std::cout << " XXXXXXXXXX -> std::max(level_shared, int(localGroupTree.top_level() - 1))" - // << std::max(level_shared, int(localGroupTree.top_level() - 1)) << std::endl; - ; for(int level = leaf_level - 1; level >= localGroupTree.top_level(); --level) { - // std::cout << " $$$$$$$$$$$$$$$$$$$$$$$$$ level " << level << " $$$$$$$$$$$$$$$$$$$$$$$$$ " - // << std::endl; std::int64_t ghost_l2l_cell{-1}; // Get the distribution at the current level, the ghost cell involved in l2l operator // and the morton index of the existing cells at this level level_dist[level] = std::move(parallel::utils::build_upper_distribution( manager, dimension, level, leafMortonIdx, ghost_l2l_cell, level_dist[level + 1])); - // io::print("rank(" + std::to_string(rank) + ") MortonIdx(" + std::to_string(level) + "): ", - // leafMortonIdx); - // std::cout << " ghost_l2l_cell: " << ghost_l2l_cell << std::endl; // Set the distribution in tres tree localGroupTree.set_cell_distribution(level, level_dist[level]); // build the m2l ghost cells at this level auto ghost_cells_level = build_let_at_level(manager, box, level, leafMortonIdx, level_dist[level], separation); - // io::print("rank(" + std::to_string(rank) + ") level=" + std::to_string(level) + - // " --> final ghost_cells(m2l): ", - // ghost_cells_level); // build the m2m ghost cells at this level auto ghost_m2m_cells = build_ghost_m2m_let_at_level( manager, box, leaf_level, leafMortonIdx, level_dist[level], level == localGroupTree.top_level()); - // io::print("rank(" + std::to_string(rank) + ") --> ghost_cells(m2m): ", ghost_m2m_cells); - + // io::print("rank(" + std::to_string(rank) + ") --> ghost_cells(m2m): ", ghost_m2m_cells); // Create the groupe of cells structure for this level localGroupTree.create_cells_at_level(level, leafMortonIdx, ghost_cells_level, ghost_m2m_cells, ghost_l2l_cell, level_dist[level][rank]); - // std::cout << " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ " << std::endl - // << std::flush; } - // std::cout << " end loop\n" << std::flush; manager.get_communicator().barrier(); - // std::cout << " end barrier\n" << std::flush; } else #endif // SCALFMM_USE_MPI { // we are in sequential 1 proc - // - std::vector<tree::leaf_info_type<morton_type>> ghostP2P_leafInfo ; - std::vector<tree::leaf_info_type<morton_type>> ghost_m2l_leafInfo ; - std::vector<tree::leaf_info_type<morton_type>> ghost_m2m_leafInfo ; - /* localGroupTree.create_from_leaf_level(localLeafInfo, ghostP2P_leafInfo, ghost_m2l_cells, + // + std::vector<tree::leaf_info_type<morton_type>> ghostP2P_leafInfo; + std::vector<tree::leaf_info_type<morton_type>> ghost_m2l_leafInfo; + std::vector<tree::leaf_info_type<morton_type>> ghost_m2m_leafInfo; + /* localGroupTree.create_from_leaf_level(localLeafInfo, ghostP2P_leafInfo, ghost_m2l_cells, ghost_m2m_cells, particles_distrib[rank], level_dist[leaf_level][rank]); */ - localGroupTree.construct(particleMortonIndex); - // then, we fill each leaf with its particles (the particle container is sorted ) - localGroupTree.fill_leaves_with_particles(particle_container); - // + localGroupTree.construct(particleMortonIndex); + // then, we fill each leaf with its particles (the particle container is sorted ) + localGroupTree.fill_leaves_with_particles(particle_container); + // localGroupTree.set_leaf_distribution(particles_distrib); localGroupTree.set_cell_distribution(leaf_level, leaves_distrib); @@ -937,17 +879,8 @@ namespace scalfmm::tree } } - // std::cout << cpp_tools::colors::red << std::endl << std::flush; - // std::cout << "set iterators \n" << std::flush << std::flush; localGroupTree.set_valid_iterators(true); - // std::cout << "begin fill_leaves_with_particles \n" << std::flush; localGroupTree.fill_leaves_with_particles(particle_container); - // std::cout << "end fill_leaves_with_particles \n" << std::flush; - - // std::cout << cpp_tools::colors::reset << std::endl; - // std::cout << cpp_tools::colors::green << " --> End let::group_let() " << cpp_tools::colors::reset - // << std::endl - // << std::flush; return localGroupTree; } diff --git a/include/scalfmm/tree/group_of_views.hpp b/include/scalfmm/tree/group_of_views.hpp index 5e3e243f1cb1e6a7051eb2cf92b8c21f07d06315..39a822b87dcda52ee41e445e5345fdd1925e2018 100644 --- a/include/scalfmm/tree/group_of_views.hpp +++ b/include/scalfmm/tree/group_of_views.hpp @@ -1,10 +1,16 @@ // -------------------------------- // See LICENCE file at project root -// File : group_of_particles.hpp +// File : scalfmm/tree/group_of_views.hpp // -------------------------------- #ifndef SCALFMM_TREE_GROUP_OF_PARTICLES_HPP #define SCALFMM_TREE_GROUP_OF_PARTICLES_HPP +#include "scalfmm/container/block.hpp" +#include "scalfmm/meta/traits.hpp" +#include "scalfmm/tree/header.hpp" +#include "scalfmm/tree/leaf_view.hpp" +#include "scalfmm/utils/massert.hpp" + #include <algorithm> #include <cstddef> #include <iterator> @@ -12,12 +18,6 @@ #include <type_traits> #include <vector> -#include "scalfmm/container/block.hpp" -#include "scalfmm/meta/traits.hpp" -#include "scalfmm/tree/header.hpp" -#include "scalfmm/tree/leaf_view.hpp" -#include "scalfmm/utils/massert.hpp" - namespace scalfmm::component { /** @@ -27,13 +27,16 @@ namespace scalfmm::component * particles or multipole/local values and symbolic information of the * components in the group and an array of components (view) that allows * to access the data inside the storage. + * + * @tparam LeafType + * @tparam ParticleType */ - template<typename Leaf, typename Particle> + template<typename LeafType, typename ParticleType> struct group_of_particles { public: - using particle_type = Particle; - using component_type = Leaf; + using particle_type = ParticleType; + using component_type = LeafType; using symbolics_type = symbolics_data<group_of_particles<component_type, particle_type>>; using symbolics_component_type = symbolics_data<component_type>; using block_type = std::vector<component_type>; @@ -41,11 +44,42 @@ namespace scalfmm::component using const_iterator_type = typename block_type::const_iterator; using storage_type = typename symbolics_type::storage_type; + /** + * @brief Construct a new group of particles object + * + */ group_of_particles() = default; + + /** + * @brief Construct a new group of particles object + * + */ group_of_particles(group_of_particles const&) = default; + + /** + * @brief Construct a new group of particles object + * + */ group_of_particles(group_of_particles&&) noexcept = default; + + /** + * @brief + * + * @return group_of_particles& + */ inline auto operator=(group_of_particles const&) -> group_of_particles& = default; + + /** + * @brief + * + * @return group_of_particles& + */ inline auto operator=(group_of_particles&&) noexcept -> group_of_particles& = default; + + /** + * @brief Destroy the group of particles object + * + */ ~group_of_particles() = default; /** @@ -74,6 +108,7 @@ namespace scalfmm::component group_symbolics.idx_global = index_global; group_symbolics.is_mine = is_mine; } + // /** // * @brief Rebuilt all the pointers inside the vector of blocks // * @@ -84,6 +119,7 @@ namespace scalfmm::component // m_vector_of_components.size() // << std::endl; // } + /** * @brief Display the elements of the group (views and the storage) * @@ -106,53 +142,120 @@ namespace scalfmm::component os << cpp_tools::colors::reset; return os; } + /** - * @brief return the symbolic structure of the group_of_particles + * @brief Returns the symbolic structure of the group_of_particles * * @return symbolics_type& */ [[nodiscard]] inline auto symbolics() -> symbolics_type& { return m_components_storage.header(); } + + /** + * @brief Returns the symbolic structure of the group_of_particles + * + * @return symbolics_type& + */ [[nodiscard]] inline auto symbolics() const -> symbolics_type const& { return m_components_storage.header(); } + + /** + * @brief Returns the symbolic structure of the group_of_particles + * + * @return symbolics_type& + */ [[nodiscard]] inline auto csymbolics() const -> symbolics_type const& { return m_components_storage.cheader(); } + /** * @brief Get the vector of components (leaves) * * @return block_type& */ [[nodiscard]] inline auto components() -> block_type& { return m_vector_of_components; } + + /** + * @brief Get the vector of components (leaves) + * + * @return block_type& + */ [[nodiscard]] inline auto components() const -> block_type const& { return m_vector_of_components; } + + /** + * @brief Get the vector of components (leaves) + * + * @return block_type& + */ [[nodiscard]] inline auto ccomponents() const -> block_type const& { return m_vector_of_components; } + /** - * @brief return the storage of the particles inside the group + * @brief Returns the storage of the particles inside the group * * @return storage_type& */ [[nodiscard]] inline auto storage() -> storage_type& { return m_components_storage; } + + /** + * @brief Returns the storage of the particles inside the group + * + * @return storage_type& + */ [[nodiscard]] inline auto storage() const -> storage_type const& { return m_components_storage; } + + /** + * @brief Returns the storage of the particles inside the group + * + * @return storage_type& + */ [[nodiscard]] inline auto cstorage() const -> storage_type const& { return m_components_storage; } + /** - * @brief get the first component iterator + * @brief Gets the first component iterator * * @return iterator_type */ [[nodiscard]] inline auto begin() -> iterator_type { return std::begin(m_vector_of_components); } + + /** + * @brief Gets the first component iterator + * + * @return iterator_type + */ [[nodiscard]] inline auto begin() const -> const_iterator_type { return std::cbegin(m_vector_of_components); } + + /** + * @brief Gets the first component iterator + * + * @return iterator_type + */ [[nodiscard]] inline auto cbegin() const -> const_iterator_type { return std::cbegin(m_vector_of_components); } + /** - * @brief get the last component iterator + * @brief Gets the last component iterator * * @return iterator_type */ [[nodiscard]] inline auto end() -> iterator_type { return std::end(m_vector_of_components); } + + /** + * @brief Gets the last component iterator + * + * @return iterator_type + */ [[nodiscard]] inline auto end() const -> const_iterator_type { return std::cend(m_vector_of_components); } + + /** + * @brief Gets the last component iterator + * + * @return iterator_type + */ [[nodiscard]] inline auto cend() const -> const_iterator_type { return std::cend(m_vector_of_components); } + /** - * @brief Return the number of components (cells or leaves) + * @brief Returns the number of components (cells or leaves) * */ [[nodiscard]] inline auto size() const noexcept { return m_number_of_component; } + /** - * @brief Check if morton_index is inside the range of morton indexes of the group_of_particles + * @brief Checks if morton_index is inside the range of morton indexes of the group_of_particles * * @param morton_index morton index * @return true if morton_index is inside the group_of_particles @@ -163,6 +266,7 @@ namespace scalfmm::component return ((morton_index >= this->csymbolics().starting_index) && (morton_index < this->csymbolics().ending_index)); } + /** * @brief Check if morton_index is below the last morton index of the group_of_particles * @@ -174,6 +278,7 @@ namespace scalfmm::component { return ((morton_index < this->csymbolics().ending_index)); } + /** * @brief Check if morton_index exists inside the group_of_particles * @@ -185,8 +290,9 @@ namespace scalfmm::component { return is_inside(morton_index) && (component_index(morton_index) != -1); } + /** - * @brief return the targeted component + * @brief Returns the targeted component * * @param component_index the index of the component * @return component_type& @@ -197,18 +303,36 @@ namespace scalfmm::component return m_vector_of_components.at(component_index); } + /** + * @brief + * + * @param component_index + * @return component_type const& + */ [[nodiscard]] inline auto component(std::size_t component_index) const -> component_type const& { assertm(component_index < std::size(m_vector_of_components), "Out of range in group_of_particles."); return m_vector_of_components.at(component_index); } + /** + * @brief + * + * @param component_index + * @return component_type const& + */ [[nodiscard]] inline auto ccomponent(std::size_t component_index) const -> component_type const& { assertm(component_index < std::size(m_vector_of_components), "Out of range in group_of_particles."); return m_vector_of_components.at(component_index); } + /** + * @brief + * + * @param index + * @return iterator_type + */ [[nodiscard]] inline auto component_iterator(std::size_t index) -> iterator_type { assertm(index < std::size(m_vector_of_components), "Out of range in group_of_particles."); @@ -217,6 +341,12 @@ namespace scalfmm::component return it; } + /** + * @brief + * + * @param index + * @return const_iterator_type + */ [[nodiscard]] inline auto component_iterator(std::size_t index) const -> const_iterator_type { assertm(index < std::size(m_vector_of_components), "Out of range in group_of_particles."); @@ -225,6 +355,12 @@ namespace scalfmm::component return it; } + /** + * @brief + * + * @param cell_index + * @return const_iterator_type + */ [[nodiscard]] inline auto ccomponent_iterator(std::size_t cell_index) const -> const_iterator_type { assertm(cell_index < std::size(m_vector_of_components), "Out of range in group_of_particles."); @@ -232,8 +368,9 @@ namespace scalfmm::component std::advance(it, cell_index); return it; } + /** - * @brief return the index of the component with the targeted Morton index + * @brief Returns the index of the component with the targeted Morton index * * @tparam MortonIndex * @param morton_index the morton index @@ -264,6 +401,15 @@ namespace scalfmm::component return -1; } + /** + * @brief + * + * @tparam MortonIndex + * @param morton_index + * @param idx_left + * @param idx_right + * @return int + */ template<typename MortonIndex = std::size_t> [[nodiscard]] inline auto component_index(MortonIndex morton_index, int idx_left, int idx_right) const -> int { @@ -286,44 +432,60 @@ namespace scalfmm::component } return -1; } + /** * @brief Display the interval of Morton index of the components inside the group * */ - void print_morton_interval() + inline auto print_morton_interval() -> void { std::cout << "group_of_particles: [" << this->csymbolics().starting_index << ", " << this->csymbolics().ending_index << "]\n"; } + /** * @brief get the dependencies for the task-based algorithm on the first particle output inside the block * * @return auto */ - auto inline depends_update() { return this->cstorage().cptr_on_output(); } + inline auto depends_update() { return this->cstorage().cptr_on_output(); } private: - /// Vector of components (cells or leaves) inside the group_of_particles + /** + * @brief Vector of components (cells or leaves) inside the group_of_particles. + * + */ block_type m_vector_of_components{}; - /// The storage of the particle or multipole/local values + + /** + * @brief The storage of the particle or multipole/local values. + * + */ storage_type m_components_storage{}; - /// number of components in the group + + /** + * @brief The number of components in the group. + * + */ const std::size_t m_number_of_component{}; }; - /// @brief The Symbolics type that stores information about the groupe of leaves - /// - /// @tparam P - template<typename Component, typename Particle> - struct symbolics_data<group_of_particles<Component, Particle>> + /** + * @brief The symbolics type that stores information about the group of leaves. + * + * @tparam ComponentType + * @tparam ParticleType + */ + template<typename ComponentType, typename ParticleType> + struct symbolics_data<group_of_particles<ComponentType, ParticleType>> { // using self_type = symbolics_data<group_of_particles<P,D>>; - using self_type = symbolics_data<group_of_particles<Component, Particle>>; + using self_type = symbolics_data<group_of_particles<ComponentType, ParticleType>>; // the current group type - using group_type = group_of_particles<Component, Particle>; - using component_type = Component; + using group_type = group_of_particles<ComponentType, ParticleType>; + using component_type = ComponentType; // the leaf type - using particle_type = Particle; + using particle_type = ParticleType; static constexpr std::size_t dimension{particle_type::dimension}; using iterator_type = typename group_type::iterator_type; // the distant group type // same if source = target or same tree @@ -335,29 +497,79 @@ namespace scalfmm::component using out_of_block_interaction_type = out_of_block_interaction<iterator_source_type, std::size_t>; // storage type to store data in group using storage_type = container::particles_block<self_type, particle_type, component_type>; - // the starting morton index in the group + + /** + * @brief The starting morton index in the group. + * + */ std::size_t starting_index{0}; - // the ending morton index in the group + + /** + * @brief The ending morton index in the group. + * + */ std::size_t ending_index{0}; + + /** + * @brief + * + */ std::size_t number_of_component_in_group{0}; - // number of particles in group + + /** + * @brief The number of particles in the group. + * + */ std::size_t number_of_particles_in_group{0}; - // index of the group + + /** + * @brief Index of the group. + * + */ std::size_t idx_global{0}; - // + + /** + * @brief + * + */ bool is_mine{true}; - // vector storing the out_of_block_interaction structure to handle the outside interactions + + /** + * @brief Vector storing the out_of_block_interaction structure to handle the outside interactions. + * + */ std::vector<out_of_block_interaction_type> outside_interactions{}; - // flagged if the vector is constructed + + /** + * @brief Flag if the vector is constructed. + * + */ bool outside_interactions_exists{false}; - // flagged if the vector is sorted + + /** + * @brief Flag if the vector is sorted. + * + */ bool outside_interactions_sorted{false}; + // #if _OPENMP - /// the P2P dependencies are set on the pointer on particles container - // std::array<group_type*, math::pow(dimension, dimension)> group_dependencies{}; + /** + * @brief the P2P dependencies are set on the pointer on particles container + * + */ std::vector<group_source_type*> group_dependencies{}; // #endif + /** + * @brief Construct a new symbolics data object + * + * @param starting_morton_idx + * @param ending_morton_idx + * @param number_of_component + * @param number_of_particles + * @param in_index_global + * @param in_is_mine + */ symbolics_data(std::size_t starting_morton_idx, std::size_t ending_morton_idx, std::size_t number_of_component, std::size_t number_of_particles, std::size_t in_index_global, bool in_is_mine) : starting_index(starting_morton_idx) diff --git a/include/scalfmm/tree/group_tree_view.hpp b/include/scalfmm/tree/group_tree_view.hpp index f58cf1199259a48bbfdec7bbb28f6b5ad9ed7a8a..500ec1c22acf194a049502cd13215dab7a064a3d 100644 --- a/include/scalfmm/tree/group_tree_view.hpp +++ b/include/scalfmm/tree/group_tree_view.hpp @@ -1,23 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : group_tree_view.hpp +// File : scalfmm/tree/group_tree_view.hpp // -------------------------------- #ifndef SCALFMM_TREE_GROUP_TREE_VIEW_HPP #define SCALFMM_TREE_GROUP_TREE_VIEW_HPP -#include <algorithm> -#include <cmath> -#include <cstddef> -#include <functional> -#include <iostream> -#include <iterator> -#include <map> -#include <memory> -#include <numeric> -#include <tuple> -#include <type_traits> -#include <utility> - #include "scalfmm/lists/policies.hpp" #include "scalfmm/lists/sequential.hpp" #include "scalfmm/meta/utils.hpp" @@ -36,30 +23,46 @@ #include "scalfmm/utils/massert.hpp" #include "scalfmm/utils/sort.hpp" +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iostream> +#include <iterator> +#include <map> +#include <memory> +#include <numeric> +#include <tuple> +#include <type_traits> +#include <utility> + namespace scalfmm::component { /** - * @brief The group tree + * @brief * - * The tree is ... - * - * @tparam Cell - * @tparam Leaf - * @tparam box<typename Leaf::position_type> + * @tparam CellType + * @tparam LeafType + * @tparam box<typename LeafType::position_type> */ - template<typename Cell, typename Leaf, typename Box = box<typename Leaf::position_type>> + template<typename CellType, typename LeafType, typename Box = box<typename LeafType::position_type>> class group_tree_view { public: - using cell_type = Cell; - using leaf_type = Leaf; - using particle_type = typename Leaf::particle_type; + using cell_type = CellType; + using leaf_type = LeafType; + using particle_type = typename LeafType::particle_type; using group_of_cell_type = group<cell_type>; // group_of_view using group_of_leaf_type = group_of_particles<leaf_type, particle_type>; using box_type = Box; using position_type = typename box_type::position_type; using position_value_type = typename box_type::value_type; + + /** + * @brief + * + */ static constexpr std::size_t dimension = box_type::dimension; // Cells // using cell_group_vector_type = std::vector<std::vector<std::shared_ptr<group_of_cell_type>>>; @@ -83,71 +86,178 @@ namespace scalfmm::component using iterator_type = std::tuple<leaf_iterator_type, cell_iterator_type>; using const_iterator_type = std::tuple<const_leaf_iterator_type, const_cell_iterator_type>; + using morton_type = std::size_t; + /** + * @brief Construct a new group tree view object + * + */ group_tree_view() = default; + + /** + * @brief Construct a new group tree view object + * + */ group_tree_view(group_tree_view const&) = default; + + /** + * @brief Construct a new group tree view object + * + */ group_tree_view(group_tree_view&&) noexcept = default; + + /** + * @brief + * + * @return group_tree_view& + */ inline auto operator=(group_tree_view const&) -> group_tree_view& = default; + + /** + * @brief + * + * @return group_tree_view& + */ inline auto operator=(group_tree_view&&) noexcept -> group_tree_view& = default; + + /** + * @brief Destroy the group tree view object + * + */ ~group_tree_view() = default; + void build_with_morton(std::vector<morton_type>& mortons) { this->construct(mortons); } + protected: - const std::size_t m_tree_height{}; ///< the height of the tree - const std::size_t m_top_level{}; ///< the level to stop the FMM algorithm (generally 2) - const std::size_t m_order{}; ///< the order of the approximation + /** + * @brief The height of the tree. + * + */ + const std::size_t m_tree_height{}; - const std::size_t - m_number_of_leaves_per_group{}; ///< the number tof leaves inside a group (except in the last group) + /** + * @brief The level to stop the FMM algorithm (generally 2). + * + */ + const std::size_t m_top_level{}; + + /** + * @brief The order of the approximation. + * + */ + const std::size_t m_order{}; + + /** + * @brief The number of leaves inside a group (except in the last group). + * + */ + const std::size_t m_number_of_leaves_per_group{}; + + /** + * @brief The number tof leaves inside a group (except in the last group). + * + */ const std::size_t m_number_of_cells_per_group{}; ///< the number of cells inside a group (except in the last group) - int m_tree_levels_above_root{}; ///< the number of level above the root needed in periodic simulation - bool m_interaction_p2p_lists_built{false}; ///< to specify if the interaction p2p list is built - bool m_interaction_m2l_lists_built{false}; ///< to specify if the interaction m2l list is built - - cell_group_vector_type m_group_of_cell_per_level; ///< vector of cells group - leaf_group_vector_type m_group_of_leaf; ///< vector of leaves group - // iterators on leaf and cell groups I own - std::array<leaf_iterator_type, 2> - m_view_on_my_leaf_groups; ///< iterators at the beginning and end of the leaf groups I own - std::vector<std::array<cell_group_level_iterator_type, 2>> - m_view_on_my_cell_groups; ///< iterators at the beginning and end of the cell groups I own - - // // - box_type m_box{}; ///< the simulation box - // + + /** + * @brief The number of level above the root needed in periodic simulation. + * + */ + int m_tree_levels_above_root{}; + + /** + * @brief To specify if the interaction p2p list is built. + * + */ + bool m_interaction_p2p_lists_built{false}; + + /** + * @brief To specify if the interaction m2l list is built. + * + */ + bool m_interaction_m2l_lists_built{false}; + + /** + * @brief Vector of cells group. + * + */ + cell_group_vector_type m_group_of_cell_per_level; + + /** + * @brief Vector of leaves group. + * + */ + leaf_group_vector_type m_group_of_leaves; + + // iterators on leaf and cell groups I own + + /** + * @brief Iterators at the beginning and end of the leaf groups I own. + * + */ + std::array<leaf_iterator_type, 2> m_view_on_my_leaf_groups; + + /** + * @brief Iterators at the beginning and end of the cell groups I own. + * + */ + std::vector<std::array<cell_group_level_iterator_type, 2>> m_view_on_my_cell_groups; + + /** + * @brief The simulation box. + * + */ + box_type m_box{}; public: + /** + * @brief + * + */ std::map<std::string, std::int64_t> m_multipoles_dependencies{}; + + /** + * @brief + * + */ std::map<std::string, std::int64_t> m_locals_dependencies{}; + /** * @brief return the leaf level * */ [[nodiscard]] inline auto leaf_level() const noexcept -> std::size_t { return m_tree_height - 1; } + /** * @brief return the top level used in the algorithm * */ [[nodiscard]] inline auto top_level() const noexcept -> std::size_t { return m_top_level; } + /** * @brief return the center of the simulation box * */ [[nodiscard]] auto box_center() const { return m_box.center(); } + /** * @brief return the width of the simulation box * */ [[nodiscard]] auto box_width(std::size_t dimension = 0) const { return m_box.width(dimension); } + /** * @brief return the width of a leaf * */ [[nodiscard]] auto leaf_width(std::size_t axe = 0) const { return m_box.width(axe) / (1 << leaf_level()); } + /** * @brief check if interaction m2l lists are built * */ [[nodiscard]] auto& is_interaction_m2l_lists_built() { return m_interaction_m2l_lists_built; } + /** * @brief check if interaction p2p lists are built * @@ -155,21 +265,23 @@ namespace scalfmm::component [[nodiscard]] auto& is_interaction_p2p_lists_built() { return m_interaction_p2p_lists_built; } private: - /// @brief This function takes a vector of tuple storing indices. - /// In the the tuple the first element is the morton index of the particle. - /// The second is the index of the particle in the particle container. - /// The function will count the duplicated morton index i.e the number of - /// particles to store in the leaf of the corresponding morton index. - /// ex : if in the vector the morton index 2 appears 4 times - /// -> 4 particles in the leaf of morton index 2. - /// Then the function removes the duplicates of morton indices. - /// It returns the vector holding the number of particle per leaves. - // - /// @tparam MortonType : the type of the morton index stored in the vector - /// @param vector_of_mortons : a vector holding indices for permutation of size number_of_particles - /// and resized to the number_of_leaves when the function returns. - /// - /// @return : a vector of std::size_t of size number_of_leaves. + /** + * @brief Get the leaves distribution object + * + * This function takes a vector of tuple storing indices. + * In the the tuple the first element is the morton index of the particle. + * The second is the index of the particle in the particle container. + * The function will count the duplicated morton index i.e the number of + * particles to store in the leaf of the corresponding morton index. + * ex : if in the vector the morton index 2 appears 4 times + * -> 4 particles in the leaf of morton index 2. + * Then the function removes the duplicates of morton indices. + * It returns the vector holding the number of particle per leaves. + * + * @tparam MortonType the type of the morton index stored in the vector + * @param vector_of_mortons a vector holding indices for permutation of size number_of_particles + * @return a vector of std::size_t of size number_of_leaves. + */ template<typename MortonType> auto get_leaves_distribution(std::vector<MortonType>& vector_of_mortons) -> std::vector<std::size_t> { @@ -202,13 +314,15 @@ namespace scalfmm::component } protected: - /// @brief It builds the groups of leaves for the leaf level. - /// - /// @tparam MortonType : the type of the morton index stored in the vector - /// @param vector_of_mortons : a vector holding indices for permutation of size number_of_leaves - /// @param number_of_particles_per_leaves: a vector holding the number of particles per leaves. - /// - /// @return : void + /** + * @brief It builds the groups of leaves for the leaf level. + * + * @tparam MortonType : the type of the morton index stored in the vector + * @param vector_of_mortons : a vector holding indices for permutation of size number_of_leaves + * @param number_of_particles_per_leaves: a vector holding the number of particles per leaves. + * @param box + * @param is_mine + */ template<typename MortonType> auto build_groups_of_leaves(std::vector<MortonType> const& vector_of_mortons, std::vector<std::size_t> const& number_of_particles_per_leaves, box_type const& box, @@ -220,7 +334,7 @@ namespace scalfmm::component // the rest of the leaves to store in the last group of a smaller size. auto remain_number_of_leaves{number_of_leaves % m_number_of_leaves_per_group}; // resize the vector of groups of leaves - m_group_of_leaf.resize(number_of_groups_at_leaf_level); + m_group_of_leaves.resize(number_of_groups_at_leaf_level); std::size_t cnt_leaves{0}; std::size_t cnt_particles{0}; // loop on the full groups @@ -241,11 +355,11 @@ namespace scalfmm::component storage_size += number_of_particles_per_leaves[cnt_leaves++]; } // we create the group and stores the pointer in the vector of groups - m_group_of_leaf[g] = + m_group_of_leaves[g] = std::make_shared<group_of_leaf_type>(starting_morton_index, ending_morton_index + 1, m_number_of_leaves_per_group, storage_size, g, is_mine); - const auto ptr_grp = m_group_of_leaf[g]; + const auto ptr_grp = m_group_of_leaves[g]; auto& particles_storage = ptr_grp->storage(); auto& leaves_storage = ptr_grp->components(); auto begin_particles = std::begin(particles_storage); @@ -299,11 +413,11 @@ namespace scalfmm::component cnt_particles = 0; // // we create the last group and store the pointer at the end of the vector of groups - m_group_of_leaf.push_back(std::make_shared<group_of_leaf_type>( + m_group_of_leaves.push_back(std::make_shared<group_of_leaf_type>( starting_morton_index, ending_morton_index + 1, remain_number_of_leaves, storage_size, number_of_groups_at_leaf_level, is_mine)); - const auto pg = m_group_of_leaf[number_of_groups_at_leaf_level]; + const auto pg = m_group_of_leaves[number_of_groups_at_leaf_level]; auto& particles_storage = pg->storage(); auto& leaves_storage = pg->components(); auto begin_particles = std::begin(particles_storage); @@ -332,24 +446,27 @@ namespace scalfmm::component } } } + // /** // * @brief Rebuilt all the pointers inside the vector of blocks // * // */ // auto rebuilt_leaf_view() -> void // { - // for(std::size_t i = 0; i < m_group_of_leaf.size(); ++i) + // for(std::size_t i = 0; i < m_group_of_leaves.size(); ++i) // { - // m_group_of_leaf[i].get()->rebuilt_leaf_view(); + // m_group_of_leaves[i].get()->rebuilt_leaf_view(); // } // } - /// @brief This function builds the groups of cells at the specified level - /// - /// @tparam MortonIndex : the type of the morton index - /// @param vector_of_mortons : the vector holding the morton indices of the cells at the specified level - /// @param level : the level to build the group - /// - /// @return void + + /** + * @brief This function builds the groups of cells at the specified level + * + * @tparam MortonIndex the type of the morton index + * @param vector_of_mortons the vector holding the morton indices of the cells at the specified level + * @param level the level to build the group + * @param is_mine + */ template<typename MortonIndex> auto build_groups_of_cells_at_level(std::vector<MortonIndex> const& vector_of_mortons, std::size_t level, const bool is_mine = true) -> void @@ -389,14 +506,14 @@ namespace scalfmm::component } } - /// @brief This function builds the cells in the groups at the specified level - /// - /// @tparam MortonType : the type of the morton index - /// @param vector_of_mortons : a vector holding the morton indices of the cells at the specified level - /// @param box : the box simulation - /// @param level : the level to construct the groups - /// - /// @return void + /** + * @brief This function builds the cells in the groups at the specified level + * + * @tparam MortonType : the type of the morton index + * @param vector_of_mortons : a vector holding the morton indices of the cells at the specified level + * @param box : the box simulation + * @param level : the level to construct the groups + */ template<typename MortonType> auto build_cells_in_groups_at_level(std::vector<MortonType> const& vector_of_mortons, box_type const& box, std::size_t level) -> void @@ -423,14 +540,17 @@ namespace scalfmm::component } public: - /// @brief Constructor of the group tree. - /// It initialized all levels with leaves and cells from the particle container passed. - /// - /// @tparam ParticleContainer : the type of the particle container - /// @param tree_height : the height of the tree - /// @param order : order of the simulation - /// @param box : the box of the simulation - /// @param number_of_leaves_per_group : blocking on the leaves/cells + /** + * @brief Constructor of the group tree. + * + * It creates an empty tree structure. + * + * @param tree_height the height of the tree + * @param order order of the simulation + * @param number_of_leaves_per_group blocking on the leaves + * @param number_of_cells_per_group blocking on the cells + * @param box the box of the simulation + */ group_tree_view(std::size_t tree_height, std::size_t order, std::size_t number_of_leaves_per_group, std::size_t number_of_cells_per_group, box_type const& box) : m_tree_height(tree_height) @@ -446,6 +566,23 @@ namespace scalfmm::component { this->init_iterators(); } + + /** + * @brief Constructor of the group tree. + * + * It creates an empty tree structure. + * + * @param tree_height the height of the tree + * @param order order of the simulation + * @param number_of_components_per_group blocking on the leaves and cells + * @param box the box of the simulation + */ + group_tree_view(std::size_t tree_height, std::size_t order, std::size_t number_of_components_per_group, + box_type const& box) + : group_tree_view(tree_height, order, number_of_components_per_group, number_of_components_per_group, box) + { + } + /// @brief Constructor of the group tree. /// It initialized all levels with leaves and cells from the particle container passed. /// @@ -502,6 +639,19 @@ namespace scalfmm::component this->set_iterators(); } + /** + * @brief Constructor of the group tree. + * + * It initialized all levels with leaves and cells from the sorted vector of Morton indexes. + * + * @tparam MortonType The type of the Morton index + * @param tree_height the height of the tree + * @param order order of the simulation + * @param box the box of the simulation + * @param number_of_leaves_per_group blocking on the leaves + * @param number_of_cells_per_group blocking on the cells + * @param vector_of_mortons the vector of sorted Morton indexes of teh particles + */ template<typename MortonType, typename = typename std::enable_if_t<std::is_integral_v<MortonType>>> group_tree_view(std::size_t tree_height, std::size_t order, box_type const& box, std::size_t number_of_leaves_per_group, std::size_t number_of_cells_per_group, @@ -519,7 +669,17 @@ namespace scalfmm::component { this->construct(vector_of_mortons); } - + /** + * @brief Construct a new group tree view object + * + * @tparam ParticleContainer + * @param tree_height + * @param order + * @param box + * @param number_of_component_per_group + * @param particle_container + * @param particles_are_sorted + */ template<typename ParticleContainer> group_tree_view(std::size_t tree_height, std::size_t order, box_type const& box, std::size_t number_of_component_per_group, ParticleContainer const& particle_container, @@ -529,6 +689,17 @@ namespace scalfmm::component { } + /** + * @brief Construct a new group tree view object + * + * @tparam MortonType + * @tparam std::enable_if_t<std::is_integral_v<MortonType>> + * @param tree_height + * @param order + * @param box + * @param number_of_component_per_group + * @param vector_of_mortons + */ template<typename MortonType, typename = typename std::enable_if_t<std::is_integral_v<MortonType>>> group_tree_view(std::size_t tree_height, std::size_t order, box_type const& box, std::size_t number_of_component_per_group, std::vector<MortonType>& vector_of_mortons) @@ -536,6 +707,7 @@ namespace scalfmm::component vector_of_mortons) { } + /** * @brief init begin and end iterators for leaves and cells * @@ -552,11 +724,13 @@ namespace scalfmm::component ++cell_target_level_it; } } + /** * @brief init begin and end iterators for leaves and cells * */ inline auto init_iterators() -> void { m_view_on_my_cell_groups.resize(m_tree_height); } + /** * @brief * @@ -596,6 +770,13 @@ namespace scalfmm::component this->set_iterators(); } + /** + * @brief + * + * @tparam MortonType + * @param vector_of_mortons + * @param box + */ template<typename MortonType> inline auto construct(std::vector<MortonType>& vector_of_mortons, box_type const& box) -> void { @@ -603,20 +784,19 @@ namespace scalfmm::component this->construct(vector_of_mortons); } - /// @brief The function fills the particle in each leaf. - /// Its uses the source index in the tuple of indices to get the - /// particle from the source container - /// - /// @tparam VectorOfTuples : a vector of tuples of indices of size number_of_leaves - /// @tparam ParticleContainer : the particle container - /// @param tuple_of_indexes : a vector holding indices for permutation of size number_of_leaves - /// @param particle_container : the container storing all the particles. - /// - /// @return - - template<typename VectorOfTuples, typename ParticleContainer> - auto fill_leaves_with_particles(VectorOfTuples const& tuple_of_indexes, - ParticleContainer const& particle_container) -> void + /** + * @brief The function fills the particle in each leaf. + * Its uses the source index in the tuple of indices to get the + * particle from the source container + * + * @tparam TupleOfIndexType a vector of tuples of indices of size number_of_leaves + * @tparam ParticleContainerType the particle container + * @param tuple_of_indexes a vector holding indices for permutation of size number_of_leaves + * @param particle_container the container storing all the particles. + */ + template<typename TupleOfIndexType, typename ParticleContainerType> + auto fill_leaves_with_particles(TupleOfIndexType const& tuple_of_indexes, + ParticleContainerType const& particle_container) -> void { // using scalfmm::details::tuple_helper; using proxy_type = typename particle_type::proxy_type; @@ -626,7 +806,7 @@ namespace scalfmm::component std::size_t part_src_index{0}; std::size_t group_index{0}; - for(auto pg: m_group_of_leaf) + for(auto pg: m_group_of_leaves) { std::size_t leaf_index{0}; auto leaves_view = pg->components(); @@ -666,7 +846,7 @@ namespace scalfmm::component #ifdef _DEBUG_BLOCK_DATA std::clog << " FINAl block\n"; int tt{0}; - for(auto pg: m_group_of_leaf) + for(auto pg: m_group_of_leaves) { std::clog << "block index " << tt++ << std::endl; pg->cstorage().print_block_data(std::clog); @@ -674,18 +854,16 @@ namespace scalfmm::component std::clog << " ---------------------------------------------------\n"; #endif } - /// @brief The function fills the particle in each leaf. - /// Its uses the source index in the tuple of indices to get the - /// particle from the source container - /// - /// @tparam VectorOfTuples : a vector of tuples of indices of size number_of_leaves - /// @tparam ParticleContainer : the particle container - /// @param tuple_of_indexes : a vector holding indices for permutation of size number_of_leaves - /// @param particle_container : the container storing all the particles. - /// - /// @return - template<typename VectorOfTuples, typename ParticleContainer> + /** + * @brief The function fills the particle in each leaf. + * Its uses the source index in the tuple of indices to get the + * particle from the source container + * + * @tparam ParticleContainer : the particle container + * @param particle_container : the container storing all the particles. + */ + template<typename ParticleContainer> auto fill_leaves_with_particles(ParticleContainer const& particle_container) -> void { // using scalfmm::details::tuple_helper; @@ -696,7 +874,7 @@ namespace scalfmm::component std::size_t part_src_index{0}; std::size_t group_index{0}; - for(auto pg: m_group_of_leaf) + for(auto pg: m_group_of_leaves) { std::size_t leaf_index{0}; auto leaves_view = pg->components(); @@ -710,7 +888,7 @@ namespace scalfmm::component for(std::size_t index_part = 0; index_part < leaf.size(); ++index_part) { // get the source index in the source container - auto source_index = part_src_index ; //std::get<1>(tuple_of_indexes.at(part_src_index)); + auto source_index = part_src_index; //std::get<1>(tuple_of_indexes.at(part_src_index)); // jump to the index in the source container auto jump_to_particle = begin_container; std::advance(jump_to_particle, int(source_index)); @@ -736,7 +914,7 @@ namespace scalfmm::component #ifndef _DEBUG_BLOCK_DATA std::clog << " FINAl block\n"; int tt{0}; - for(auto pg: m_group_of_leaf) + for(auto pg: m_group_of_leaves) { std::clog << "block index " << tt++ << std::endl; pg->cstorage().print_block_data(std::clog); @@ -744,61 +922,110 @@ namespace scalfmm::component std::clog << " ---------------------------------------------------\n"; #endif } + /** * @brief Construct the P2P and M2L interaction lists * - * @param[in] tree_source the tree containing the source cells and leaves + * @tparam SourceTreeType + * @param[in] source_tree the tree containing the source cells and leaves * @param[in] neighbour_separation separation criterion use to separate teh near and the far field * @param[in] mutual boolean to specify if the direct pass use a symmetric algorithm (mutual interactions) * @param[in] policy the policy to compute the interaction list (sequential, omp ) */ - template<typename TREE_EXT> - inline auto build_interaction_lists(TREE_EXT const& tree_source, const int& neighbour_separation, - const bool mutual, const int& policy = scalfmm::list::policies::sequential) - -> void + template<typename SourceTreeType> + inline auto build_interaction_lists(SourceTreeType const& source_tree, const int& neighbour_separation, + const bool mutual, + const int& policy = scalfmm::list::policies::sequential) -> void { switch(policy) { case list::policies::sequential: - scalfmm::list::sequential::build_interaction_lists(tree_source, *this, neighbour_separation, mutual); + scalfmm::list::sequential::build_interaction_lists(source_tree, *this, neighbour_separation, mutual); break; #ifdef _OpenMP case list::policies::omp: - scalfmm::list::omp::build_interaction_lists(tree_source, *this, neighbour_separation, mutual); + scalfmm::list::omp::build_interaction_lists(source_tree, *this, neighbour_separation, mutual); break; #endif default: - scalfmm::list::sequential::build_interaction_lists(tree_source, *this, neighbour_separation, mutual); + scalfmm::list::sequential::build_interaction_lists(source_tree, *this, neighbour_separation, mutual); } } - /// @brief reset all outputs in particle structure - /// - /// @return - inline auto reset_particles() + /** + * @brief Resets all particles (positions, inputs, outputs and variables). + * + */ + inline auto reset_particles() -> void { - for(auto pg: m_group_of_leaf) + // loop on group of leaves + for(auto pg: m_group_of_leaves) { - // loop on leaves - for(auto& leaf: pg->block()) - { - leaf.particles().clear(); - } + pg->storage().reset_particles(); } } - /// @brief reset all - /// - /// @return - inline auto reset_outputs() + + /** + * @brief Resets all the positions of the particles. + * + */ + inline auto reset_positions() -> void { // loop on group of leaves - for(auto pg: m_group_of_leaf) + for(auto pg: m_group_of_leaves) + { + // reset the positions in the block + pg->storage().reset_positions(); + } + } + + /** + * @brief Resets all the inputs of the particles. + * + */ + inline auto reset_inputs() -> void + { + // loop on group of leaves + for(auto pg: m_group_of_leaves) + { + // reset the inputs in the block + pg->storage().reset_inputs(); + } + } + + /** + * @brief Resets all the outputs of the particles. + * + */ + inline auto reset_outputs() -> void + { + // loop on group of leaves + for(auto pg: m_group_of_leaves) { // reset the output in the block pg->storage().reset_outputs(); } } - inline void reset_far_field() + + /** + * @brief Resets all the variables of the particles. + * + */ + inline auto reset_variables() -> void + { + // loop on group of leaves + for(auto pg: m_group_of_leaves) + { + // reset the variables in the block + pg->storage().reset_variables(); + } + } + + /** + * @brief Resets all the multipoles and locals. + * + */ + inline auto reset_far_field() -> void { auto cell_level_it = this->cbegin_cells() + (m_tree_height - 1); @@ -821,7 +1048,12 @@ namespace scalfmm::component --cell_level_it; } } - inline void reset_multipoles() + + /** + * @brief Resets all the multipoles. + * + */ + inline auto reset_multipoles() -> void { auto cell_level_it = this->cbegin_cells() + (m_tree_height - 1); @@ -838,7 +1070,12 @@ namespace scalfmm::component --cell_level_it; } } - inline void reset_locals() + + /** + * @brief Resets all the locals. + * + */ + inline auto reset_locals() -> void { auto cell_level_it = this->cbegin_cells() + (m_tree_height - 1); @@ -855,6 +1092,14 @@ namespace scalfmm::component --cell_level_it; } } + + /** + * @brief + * + * @tparam MortonType + * @param vector_of_mortons + * @param number_of_particles_per_leaves + */ template<typename MortonType> auto stats(std::vector<MortonType> const& vector_of_mortons, std::vector<std::size_t> const& number_of_particles_per_leaves) -> void @@ -888,30 +1133,35 @@ namespace scalfmm::component // } } - /// - /// \brief trace the index of the cells and leaves in the tree - /// - /// Depending on the level we print more or less details - /// level_trace = 1 print minimal information (height, order, group size) - /// level_trace = 2 print information of the tree (group interval and index inside) - /// level_trace = 3 print information of the tree (leaf interval and index inside and their p2p interaction - /// list) level_trace = 4 print information of the tree (cell interval and index inside and their m2l - /// interaction list) - /// level_trace = 5 print information of the tree (leaf and cell interval and index inside - /// and their p2p and m2l interaction lists) - /// - /// @warning to have the right p2p list we have to have the group size in the tree equal to the number - /// of leaves otherwise, we only print the index inside the group. - /// - /// @param[in] level_trace level of the trace - /// + /** + * @brief trace the index of the cells and leaves in the tree + * + * Depending on the level we print more or less details + * level_trace = 1 print minimal information (height, order, group size) + * level_trace = 2 print information of the tree (group interval and index inside) + * level_trace = 3 print information of the tree (leaf interval and index inside and their p2p interaction + * list) level_trace = 4 print information of the tree (cell interval and index inside and their m2l + * interaction list) + * level_trace = 5 print information of the tree (leaf and cell interval and index inside + * and their p2p and m2l interaction lists) + * + * @warning to have the right p2p list we have to have the group size in the tree equal to the number + * of leaves otherwise, we only print the index inside the group. + * + * @param os + * @param[in] level_trace level of the trace + */ inline auto trace(std::ostream& os, const std::size_t level_trace = 0) -> void { scalfmm::io::trace(os, *this, level_trace); } - /// - /// \brief trace the index of the cells and leaves in the tree - /// + + /** + * @brief Traces the index of the cells and leaves in the tree. + * + * @param header + * @param os + */ inline auto statistics(std::string header, std::ostream& os) -> void { auto& tree = (*this); @@ -935,24 +1185,51 @@ namespace scalfmm::component mean = sum / tot; stdev = stdev / tot - mean * mean; os << header << std::endl; - os << "[stats][group number] : " << tree.leaf_groups_size() << "\n"; + os << "[stats][group number] : " << tree.leaf_groups_size() << "\n"; os << "[stats][min:max] : " << min << ':' << max << "\n"; os << "[stats][mean] : " << mean << "\n"; os << "[stats][stdev] : " << stdev << "\n"; os << "[stats][number leaf] : " << tot << "\n"; } + + /** + * @brief + * + * @return std::size_t + */ [[nodiscard]] inline auto height() const noexcept -> std::size_t { return m_tree_height; } + /** + * @brief + * + * @return std::size_t + */ [[nodiscard]] inline auto levels_above_root() const noexcept -> std::size_t { return m_tree_levels_above_root; } + /** + * @brief Set the levels above root object + * + * @param in_nb_levels + */ inline auto set_levels_above_root(const int in_nb_levels) noexcept -> void { m_tree_levels_above_root = in_nb_levels; } + /** + * @brief + * + * @return box_type const& + */ [[nodiscard]] inline auto box() const noexcept -> box_type const& { return m_box; } + /** + * @brief + * + * @return std::size_t + */ [[nodiscard]] inline auto order() const noexcept -> std::size_t { return m_order; } + /** * @brief return the set of leaf groups vector of shared pointer of groups * @@ -960,19 +1237,90 @@ namespace scalfmm::component */ [[nodiscard]] inline auto vector_of_leaf_groups() noexcept -> leaf_group_vector_type& { - return m_group_of_leaf; + return m_group_of_leaves; } + + /** + * @brief + * + * @return leaf_group_vector_type const& + */ [[nodiscard]] inline auto vector_of_leaf_groups() const noexcept -> leaf_group_vector_type const& { - return m_group_of_leaf; + return m_group_of_leaves; } + + /** + * @brief + * + * @return leaf_group_vector_type& + */ [[nodiscard]] [[deprecated]] inline auto group_of_leaves() noexcept -> leaf_group_vector_type& { - return m_group_of_leaf; + return m_group_of_leaves; } + + /** + * @brief + * + * @return leaf_group_vector_type const& + */ [[nodiscard]] [[deprecated]] inline auto group_of_leaves() const noexcept -> leaf_group_vector_type const& { - return m_group_of_leaf; + return m_group_of_leaves; + } + /** + * @brief Compute the number of cells in the tree + * + * @return the number of cells (std::size_t) + */ + auto number_of_cells() const -> std::size_t + { + std::size_t num_cells{0}; + for(std::size_t l = 0; l < m_tree_height; ++l) + { + auto const& group_of_cell_per_level = m_group_of_cell_per_level[l]; + auto nb_blocks = group_of_cell_per_level.size(); + if(nb_blocks > 0) + { + num_cells += m_number_of_cells_per_group * (nb_blocks - 1) + group_of_cell_per_level.back()->size(); + } + } + return num_cells; + } + + /** + * @brief Compute the number of leaves in the tree + * + * @return the number of leaves (std::size_t) + */ + auto number_of_leaves() const -> std::size_t + { + std::size_t num_leaves{0}; + auto const& group_of_leaves = m_group_of_leaves; + auto nb_blocks = group_of_leaves.size(); + if(nb_blocks > 0) + { + num_leaves += m_number_of_leaves_per_group * (nb_blocks - 1) + group_of_leaves.back()->size(); + } + + return num_leaves; + } + + /** + * @brief Compute the number of particles in the tree + * + * @return the number of particles (std::size_t) + */ + auto number_of_particles() const -> std::size_t + { + std::size_t num_particles{0}; + for(auto const& pg: m_group_of_leaves) + { + num_particles += pg->storage().size(); + } + + return num_particles; } /** * @brief Return the vector ol groups of cells at level l @@ -984,80 +1332,143 @@ namespace scalfmm::component { return m_group_of_cell_per_level[l]; } + + /** + * @brief + * + * @param l + * @return cell_group_level_type const& + */ [[nodiscard]] inline auto vector_of_cell_groups(const int l) const noexcept -> cell_group_level_type const& { return m_group_of_cell_per_level[l]; } + + /** + * @brief + * + * @return std::size_t + */ [[nodiscard]] inline auto group_of_leaf_size() const noexcept -> std::size_t { return m_number_of_leaves_per_group; } - [[nodiscard]] inline auto leaf_groups_size() const noexcept -> std::size_t { return m_group_of_leaf.size(); } + + /** + * @brief + * + * @return std::size_t + */ + [[nodiscard]] inline auto leaf_groups_size() const noexcept -> std::size_t { return m_group_of_leaves.size(); } + + /** + * @brief + * + * @return std::size_t + */ [[nodiscard]] inline auto cell_groups_size() const noexcept -> std::size_t { return m_number_of_cells_per_group; } + + /** + * @brief + * + * @return std::size_t + */ [[nodiscard]] inline auto group_of_cell_size() const noexcept -> std::size_t { return m_number_of_cells_per_group; } + // iterators related /** * @brief return the begin iterator on the groups for each level * - * * @return tuple of begin iterator organized as follows (leaf, leaf_cell, ... root_cell) */ [[nodiscard]] inline auto begin() -> iterator_type { - return std::make_tuple(std::begin(m_group_of_leaf), std::begin(m_group_of_cell_per_level)); + return std::make_tuple(std::begin(m_group_of_leaves), std::begin(m_group_of_cell_per_level)); } + /** + * @brief + * + * @return const_iterator_type + */ [[nodiscard]] inline auto begin() const -> const_iterator_type { - return std::make_tuple(std::cbegin(m_group_of_leaf), std::cbegin(m_group_of_cell_per_level)); + return std::make_tuple(std::cbegin(m_group_of_leaves), std::cbegin(m_group_of_cell_per_level)); } + /** + * @brief + * + * @return const_iterator_type + */ [[nodiscard]] inline auto cbegin() const -> const_iterator_type { - return std::make_tuple(std::cbegin(m_group_of_leaf), std::cbegin(m_group_of_cell_per_level)); + return std::make_tuple(std::cbegin(m_group_of_leaves), std::cbegin(m_group_of_cell_per_level)); } + /** * @brief return the end iterator on the groups for each level * - * * @return tuple of end iterator organized as follows (leaf, leaf_cell, ... root_cell) */ [[nodiscard]] inline auto end() -> iterator_type { - return std::make_tuple(std::end(m_group_of_leaf), std::end(m_group_of_cell_per_level)); + return std::make_tuple(std::end(m_group_of_leaves), std::end(m_group_of_cell_per_level)); } + /** + * @brief + * + * @return const_iterator_type + */ [[nodiscard]] inline auto end() const -> const_iterator_type { - return std::make_tuple(std::cend(m_group_of_leaf), std::cend(m_group_of_cell_per_level)); + return std::make_tuple(std::cend(m_group_of_leaves), std::cend(m_group_of_cell_per_level)); } + /** + * @brief + * + * @return const_iterator_type + */ [[nodiscard]] inline auto cend() const -> const_iterator_type { - return std::make_tuple(std::cend(m_group_of_leaf), std::cend(m_group_of_cell_per_level)); + return std::make_tuple(std::cend(m_group_of_leaves), std::cend(m_group_of_cell_per_level)); } + /** * @brief return iterator on beginning of leaves * * @return leaf_iterator_type */ - [[nodiscard]] inline auto begin_leaves() -> leaf_iterator_type { return std::begin(m_group_of_leaf); } + [[nodiscard]] inline auto begin_leaves() -> leaf_iterator_type { return std::begin(m_group_of_leaves); } + /** + * @brief + * + * @return const_leaf_iterator_type + */ [[nodiscard]] inline auto begin_leaves() const -> const_leaf_iterator_type { - return std::cbegin(m_group_of_leaf); + return std::cbegin(m_group_of_leaves); } + /** + * @brief + * + * @return const_leaf_iterator_type + */ [[nodiscard]] inline auto cbegin_leaves() const -> const_leaf_iterator_type { - return std::cbegin(m_group_of_leaf); + return std::cbegin(m_group_of_leaves); } + /** * @brief return the iterator on the first valid leaf group (leaves I own) * @@ -1065,30 +1476,54 @@ namespace scalfmm::component */ [[nodiscard]] inline auto begin_mine_leaves() -> leaf_iterator_type { return m_view_on_my_leaf_groups[0]; } + /** + * @brief + * + * @return const_leaf_iterator_type + */ [[nodiscard]] inline auto begin_mine_leaves() const -> const_leaf_iterator_type { return static_cast<const_leaf_iterator_type>(m_view_on_my_leaf_groups[0]); } + /** + * @brief + * + * @return const_leaf_iterator_type + */ [[nodiscard]] inline auto cbegin_mine_leaves() const -> const_leaf_iterator_type { return static_cast<const_leaf_iterator_type>(m_view_on_my_leaf_groups[0]); } + /** * @brief return the iterator on the last valid leaf group (leaves I own) * * @return leaf_iterator_type */ [[nodiscard]] inline auto end_mine_leaves() -> leaf_iterator_type { return m_view_on_my_leaf_groups[1]; } + + /** + * @brief + * + * @return const_leaf_iterator_type + */ [[nodiscard]] inline auto end_mine_leaves() const -> const_leaf_iterator_type { return static_cast<const_leaf_iterator_type>(m_view_on_my_leaf_groups[1]); // return std::cend(m_view_on_my_leaf_groups); } + + /** + * @brief + * + * @return const_leaf_iterator_type + */ [[nodiscard]] inline auto cend_mine_leaves() const -> const_leaf_iterator_type { return static_cast<const_leaf_iterator_type>(m_view_on_my_leaf_groups[1]); } + /** * @brief return the iterator on the first valid cell group (leaves I own) at level level * @@ -1100,15 +1535,28 @@ namespace scalfmm::component return m_view_on_my_cell_groups[level][0]; } + /** + * @brief + * + * @param level + * @return const_cell_group_level_iterator_type + */ [[nodiscard]] inline auto begin_mine_cells(const int& level) const -> const_cell_group_level_iterator_type { return static_cast<const_cell_group_level_iterator_type>(m_view_on_my_cell_groups[level][0]); } + /** + * @brief + * + * @param level + * @return const_cell_group_level_iterator_type + */ [[nodiscard]] inline auto cbegin_mine_cells(const int& level) const -> const_cell_group_level_iterator_type { return static_cast<const_cell_group_level_iterator_type>(m_view_on_my_cell_groups[level][0]); } + /** * @brief return the iterator on the last valid cell group (leaves I own) at level level * @@ -1119,14 +1567,29 @@ namespace scalfmm::component { return m_view_on_my_cell_groups[level][1]; } + + /** + * @brief + * + * @param level + * @return const_cell_group_level_iterator_type + */ [[nodiscard]] inline auto end_mine_cells(const int& level) const -> const_cell_group_level_iterator_type { return static_cast<const_cell_group_level_iterator_type>(m_view_on_my_cell_groups[level][1]); } + + /** + * @brief + * + * @param level + * @return const_cell_group_level_iterator_type + */ [[nodiscard]] inline auto cend_mine_cells(const int& level) const -> const_cell_group_level_iterator_type { return static_cast<const_cell_group_level_iterator_type>(m_view_on_my_cell_groups[level][1]); } + /** * @brief return the iterator on the first vector of cell group * @@ -1135,10 +1598,21 @@ namespace scalfmm::component */ [[nodiscard]] inline auto begin_cells() -> cell_iterator_type { return std::begin(m_group_of_cell_per_level); } + /** + * @brief + * + * @return const_cell_iterator_type + */ [[nodiscard]] inline auto begin_cells() const -> const_cell_iterator_type { return std::cbegin(m_group_of_cell_per_level); } + + /** + * @brief + * + * @return const_cell_iterator_type + */ [[nodiscard]] inline auto cbegin_cells() const -> const_cell_iterator_type { return std::cbegin(m_group_of_cell_per_level); @@ -1155,15 +1629,28 @@ namespace scalfmm::component return std::begin(m_group_of_cell_per_level[level]); } + /** + * @brief + * + * @param level + * @return const_cell_group_level_iterator_type + */ [[nodiscard]] inline auto begin_cells(const int& level) const -> const_cell_group_level_iterator_type { return std::cbegin(m_group_of_cell_per_level[level]); } + /** + * @brief + * + * @param level + * @return const_cell_group_level_iterator_type + */ [[nodiscard]] inline auto cbegin_cells(const int& level) const -> const_cell_group_level_iterator_type { return std::cbegin(m_group_of_cell_per_level[level]); } + /** * @brief return the iterator on the last cell group (cells I own) at level level * @@ -1174,50 +1661,82 @@ namespace scalfmm::component { return std::end(m_group_of_cell_per_level[level]); } + + /** + * @brief + * + * @param level + * @return const_cell_group_level_iterator_type + */ [[nodiscard]] inline auto end_cells(const int& level) const -> const_cell_group_level_iterator_type { return std::cend(m_group_of_cell_per_level[level]); } + + /** + * @brief + * + * @param level + * @return const_cell_group_level_iterator_type + */ [[nodiscard]] inline auto cend_cells(const int& level) -> const_cell_group_level_iterator_type { return std::cend(m_group_of_cell_per_level[level]); } - [[nodiscard]] inline auto end_leaves() -> leaf_iterator_type { return std::end(m_group_of_leaf); } + /** + * @brief + * + * @return leaf_iterator_type + */ + [[nodiscard]] inline auto end_leaves() -> leaf_iterator_type { return std::end(m_group_of_leaves); } - [[nodiscard]] inline auto end_leaves() const -> const_leaf_iterator_type { return std::cend(m_group_of_leaf); } + /** + * @brief + * + * @return const_leaf_iterator_type + */ + [[nodiscard]] inline auto end_leaves() const -> const_leaf_iterator_type + { + return std::cend(m_group_of_leaves); + } + + /** + * @brief + * + * @return const_leaf_iterator_type + */ + [[nodiscard]] inline auto cend_leaves() const -> const_leaf_iterator_type + { + return std::cend(m_group_of_leaves); + } - [[nodiscard]] inline auto cend_leaves() const -> const_leaf_iterator_type { return std::cend(m_group_of_leaf); } /** * @brief return the iterator on the last vector of cell group * * @return cell_iterator_type */ - [[nodiscard]] inline auto end_cells() -> cell_iterator_type { return std::end(m_group_of_cell_per_level); } + /** + * @brief + * + * @return const_cell_iterator_type + */ [[nodiscard]] inline auto end_cells() const -> const_cell_iterator_type { return std::cend(m_group_of_cell_per_level); } + /** + * @brief + * + * @return const_cell_iterator_type + */ [[nodiscard]] inline auto cend_cells() const -> const_cell_iterator_type { return std::cend(m_group_of_cell_per_level); } - - // inline void reset_outputs() - // { - // // leaf level update only P2P ??? - // component::for_each(std::get<0>(begin()), std::get<0>(end()), - // [this](auto& group) - // { - // std::size_t index_in_group{0}; - // component::for_each(std::begin(*group), std::end(*group), - // [&group, &index_in_group, this](auto& leaf) - // { leaf.reset_outputs(); }); - // }); - // } }; } // namespace scalfmm::component diff --git a/include/scalfmm/tree/header.hpp b/include/scalfmm/tree/header.hpp index 04b23a81177965f1eeae14f912ca2a3ca28b54f9..c9324bb231cf816098144e94fa800277c8420a74 100644 --- a/include/scalfmm/tree/header.hpp +++ b/include/scalfmm/tree/header.hpp @@ -1,36 +1,64 @@ // -------------------------------- // See LICENCE file at project root -// File : header.hpp +// File : scalfmm/tree/header.hpp // -------------------------------- #ifndef SCALFMM_TREE_SYMBOLICS_HPP #define SCALFMM_TREE_SYMBOLICS_HPP namespace scalfmm::component { + + /** + * @brief + * + * @tparam Component + */ template<typename Component> struct symbolics_data { }; - /// @brief This structure stores the required information - /// to handle out of group interactions. - /// - /// @tparam IndexType : the indexing type - /// @tparam CoordinateType : the coordinate type of the components + /** + * @brief This structure stores the required information + * to handle out of group interactions. + * + * @tparam Iterator_type + * @tparam IndexType + */ template<typename Iterator_type, typename IndexType = std::size_t> struct out_of_block_interaction { using iterator_type = Iterator_type; /// interaction between A and B - /// morton index of leaf A inside the group + + /** + * @brief morton index of leaf A inside the group. + * + */ IndexType inside_index{}; - /// morton index of leaf B outside of the group + + /** + * @brief morton index of leaf B outside of the group. + * + */ IndexType outside_index{}; - /// index in the group of leaf A inside the group + + /** + * @brief index in the group of leaf A inside the group. + * + */ int inside_index_in_block{-1}; - /// index in the group of leaf B inside the group + + /** + * @brief index in the group of leaf B inside the group. + * + */ int outside_index_in_block{-1}; - /// Iterator of leaf B inside the group + + /** + * @brief Iterator of leaf B inside the group. + * + */ Iterator_type outside_iterator; /** @@ -47,8 +75,19 @@ namespace scalfmm::component , outside_index_in_block(-1) { } + + /** + * @brief Construct a new out of block interaction object + * + */ out_of_block_interaction() = default; + + /** + * @brief Destroy the out of block interaction object + * + */ ~out_of_block_interaction() = default; + /** * @brief Display teh out_of_block_interaction struct * @@ -56,7 +95,8 @@ namespace scalfmm::component * @param u the out_of_block_interaction struct * @return std::ostream& */ - inline friend auto operator<<(std::ostream& os, const out_of_block_interaction<Iterator_type, IndexType>& u) -> std::ostream& + inline friend auto operator<<(std::ostream& os, + const out_of_block_interaction<Iterator_type, IndexType>& u) -> std::ostream& { os << "[ (" << u.inside_index << ", " << u.inside_index_in_block << "), (" << u.outside_index << ", " << u.outside_index_in_block << ")]"; diff --git a/include/scalfmm/tree/io.hpp b/include/scalfmm/tree/io.hpp index 2b4e88b76d03f957fb23bc64985c214fcff96e97..64753b5cfbb3d35f28424e2430da3b54e706584a 100644 --- a/include/scalfmm/tree/io.hpp +++ b/include/scalfmm/tree/io.hpp @@ -1,21 +1,30 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/tree/io.hpp +// -------------------------------- #pragma once +#include "scalfmm/utils/io_helpers.hpp" + #include <fstream> #include <iostream> #include <map> #include <sstream> #include <string> -#include "scalfmm/utils/io_helpers.hpp" - namespace scalfmm::io { - - template<typename Cell> - void print_cell(const Cell& cell, bool print_aux = false) + /** + * @brief + * + * @tparam CellType + * @param cell + * @param print_aux + */ + template<typename CellType> + auto print_cell(const CellType& cell, bool print_aux = false) -> void { auto m = cell.cmultipoles(); - // auto tm = cell.ctransformed_multipoles(); auto nb_m = m.size(); std::cout << "cell index: " << cell.index() << " level " << cell.csymbolics().level << "\n"; @@ -23,10 +32,6 @@ namespace scalfmm::io { auto ten = m.at(i); std::cout << " multi(" << i << "): \n" << m.at(i) << std::endl; - // if(print_aux) - // { - // std::cout << " transf multi:\n " << tm.at(i) << std::endl; - // } } auto loc = cell.clocals(); auto nb_loc = loc.size(); @@ -37,61 +42,67 @@ namespace scalfmm::io std::cout << " loc(" << i << "): \n" << loc.at(i) << std::endl; } } + /** * @brief Display particles in leaf and compute the current Morton index of the particle * - * @tparam Leaf type + * @tparam LeafType type * @tparam Box type * @param leaf the current leaf * @param box The box to compute the Morton index * @param level The level to compute the Morton index */ - template<typename Leaf, typename Box> - void print_leaf(const Leaf& leaf, Box box, int level) + template<typename LeafType, typename Box> + auto print_leaf(const LeafType& leaf, Box box, int level) -> void { auto morton = leaf.index(); int i{0}; for(auto const& pl: leaf) { - const auto p = typename Leaf::const_proxy_type(pl); + const auto p = typename LeafType::const_proxy_type(pl); std::cout << i++ << " morton: " << morton << " part= " << p << " cmp_morton " << scalfmm::index::get_morton_index(p.position(), box, level) << std::endl; } } + /** * @brief Display particles in leaf * - * @tparam Leaf the type of the leaf + * @tparam LeafType the type of the leaf * @param leaf the current leaf */ - template<typename Leaf> - void print_leaf(const Leaf& leaf) + template<typename LeafType> + auto print_leaf(const LeafType& leaf) -> void { auto morton = leaf.index(); int i{0}; for(auto const& p: leaf) { - const auto pp = typename Leaf::const_proxy_type(p); + const auto pp = typename LeafType::const_proxy_type(p); std::cout << i++ << " morton: " << morton << " part= " << pp << std::endl; } } - /// - /// \brief trace the index of the cells and leaves in the tree - /// - /// Depending on the level we print more or less details - /// level_trace = 1 print minimal information (height, order, group size) - /// level_trace = 2 print information of the tree (group interval and index inside) - /// level_trace = 3 print information of the tree (leaf interval and index inside and their p2p interaction - /// list) level_trace = 4 print information of the tree (cell interval and index inside and their m2l - /// interaction list) - /// level_trace = 5 print information of the tree (leaf and cell interval and index inside - /// and their p2p and m2l interaction lists) - /// - /// @warning to have the right p2p list we have to have the group size in the tree equal to the number - /// of leaves otherwise, we only print the index inside the group. - /// - /// @param[in] level_trace level of the trace - /// + + /** + * @brief trace the index of the cells and leaves in the tree + * + * Depending on the level we print more or less details + * level_trace = 1 print minimal information (height, order, group size) + * level_trace = 2 print information of the tree (group interval and index inside) + * level_trace = 3 print information of the tree (leaf interval and index inside and their p2p interaction + * list) level_trace = 4 print information of the tree (cell interval and index inside and their m2l + * interaction list) + * level_trace = 5 print information of the tree (leaf and cell interval and index inside + * and their p2p and m2l interaction lists) + * + * @warning to have the right p2p list we have to have the group size in the tree equal to the number + * of leaves otherwise, we only print the index inside the group. + * + * @tparam TreeType + * @param os + * @param tree + * @param level_trace + */ template<typename TreeType> inline auto trace(std::ostream& os, const TreeType& tree, const std::size_t level_trace = 0) -> void { @@ -139,8 +150,7 @@ namespace scalfmm::io os << " group size: " << current_group_symbolics.number_of_component_in_group << ", "; os << "global index = " << current_group_symbolics.idx_global << " \n"; os << " index: "; - component::for_each(std::begin(*ptr_group), std::end(*ptr_group), - [&os](auto& leaf) + component::for_each(std::begin(*ptr_group), std::end(*ptr_group), [&os](auto& leaf) { os << leaf.index() << "(" << leaf.size() << ") "; }); os << std::endl; }); @@ -305,10 +315,13 @@ namespace scalfmm::io } } - /// - /// \brief trace the group dependencies for the transfer pass (M2L) - /// - /// + /** + * @brief trace the group dependencies for the transfer pass (M2L) + * + * @tparam TreeType + * @param os + * @param tree + */ template<typename TreeType> inline auto trace_m2l_dep(std::ostream& os, const TreeType& tree) -> void { @@ -341,6 +354,11 @@ namespace scalfmm::io }); } } + + /** + * @brief + * + */ auto print_map = [](auto const comment, auto const& map) { std::cout << comment << "{"; @@ -349,6 +367,10 @@ namespace scalfmm::io std::cout << "}\n"; }; + /** + * @brief + * + */ auto print_with_map = [](std::ostream& os, auto const comment, auto const& v, auto& map) { std::cout << comment << " (" << v.size() << ") "; @@ -364,6 +386,13 @@ namespace scalfmm::io } os << "\n"; }; + + /** + * @brief + * + * @tparam TreeType + * @param tree + */ template<typename TreeType> inline auto init_trace_group_dependencies(TreeType& tree) -> void { @@ -390,38 +419,49 @@ namespace scalfmm::io tree.m_locals_dependencies[address_loc.str()] = id_group; ++id_group; }); - std::cout << " end " << id_group -1 << std::endl; + std::cout << " end " << id_group - 1 << std::endl; } print_map("Map mult: ", tree.m_multipoles_dependencies); print_map("Map loc: ", tree.m_locals_dependencies); } - template<typename TreeSource, typename TreeTarget> - inline auto trace_group_dependencies(std::ostream& os, TreeSource& tree_source, TreeTarget& tree_target) -> void + + /** + * @brief + * + * @tparam SourceTreeType + * @tparam TargetTreeType + * @param os + * @param source_tree + * @param target_tree + */ + template<typename SourceTreeType, typename TargetTreeType> + inline auto trace_group_dependencies(std::ostream& os, SourceTreeType& source_tree, + TargetTreeType& target_tree) -> void { std::cout << "Trace of the group dependencies\n"; bool same_tree{false}; - if constexpr(std::is_same_v<TreeSource, TreeTarget>) + if constexpr(std::is_same_v<SourceTreeType, TargetTreeType>) { - same_tree = (&tree_source == &tree_target); + same_tree = (&source_tree == &target_tree); } - auto& tree = tree_target; - init_trace_group_dependencies(tree_source); - init_trace_group_dependencies(tree_target); + auto& tree = target_tree; + init_trace_group_dependencies(source_tree); + init_trace_group_dependencies(target_tree); os << "========================== M2L Group dependencies ========================= \n"; const std::size_t top_level = tree.box().is_periodic() ? 0 : 2; const auto leaf_level = tree.leaf_level(); - auto& key_mul = tree_source.m_multipoles_dependencies; - auto& key_loc = tree_target.m_locals_dependencies; + auto& key_mul = source_tree.m_multipoles_dependencies; + auto& key_loc = target_tree.m_locals_dependencies; os << "======================================================\n"; os << "========== M2M dependencies source ======\n"; os << "======================================================\n"; - for(std::size_t level = leaf_level-1; level >= top_level; --level) + for(std::size_t level = leaf_level - 1; level >= top_level; --level) { os << "========== level : " << level << " ============================\n"; - const auto group_of_cell_begin = tree_source.cbegin_mine_cells(level); - const auto group_of_cell_end = tree_source.cend_mine_cells(level); + const auto group_of_cell_begin = source_tree.cbegin_mine_cells(level); + const auto group_of_cell_end = source_tree.cend_mine_cells(level); std::for_each( group_of_cell_begin, group_of_cell_end, [&os, &key_mul](auto const& ptr_group) @@ -444,8 +484,8 @@ namespace scalfmm::io for(std::size_t level = leaf_level; level >= top_level; --level) { os << "========== level : " << level << " ============================\n"; - const auto group_of_cell_begin = tree_target.cbegin_mine_cells(level); - const auto group_of_cell_end = tree_target.cend_mine_cells(level); + const auto group_of_cell_begin = target_tree.cbegin_mine_cells(level); + const auto group_of_cell_end = target_tree.cend_mine_cells(level); std::for_each( group_of_cell_begin, group_of_cell_end, [&os, level, top_level, &key_mul, &key_loc](auto const& ptr_group) @@ -477,6 +517,14 @@ namespace scalfmm::io }); } } + + /** + * @brief + * + * @tparam TreeType + * @param os + * @param tree + */ template<typename TreeType> inline auto trace_group_dependencies(std::ostream& os, TreeType& tree) -> void { diff --git a/include/scalfmm/tree/leaf.hpp b/include/scalfmm/tree/leaf.hpp index 50690a99a9c39d9208e98f569194421d210d5f8c..9508cc43c3fe2a4c682ee9e2cd05dd4ebe6437e7 100644 --- a/include/scalfmm/tree/leaf.hpp +++ b/include/scalfmm/tree/leaf.hpp @@ -1,24 +1,12 @@ // -------------------------------- // See LICENCE file at project root -// File : leaf.hpp +// File : scalfmm/tree/leaf.hpp // -------------------------------- #ifndef SCALFMM_TREE_LEAF_HPP #define SCALFMM_TREE_LEAF_HPP #warning("Do not use this file - old leaf format") -//#include <xsimd/config/xsimd_config.hpp> -#include <algorithm> -#include <any> -#include <array> -#include <cmath> -#include <cstddef> -#include <iterator> -#include <tuple> -#include <type_traits> -#include <utility> -#include <vector> - #include "scalfmm/container/particle_container.hpp" #include "scalfmm/container/point.hpp" #include "scalfmm/container/variadic_adaptor.hpp" @@ -32,18 +20,35 @@ #include "scalfmm/utils/massert.hpp" #include "scalfmm/utils/math.hpp" +#include <algorithm> +#include <any> +#include <array> +#include <cmath> +#include <cstddef> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + namespace scalfmm::component { - /// @brief This is the leaf type stored at the bottom of the tree. - /// Its stores the particle container and some symbolics informations. - /// - /// @tparam Particle : the type of particle to store. + /** + * @brief This is the leaf type stored at the bottom of the tree. + * Its stores the particle container and some symbolics informations. + * + * @tparam Particle : the type of particle to store. + * @tparam D + */ template<typename Particle, typename D = void> class leaf { public: - // static dimension of the particle + /** + * @brief Static dimension of the space. + * + */ static constexpr std::size_t dimension = Particle::dimension; // self type using self_type = leaf<Particle>; @@ -62,29 +67,65 @@ namespace scalfmm::component using particle_container_type = container::particle_container<particle_type>; // rule of five generated by the compiler + + /** + * @brief Construct a new leaf object + * + */ leaf() = default; + + /** + * @brief Construct a new leaf object + * + */ leaf(leaf const&) = default; + + /** + * @brief Construct a new leaf object + * + */ leaf(leaf&&) noexcept = default; + + /** + * @brief + * + * @return leaf& + */ inline auto operator=(leaf const&) -> leaf& = default; + + /** + * @brief + * + * @return leaf& + */ inline auto operator=(leaf&&) noexcept -> leaf& = default; + + /** + * @brief Destroy the leaf object + * + */ ~leaf() = default; - /// @brief Constructors from center and width - /// - /// @param center : the center of the leaf - /// @param width : the width of the leaf + /** + * @brief Constructors from center and width + * + * @param center : the center of the leaf + * @param width : the width of the leaf + */ leaf(position_type const& center, value_type width) : m_center(center) , m_width(width) { } - /// @brief Constructors from number of particles, center, width and a morton index - /// - /// @param nb_particles : number of particles in the container - /// @param center : the center of the leaf - /// @param width : the width of the leaf - /// @param morton_index : the morton index of the leaf + /** + * @brief Constructors from number of particles, center, width and a morton index + * + * @param nb_particles : number of particles in the container + * @param center : the center of the leaf + * @param width : the width of the leaf + * @param morton_index : the morton index of the leaf + */ leaf(std::size_t nb_particles, position_type const& center, value_type width, std::size_t morton_index) : m_particles(nb_particles) , m_symbolics{morton_index} @@ -95,15 +136,17 @@ namespace scalfmm::component m_symbolics.morton_index = morton_index; } - /// @brief - /// Constructors from a container of particles, center, width and a morton index - /// It takes an rvalue reference to the container allowing you to move the container inside the leaf. - /// - /// @param container : rvalue ref to the container - /// @param nb_particles : number of particles - /// @param center : the center of the leaf - /// @param width : the width of the leaf - /// @param morton_index : the morton index of the leaf + /** + * @brief Construct a new leaf object + * Constructors from a container of particles, center, width and a morton index + * It takes an rvalue reference to the container allowing you to move the container inside the leaf. + * + * @param container : rvalue ref to the container + * @param nb_particles : number of particles + * @param center : the center of the leaf + * @param width : the width of the leaf + * @param morton_index : the morton index of the leaf + */ leaf(particle_container_type&& container, std::size_t nb_particles, position_type const& center, value_type width, std::size_t morton_index) : m_particles(container) @@ -114,14 +157,16 @@ namespace scalfmm::component { } - /// @brief Constructors from a container of particles with iterators, center, width and a morton index - /// - /// @param begin : iterator on the begin of the particle container to copy - /// @param end : iterator on the end of the particle container to copy - /// @param nb_particles : number of particles - /// @param center : the center of the leaf - /// @param width : the width of the leaf - /// @param morton_index : the morton index of the leaf + /** + * @brief Constructors from a container of particles with iterators, center, width and a morton index + * + * @param begin : iterator on the begin of the particle container to copy + * @param end : iterator on the end of the particle container to copy + * @param nb_particles : number of particles + * @param center : the center of the leaf + * @param width : the width of the leaf + * @param morton_index : the morton index of the leaf + */ leaf(typename particle_container_type::const_iterator begin, typename particle_container_type::const_iterator end, std::size_t nb_particles, position_type const& center, value_type width, std::size_t morton_index) @@ -134,91 +179,168 @@ namespace scalfmm::component std::copy(begin, end, std::begin(m_particles)); } - /// @brief Returns the center of the leaf - /// - /// @return position_type + /** + * @brief Returns the center of the leaf. + * + * @return position_type const& + */ [[nodiscard]] inline auto center() const noexcept -> position_type const& { return m_center; } - /// @brief Returns the width of the leaf - /// - /// @return value_type + + /** + * @brief Returns the width of the leaf. + * + * @return value_type + */ [[nodiscard]] inline auto width() const noexcept -> value_type { return m_width; } - /// @brief Return the number of particles - /// - /// @return std::size_t + + /** + * @brief Returns the number of particles + * + * @return std::size_t + */ [[nodiscard]] inline auto size() const noexcept -> std::size_t { return m_nb_particles; } - /// @brief Non const accessor on the container - /// - /// @return a reference on the particle_container_type + /** + * @brief Non-const accessor on the container + * + * @return particle_container_type& + */ [[nodiscard]] inline auto particles() -> particle_container_type& { return m_particles; } - /// @brief Const accessor on the container - /// - /// @return a const reference on the particle_container_type + + /** + * @brief Const accessor on the container. + * + * @return particle_container_type const& + */ [[nodiscard]] inline auto particles() const -> particle_container_type const& { return m_particles; } - /// @brief Const accessor on the container - /// - /// @return a const reference on the particle_container_type + + /** + * @brief Const accessor on the container. + * + * @return particle_container_type const& + */ [[nodiscard]] inline auto cparticles() const -> particle_container_type const& { return m_particles; } - /// @brief Indexed accessor on the particle container - /// - /// @param i : the index at which the access is performed. - /// - /// @return a particle_type reference + /** + * @brief Indexed accessor on the particle container. + * + * @param i the index at which the access is performed. + * @return particle_type& + */ [[nodiscard]] inline auto particles(std::size_t i) -> particle_type& { return m_particles.at(i); } + + /** + * @brief Indexed accessor on the particle container. + * + * @param i the index at which the access is performed. + * @return particle_type& + */ [[nodiscard]] inline auto particle(std::size_t i) -> particle_type& { return m_particles.at(i); } - /// @brief Indexed accessor on the particle container - /// - /// @param i : the index at which the access is performed. - /// - /// @return a particle_type const reference + + /** + * @brief Indexed accessor on the particle container. + * + * @param i the index at which the access is performed. + * @return particle_type const& + */ [[nodiscard]] inline auto particles(std::size_t i) const -> particle_type const& { return m_particles.at(i); } + + /** + * @brief Indexed accessor on the particle container. + * + * @param i the index at which the access is performed. + * @return particle_type const& + */ [[nodiscard]] inline auto particle(std::size_t i) const -> particle_type const& { return m_particles.at(i); } + /** + * @brief + * + * @return particle_container_type::iterator + */ [[nodiscard]] inline auto begin() -> typename particle_container_type::iterator { return m_particles.begin(); } + + /** + * @brief + * + * @return particle_container_type::const_iterator + */ [[nodiscard]] inline auto begin() const -> typename particle_container_type::const_iterator { return m_particles.begin(); } + + /** + * @brief + * + * @return particle_container_type::const_iterator + */ [[nodiscard]] inline auto cbegin() const -> typename particle_container_type::const_iterator { return m_particles.cbegin(); } + /** + * @brief + * + * @return particle_container_type::iterator + */ [[nodiscard]] inline auto end() -> typename particle_container_type::iterator { return m_particles.end(); } + + /** + * @brief + * + * @return particle_container_type::const_iterator + */ [[nodiscard]] inline auto end() const -> typename particle_container_type::const_iterator { return m_particles.end(); } + + /** + * @brief + * + * @return particle_container_type::const_iterator + */ [[nodiscard]] inline auto cend() const -> typename particle_container_type::const_iterator { return m_particles.cend(); } - /// @brief Access to the symbolic type - /// - /// @return a symbolics_type reference + /** + * @brief Access to the symbolic type. + * + * @return symbolics_type& + */ [[nodiscard]] inline auto symbolics() -> symbolics_type& { return m_symbolics; } - /// @brief Access to the symbolic type - /// - /// @return a const symbolics_type reference + + /** + * @brief Access to the symbolic type. + * + * @return symbolics_type const& + */ [[nodiscard]] inline auto symbolics() const -> symbolics_type const& { return m_symbolics; } - /// @brief Access to the symbolic type - /// - /// @return a const symbolics_type reference + + /** + * @brief Access to the symbolic type. + * + * @return symbolics_type const& + */ [[nodiscard]] inline auto csymbolics() const -> symbolics_type const& { return m_symbolics; } - /// @brief Returns the morton index of the leaf - /// - /// @return std::size_t + /** + * @brief Returns the morton index of the leaf. + * + * @return std::size_t + */ [[nodiscard]] inline auto index() const noexcept -> std::size_t { return m_symbolics.morton_index; } - /// @brief Insert a particle at the index - /// - /// @param part : the particle to insert - /// @param index : the position to insert the particle - /// - /// @return void + /** + * @brief Insert a particle at the index. + * + * @param part the particle to insert. + * @param index the position to insert the particle. + */ inline auto insert_particle(particle_type const& part, std::size_t index) -> void { assertm(index < m_nb_particles, "Inserting particle out of range in leaf."); @@ -226,12 +348,13 @@ namespace scalfmm::component *it = part.as_tuple(); } - /// @brief Insert a particle at the index - /// - /// @param part : the tuple corresponding to the particle to insert - /// @param index : the position to insert the particle - /// - /// @return void + /** + * @brief Insert a particle at the index. + * + * @tparam Types + * @param part the tuple corresponding to the particle to insert. + * @param index the position to insert the particle. + */ template<typename... Types> inline auto insert_particle(std::tuple<Types...> const& part, std::size_t index) -> void { @@ -241,32 +364,74 @@ namespace scalfmm::component *it = part; } - /// - /// \brief reset_outputs reset outputs in the leaf - /// - inline void reset_outputs() - { - // - m_particles.reset_outputs(); - } + /** + * @brief Resets the positions of the leaves. + * + */ + inline auto reset_positions() -> void { m_particles.reset_positions(); } + + /** + * @brief Resets the inputs of the leaves. + * + */ + inline auto reset_inputs() -> void { m_particles.reset_inputs(); } + + /** + * @brief Resets the outputs of the leaves. + * + */ + inline auto reset_outputs() -> void { m_particles.reset_outputs(); } + + /** + * @brief Resets the variables of the leaves. + * + */ + inline auto reset_variables() -> void { m_particles.reset_variables(); } + + /** + * @brief Resets the particles of the leaves. + * + */ + inline auto reset_particles() -> void { m_particles.reset_particles(); } private: - // The particle container + /** + * @brief The particle container. + * + */ particle_container_type m_particles{}; - // The symbolic type + + /** + * @brief The symbolic data. + * + */ symbolics_type m_symbolics{}; - // The number of particles + + /** + * @brief The number of particles. + * + */ std::size_t m_nb_particles{}; - // Position of the center + + /** + * @brief Position of the center. + * + */ position_type m_center{}; - // The width of the leaf + + /** + * @brief The width of the leaf. + * + */ value_type m_width{}; }; - /// @brief The symbolics type stores information about the leaf - /// It represents a generic that also exists on the cells - /// - /// @tparam P + /** + * @brief The symbolics type stores information about the leaf + * It represents a generic that also exists on the cells + * + * @tparam P + */ template<typename P> struct symbolics_data<leaf<P>> { @@ -275,8 +440,9 @@ namespace scalfmm::component // the group type using group_type = group<component_type>; // the position type - using position_type = typename component_type::particle_type::position_type; // container::point<position_value_type, - // P::dimension>; + using position_type = + typename component_type::particle_type::position_type; // container::point<position_value_type, + // P::dimension>; // the coordinate type to store the coordinate in the tree using coordinate_type = decltype(index::get_coordinate_from_morton_index<position_type::dimension>(std::size_t{})); @@ -288,31 +454,66 @@ namespace scalfmm::component // type of the array storing the iterators of the interacting leaves available in the current group using iterator_array_type = std::array<typename group_type::iterator_type, number_of_interactions>; using iterator_type = typename iterator_array_type::value_type; - // the morton index of the leaf + /** + * @brief The morton index of the leaf. + * + */ std::size_t morton_index{0}; - // the array storing the indexes of the theoretical interaction list + + /** + * @brief The array storing the indices of the theoritical interaction list. + * + */ interaction_index_array_type interaction_indexes{}; - // the array storing the iterators of the interacting leaves available in the current group + + /** + * @brief The array storing the iterators of the interacting leaves available in the current group. + * + */ iterator_array_type interaction_iterators{}; - // the theoretical number of neighbors or the Number of Morton index available in Mutual algo + + /** + * @brief The theoritical number of neighbors of the number of morton indices available (in the mutual algorithm). + * + */ std::size_t number_of_neighbors{0}; - // the number of Morton index available in the group of the leaf + + /** + * @brief The number of morton indices available in the group of the leaf. + * + */ std::size_t existing_neighbors_in_group{0}; - void set(int counter, std::size_t const& idx, const iterator_type& leaf_iter) + /** + * @brief + * + * @param counter + * @param idx + * @param leaf_iter + */ + inline auto set(int counter, std::size_t const& idx, const iterator_type& leaf_iter) -> void { interaction_iterators.at(counter) = leaf_iter; } - void finalize(bool done, std::size_t const& counter_existing_component) + + /** + * @brief + * + * @param done + * @param counter_existing_component + */ + inline auto finalize(bool done, std::size_t const& counter_existing_component) -> void { number_of_neighbors = counter_existing_component; } }; - /// @brief The Symbolics type that stores information about the groupe of leaves - /// - /// @tparam P + /** + * @brief The symbolics type that stores information about the group of leaves. + * + * @tparam P + */ template<typename P> struct symbolics_data<group<leaf<P>>> { @@ -331,31 +532,79 @@ namespace scalfmm::component using iterator_source_type = std::tuple_element_t<0, seq_iterator_type>; // using iterator_array_type = std::array<iterator_source_type, number_of_interactions>; using out_of_block_interaction_type = out_of_block_interaction<iterator_type, std::size_t>; - // the starting morton index in the group + + /** + * @brief The starting morton index in the group. + * + */ std::size_t starting_index{0}; - // the ending morton index in the group + + /** + * @brief The ending morton index in the group. + * + */ std::size_t ending_index{0}; - // number of leaves in the group + + /** + * @brief The number of leaves in the group. + * + */ std::size_t number_of_component_in_group{0}; - // number of particles in group + + /** + * @brief The number of particles in the group. + * + */ std::size_t number_of_particles_in_group{0}; - // index of the group + + /** + * @brief The index of the group. + * + */ std::size_t idx_global{0}; - // + + /** + * @brief + * + */ bool is_mine{false}; - // vector storing the out_of_block_interaction structure to handle the outside interactions + + /** + * @brief Vector storing the out_of_block_interaction structure to handle the outside interactions. + * + */ std::vector<out_of_block_interaction_type> outside_interactions{}; - // flagged if the vector is constructed + + /** + * @brief Flag if the vector is constructed. + * + */ bool outside_interactions_exists{false}; - // flagged if the vector is sorted + + /** + * @brief Flag if the vector is sorted. + * + */ bool outside_interactions_sorted{false}; #if _OPENMP - // the dependencies are set on the pointer on leaf group (same as particles container) + /** + * @brief the dependencies are set on the pointer on the leaf group (same as particle container). + * + */ std::vector<group_type*> group_dependencies{}; #endif - symbolics_data<group<leaf<P>>>(size_t starting_morton_idx, size_t ending_morton_idx, size_t number_of_component, - size_t in_index_global, bool in_is_mine) + /** + * @brief Construct a new symbolics data object + * + * @param starting_morton_idx + * @param ending_morton_idx + * @param number_of_component + * @param in_index_global + * @param in_is_mine + */ + symbolics_data(size_t starting_morton_idx, size_t ending_morton_idx, size_t number_of_component, + size_t in_index_global, bool in_is_mine) : starting_index(starting_morton_idx) , ending_index(ending_morton_idx) , number_of_component_in_group(number_of_component) diff --git a/include/scalfmm/tree/leaf_view.hpp b/include/scalfmm/tree/leaf_view.hpp index 1b6042e2b1131d5f1c2d1471a66930aa3e2e48c1..1d5b6ac29f0e96e447cd57f1f584cbebfd451bcf 100644 --- a/include/scalfmm/tree/leaf_view.hpp +++ b/include/scalfmm/tree/leaf_view.hpp @@ -1,17 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : leaf_view.hpp +// File : scalfmm/tree/leaf_view.hpp // -------------------------------- #ifndef SCALFMM_TREE_LEAF_VIEW_HPP #define SCALFMM_TREE_LEAF_VIEW_HPP -#include <algorithm> -#include <array> -#include <iterator> -#include <tuple> - -#include <cpp_tools/colors/colorized.hpp> - #include "scalfmm/container/access.hpp" #include "scalfmm/container/particle_container.hpp" #include "scalfmm/container/point.hpp" @@ -24,38 +17,60 @@ #include "scalfmm/utils/massert.hpp" #include "scalfmm/utils/math.hpp" +#include <cpp_tools/colors/colorized.hpp> + +#include <algorithm> +#include <array> +#include <iterator> +#include <tuple> + namespace scalfmm::component { + /** + * @brief + * + * @tparam Component + * @tparam Particle + */ template<typename Component, typename Particle> struct group_of_particles; - // template<typename Particle, typename D=void> - // class leaf_view; - /// @brief This is the leaf type stored at the bottom of the tree. - /// Its stores the particle container and some symbolics informations. - /// - /// @tparam Particle : the type of particle to store. + /** + * @brief This is the leaf type stored at the bottom of the tree. + * Its stores the particle container and some symbolics informations. + * + * @tparam Particle : the type of particle to store. + * @tparam D + */ template<typename Particle, typename D = void> class leaf_view { public: - // static dimension of the particle + /** + * @brief Static dimension of the space. + * + */ static constexpr std::size_t dimension = Particle::dimension; // self type using self_type = leaf_view<Particle>; // the particle type stored in the container using particle_type = Particle; - using proxy_type = typename Particle::proxy_type; - using const_proxy_type = typename Particle::const_proxy_type; + using proxy_type = typename particle_type::proxy_type; + using const_proxy_type = typename particle_type::const_proxy_type; // Symbolic types : the type storing informations about the leaf component - using symbolics_type = symbolics_data<std::conditional_t<std::is_void_v<D>,self_type,D> >; + using symbolics_type = symbolics_data<std::conditional_t<std::is_void_v<D>, self_type, D>>; // the position type extract from the symbolic information using position_type = typename container::particle_traits<particle_type>::position_type; // the position value type using value_type = typename position_type::value_type; using position_coord_type = typename position_type::value_type; - using inputs_type = typename container::particle_traits<particle_type>::inputs_type::value_type; - using outputs_type = typename container::particle_traits<particle_type>::outputs_type::value_type; + using inputs_type = typename container::particle_traits<particle_type>::inputs_type; + using outputs_type = typename container::particle_traits<particle_type>::outputs_type; + using variables_type = typename container::particle_traits<particle_type>::variables_type; + // + using position_value_type = typename container::particle_traits<particle_type>::position_value_type; + using inputs_value_type = typename container::particle_traits<particle_type>::inputs_value_type; + using outputs_value_type = typename container::particle_traits<particle_type>::outputs_value_type; // using group_type = typename symbolics_type::group_type; // the particle container type @@ -64,20 +79,48 @@ namespace scalfmm::component using const_iterator = typename storage_type::const_iterator; // the number of inputs static constexpr std::size_t inputs_size = container::particle_traits<particle_type>::inputs_size; + // the number of outputs + static constexpr std::size_t outputs_size = container::particle_traits<particle_type>::outputs_size; // rule of five generated by the compiler + + /** + * @brief Construct a new leaf view object + * + */ leaf_view() = default; + + /** + * @brief Construct a new leaf view object + * + */ leaf_view(leaf_view const&) = default; + + /** + * @brief Construct a new leaf view object + * + */ leaf_view(leaf_view&&) noexcept = default; + + /** + * @brief + * + * @return leaf_view& + */ inline auto operator=(leaf_view const&) -> leaf_view& = default; + + /** + * @brief + * + * @return leaf_view& + */ inline auto operator=(leaf_view&&) noexcept -> leaf_view& = default; - ~leaf_view() = default; - /// @brief Constuctors from center and width - /// - /// @param center : the center of the leaf - /// @param width : the width of the feaf - /// + /** + * @brief Destroy the leaf view object + * + */ + ~leaf_view() = default; /** * @brief Construct a new leaf view object @@ -91,115 +134,213 @@ namespace scalfmm::component , m_symbolics(symbolics_ptr) { } + + /** + * @brief Construct a new leaf view object + * + * @param particles_range is a pair of iterators (begin, end) in the block to access to the particles in the leaf + * @param symbolics_ptr is a pointer on the symbolic structure of the leaf + */ leaf_view(std::pair<const_iterator, const_iterator> particles_range, symbolics_type* const symbolics_ptr) : m_particles_range(particles_range) , m_const_particles_range(particles_range) , m_symbolics(symbolics_ptr) { } - /// @brief Returns the center of the leaf - /// - /// @return position_type + + /** + * @brief Returns the center of the leaf. + * + * @return position_type const& + */ [[nodiscard]] inline auto center() const noexcept -> position_type const& { return m_symbolics->center; } + + /** + * @brief Returns the center of the leaf. + * + * @return position_type const& + */ [[nodiscard]] inline auto center() noexcept -> position_type& { return m_symbolics->center; } - /// @brief Returns the width of the leaf - /// - /// @return value_type + + /** + * @brief Returns the width of the leaf + * + * @return value_type + */ [[nodiscard]] inline auto width() const noexcept -> value_type { return m_symbolics->width; } + + /** + * @brief Returns the width of the leaf + * + * @return value_type + */ [[nodiscard]] inline auto width() noexcept -> value_type& { return m_symbolics->width; } + /** + * @brief + * + * @return iterator + */ [[nodiscard]] inline auto begin() -> iterator { return m_particles_range.first; } + + /** + * @brief + * + * @return const_iterator + */ [[nodiscard]] inline auto begin() const -> const_iterator { return const_iterator(m_particles_range.first); } + + /** + * @brief + * + * @return const_iterator + */ [[nodiscard]] inline auto cbegin() const -> const_iterator { return const_iterator(m_particles_range.first); } + /** + * @brief + * + * @return iterator + */ [[nodiscard]] inline auto end() -> iterator { return m_particles_range.second; } + + /** + * @brief + * + * @return const_iterator + */ [[nodiscard]] inline auto end() const -> const_iterator { return const_iterator(m_particles_range.second); } + + /** + * @brief + * + * @return const_iterator + */ [[nodiscard]] inline auto cend() const -> const_iterator { return const_iterator(m_particles_range.second); } - /// @brief Return the number of particles - /// - /// @return std::size_t + /** + * @brief Returns the number of particles. + * + * @return std::size_t + */ [[nodiscard]] inline auto size() const noexcept -> std::size_t { return std::distance(m_particles_range.first, m_particles_range.second); } - /// @brief Non const accessor on the container - /// - /// @return a reference on the particle_container_type + /** + * @brief Non-const accessor on the container. + * + * @return a reference on the particle_container_type. + */ [[nodiscard]] inline auto particles() -> std::pair<iterator, iterator> const& { return m_particles_range; } - /// @brief Const accessor on the container - /// - /// @return a const reference on the particle_container_type + + /** + * @brief Const accessor on the container. + * + * @return std::pair<const_iterator, const_iterator> const& + */ [[nodiscard]] inline auto particles() const noexcept -> std::pair<const_iterator, const_iterator> const& { return m_const_particles_range; } - // { return std::make_pair(const_iterator(m_particles_range.first) - // , const_iterator(m_particles_range.second)); } - /// @brief Const accessor on the container - /// - /// @return a const reference on the particle_container_type + + /** + * @brief Const accessor on the container + * + * @return a const reference on the particle_container_type + */ [[nodiscard]] inline auto cparticles() const noexcept -> std::pair<const_iterator, const_iterator> const& - // { - // return std::make_pair(const_iterator(m_particles_range.first) - // , const_iterator(m_particles_range.second)); - // } { return m_const_particles_range; } - /// @brief Indexed accessor on the particle container - /// - /// @param i : the index at which the access is performed. - /// - /// @return a particle_type reference - [[nodiscard]] inline auto particle(std::size_t i) noexcept -> proxy_type { return proxy_type(*(m_particles_range.first + int(i))); } - /// @brief Indexed accessor on the particle container - /// - /// @param i : the index at which the access is performed. - /// - /// @return a particle_type const reference - [[nodiscard]] inline auto particle(std::size_t i) const noexcept -> const_proxy_type { return const_proxy_type(*(m_particles_range.first + int(i))); } - - /// @brief subscript operator - /// - /// @param i : the index at which the access is performed. - /// - /// @return a particle_type reference + + /** + * @brief Indexed accessor on the particle container + * + * @param i the index at which the access is performed. + * @return a particle_type reference + */ + [[nodiscard]] inline auto particle(std::size_t i) noexcept -> proxy_type + { + return proxy_type(*(m_particles_range.first + int(i))); + } + + /** + * @brief Indexed accessor on the particle container + * + * @param i the index at which the access is performed. + * @return a particle_type const reference + */ + [[nodiscard]] inline auto particle(std::size_t i) const noexcept -> const_proxy_type + { + return const_proxy_type(*(m_particles_range.first + int(i))); + } + + /** + * @brief subscript operator + * + * @param i : the index at which the access is performed. + * @return a particle_type reference + */ [[nodiscard]] inline auto operator[](std::size_t i) noexcept -> proxy_type { return proxy_type(*(m_particles_range.first + int(i))); } - /// @brief subscript operator - /// - /// @param i : the index at which the access is performed. - /// - /// @return a particle_type const reference + /** + * @brief subscript operator + * + * @param i the index at which the access is performed. + * @return a particle_type const reference + */ [[nodiscard]] inline auto operator[](std::size_t i) const noexcept -> const_proxy_type { return const_proxy_type(*(m_particles_range.first + int(i))); } - /// @brief Access to the symbolic type - /// - /// @return a symbolics_type reference + /** + * @brief Access to the symbolic type. + * + * @return symbolics_type& + */ [[nodiscard]] inline auto symbolics() noexcept -> symbolics_type& { return *m_symbolics; } - /// @brief Access to the symbolic type - /// - /// @return a const symbolics_type reference + + /** + * @brief Access to the symbolic type. + * + * @return symbolics_type const& + */ [[nodiscard]] inline auto symbolics() const noexcept -> symbolics_type const& { return *m_symbolics; } - /// @brief Access to the symbolic type - /// - /// @return a const symbolics_type reference + + /** + * @brief Access to the symbolic type. + * + * @return symbolics_type const& + */ [[nodiscard]] inline auto csymbolics() const noexcept -> symbolics_type const& { return *m_symbolics; } - /// @brief Returns the morton index of the leaf - /// - /// @return std::size_t + /** + * @brief Returns the morton index of the leaf. + * + * @return std::size_t + */ [[nodiscard]] inline auto index() const noexcept -> std::size_t { return m_symbolics->morton_index; } + + /** + * @brief Returns the morton index of the leaf. + * + * @return std::size_t& + */ [[nodiscard]] inline auto index() noexcept -> std::size_t& { return m_symbolics->morton_index; } + /** + * @brief + * + * @param os + * @param leaf + * @return std::ostream& + */ inline friend auto operator<<(std::ostream& os, const leaf_view& leaf) -> std::ostream& { os << cpp_tools::colors::blue; @@ -213,43 +354,140 @@ namespace scalfmm::component return os; } - /// - /// \brief reset_outputs reset outputs in the leaf - /// - // inline void reset_outputs() - // { - // // - // m_particles.reset_outputs(); - // } - inline void reset_outputs() + /** + * @brief Resets the positions in the leaf. + * + */ + inline auto reset_positions() noexcept -> void + { + auto it_begin{m_particles_range.first}; + auto it_end{m_particles_range.second}; + + while(it_begin != it_end) + { + auto proxy = proxy_type(*it_begin); + for(std::size_t ii{0}; ii < dimension; ++ii) + { + proxy.position(ii) = position_value_type(0.0); + } + ++it_begin; + } + } + + /** + * @brief Resets the inputs in the leaf. + * + */ + inline auto reset_inputs() noexcept -> void + { + auto it_begin{m_particles_range.first}; + auto it_end{m_particles_range.second}; + + while(it_begin != it_end) + { + auto proxy = proxy_type(*it_begin); + for(std::size_t ii{0}; ii < inputs_size; ++ii) + { + proxy.inputs(ii) = inputs_value_type(0.0); + } + ++it_begin; + } + } + + /** + * @brief Resets the outputs in the leaf. + * + */ + inline auto reset_outputs() noexcept -> void { - std::clog << "leaf_view::reset_outputs not yet implemented.\n "; - // // leaf level update only P2P ??? - // component::for_each(std::get<0>(begin()), std::get<0>(end()), - // [this](auto& group) - // { - // std::size_t index_in_group{0}; - // component::for_each(std::begin(*group), std::end(*group), - // [&group, &index_in_group, this](auto& leaf) - // { leaf.reset_outputs(); }); - // }); + auto it_begin{m_particles_range.first}; + auto it_end{m_particles_range.second}; + + while(it_begin != it_end) + { + auto proxy = proxy_type(*it_begin); + for(std::size_t ii{0}; ii < outputs_size; ++ii) + { + proxy.outputs(ii) = outputs_value_type(0.0); + } + ++it_begin; + } + } + + /** + * @brief Resets the variables in the leaf. + * + */ + inline auto reset_variables() noexcept -> void + { + auto it_begin{m_particles_range.first}; + auto it_end{m_particles_range.second}; + + while(it_begin != it_end) + { + auto proxy = proxy_type(*it_begin); + proxy.variables() = variables_type{}; + ++it_begin; + } + } + + /** + * @brief Resets the particles in the leaf. + * + */ + inline auto reset_particles() noexcept -> void + { + auto it_begin{m_particles_range.first}; + auto it_end{m_particles_range.second}; + + while(it_begin != it_end) + { + auto proxy = proxy_type(*it_begin); + for(std::size_t ii{0}; ii < dimension; ++ii) + { + proxy.position(ii) = position_value_type(0.0); + } + for(std::size_t ii{0}; ii < inputs_size; ++ii) + { + proxy.inputs(ii) = inputs_value_type(0.0); + } + for(std::size_t ii{0}; ii < outputs_size; ++ii) + { + proxy.outputs(ii) = outputs_value_type(0.0); + } + proxy.variables() = variables_type{}; + ++it_begin; + } } private: - /// The particle container - /// (begin, end) the iterators to access the particles in the group container - /// iterator est une proxy_particle_iterator and the end operator has a lazy - /// evaluation. This means that you HAVE to use *end to evaluate it + /** + * @brief The particle range + * (begin, end) the iterators to access the particles in the group container + * iterator est une proxy_particle_iterator and the end operator has a lazy + * evaluation. This means that you HAVE to use *end to evaluate it + */ std::pair<iterator, iterator> m_particles_range{}; + + /** + * @brief The particle range (const version). + * + */ std::pair<const_iterator, const_iterator> m_const_particles_range{}; - /// a pointer to he symbolic type + + /** + * @brief A pointer to he symbolic type + * + */ symbolics_type* m_symbolics{nullptr}; }; - /// @brief The symbolics type stores information about the leaf - /// It represents a generic that also exists on the cells - /// - /// @tparam P + /** + * @brief The symbolics type stores information about the leaf + * It represents a generic that also exists on the cells + * + * @tparam P + */ template<typename P> struct symbolics_data<leaf_view<P>> { @@ -276,26 +514,67 @@ namespace scalfmm::component using iterator_source_type = std::tuple_element_t<0, seq_iterator_type>; using iterator_array_type = std::array<iterator_source_type, number_of_interactions>; - // the array storing the indexes of the theoretical interaction list + /** + * @brief the array storing the indexes of the theoretical interaction list. + * + */ interaction_index_array_type interaction_indexes{}; - // the array storing the iterators of the interacting leaves available in the current group + + /** + * @brief the array storing the indexes of the theoretical interaction list. + * + */ iterator_array_type interaction_iterators{}; - // the morton index of the leaf + + /** + * @brief the morton index of the leaf. + * + */ std::size_t morton_index{0}; - // the theoretical number of neighbors. + + /** + * @brief the theoretical number of neighbors. + * + */ std::size_t number_of_neighbors{0}; - // the number of numbers available in the group of the leaf + + /** + * @brief the number of numbers available in the group of the leaf. + * + */ std::size_t existing_neighbors_in_group{0}; - // Position of the center + + /** + * @brief Position of the center + * + */ position_type center{}; - // The width of the leaf + + /** + * @brief The width of the leaf. + * + */ position_value_type width{}; - void set(int counter, std::size_t const& idx, const iterator_source_type& leaf_iter) + /** + * @brief + * + * @param counter + * @param idx + * @param leaf_iter + */ + inline auto set(int counter, std::size_t const& idx, const iterator_source_type& leaf_iter) -> void { interaction_iterators.at(counter) = leaf_iter; } - void finalize(bool done, std::size_t const& counter_existing_component) + + /** + * @brief + * + * @param done + * @param counter_existing_component + */ + inline auto finalize(bool done, std::size_t const& counter_existing_component) -> void { number_of_neighbors = counter_existing_component; } diff --git a/include/scalfmm/tree/morton_curve.hpp b/include/scalfmm/tree/morton_curve.hpp index dc577fc280db7a6c7b51cdd22a68279499aee4a0..d0934d055992b7299f60075bd0f27b067250e30a 100644 --- a/include/scalfmm/tree/morton_curve.hpp +++ b/include/scalfmm/tree/morton_curve.hpp @@ -1,62 +1,106 @@ +// -------------------------------- // See LICENCE file at project root -// +// File : scalfmm/tree/morton_curve.hpp +// -------------------------------- #ifndef SCALFMM_TREE_MORTON_CURVE_HPP #define SCALFMM_TREE_MORTON_CURVE_HPP -#include <scalfmm/container/point.hpp> -#include <scalfmm/utils/math.hpp> +#include "scalfmm/container/point.hpp" +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/utils/math.hpp" + #include <array> #include <cstddef> -#include "scalfmm/meta/utils.hpp" - -/** Provides the corner traversal order of an N dimension hypercube - * - * The positions returned are array of booleans. Each boolean tells where - * to place the element in the binary grid. - * - * For instance, in 2D: - * - * - * __0__ __1__ - * | | | - * 0| | X | pos(X) = [true, false] - * |_____|_____| - * | | | - * 1| | Y | pos(Y) = [true, true ] - * |_____|_____| - * - * - * \tparam dimension The hypercube dimension. - */ namespace scalfmm::component { - template<std::size_t Dim> + /** Provides the corner traversal order of an N dimension hypercube + * + * The positions returned are array of booleans. Each boolean tells where + * to place the element in the binary grid. + * + * For instance, in 2D: + * + * + * __0__ __1__ + * | | | + * 0| | X | pos(X) = [true, false] + * |_____|_____| + * | | | + * 1| | Y | pos(Y) = [true, true ] + * |_____|_____| + * + * + * @tparam Dimension The hypercube dimension. + */ + template<std::size_t Dimension> struct z_curve { public: - /// Space dimension count - static constexpr std::size_t dimension = Dim; + /** + * @brief Static dimension of the space. + * + */ + static constexpr std::size_t dimension = Dimension; /// Template alias template<typename T> using position_alias = container::point<T, dimension>; /// Position type used using position_type = container::point<bool, dimension>; - /// Position count in the grid + + /** + * @brief Position count in the grid. + * + */ static constexpr std::size_t pos_count = math::pow(2, dimension); + /** + * @brief + * + */ constexpr z_curve() { _positions = create_array(); } + + /** + * @brief + * + */ constexpr z_curve(z_curve const&) = default; + + /** + * @brief + * + */ constexpr z_curve(z_curve&&) noexcept = default; + + /** + * @brief + * + * @return z_curve& + */ constexpr inline auto operator=(z_curve const&) -> z_curve& = default; + + /** + * @brief + * + * @return z_curve& + */ constexpr inline auto operator=(z_curve&&) noexcept -> z_curve& = default; + + /** + * @brief Destroy the z curve object + * + */ ~z_curve() = default; private: /// Array of positions type using position_array_t = std::array<position_type, pos_count>; - /** Creates an array of positions to initialize #_positions */ + /** + * @brief Create an array of positions to initialize #_positions + * + * @return position_array_t + */ constexpr auto create_array() noexcept -> position_array_t { position_array_t positions; @@ -71,24 +115,29 @@ namespace scalfmm::component return positions; } - /// Array to cache the positions corresponding to indexes + /** + * @brief Array to cache the positions corresponding to indices. + * + */ position_array_t _positions; public: - /** The position corresponding to an index + /** + * @brief the position corresponding to an index * - * \param idx The index of the point in the space filling curve - * \return The position corresponding to the space filling curve index + * @param idx The index of the point in the space filling curve + * @return The position corresponding to the space filling curve index */ [[nodiscard]] constexpr auto position(std::size_t idx) const noexcept -> position_type { return _positions[idx]; } - /** Index in the space filling curve of a boolean position + /** + * @brief Index in the space filling curve of a boolean position * - * \param p The position - * \return The space filling curve index corresponding to the position + * @param p The position + * @return The space filling curve index corresponding to the position */ [[nodiscard]] constexpr auto index(const position_type& p) const noexcept -> std::size_t { @@ -101,15 +150,16 @@ namespace scalfmm::component return idx; } - /** Index in the space filling curve of a real position relative to the center of the hypercube + /** + * @brief Index in the space filling curve of a real position relative to the center of the hypercube. * - * \param p The position - * \param center The center of the hypercube - * \return The space filling curve index corresponding to the position + * @param p The position + * @param center The center of the hypercube + * @return The space filling curve index corresponding to the position */ template<typename T> - [[nodiscard]] constexpr auto index(const position_alias<T>& p, const position_alias<T>& center) const noexcept - -> std::size_t + [[nodiscard]] constexpr auto index(const position_alias<T>& p, + const position_alias<T>& center) const noexcept -> std::size_t { std::size_t idx = 0; for(std::size_t i = 0; i < dimension; ++i) diff --git a/include/scalfmm/tree/utils.hpp b/include/scalfmm/tree/utils.hpp index ce1169fee261cae973acfbd84a8e3d27c8b7f951..f91229e48da054bdca4fc48618f3767c5221f1d1 100644 --- a/include/scalfmm/tree/utils.hpp +++ b/include/scalfmm/tree/utils.hpp @@ -1,17 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : tree/utils.hpp +// File : scalfmm/tree/utils.hpp // -------------------------------- #ifndef SCALFMM_TREE_UTILS_HPP #define SCALFMM_TREE_UTILS_HPP -#include <array> -#include <fstream> -#include <iostream> -#include <iterator> -#include <tuple> -#include <type_traits> - #include "scalfmm/container/point.hpp" #include "scalfmm/meta/traits.hpp" #include "scalfmm/meta/utils.hpp" @@ -20,16 +13,31 @@ #include "scalfmm/utils/massert.hpp" #include "scalfmm/utils/math.hpp" +#include <array> +#include <fstream> +#include <iostream> +#include <iterator> +#include <tuple> +#include <type_traits> + // namespace scalfmm::utils namespace scalfmm::index { namespace impl { - template<typename RelPosition, typename IndexType = std::size_t> - auto build_morton_index(RelPosition& coords) -> IndexType + /** + * @brief + * + * @tparam RelativePositionType + * @tparam IndexType + * @param coords + * @return IndexType + */ + template<typename RelativePositionType, typename IndexType = std::size_t> + auto build_morton_index(RelativePositionType& coords) -> IndexType { - using dim_t = typename RelPosition::size_type; - constexpr dim_t Dim = std::tuple_size<RelPosition>::value; + using dim_t = typename RelativePositionType::size_type; + constexpr dim_t Dim = std::tuple_size<RelativePositionType>::value; IndexType mask = 1; IndexType idx = 0; auto not_done = [&] @@ -60,39 +68,50 @@ namespace scalfmm::index return idx; } } // namespace impl - /// - /// \brief get the morton index of point pos inside box at level - /// \param pos the real coordinate of the point inside the box - /// \param box the box containing the 2^d tree - /// \param level the level to compute the morton index - /// \return the morton index - /// - template<typename IndexType = std::size_t, typename Box, typename Position> - auto get_morton_index(const Position& pos, const Box& box, const std::size_t level) -> IndexType + + /** + * @brief get the morton index of point pos inside box at level + * + * @tparam IndexType + * @tparam BoxType + * @tparam PositionType + * @param pos the real coordinate of the point inside the box + * @param box the box containing the 2^d tree + * @param level the level to compute the morton index + * @return the morton index + */ + template<typename IndexType = std::size_t, typename BoxType, typename PositionType> + auto get_morton_index(const PositionType& pos, const BoxType& box, const std::size_t level) -> IndexType { - constexpr static const std::size_t Dim = Position::dimension; + constexpr static const std::size_t Dim = PositionType::dimension; using dim_t = std::size_t; + // we set double rather than typename Position::value_type for a better accuracy double cell_width = box.width(0) / (static_cast<IndexType>(1) << level); std::array<IndexType, Dim> coords{}; for(dim_t i = 0; i < Dim; ++i) { coords.at(Dim - i - 1) = static_cast<IndexType>((pos.at(i) - box.c1().at(i)) / cell_width); - coords.at(Dim - i - 1) <<= Dim - i - 1; + coords.at(Dim - i - 1) <<= Dim - i - dim_t(1); } + // std::cout << " get_morton_index. pos" << pos << " " << coords.at(0) << " " << coords.at(1) << " " << box + // << " " << cell_width << std::endl; return impl::build_morton_index(coords); } - /// - /// \brief get morton index from a relative position - /// \param[in] pos the relative position in the d-grid. array of d index - /// \return the morton index - /// - template<typename Position, typename IndexType = std::size_t> - auto get_morton_index(const Position& pos) -> IndexType + /** + * @brief get morton index from a relative position + * + * @tparam PositionType + * @tparam IndexType + * @param[in] pos the relative position in the d-grid. array of d index + * @return the morton index + */ + template<typename PositionType, typename IndexType = std::size_t> + auto get_morton_index(const PositionType& pos) -> IndexType { - constexpr static const std::size_t Dim = Position::dimension; + constexpr static const std::size_t Dim = PositionType::dimension; using dim_t = std::size_t; std::array<IndexType, Dim> coords{}; @@ -104,6 +123,17 @@ namespace scalfmm::index return impl::build_morton_index(coords); } + /** + * @brief Get the tree coordinate object + * + * @tparam ValueType + * @tparam CoordinateType + * @param relative_position + * @param box_width + * @param box_width_at_leaf_level + * @param tree_height + * @return std::enable_if_t<std::is_integral_v<CoordinateType>, CoordinateType> + */ template<typename ValueType, typename CoordinateType = std::int64_t> inline auto get_tree_coordinate(ValueType relative_position, ValueType box_width, ValueType box_width_at_leaf_level, @@ -118,6 +148,18 @@ namespace scalfmm::index return static_cast<CoordinateType>(relative_position / box_width_at_leaf_level); } + /** + * @brief Get the coordinate from position and corner object + * + * @tparam ValueType + * @tparam CoordinateType + * @tparam Dimension + * @param position + * @param corner_of_box + * @param box_width + * @param tree_height + * @return std::enable_if_t<std::is_integral_v<CoordinateType>, container::point<CoordinateType, Dimension>> + */ template<typename ValueType, typename CoordinateType = std::int64_t, std::size_t Dimension> inline auto get_coordinate_from_position_and_corner(container::point<ValueType, Dimension> const& position, container::point<ValueType, Dimension> const& corner_of_box, @@ -139,6 +181,18 @@ namespace scalfmm::index return host; } + /** + * @brief Get the position from coordinate object + * + * @tparam ValueType + * @tparam CoordinateType + * @tparam Dimension + * @param coordinate + * @param center_of_box + * @param box_width + * @param tree_height + * @return std::enable_if_t<std::is_integral_v<CoordinateType>, container::point<ValueType, Dimension>> + */ template<typename ValueType, typename CoordinateType, std::size_t Dimension> inline auto get_position_from_coordinate(container::point<CoordinateType, Dimension> const& coordinate, container::point<ValueType, Dimension> const& center_of_box, @@ -154,6 +208,15 @@ namespace scalfmm::index return host + box_corner; } + /** + * @brief Get the coordinate from morton index object + * + * @tparam Dimension + * @tparam MortonIndex + * @tparam CoordinateType + * @param morton_index + * @return std::enable_if_t<std::is_integral_v<CoordinateType>, container::point<CoordinateType, Dimension>> + */ template<std::size_t Dimension, typename MortonIndex = std::size_t, typename CoordinateType = std::int64_t> inline auto get_coordinate_from_morton_index(MortonIndex morton_index) -> std::enable_if_t<std::is_integral_v<CoordinateType>, container::point<CoordinateType, Dimension>> @@ -183,23 +246,26 @@ namespace scalfmm::index return coord; } - /// - /// \brief get_grid_index return the grid coordinate of a linear index - /// - /// get_grid_index return the grid coordinate of a linear index - /// between 0 and nbNeigPerDim^dimension in 3x3 grid centered in 0. - /// - /// Each component od the grid index is between -a and a where a = - /// (nbNeigPerDim-1)/2, i.e. the number of neighbors - /// in on direction (generally when we consider the first neighbors a = 1) - /// - /// \param idx the linear index - /// \param nbNeigPerDim the number of points per line - /// - /// \return and array of size dimension - /// + /** + * @brief get_grid_index return the grid coordinate of a linear index + * + * get_grid_index return the grid coordinate of a linear index + * between 0 and nbNeigPerDim^dimension in 3x3 grid centered in 0. + * + * Each component od the grid index is between -a and a where a = + * (nbNeigPerDim-1)/2, i.e. the number of neighbors + * in on direction (generally when we consider the first neighbors a = 1) + * + * @tparam Dimension + * @tparam MortonIndex + * @tparam CoordinateType + * @param idx the linear index + * @param nbNeigPerDim the number of points per line + * @return and array of size dimension + */ template<std::size_t Dimension, typename MortonIndex = std::size_t, typename CoordinateType = std::int64_t> - inline typename container::point<CoordinateType, Dimension> get_grid_index(MortonIndex& idx, const int nbNeigPerDim) + inline auto get_grid_index(MortonIndex& idx, const int nbNeigPerDim) -> + typename container::point<CoordinateType, Dimension> { container::point<CoordinateType, Dimension> coordinate{}; auto tmp = idx; @@ -214,6 +280,19 @@ namespace scalfmm::index return coordinate; } + + /** + * @brief + * + * @tparam Dimension + * @tparam CoordinatePointType + * @tparam ArrayType + * @param coord + * @param period + * @param limite1d + * @return true + * @return false + */ template<std::size_t Dimension, typename CoordinatePointType, typename ArrayType> auto check_limit(CoordinatePointType& coord, const ArrayType& period, const int& limite1d) -> bool { @@ -239,24 +318,28 @@ namespace scalfmm::index } return check; } - /// - /// \brief get_grid_index return the grid coordinate of a linear index - /// - /// get_grid_index return the grid coordinate of a linear index - /// between 0 and nbNeigPerDim^dimension in 3x3 grid centered in 0. - /// - /// Each component od the grid index is between -a and a where a = - /// (nbNeigPerDim-1)/2, i.e. the number of neighbors - /// in on direction (generally when we consider the first neighbors a = 1) - /// - /// \param idx the linear index - /// \param nbNeigPerDim the number of points per line - /// - /// \return and array of size dimension - /// + + /** + * @brief get_grid_index return the grid coordinate of a linear index + * + * get_grid_index return the grid coordinate of a linear index + * between 0 and nbNeigPerDim^dimension in 3x3 grid centered in 0. + * + * Each component od the grid index is between -a and a where a = + * (nbNeigPerDim-1)/2, i.e. the number of neighbors + * in on direction (generally when we consider the first neighbors a = 1) + * + * + * @tparam Dimension + * @tparam IndexType + * @tparam CoordinateType + * @param idx the linear index + * @param nbNeigPerDim the number of points per line + * @return and array of size dimension + */ template<std::size_t Dimension, typename IndexType = std::size_t, typename CoordinateType = std::int64_t> - inline typename container::point<CoordinateType, Dimension> get_grid_3x3_index(CoordinateType& idx, - const int nbNeigPerDim = 3) + inline auto get_grid_3x3_index(CoordinateType& idx, const int nbNeigPerDim = 3) -> + typename container::point<CoordinateType, Dimension> { container::point<CoordinateType, Dimension> coordinate{}; auto tmp = idx; @@ -272,35 +355,30 @@ namespace scalfmm::index return coordinate; } - /// @ingroup get_interaction_neighbors - /// @brief Compute the neighbors of the coordinate component - /// - /// @todo Problem with neighbour_separation /= 1 and the use of the array - /// structure !!! - /// - /// - /// @tparam Dimension - /// @tparam IndexType - /// @tparam CoordinateType - /// - /// @param[in] coordinate the grid coordinate of the morton index of the - /// current box - /// - /// @param[in] level The level to compute the neighbors - /// - /// @param[in] period the periodicity in the different directions (array of bool) - /// - /// @param[in] neighbour_separation the number of neighbors in one - /// direction (default 1 = the neighbors at distance 1 of me) - /// - /// @return a tuple containing - /// the sorted morton index of the neighbors - /// the number of neighbors - /// the position of the index - template<std::size_t Dimension, typename IndexType = std::size_t, typename Array_T, + /** + * @ingroup get_interaction_neighbors + * @brief Compute the neighbors of the coordinate component + * + * @todo Problem with neighbour_separation /= 1 and the use of the array + * structure !!! + * + * @tparam Dimension + * @tparam IndexType + * @tparam ArrayType + * @tparam CoordinateType + * @param[in] coordinate the grid coordinate of the morton index of the current box + * @param[in] level The level to compute the neighbors + * @param[in] period the periodicity in the different directions (array of bool) + * @param[in] neighbour_separation the number of neighbors in one direction (default 1 = the neighbors at distance 1 of me) + * @return a tuple containing + * the sorted morton index of the neighbors + * the number of neighbors + * the position of the index + */ + template<std::size_t Dimension, typename IndexType = std::size_t, typename ArrayType, typename CoordinateType = std::int64_t> inline auto get_neighbors(container::point<CoordinateType, Dimension> const& coordinate, std::size_t level, - Array_T const& period, int neighbor_separation) + ArrayType const& period, int neighbor_separation) { const std::size_t nbNeigPerDim = 3; using position_type = container::point<CoordinateType, Dimension>; @@ -357,10 +435,25 @@ namespace scalfmm::index } /////////////////////////// - template<std::size_t Dimension, typename IndexType = std::size_t, typename Array_T, + + /** + * @brief Get the neighbors new object + * + * @tparam Dimension + * @tparam IndexType + * @tparam ArrayType + * @tparam CoordinateType + * @param coordinate + * @param level + * @param period + * @param true_pos + * @param neighbour_separation + * @return auto + */ + template<std::size_t Dimension, typename IndexType = std::size_t, typename ArrayType, typename CoordinateType = std::int64_t> inline auto get_neighbors_new(container::point<CoordinateType, Dimension> const& coordinate, std::size_t level, - Array_T const& period, const bool true_pos, const int neighbour_separation) + ArrayType const& period, const bool true_pos, const int neighbour_separation) { const int nbNeigPerDim = 3 /*2* neighbour_separation + 1 */; using position_type = container::point<CoordinateType, Dimension>; @@ -401,10 +494,12 @@ namespace scalfmm::index return std::make_tuple(indexes, idx_pos, idx_neig); } - /** - * @brief Get the index of a interaction neighbors (for M2L) + /** + * @brief Get the index of a interaction neighbors (for M2L) * + * @tparam Dimension + * @tparam CoordinateType * @param p position in the interactions (from -3 to +3)^Dimension * @return the index (from 0 to 342) */ @@ -418,6 +513,15 @@ namespace scalfmm::index } return pos; } + + /** + * @brief + * + * @tparam Dimension + * @tparam CoordinateType + * @param p + * @return int + */ template<std::size_t Dimension, typename CoordinateType = std::int64_t> inline auto neighbor_index(std::array<CoordinateType, Dimension> const& p) -> int { @@ -428,36 +532,31 @@ namespace scalfmm::index } return pos; } - /// @ingroup get_m2l_list - /// @brief Compute the interaction list of coordinate box - /// - /// @warning Problem with neighbour_separation /! 1 and the use of the array - /// structure !!! - /// - /// - /// @tparam Dimension - /// @tparam IndexType - /// @tparam CoordinateType - /// - /// @param[in] coordinate; the grid coordinate of the morton index of the - /// current box - /// - /// @param[in] level The level to compute the interaction list - /// - /// @param[in] period the vector of periodicity - /// - /// @param[in] neighbour_separation the number of neighbors in one - /// direction (default 1 = the neighbors at distance 1 of me) - /// - /// @return a tuple containing - /// the morton index of the cells in the interaction list - /// the position in the d grid of size (1+3*neighbour_separation)^Dimension - /// the number of neighbors - - template<std::size_t Dimension, typename MortonIndex = std::size_t, typename Array_T, + + /** + * @ingroup get_m2l_list + * @brief Compute the interaction list of coordinate box + * + * @warning Problem with neighbour_separation /! 1 and the use of the array + * structure !!! + * + * @tparam Dimension + * @tparam MortonIndex + * @tparam ArrayType + * @tparam CoordinateType + * @param[in] coordinate; the grid coordinate of the morton index of the current box. + * @param[in] level The level to compute the interaction list + * @param[in] period the vector of periodicity + * @param[in] neighbour_separation the number of neighbors in one direction (default 1 = the neighbors at distance 1 of me). + * @return a tuple containing + * the morton index of the cells in the interaction list + * the position in the d grid of size (1+3*neighbour_separation)^Dimension + * the number of neighbors + */ + template<std::size_t Dimension, typename MortonIndex = std::size_t, typename ArrayType, typename CoordinateType = std::int64_t> inline auto get_m2l_list(container::point<CoordinateType, Dimension> const& coordinate, std::size_t level, - Array_T const& period, const int neighbour_separation) + ArrayType const& period, const int neighbour_separation) { // neighbour_separation<< ")\n"; // const int nbNeigPerDim = 6 /* 2*(2* neighbour_separation + 1 ) */; @@ -548,22 +647,26 @@ namespace scalfmm::index return std::make_tuple(indexes, indexes_in_array, idx_m2L_list); } - /// @defgroup get_interaction_neighbors get_interaction_neighbors - /// - - /// @ingroup get_interaction_neighbors - /// @brief - /// - /// @tparam Dimension - /// @tparam MortonIndex - /// @tparam CoordinateType - /// @param t - /// @param coordinate - /// @param level - /// @param neighbour_separtion - /// - /// @return - // TODO: metapragrammed this ! + /** + * @brief Get the interaction neighbors object + * + * @defgroup get_interaction_neighbors get_interaction_neighbors + * + * @ingroup get_interaction_neighbors + * + * @todo use metaprogrammation. + * + * @tparam Dimension + * @tparam MortonIndex + * @tparam CoordinateType + * @tparam Array + * @param t + * @param coordinate + * @param level + * @param period + * @param neighbour_separtion + * @return auto + */ template<std::size_t Dimension, typename MortonIndex = std::size_t, typename CoordinateType = std::int64_t, typename Array> inline auto get_interaction_neighbors(operators::impl::tag_m2l t, @@ -573,12 +676,29 @@ namespace scalfmm::index return get_m2l_list(coordinate, level, period, neighbour_separtion); } + /** + * @brief Get the opposite inter index object + * + * @tparam Dimension + * @tparam IndexType + * @param index + * @return IndexType + */ template<std::size_t Dimension, typename IndexType> inline auto get_opposite_inter_index(IndexType index) -> IndexType { static constexpr std::size_t i = math::pow(7, Dimension); return static_cast<IndexType>(i) - index - IndexType(1); } + + /** + * @brief Get the opposite p2p inter index object + * + * @tparam Dimension + * @tparam IndexType + * @param index + * @return IndexType + */ template<std::size_t Dimension, typename IndexType> inline auto get_opposite_p2p_inter_index(IndexType index) -> IndexType { @@ -606,23 +726,22 @@ namespace scalfmm::index // { // return get_neighbors(coordinate, leaf_level, period, neighbour_separtion); // } - /// - ///@brief Get the interaction neighbors od the component located by its coordinate - /// - /// @param[in] t tag to specialize the function - /// @param[in] coordinate the grid coordinate of the morton index of the - /// current component - /// - /// @param[in] level The level to compute the neighbors - /// - /// @param[in] period the periodicity in the different directions (array of bool) - /// - /// @param[in] neighbour_separation the number of neighbors in one - /// direction (default 1 = the neighbors at distance 1 of me) - /// - /// @return a tuple containing the sorted morton index of the neighbors - /// the number of neighbors - /// + + /** + * @brief Get the interaction neighbors of the component located by its coordinate. + * + * @tparam Dimension + * @tparam IndexType + * @tparam ArrayType + * @tparam CoordinateType + * @param[in] t tag to specialize the function. + * @param[in] coordinate the grid coordinate of the morton index of the current component. + * @param[in] level The level to compute the neighbors. + * @param[in] period the periodicity in the different directions (array of bool). + * @param[in] neighbour_separation the number of neighbors in one direction (default 1 = the neighbors at distance 1 of me). + * @return a tuple containing the sorted morton index of the neighbors + * the number of neighbors + */ template<std::size_t Dimension, typename IndexType = std::size_t, typename ArrayType, typename CoordinateType = std::int64_t> inline auto get_interaction_neighbors(operators::impl::tag_p2p t, @@ -632,6 +751,19 @@ namespace scalfmm::index return get_neighbors(coordinate, leaf_level, period, neighbour_separation); } + /** + * @brief Get the box object + * + * @tparam ValueType + * @tparam Dimension + * @tparam CoordinateType + * @param corner + * @param width + * @param coordinate + * @param tree_height + * @param level + * @return std::tuple<ValueType, container::point<ValueType, Dimension>> + */ template<typename ValueType, std::size_t Dimension, typename CoordinateType> inline auto get_box(container::point<ValueType, Dimension> const& corner, ValueType width, container::point<CoordinateType, Dimension> const& coordinate, std::size_t tree_height, @@ -645,34 +777,28 @@ namespace scalfmm::index { return c + ValueType(coord) * width_at_current_level + width_at_current_level_div2; }); return std::make_tuple(width_at_current_level, new_center); } + /** * @brief set the good morton index at fake level in periodic * + * @tparam Dimension * @param index morton to correct - * @return the true morton index + * @return std::size_t */ template<std::size_t Dimension> auto correctFakeMorton(std::size_t index) -> std::size_t { return (index > 3) ? index - ((index >> Dimension) << Dimension) : index; } + /** - * @brief checkLimit + * @brief Get the parent morton indices object * - * @param coord - * @param period - * @param limite1d - * @return true - * @return false + * @tparam MortonType + * @param vector_of_mortons + * @param dimension + * @param offset */ - - /// @brief - /// - /// @param vector_of_mortons - /// @param offset - /// @param dimension - /// - /// @return template<typename MortonType> inline auto get_parent_morton_indices(std::vector<MortonType>& vector_of_mortons, std::size_t dimension, std::size_t offset = 0) -> void @@ -695,17 +821,18 @@ namespace scalfmm::index vector_of_mortons.erase(last, vector_of_mortons.end()); } } + /** * @brief check that the group intersects the interval [start, end[. * - * @tparam Group + * @tparam GroupType * @param start starting morton index * @param end ending morton index * @param g the group * @return return true if the intersection in not empty */ - template<typename Group> - auto is_in_range(std::size_t start, std::size_t end, Group const& g) -> bool + template<typename GroupType> + auto is_in_range(std::size_t start, std::size_t end, GroupType const& g) -> bool { auto const& csym = g.csymbolics(); if(end <= csym.starting_index or csym.ending_index <= start) @@ -716,9 +843,19 @@ namespace scalfmm::index return true; } - template<typename GroupIterator> - auto get_parent_group_range(std::size_t begin_range, std::size_t end_range, GroupIterator begin_groups, - GroupIterator end_groups) + /** + * @brief Get the parent group range object + * + * @tparam GroupIteratorType + * @param begin_range + * @param end_range + * @param begin_groups + * @param end_groups + * @return auto + */ + template<typename GroupIteratorType> + auto get_parent_group_range(std::size_t begin_range, std::size_t end_range, GroupIteratorType begin_groups, + GroupIteratorType end_groups) { if(begin_groups == end_groups) { @@ -735,7 +872,7 @@ namespace scalfmm::index } } - GroupIterator first{begin_groups}; + GroupIteratorType first{begin_groups}; while(is_in_range(begin_range, end_range, **begin_groups)) { @@ -745,26 +882,31 @@ namespace scalfmm::index return std::make_tuple(first, end_groups); } } - GroupIterator last{begin_groups}; + + GroupIteratorType last{begin_groups}; return std::make_tuple(first, last); } } + /** * @brief Get the child group range object * - * @tparam dimension - * @tparam GroupChildIterator - * @tparam GroupParent + * @tparam Dimension + * @tparam GroupChildIteratorType + * @tparam GroupParentType * @param begin_groups * @param end_groups * @param parent + * @param verbose * @return auto */ - template<std::size_t dimension, typename GroupChildIterator, typename GroupParent> - auto get_child_group_range(GroupChildIterator begin_groups, GroupChildIterator end_groups, - GroupParent const& parent, bool verbose = false) + template<std::size_t Dimension, typename GroupChildIteratorType, typename GroupParentType> + auto get_child_group_range(GroupChildIteratorType begin_groups, GroupChildIteratorType end_groups, + GroupParentType const& parent, bool verbose = false) { + static constexpr std::size_t dimension = Dimension; + if(begin_groups == end_groups) { return std::make_tuple(begin_groups, end_groups); @@ -783,7 +925,7 @@ namespace scalfmm::index } } - GroupChildIterator first{begin_groups}; + GroupChildIteratorType first{begin_groups}; while(is_in_range(((*begin_groups)->csymbolics().starting_index >> dimension), (((*begin_groups)->csymbolics().ending_index - 1) >> dimension) + 1, parent)) @@ -794,28 +936,33 @@ namespace scalfmm::index return std::make_tuple(first, end_groups); } } - GroupChildIterator last{begin_groups}; + + GroupChildIteratorType last{begin_groups}; return std::make_tuple(first, last); } } + /** * @brief Get the shift to apply on center2 when the simulation box is periodic * * The shift is used to move the center2 near center1 according to the periodicity + * + * @tparam PointType + * @tparam PeriodicityVectorType * @param center1 the current box * @param center2 the box which could be deplaced due to the periodicity * @param pbc array of periodic direction (true if periodic) * @param box_width the width of the simulation box * @return vector of shift */ - template<typename Points_type, typename Periodicity_vector> - inline auto get_shift(const Points_type& center1, const Points_type& center2, const Periodicity_vector pbc, - typename Points_type::value_type const& box_width) -> const Points_type + template<typename PointType, typename PeriodicityVectorType> + inline auto get_shift(const PointType& center1, const PointType& center2, const PeriodicityVectorType pbc, + typename PointType::value_type const& box_width) -> const PointType { - Points_type shift(0.0); + PointType shift(0.0); auto half_width{0.5 * box_width}; - for(int i = 0; i < Points_type::dimension; ++i) + for(int i = 0; i < PointType::dimension; ++i) { if(pbc[i]) { @@ -825,13 +972,21 @@ namespace scalfmm::index return shift; } } // namespace scalfmm::index + namespace scalfmm::utils { - template<typename Cell> - void print_cell(const Cell& cell, bool print_aux = false) + + /** + * @brief + * + * @tparam CellType + * @param cell + * @param print_aux + */ + template<typename CellType> + auto print_cell(const CellType& cell, bool print_aux = false) -> void { auto m = cell.cmultipoles(); - // auto tm = cell.ctransformed_multipoles(); auto nb_m = m.size(); std::cout << "cell index: " << cell.index() << " level " << cell.csymbolics().level << "\n"; @@ -839,10 +994,6 @@ namespace scalfmm::utils { auto ten = m.at(i); std::cout << " multi(" << i << "): \n" << m.at(i) << std::endl; - // if(print_aux) - // { - // std::cout << " transf multi:\n " << tm.at(i) << std::endl; - // } } auto loc = cell.clocals(); auto nb_loc = loc.size(); @@ -853,41 +1004,43 @@ namespace scalfmm::utils std::cout << " loc(" << i << "): \n" << loc.at(i) << std::endl; } } + /** * @brief Display particles in leaf and compute the current Morton index of the particle * - * @tparam Leaf type - * @tparam Box type + * @tparam LeafType + * @tparam BoxType * @param leaf the current leaf * @param box The box to compute the Morton index * @param level The level to compute the Morton index */ - template<typename Leaf, typename Box> - void print_leaf(const Leaf& leaf, Box box, int level) + template<typename LeafType, typename BoxType> + auto print_leaf(const LeafType& leaf, BoxType box, int level) -> void { auto morton = leaf.index(); int i{0}; for(auto const& pl: leaf) { - const auto p = typename Leaf::const_proxy_type(pl); + const auto p = typename LeafType::const_proxy_type(pl); std::cout << i++ << " morton: " << morton << " part= " << p << " cmp_morton " << scalfmm::index::get_morton_index(p.position(), box, level) << std::endl; } } + /** - * @brief Display particles in leaf + * @brief Display particles in the leaf. * - * @tparam Leaf the type of the leaf - * @param leaf the current leaf + * @tparam LeafType + * @param leaf the current leaf. */ - template<typename Leaf> - void print_leaf(const Leaf& leaf) + template<typename LeafType> + auto print_leaf(const LeafType& leaf) -> void { auto morton = leaf.index(); int i{0}; for(auto const& p: leaf) { - const auto pp = typename Leaf::const_proxy_type(p); + const auto pp = typename LeafType::const_proxy_type(p); std::cout << i++ << " morton: " << morton << " part= " << pp << std::endl; } } diff --git a/include/scalfmm/utils/accurater.hpp b/include/scalfmm/utils/accurater.hpp index 45b67d1327a4f681c4f6ae8959387e0ad659c7a5..451157c5426d9a9d7f951bb8729554374a726f8c 100644 --- a/include/scalfmm/utils/accurater.hpp +++ b/include/scalfmm/utils/accurater.hpp @@ -1,4 +1,7 @@ -// See LICENCE file at project root +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/utils/accurater.hpp +// -------------------------------- #ifndef SCALFMM_UTILS_ACCURATER_HPP #define SCALFMM_UTILS_ACCURATER_HPP @@ -7,27 +10,97 @@ #include <cstdlib> #include <iostream> -/** - * \class A class to compute accuracy between data - * - * - */ namespace scalfmm { namespace utils { - template<class real_type> + /** + * @brief A class to compute accuracy between data. + * + * @tparam ValueType + */ + template<class ValueType> class accurater { - std::size_t _nb_elements{}; ///< number of elements used to compute the error - real_type _l2_norm{}; ///< l2-norm of the reference value - real_type _l2_diff{}; ///< l2-norm of the difference between the reference value and the value - real_type _max{}; ///< infinity norm of the reference value - real_type _max_diff{}; ///< infinity norm of the difference between the reference value and the value + using value_type = ValueType; + + /** + * @brief Number of elements used to compute the error. + * + */ + std::size_t _nb_elements{}; + + /** + * @brief l2-norm of the reference value. + * + */ + value_type _l2_norm{}; + + /** + * @brief l2-norm of the difference between the reference value and the value. + * + */ + value_type _l2_diff{}; + + /** + * @brief Infinity norm of the reference value. + * + */ + value_type _max{}; + + /** + * @brief Infinity norm of the difference between the reference value and the value. + * + */ + value_type _max_diff{}; + public: + /** + * @brief Construct a new accurater object + * + */ accurater() = default; - /** with inital values */ - accurater(const real_type ref_value[], const real_type value[], const std::size_t& nbValues) + + /** + * @brief Construct a new accurater object + * + */ + accurater(accurater const&) = default; + + /** + * @brief Construct a new accurater object + * + */ + accurater(accurater&&) noexcept = default; + + /** + * @brief + * + * @return accurater& + */ + inline auto operator=(accurater const&) -> accurater& = default; + + /** + * @brief + * + * @return accurater& + */ + inline auto operator=(accurater&&) noexcept -> accurater& = default; + + /** + * @brief Destroy the accurater object + * + */ + ~accurater() = default; + + /** + * @brief Construct a new accurater object with initial values. + * + * @param ref_value + * @param value + * @param nbValues + */ + accurater(const value_type ref_value[], const value_type value[], const std::size_t& nbValues) { this->add(ref_value, value, nbValues); } @@ -38,8 +111,7 @@ namespace scalfmm * @param ref_value * @param value */ - - void add(const real_type& ref_value, const real_type& value) + inline auto add(const value_type& ref_value, const value_type& value) -> void { _l2_diff += (value - ref_value) * (value - ref_value); _l2_norm += ref_value * ref_value; @@ -47,13 +119,16 @@ namespace scalfmm _max_diff = std::max(_max_diff, std::abs(ref_value - value)); ++_nb_elements; } + /** * @brief Add all vector elements |ref_value[i]-value[i]| to the current accurater * * @param ref_value reference array * @param value array to check + * @param nb_values */ - void add(const real_type* const ref_values, const real_type* const values, const std::size_t& nb_values) + inline auto add(const value_type* const ref_values, const value_type* const values, + const std::size_t& nb_values) -> void { for(std::size_t idx = 0; idx < nb_values; ++idx) { @@ -61,8 +136,16 @@ namespace scalfmm } _nb_elements += nb_values; } - template<class vector_type> - void add(const vector_type& ref_values, const vector_type& values) + + /** + * @brief + * + * @tparam VectorType + * @param ref_values + * @param values + */ + template<class VectorType> + inline auto add(const VectorType& ref_values, const VectorType& values) -> void { if(values->size() != ref_values->size()) { @@ -71,8 +154,13 @@ namespace scalfmm } this->add(ref_values->data(), values->data(), values->size()); } - /** Add an accurater*/ - void add(const accurater& inAcc) + + /** + * @brief Add an accurater + * + * @param inAcc + */ + inline auto add(const accurater& inAcc) -> void { _l2_diff += inAcc.get_l2_diff_norm(); _l2_norm += inAcc.get_l2_norm(); @@ -80,88 +168,121 @@ namespace scalfmm _max_diff = std::max(_max_diff, inAcc.get_infinity_norm()); _nb_elements += inAcc.get_nb_elements(); } + /** * @brief Get the the square of the l2 norm of the error sum_i(ref_i-val_i)^2) * - * @return real_type + * @return value_type */ - real_type get_l2_diff_norm() const { return _l2_diff; } - real_type get_l2_diff_norm2() const { return _l2_diff; } + inline auto get_l2_diff_norm() const -> value_type { return _l2_diff; } + + /** + * @brief Get the l2 diff norm2 object + * + * @return value_type + */ + inline auto get_l2_diff_norm2() const -> value_type { return _l2_diff; } + /** * @brief Get the l2 norm of the error sqrt( sum_i(ref_i-val_i)^2) * - * @return real_type + * @return value_type */ - real_type get_l2_norm() const { return std::sqrt(_l2_diff); } + inline auto get_l2_norm() const -> value_type { return std::sqrt(_l2_diff); } + /** * @brief Get the l2 norm of the reference vector sqrt( sum_i(ref_i)^2) * - * @return real_type + * @return value_type */ - real_type get_ref_l2_norm() const { return std::sqrt(_l2_norm); } + inline auto get_ref_l2_norm() const -> value_type { return std::sqrt(_l2_norm); } + /** * @brief Get the maximum value of the reference max_i(|ref_i|) * - * @return real_type + * @return value_type */ - real_type get_ref_max() const { return _max; } + inline auto get_ref_max() const -> value_type { return _max; } /** * @brief Get the nb elements used in the norm computation * * @return auto */ - auto get_nb_elements() const { return _nb_elements; } + inline auto get_nb_elements() const { return _nb_elements; } - void set_nb_elements(const std::size_t& n) { _nb_elements = n; } + /** + * @brief Set the nb elements object + * + * @param n + */ + inline auto set_nb_elements(const std::size_t& n) -> void { _nb_elements = n; } + + /** + * @brief Get the mean squared error object. + * + * @return value_type + */ + inline auto get_mean_squared_error() const -> value_type { return std::sqrt(_l2_diff); } + + /** + * @brief Get the root-mean-square error. + * + * @return value_type + */ + inline auto get_rms_error() const -> value_type + { + return std::sqrt(_l2_diff / static_cast<value_type>(_nb_elements)); + } - /** Get the root mean squared error*/ - real_type get_mean_squared_error() const { return std::sqrt(_l2_diff); } - /** Get the root-mean-square error */ - real_type get_rms_error() const { return std::sqrt(_l2_diff / static_cast<real_type>(_nb_elements)); } /** * @brief Get the infinity norm of the error max_i|ref_i-val_i| * - * @return real_type + * @return value_type */ - real_type get_infinity_norm() const { return _max_diff; } + inline auto get_infinity_norm() const -> value_type { return _max_diff; } + /** * @brief Get the relative L2 norm of the error sqrt( sum_i(ref_i-val_i)^2/sum_i(ref_i^2)) * - * @return real_type + * @return value_type */ - real_type get_relative_l2_norm() const { return std::sqrt(_l2_diff / _l2_norm); } + inline auto get_relative_l2_norm() const -> value_type { return std::sqrt(_l2_diff / _l2_norm); } + /** * @brief Get the relative infinity norm of the error max_i|ref_i-val_i|/max_i|ref_i| * - * @return real_type + * @return value_type */ - real_type get_relative_infinity_norm() const { return _max_diff / _max; } + inline auto get_relative_infinity_norm() const -> value_type { return _max_diff / _max; } + /** * @brief Print the relative errors (L2, RMS, Infinity) * + * @tparam StreamClass * @param output the stream * @param inAccurater the accurater containing the errors to print - * + * @return StreamClass& */ - template<class StreamClass> - friend StreamClass& operator<<(StreamClass& output, const accurater& inAccurater) + template<typename StreamClass> + inline friend auto operator<<(StreamClass& output, const accurater& inAccurater) -> StreamClass& { output << "[Error] Relative L2-norm = " << inAccurater.get_relative_l2_norm() << " \t RMS norm = " << inAccurater.get_rms_error() << " \t Relative infinity norm = " << inAccurater.get_relative_infinity_norm(); return output; } + /** * @brief reset the accurater * */ - void reset() + inline auto reset() -> void { - _l2_norm = real_type(0); - _l2_diff = real_type(0); - _max = real_type(0); - _max_diff = real_type(0); + _l2_norm = value_type(0); + _l2_diff = value_type(0); + _max = value_type(0); + _max_diff = value_type(0); _nb_elements = 0; } }; diff --git a/include/scalfmm/utils/compare_results.hpp b/include/scalfmm/utils/compare_results.hpp index 532fb886096b4eb9b63ca7ec6149a78ed2b171a0..5e43bde84aee1051da8b6b57d9176014af6f4a1b 100644 --- a/include/scalfmm/utils/compare_results.hpp +++ b/include/scalfmm/utils/compare_results.hpp @@ -1,7 +1,14 @@ -// See LICENCE file at project root +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/utils/compare_results.hpp +// -------------------------------- #ifndef SCALFMM_UTILS_COMPARE_RESULTS_HPP #define SCALFMM_UTILS_COMPARE_RESULTS_HPP +#include "scalfmm/tree/utils.hpp" +#include "scalfmm/utils/accurater.hpp" +#include "scalfmm/utils/sort.hpp" + #include <algorithm> #include <array> #include <cstdlib> @@ -10,24 +17,28 @@ #include <tuple> #include <vector> -#include "scalfmm/tree/utils.hpp" -#include "scalfmm/utils/accurater.hpp" -#include "scalfmm/utils/sort.hpp" - namespace scalfmm { namespace utils { - ///////////////////////////////// - /// - /// - template<class array_type> - void compare_two_arrays(const std::string& tag, const int dimension, const std::size_t& nbParticles, + /** + * @brief + * + * @tparam ArrayType + * @param tag + * @param dimension + * @param nbParticles + * @param index1_to_compare + * @param index2_to_compare + * @param array1 + * @param array2 + */ + template<class ArrayType> + auto compare_two_arrays(const std::string& tag, const int dimension, const std::size_t& nbParticles, const std::vector<int>& index1_to_compare, const std::vector<int>& index2_to_compare, - const array_type& array1, const array_type& array2) + const ArrayType& array1, const ArrayType& array2) -> void { - // - using value_type = typename array_type::value_type; + using value_type = typename ArrayType::value_type; int nb_val_per_part1 = static_cast<int>(array1.size() / nbParticles); int nb_val_per_part2 = static_cast<int>(array2.size() / nbParticles); int nb_index = static_cast<int>(index1_to_compare.size()); diff --git a/include/scalfmm/utils/compare_trees.hpp b/include/scalfmm/utils/compare_trees.hpp index 1cc69409c29b8949ed5910fcc9c3983f21e3c6db..3b4318ac705157bdafc47efc9e2b0172165f8b89 100644 --- a/include/scalfmm/utils/compare_trees.hpp +++ b/include/scalfmm/utils/compare_trees.hpp @@ -1,14 +1,17 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/utils/compare_trees.hpp +// -------------------------------- #pragma once -#include <iostream> -#include <xtensor-blas/xblas.hpp> +#include "scalfmm/utils/io_helpers.hpp" + +#include "xtensor-blas/xblas.hpp" -#include <scalfmm/utils/io_helpers.hpp> +#include <iostream> namespace scalfmm::utils { - ///////////////////////////////// - /// /** * @brief compare the cells of two trees * @@ -19,14 +22,18 @@ namespace scalfmm::utils * option 2 only the locals * option 3 both multipoles and locals * + * @tparam TreeType1 + * @tparam TreeType2 + * @tparam ValueType * @param tree1 first tree * @param tree2 second tree * @param eps the threshold * @param option int (1,2,3) -the option describe above * @return the comparaison */ - template<typename Tree1, typename Tree2, typename Value_type> - auto inline compare_two_trees(Tree1 const& tree1, Tree2 const& tree2, Value_type const eps, int option) -> bool + template<typename TreeType1, typename TreeType2, typename ValueType> + inline auto compare_two_trees(TreeType1 const& tree1, TreeType2 const& tree2, ValueType const eps, + int option) -> bool { bool check{true}, check_mul{true}, check_loc{true}; @@ -136,8 +143,8 @@ namespace scalfmm::utils } } } // end option - } // end cells - } // end groups + } // end cells + } // end groups if(check_level) { std::cout << "level: " << level << " is good !\n"; diff --git a/include/scalfmm/utils/fftw.hpp b/include/scalfmm/utils/fftw.hpp index f4768b0c622b82a390406000adb41deee707b515..00d947dd764901d6e5fd1ee2d8fd6a9309f7f66e 100644 --- a/include/scalfmm/utils/fftw.hpp +++ b/include/scalfmm/utils/fftw.hpp @@ -1,43 +1,55 @@ // -------------------------------- // See LICENCE file at project root -// File : utils/fftw.hpp +// File : scalfmm/utils/fftw.hpp // -------------------------------- #ifndef SCALFMM_UTILS_FFTW_HPP #define SCALFMM_UTILS_FFTW_HPP +#include "scalfmm/utils/massert.hpp" + +#include "xtensor-fftw/basic.hpp" +#include "xtensor-fftw/common.hpp" +#include "xtensor-fftw/xtensor-fftw_config.hpp" +#include "xtensor/xarray.hpp" +#include "xtensor/xcontainer.hpp" +#include "xtensor/xsemantic.hpp" +#include "xtensor/xstorage.hpp" +#include "xtensor/xtensor_forward.hpp" +#include "xtl/xcomplex.hpp" + +#include <fftw3.h> + +#include <algorithm> #include <any> #include <complex> #include <cstddef> #include <cstring> -#include <mutex> -#include <regex> -#include <type_traits> -#include <valarray> -#include <algorithm> #include <functional> #include <iterator> +#include <mutex> #include <numeric> +#include <regex> #include <stdexcept> +#include <type_traits> +#include <valarray> #include <vector> -#include "xtensor-fftw/common.hpp" -#include "xtensor-fftw/xtensor-fftw_config.hpp" -#include "xtensor/xcontainer.hpp" -#include "xtensor/xsemantic.hpp" -#include "xtensor/xstorage.hpp" -#include <fftw3.h> -#include <xtensor-fftw/basic.hpp> -#include <xtensor/xarray.hpp> -#include <xtensor/xtensor_forward.hpp> -#include <xtl/xcomplex.hpp> - -#include "scalfmm/utils/massert.hpp" - namespace scalfmm::fftw { + /** + * @brief + * + * @tparam ValueType + * @tparam dim + */ template<typename ValueType, std::size_t dim> struct fft; + /** + * @brief + * + * @tparam dim + */ template<std::size_t dim> struct fft<double, dim> { @@ -45,18 +57,54 @@ namespace scalfmm::fftw using fft_type = xt::xarray<double>; using transformed_fft_type = xt::xarray<std::complex<double>>; + /** + * @brief + * + * @return std::mutex& + */ inline std::mutex& fftw_global_mutex() { static std::mutex m; return m; } + /** + * @brief Construct a new fft object + * + */ fft() = default; + + /** + * @brief Construct a new fft object + * + */ fft(fft const&) = default; + + /** + * @brief Construct a new fft object + * + */ fft(fft&&) = delete; + + /** + * @brief + * + * @return fft& + */ inline auto operator=(fft const&) -> fft& = default; + + /** + * @brief + * + * @return fft& + */ inline auto operator=(fft&&) noexcept -> fft& = delete; + /** + * @brief + * + * @param order + */ auto initialize(std::size_t order) -> void { std::vector<std::size_t> input_forward_shape(dim, (2 * order - 1)); @@ -68,6 +116,10 @@ namespace scalfmm::fftw create_inverse_plan(true); } + /** + * @brief Create a plan object + * + */ inline auto create_plan() -> void { if(!plan_exists) @@ -101,6 +153,11 @@ namespace scalfmm::fftw } } + /** + * @brief Create a inverse plan object + * + * @param odd_last_dim + */ inline auto create_inverse_plan(bool odd_last_dim = false) -> void { if(!plan_inv_exists) @@ -135,6 +192,12 @@ namespace scalfmm::fftw } } + /** + * @brief + * + * @param input + * @param output + */ inline auto execute_plan(xt::xarray<double> const& input, xt::xarray<std::complex<double>>& output) -> void { using fftw_input_t = double; @@ -158,6 +221,11 @@ namespace scalfmm::fftw std::copy(m_complex_buffer.begin(), m_complex_buffer.end(), output.begin()); } + /** + * @brief + * + * @param input + */ inline auto execute_plan(xt::xarray<double> const& input) -> void { using fftw_input_t = double; @@ -178,8 +246,14 @@ namespace scalfmm::fftw reinterpret_cast<fftw_output_t*>(m_complex_buffer.data())); } - inline auto execute_inverse_plan(xt::xarray<std::complex<double>> const& input, xt::xarray<double>& output) - -> void + /** + * @brief + * + * @param input + * @param output + */ + inline auto execute_inverse_plan(xt::xarray<std::complex<double>> const& input, + xt::xarray<double>& output) -> void { using fftw_input_t = fftw_complex; using fftw_output_t = double; @@ -203,6 +277,11 @@ namespace scalfmm::fftw std::copy(m_real_buffer.begin(), m_real_buffer.end(), output.begin()); } + /** + * @brief + * + * @param input + */ inline auto execute_inverse_plan(xt::xarray<std::complex<double>> const& input) -> void { using fftw_input_t = fftw_complex; @@ -224,6 +303,10 @@ namespace scalfmm::fftw m_real_buffer /= N_dft; } + /** + * @brief + * + */ inline auto destroy_plan() -> void { if(plan_exists) @@ -234,6 +317,10 @@ namespace scalfmm::fftw } } + /** + * @brief + * + */ inline auto destroy_inverse_plan() -> void { if(plan_inv_exists) @@ -244,28 +331,106 @@ namespace scalfmm::fftw } } + /** + * @brief + * + * @return fft_type const& + */ [[nodiscard]] inline auto real_buffer() const -> fft_type const& { return m_real_buffer; } + + /** + * @brief + * + * @return fft_type const& + */ [[nodiscard]] inline auto creal_buffer() const -> fft_type const& { return m_real_buffer; } + + /** + * @brief + * + * @return fft_type& + */ [[nodiscard]] inline auto real_buffer() -> fft_type& { return m_real_buffer; } + + /** + * @brief + * + * @return transformed_fft_type const& + */ [[nodiscard]] inline auto complex_buffer() const -> transformed_fft_type const& { return m_complex_buffer; } + + /** + * @brief + * + * @return transformed_fft_type const& + */ [[nodiscard]] inline auto ccomplex_buffer() const -> transformed_fft_type const& { return m_complex_buffer; } + + /** + * @brief + * + * @return transformed_fft_type& + */ [[nodiscard]] inline auto complex_buffer() -> transformed_fft_type& { return m_complex_buffer; } + /** + * @brief Destroy the fft object + * + */ ~fft() { destroy_plan(); destroy_inverse_plan(); } + /** + * @brief + * + */ fft_type m_real_buffer{}; + + /** + * @brief + * + */ transformed_fft_type m_complex_buffer{}; + + /** + * @brief + * + */ fftw_plan plan; + + /** + * @brief + * + */ fftw_plan plan_inv; + + /** + * @brief + * + */ double N_dft{1}; + + /** + * @brief + * + */ bool plan_exists{false}; + + /** + * @brief + * + */ bool plan_inv_exists{false}; }; + /** + * @brief + * + * @tparam dim + */ template<std::size_t dim> struct fft<float, dim> { @@ -273,18 +438,54 @@ namespace scalfmm::fftw using fft_type = xt::xarray<float>; using transformed_fft_type = xt::xarray<std::complex<float>>; + /** + * @brief + * + * @return std::mutex& + */ inline std::mutex& fftw_global_mutex() { static std::mutex m; return m; } + /** + * @brief Construct a new fft object + * + */ fft() = default; + + /** + * @brief Construct a new fft object + * + */ fft(fft const&) = delete; + + /** + * @brief Construct a new fft object + * + */ fft(fft&&) = delete; + + /** + * @brief + * + * @return fft& + */ inline auto operator=(fft const&) -> fft& = delete; + + /** + * @brief + * + * @return fft& + */ inline auto operator=(fft&&) noexcept -> fft& = delete; + /** + * @brief + * + * @param order + */ auto initialize(std::size_t order) -> void { std::vector<std::size_t> input_forward_shape(dim, (2 * order - 1)); @@ -296,6 +497,10 @@ namespace scalfmm::fftw create_inverse_plan(true); } + /** + * @brief Create a plan object + * + */ inline auto create_plan() -> void { if(!plan_exists) @@ -329,6 +534,11 @@ namespace scalfmm::fftw } } + /** + * @brief Create a inverse plan object + * + * @param odd_last_dim + */ inline auto create_inverse_plan(bool odd_last_dim = false) -> void { if(!plan_inv_exists) @@ -363,6 +573,12 @@ namespace scalfmm::fftw } } + /** + * @brief + * + * @param input + * @param output + */ inline auto execute_plan(xt::xarray<float> const& input, xt::xarray<std::complex<float>>& output) -> void { using fftw_input_t = float; @@ -386,6 +602,11 @@ namespace scalfmm::fftw std::copy(m_complex_buffer.begin(), m_complex_buffer.end(), output.begin()); } + /** + * @brief + * + * @param input + */ inline auto execute_plan(xt::xarray<float> const& input) -> void { using fftw_input_t = float; @@ -406,8 +627,14 @@ namespace scalfmm::fftw reinterpret_cast<fftw_output_t*>(m_complex_buffer.data())); } - inline auto execute_inverse_plan(xt::xarray<std::complex<float>> const& input, xt::xarray<float>& output) - -> void + /** + * @brief + * + * @param input + * @param output + */ + inline auto execute_inverse_plan(xt::xarray<std::complex<float>> const& input, + xt::xarray<float>& output) -> void { using fftw_input_t = fftwf_complex; using fftw_output_t = float; @@ -431,6 +658,11 @@ namespace scalfmm::fftw std::copy(m_real_buffer.begin(), m_real_buffer.end(), output.begin()); } + /** + * @brief + * + * @param input + */ inline auto execute_inverse_plan(xt::xarray<std::complex<float>> const& input) -> void { using fftw_input_t = fftwf_complex; @@ -452,6 +684,10 @@ namespace scalfmm::fftw m_real_buffer /= N_dft; } + /** + * @brief + * + */ inline auto destroy_plan() -> void { if(plan_exists) @@ -462,6 +698,10 @@ namespace scalfmm::fftw } } + /** + * @brief + * + */ inline auto destroy_inverse_plan() -> void { if(plan_inv_exists) @@ -472,25 +712,91 @@ namespace scalfmm::fftw } } + /** + * @brief + * + * @return fft_type const& + */ [[nodiscard]] inline auto real_buffer() const -> fft_type const& { return m_real_buffer; } + + /** + * @brief + * + * @return fft_type const& + */ [[nodiscard]] inline auto creal_buffer() const -> fft_type const& { return m_real_buffer; } + + /** + * @brief + * + * @return fft_type& + */ [[nodiscard]] inline auto real_buffer() -> fft_type& { return m_real_buffer; } + /** + * @brief + * + * @return transformed_fft_type const& + */ [[nodiscard]] inline auto complex_buffer() const -> transformed_fft_type const& { return m_complex_buffer; } + + /** + * @brief + * + * @return transformed_fft_type& + */ [[nodiscard]] inline auto complex_buffer() -> transformed_fft_type& { return m_complex_buffer; } + /** + * @brief Destroy the fft object + * + */ ~fft() { destroy_plan(); destroy_inverse_plan(); } + /** + * @brief + * + */ fft_type m_real_buffer{}; + + /** + * @brief + * + */ transformed_fft_type m_complex_buffer{}; + + /** + * @brief + * + */ fftwf_plan plan; + + /** + * @brief + * + */ fftwf_plan plan_inv; + + /** + * @brief + * + */ float N_dft{1}; + + /** + * @brief + * + */ bool plan_exists{false}; + + /** + * @brief + * + */ bool plan_inv_exists{false}; }; } // namespace scalfmm::fftw diff --git a/include/scalfmm/utils/generate.hpp b/include/scalfmm/utils/generate.hpp index f4d28e20d9e8afee5f652a938d507d052d5a9040..6f92022cdd9a43aa7545c51c900a4b94f5bb628e 100644 --- a/include/scalfmm/utils/generate.hpp +++ b/include/scalfmm/utils/generate.hpp @@ -1,38 +1,40 @@ // -------------------------------- // See LICENCE file at project root -// File : utils/generate.hpp +// File : scalfmm/utils/generate.hpp // -------------------------------- #ifndef SCALFMM_UTILS_GENERATE_HPP #define SCALFMM_UTILS_GENERATE_HPP -// #include <algorithm> -// #include <cmath> -// #include <cstddef> -// #include <functional> -// #include <iomanip> -// #include <limits> -#include <random> - -// #include "scalfmm/container/particle.hpp" -// #include "scalfmm/container/particle_container.hpp" -// #include "scalfmm/container/point.hpp" #include "scalfmm/meta/utils.hpp" #include "scalfmm/tree/utils.hpp" #include "scalfmm/utils/math.hpp" +#include <random> + namespace scalfmm::utils { - template<typename Container, typename Value_type> - auto generate_particle_per_leaf(Value_type const& box_width, int const& tree_height, Value_type const& decal) -> Container + /** + * @brief + * + * @tparam ContainerType + * @tparam ValueType + * @param box_width + * @param tree_height + * @param decal + * @return ContainerType + */ + template<typename ContainerType, typename ValueType> + auto generate_particle_per_leaf(ValueType const& box_width, int const& tree_height, + ValueType const& decal) -> ContainerType { - using particle_type = typename Container::value_type; + using particle_type = typename ContainerType::value_type; static constexpr std::size_t dimension{particle_type::dimension}; static constexpr std::size_t dimpow2 = math::pow(2, dimension); - using point_type = scalfmm::container::point<Value_type, dimension>; + using point_type = scalfmm::container::point<ValueType, dimension>; // step is the leaf size - Value_type step{box_width / std::pow(2, (tree_height - 1))}; + ValueType step{box_width / std::pow(2, (tree_height - 1))}; auto number_of_values_per_dimension = std::size_t(scalfmm::math::pow(2, (tree_height - 1))); std::cout << "Number of value per dimension = " << number_of_values_per_dimension << '\n'; std::cout << "Step = " << step << '\n'; @@ -42,7 +44,7 @@ namespace scalfmm::utils auto number_of_particles = std::pow(dimpow2, (tree_height - 1)); std::cout << "number_of_particles = " << number_of_particles << " box_width " << box_width << '\n'; - Container particles(number_of_particles); + ContainerType particles(number_of_particles); for(std::size_t index{0}; index < number_of_particles; ++index) { // get coord of the cell in the grid with the morton index @@ -60,24 +62,39 @@ namespace scalfmm::utils } return particles; } + + /** + * @brief + * + * @tparam ParticleType + * @param nb_particles + * @param center + * @param width + * @param seed + * @return container::particle_container<ParticleType> + */ template<typename ParticleType> - auto generate_particles(std::size_t nb_particle, typename ParticleType::position_type center, + auto generate_particles(std::size_t nb_particles, typename ParticleType::position_type center, typename ParticleType::position_value_type width, const int seed = 33) -> container::particle_container<ParticleType> { using particle_type = ParticleType; - using value_type = typename ParticleType::position_value_type; - using container_type = container::particle_container<ParticleType>; + using value_type = typename particle_type::position_value_type; + using container_type = container::particle_container<particle_type>; + + // init container (returned via RVO) + container_type container(nb_particles); - container_type container(nb_particle); - // const auto seed{33}; + // init random generator std::mt19937_64 gen(seed); constexpr value_type half{0.5}; - std::uniform_real_distribution<> dist(-width * half, width * half); + std::uniform_real_distribution<value_type> dist(-width * half, width * half); + + // update each particle auto it = std::begin(container); - for(std::size_t i = 0; i < nb_particle; ++i) + for(std::size_t i = 0; i < nb_particles; ++i) { - meta::for_each(*it, [&dist, &gen](auto& v) { return v = dist(gen); }); + meta::for_each(*it, [&dist, &gen](auto& v) { v = dist(gen); }); auto part = particle_type(*it); auto& pos = part.position(); auto& out = part.outputs(); @@ -86,9 +103,63 @@ namespace scalfmm::utils *it = part.as_tuple(); ++it; } + + return container; + } + + /** + * @brief + * + * @tparam ParticleType + * @param nb_particles + * @param center + * @param width + * @param seed + * @return std::vector<ParticleType> + */ + template<typename ParticleType> + auto generate_vector_particles(std::size_t nb_particles, typename ParticleType::position_type center, + typename ParticleType::position_value_type width, + const int seed = 33) -> std::vector<ParticleType> + { + using particle_type = ParticleType; + using value_type = typename particle_type::position_value_type; + using container_type = std::vector<particle_type>; + + // init container (returned via RVO) + container_type container(nb_particles); + + // Init random generator + std::mt19937_64 gen(seed); + constexpr value_type half{0.5}; + std::uniform_real_distribution<value_type> dist(-width * half, width * half); + + // update each particle + for(std::size_t i = 0; i < nb_particles; ++i) + { + // particle_type p; + auto& part = container[i]; + auto& position = part.position(); + auto& inputs = part.inputs(); + auto& outputs = part.outputs(); + + meta::for_each(position, [&dist, &gen](auto& v) { v = dist(gen); }); + meta::for_each(inputs, [&dist, &gen](auto& v) { v = dist(gen); }); + meta::for_each(outputs, [](auto& v) { v = 0.; }); + + position += center; + } + return container; } + /** + * @brief Get the center object + * + * @tparam T + * @tparam dimension + * @return constexpr auto + */ template<typename T, std::size_t dimension> constexpr auto get_center() { @@ -106,15 +177,35 @@ namespace scalfmm::utils } } + /** + * @brief + * + * @tparam ValueType + * @tparam dimension + * @tparam pv + */ template<typename ValueType, std::size_t dimension, std::size_t pv> struct get_particle_type { }; - // full direct for unit testing + /** + * @brief full direct for unit testing. + * + * @tparam ValueType + * @tparam dimension + * @tparam physical_values + * @tparam ContainerIterator + * @tparam MatrixKernelType + * @tparam Outputs + * @param begin + * @param end + * @param matrix_kernel + * @param out + */ template<typename ValueType, std::size_t dimension, std::size_t physical_values, typename ContainerIterator, - typename MatrixKernel, typename Outputs> - inline auto full_direct_test(ContainerIterator begin, ContainerIterator end, MatrixKernel matrix_kernel, + typename MatrixKernelType, typename Outputs> + inline auto full_direct_test(ContainerIterator begin, ContainerIterator end, MatrixKernelType matrix_kernel, Outputs& out) -> void { using particle_type = typename utils::get_particle_type<ValueType, dimension, physical_values>::type; @@ -123,7 +214,7 @@ namespace scalfmm::utils for(auto it_p = begin; it_p < end; ++it_p) { auto pt_x = particle_type(*it_p).position(); - // 1 is the number of physical values (info coming from the MatrixKernel + // 1 is the number of physical values (info coming from the MatrixKernelType // get the potential for(auto it_p2 = begin; it_p2 < it_p; ++it_p2) { @@ -145,18 +236,53 @@ namespace scalfmm::utils } } + /** + * @brief + * + * @tparam T + * @tparam U + * @tparam F + * @tparam Is + * @param t + * @param u + * @param f + * @param s + * @return true + * @return false + */ template<typename T, typename U, typename F, std::size_t... Is> constexpr auto meta_compare_impl(T const& t, U const& u, F&& f, std::index_sequence<Is...> s) -> bool { return (std::invoke(std::forward<F>(f), meta::get<Is>(t), meta::get<Is>(u)) && ...); } + /** + * @brief + * + * @tparam T + * @tparam U + * @tparam F + * @param t + * @param u + * @param f + * @return true + * @return false + */ template<typename T, typename U, typename F> constexpr auto meta_compare(T const& t, U const& u, F&& f = std::equal<>) -> bool { return meta_compare_impl(t, u, std::forward<F>(f), std::make_index_sequence<meta::tuple_size_v<T>>{}); } + /** + * @brief + * + * @tparam T + * @param x + * @param y + * @param ulp + * @return std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type + */ template<class T> typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type almost_equal(T x, T y, int ulp) { diff --git a/include/scalfmm/utils/io_helpers.hpp b/include/scalfmm/utils/io_helpers.hpp index bbab158ca85f98e8e02758963c3e64333c64d3ce..7bb1664c04d16f29084f042b68c2e9487e423576 100644 --- a/include/scalfmm/utils/io_helpers.hpp +++ b/include/scalfmm/utils/io_helpers.hpp @@ -1,6 +1,15 @@ -#ifndef SCALFMM_UTILS_OSTREAM_TUPLE_HPP +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/utils/io_helpers.hpp +// -------------------------------- +#ifndef SCALFMM_UTILS_OSTREAM_TUPLE_HPP #define SCALFMM_UTILS_OSTREAM_TUPLE_HPP +#include "inria/integer_sequence.hpp" +#include "inria/ostream_joiner.hpp" + +#include "scalfmm/meta/utils.hpp" + #include <array> #include <cstddef> #include <fstream> @@ -10,29 +19,24 @@ #include <tuple> #include <utility> -#include "inria/integer_sequence.hpp" -#include "inria/ostream_joiner.hpp" - -#include "scalfmm/meta/utils.hpp" namespace scalfmm { namespace details { namespace tuple_helper { - /** \brief Helper for tuple formatted output + /** + * @brief Helper for tuple formatted output * - * \param os Output stream - * \param t Printed tuple - * \param index_sequence unnamed parameter for automatic deduction of - * Indices template parameter + * @tparam Types Types contained in the tuple, automatically deduced + * @tparam Indices Type indices, automatically deduced * - * \tparam Types Types contained in the tuple, automatically deduced - * \tparam Indices Type indices, automatically deduced + * @param os Output stream + * @param t Tuple to print */ template<typename... Types, std::size_t... Indices> - inline void formated_output_old(std::ostream& os, const std::tuple<Types...>& t, - inria::index_sequence<Indices...>) + inline auto formated_output_old(std::ostream& os, const std::tuple<Types...>& t, + inria::index_sequence<Indices...>) -> void { os << "("; // opening brace // initializer_list members are evaluated from left to right; this @@ -47,25 +51,35 @@ namespace scalfmm (..., (os << (Indices == 0 ? "" : ", ") << std::get<Indices>(t))); os << ")"; // closing brace } + /** * @brief Print formated tuple - * - * @tparam Tuple - * @tparam Indices - * @param os the stream + * + * @tparam Tuple + * @tparam Indices + * @param os the stream * @param t the tuple */ - template<typename Tuple, std::size_t... Indices> - inline void formated_output(std::ostream& os, const Tuple& t, - inria::index_sequence<Indices...>) + template<typename Tuple, std::size_t... Indices> + inline auto formated_output(std::ostream& os, const Tuple& t, inria::index_sequence<Indices...>) -> void { - os << "["; + os << "["; (..., (os << (Indices == 0 ? "" : ", ") << std::get<Indices>(t))); os << "]"; // closing brace } + + /** + * @brief + * + * @tparam Ts + */ template<class... Ts> struct tuple_wrapper { + /** + * @brief + * + */ const std::tuple<Ts...>& t; friend std::ostream& operator<<(std::ostream& os, const tuple_wrapper& w) { @@ -76,12 +90,23 @@ namespace scalfmm }; } // namespace tuple_helper - } // namespace details + } // namespace details namespace { + /** + * @brief + * + */ constexpr struct { + /** + * @brief + * + * @tparam Ts + * @param t + * @return details::tuple_helper::tuple_wrapper<Ts...> + */ template<class... Ts> details::tuple_helper::tuple_wrapper<Ts...> operator()(const std::tuple<Ts...>& t) const { @@ -94,10 +119,12 @@ namespace scalfmm namespace details { /** - * \brief Silence the `unused variable` warnings about tuple_out + * @brief Silence the `unused variable` warnings about tuple_out + * + * @tparam T */ template<class T> - void silence_tuple_out_warning() + inline auto silence_tuple_out_warning() -> void { tuple_out(std::tuple<>{}); } @@ -106,16 +133,20 @@ namespace scalfmm namespace io { - /// - /// \brief operator << for array std::array<T,N> - /// - /// using namespace io ; - /// std::array(int, 2) a; - /// std::cout << a << std::endl ; - /// print array [a_1, ..., a_N] - /// \param os ostream - /// \param array to print - /// + /** + * @brief operator << for array std::array<T,N> + * + * using namespace io ; + * std::array(int, 2) a; + * std::cout << a << std::endl ; + * print array [a_1, ..., a_N] + * + * @tparam T + * @tparam N + * @param os + * @param array + * @return std::ostream& + */ template<typename T, std::size_t N> inline auto operator<<(std::ostream& os, const std::array<T, N>& array) -> std::ostream& { @@ -127,19 +158,21 @@ namespace scalfmm os << array.back() << "]"; return os; } - /// - /// \brief print a vector and its size - /// - /// The output is - /// title (size) values - /// - /// The values are separated by a white space - /// - /// \param[in] title - /// \param[in] v a vector (need size method) - /// - template<typename Vector_type> - void print(const std::string&& title, Vector_type const& v) + + /** + * @brief print a vector and its size + * + * The output is + * title (size) values + * + * The values are separated by a white space + * + * @tparam VectorType + * @param title + * @param v + */ + template<typename VectorType> + inline auto print(const std::string&& title, VectorType const& v) -> void { std::cout << title << " (" << v.size() << ") "; if(v.size() > 0) @@ -148,23 +181,26 @@ namespace scalfmm std::cout << i << ' '; } std::cout << '\n'; - }; + } - /// - /// \brief print print a vector and its size starting at first and ending at end - /// - /// The output is - /// title (size) [value1 value2 ...] - /// - /// The values are separated by a white space - /// - /// \param title string to print - /// \param first begin iterator - /// \param last end iterator - /// - template<typename Iterator_type> - void print(std::ostream& out, const std::string&& title, Iterator_type first, Iterator_type last, - std::string&& sep = " ") + /** + * @brief print print a vector and its size starting at first and ending at end + * + * The output is + * title (size) [value1 value2 ...] + * + * The values are separated by a white space + * + * @tparam IteratorType + * @param out + * @param title string to print + * @param first begin iterator + * @param last end iterator + * @param sep + */ + template<typename IteratorType> + inline auto print(std::ostream& out, const std::string&& title, IteratorType first, IteratorType last, + std::string&& sep = " ") -> void { auto size = std::distance(first, last); @@ -172,15 +208,16 @@ namespace scalfmm if(size) { auto lastm1 = --last; - Iterator_type it; + IteratorType it; for(it = first; it != lastm1; ++it) out << *it << sep; out << *it; } out << ']'; - }; - // template<typename Iterator_type> - // void print(std::ostream& out, const std::string&& title, Iterator_type first, Iterator_type last, + } + + // template<typename IteratorType> + // void print(std::ostream& out, const std::string&& title, IteratorType first, IteratorType last, // std::string&& first_str = "[", std::string&& sep = " ", std::string&& last_str = "]") // { // auto size = std::distance(first, last); @@ -189,114 +226,124 @@ namespace scalfmm // if(size) // { // auto lastm1 = --last; - // Iterator_type it; + // IteratorType it; // for(it = first; it != lastm1; ++it) // out << *it << sep; // out << *it; // } // out << last_str; // }; - template<typename Vector_type> - void print(std::ostream& out, const std::string&& title, Vector_type& v, std::string&& sep = " ") + + /** + * @brief + * + * @tparam VectorType + * @param out + * @param title + * @param v + * @param sep + */ + template<typename VectorType> + inline auto print(std::ostream& out, const std::string&& title, VectorType& v, std::string&& sep = " ") -> void { print(out, std::move(title), v.begin(), v.end(), std::move(sep)); out << std::endl; - }; + } + /** * @brief Print a part or a full array * * print the array array[0:size] * - * @tparam ARRAY type of teh array + * @tparam ArrayType type of teh array * @param array the array to print * @param size the list of elements to print */ - template<typename ARRAY> - auto print_array(ARRAY const& array, const int& size) -> void + template<typename ArrayType> + inline auto print_array(ArrayType const& array, const int& size) -> void { - std::cout << "[ "<< array[0]; + std::cout << "[ " << array[0]; for(int i{1}; i < size; ++i) { - std::cout << ", "<< array[i] ; + std::cout << ", " << array[i]; } std::cout << " ]"; } - template<typename T, std::size_t N> - void print(std::ostream& out, std::array<T,N>const & a) - { + + /** + * @brief + * + * @tparam T + * @tparam N + * @param out + * @param a + */ + template<typename T, std::size_t N> + inline auto print(std::ostream& out, std::array<T, N> const& a) -> void + { out << "["; for(std::size_t i{0}; i < N; ++i) { - out << (i==0 ? "" : ", ") << a[i] ; + out << (i == 0 ? "" : ", ") << a[i]; } out << "]"; } + /** - * @brief print a tuple - * + * @brief print a tuple + * * std::tuple<int, double, int> a{1,0.3,8}; - * io::print(a) + * io::print(a) * the output is [1, 0.3, 8] * + * @tparam T * @param out the stream * @param tup the tuple */ - - template<class... T> - void print(std::ostream& out, const std::tuple<T...>& tup) + template<typename... T> + inline auto print(std::ostream& out, const std::tuple<T...>& tup) -> void { scalfmm::details::tuple_helper::formated_output(out, tup, std::make_index_sequence<sizeof...(T)>()); } + /** * @brief print a sequence (tuple, array) * tuple<int, double> t{3,0.5} * std::cout << t << std::endl shows [3, 0.5, ] * + * @tparam Seq * @param[inout] out the stream * @param[in] seq the sequence to print * @return auto */ template<typename Seq> - inline auto print_seq(std::ostream& out, Seq const& seq) + inline auto print_seq(std::ostream& out, Seq const& seq) -> void { out << "["; int i{0}; - meta::for_each(seq, [&out, &i](const auto& s) { out << (i++==0 ? "":", ")<< s ; }); - out << "]"; + meta::for_each(seq, [&out, &i](const auto& s) { out << (i++ == 0 ? "" : ", ") << s; }); + out << "]"; } + /** * @brief print the address ot the element of a sequence (tuple, array) * * tuple<int, double> t{3,0.5} * std::cout << t << std::endl * + * @tparam Seq * @param[inout] out the stream * @param[in] seq the sequence to print - * @return auto */ template<typename Seq> - inline auto print_ptr_seq(std::ostream& out, const Seq& seq) + inline auto print_ptr_seq(std::ostream& out, const Seq& seq) -> void { out << "["; int i{0}; - meta::for_each(seq, [&out, &i](const auto& s) { out << (i++==0 ? "":", ")<< &s ; }); + meta::for_each(seq, [&out, &i](const auto& s) { out << (i++ == 0 ? "" : ", ") << &s; }); out << "]"; } - // template<class... T> - // inline auto operator<<(std::ostream& os, std::tuple<T...>const & t) -> std::ostream& - // { - // meta::td<decltype(t)> tt; - // scalfmm::details::tuple_helper::formated_output_n(os, t, std::make_index_sequence<sizeof...(T)>()); - // return os; - // } - - // template<typename... Args> - // inline auto print(std::ostream& out, Args&&... args) - // { - // ((out << ' ' << std::forward<Args>(args)), ...); - // } - } // namespace io } // namespace scalfmm diff --git a/include/scalfmm/utils/low_rank.hpp b/include/scalfmm/utils/low_rank.hpp index 24e2413b239ac96a20e483a6572fe0427d3d9e2e..0b40a83787b64fea3e8b52cc04464e2123adf29f 100644 --- a/include/scalfmm/utils/low_rank.hpp +++ b/include/scalfmm/utils/low_rank.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : utils/low_rank.hpp +// File : scalfmm/utils/low_rank.hpp // -------------------------------- #ifndef SCALFMM_UTILS_LOW_RANK_HPP #define SCALFMM_UTILS_LOW_RANK_HPP @@ -9,18 +9,35 @@ namespace scalfmm::low_rank { - template<typename ValueType, typename MatrixKernel, typename TensorViewX, typename TensorViewY> - inline auto paca(MatrixKernel const& mk, TensorViewX&& X, TensorViewY&& Y, xt::xarray<ValueType> const& weights, - ValueType epsilon, std::size_t kn, std::size_t km) -> std::tuple<xt::xtensor<ValueType, 2>, xt::xtensor<ValueType, 2>> + /** + * @brief + * + * @tparam ValueType + * @tparam MatrixKernelType + * @tparam TensorViewX + * @tparam TensorViewY + * @param mk + * @param X + * @param Y + * @param weights + * @param epsilon + * @param kn + * @param km + * @return std::tuple<xt::xtensor<ValueType, 2>, xt::xtensor<ValueType, 2>> + */ + template<typename ValueType, typename MatrixKernelType, typename TensorViewX, typename TensorViewY> + inline auto paca(MatrixKernelType const& mk, TensorViewX&& X, TensorViewY&& Y, xt::xarray<ValueType> const& weights, + ValueType epsilon, std::size_t kn, + std::size_t km) -> std::tuple<xt::xtensor<ValueType, 2>, xt::xtensor<ValueType, 2>> { - const std::size_t nnodes{X.size()}; + const std::size_t nnodes{X.size()}; std::vector<bool> row_bools(nnodes, true); std::vector<bool> col_bools(nnodes, true); xt::xtensor<ValueType, 2, xt::layout_type::column_major> U; xt::xtensor<ValueType, 2, xt::layout_type::column_major> V; // initialize rank r - std::size_t r{0}; + std::size_t r{0}; const auto max_r{nnodes}; // resize U V @@ -39,29 +56,29 @@ namespace scalfmm::low_rank { for(std::size_t i = Ib; i < Ie; ++i) { - view.at(idx) = weights[i] * weights[j] * - mk.evaluate(X[i], Y[j]).at(kn * MatrixKernel::km + km); + view.at(idx) = weights[i] * weights[j] * mk.evaluate(X[i], Y[j]).at(kn * MatrixKernelType::km + km); ++idx; } } }; - const auto epsilon2 = epsilon*epsilon; - std::size_t I{0}; - std::size_t J{0}; - do { + const auto epsilon2 = epsilon * epsilon; + std::size_t I{0}; + std::size_t J{0}; + do + { // compute row I and its residual auto V_ = xt::view(V, xt::all(), r); - evaluate(V_, I, I+1, 0, nnodes); + evaluate(V_, I, I + 1, 0, nnodes); row_bools[I] = false; - for(std::size_t l=0; l<r; ++l) + for(std::size_t l = 0; l < r; ++l) { auto col_u = xt::view(U, xt::all(), l); auto col_v = xt::view(V, xt::all(), l); - V_ -= col_u.at(I)*col_v; + V_ -= col_u.at(I) * col_v; } // find max of residual and argmax ValueType val_max{0.}; - for(std::size_t j=0; j<nnodes; ++j) + for(std::size_t j = 0; j < nnodes; ++j) { const auto val_abs = std::abs(V_.at(j)); if(col_bools[j] && val_max < val_abs) @@ -76,17 +93,17 @@ namespace scalfmm::low_rank // compute col J and its residual auto U_ = xt::view(U, xt::all(), r); - evaluate(U_, 0, nnodes, J, J+1); + evaluate(U_, 0, nnodes, J, J + 1); col_bools[J] = false; for(std::size_t l = 0; l < r; ++l) { auto col_u = xt::view(U, xt::all(), l); auto col_v = xt::view(V, xt::all(), l); - U_ -= col_v.at(J)*col_u; + U_ -= col_v.at(J) * col_u; } // find max of residual and argmax val_max = 0.; - for(std::size_t i=0; i<nnodes; ++i) + for(std::size_t i = 0; i < nnodes; ++i) { const auto val_abs = std::abs(U_.at(i)); if(row_bools[i] && val_max < val_abs) @@ -97,28 +114,43 @@ namespace scalfmm::low_rank } // increment Frobenius norm: |Sk|^2 += |uk|^2 |vk|^2 + 2 sumj ukuj vjvk ValueType normuuvv{0.}; - for(std::size_t l=0; l<r; ++l) + for(std::size_t l = 0; l < r; ++l) { auto col_u = xt::view(U, xt::all(), l); auto col_v = xt::view(V, xt::all(), l); - normuuvv += xt::linalg::vdot(U_, col_u)*xt::linalg::vdot(col_v, V_); + normuuvv += xt::linalg::vdot(U_, col_u) * xt::linalg::vdot(col_v, V_); } norm2uv = xt::linalg::vdot(U_, U_) * xt::linalg::vdot(V_, V_); - norm2s += norm2uv + ValueType(2.)*normuuvv; + norm2s += norm2uv + ValueType(2.) * normuuvv; // increment low-rank ++r; - } - while(norm2uv > epsilon2 * norm2s); + } while(norm2uv > epsilon2 * norm2s); - auto UU = xt::eval(xt::view(U, xt::all(), xt::range(0,r))); - auto VV = xt::eval(xt::view(V, xt::all(), xt::range(0,r))); + auto UU = xt::eval(xt::view(U, xt::all(), xt::range(0, r))); + auto VV = xt::eval(xt::view(V, xt::all(), xt::range(0, r))); return std::make_tuple(UU, VV); } - - template<typename ValueType, typename MatrixKernel, typename TensorViewX, typename TensorViewY> - inline auto generate(MatrixKernel const& mk, TensorViewX&& X, TensorViewY&& Y, xt::xarray<ValueType> const& weights, - ValueType epsilon, std::size_t kn, std::size_t km) -> std::tuple<xt::xtensor<ValueType, 2>, xt::xtensor<ValueType, 2>> + /** + * @brief + * + * @tparam ValueType + * @tparam MatrixKernelType + * @tparam TensorViewX + * @tparam TensorViewY + * @param mk + * @param X + * @param Y + * @param weights + * @param epsilon + * @param kn + * @param km + * @return std::tuple<xt::xtensor<ValueType, 2>, xt::xtensor<ValueType, 2>> + */ + template<typename ValueType, typename MatrixKernelType, typename TensorViewX, typename TensorViewY> + inline auto generate(MatrixKernelType const& mk, TensorViewX&& X, TensorViewY&& Y, + xt::xarray<ValueType> const& weights, ValueType epsilon, std::size_t kn, + std::size_t km) -> std::tuple<xt::xtensor<ValueType, 2>, xt::xtensor<ValueType, 2>> { xt::xtensor<ValueType, 2, xt::layout_type::column_major> U; xt::xtensor<ValueType, 2, xt::layout_type::column_major> V; @@ -129,8 +161,8 @@ namespace scalfmm::low_rank std::tie(U, V) = paca(mk, std::forward<TensorViewX>(X), std::forward<TensorViewY>(Y), weights, epsilon, kn, km); - std::tie(QU,RU) = xt::linalg::qr(U); - std::tie(QV,RV) = xt::linalg::qr(V); + std::tie(QU, RU) = xt::linalg::qr(U); + std::tie(QV, RV) = xt::linalg::qr(V); auto nnodes = U.shape()[0]; auto rank = U.shape()[1]; @@ -150,7 +182,7 @@ namespace scalfmm::low_rank { for(std::size_t i = 0; i < rank; ++i) { - U_.at(i,j) *= S.at(j); + U_.at(i, j) *= S.at(j); } } @@ -161,7 +193,7 @@ namespace scalfmm::low_rank //{ // for(std::size_t j{0}; j < nnodes; ++j) // { - // K.at(i, j) = weights[i] * weights[j] * mk.evaluate(X[i], Y[j]).at(kn * MatrixKernel::km + km); + // K.at(i, j) = weights[i] * weights[j] * mk.evaluate(X[i], Y[j]).at(kn * MatrixKernelType::km + km); // } //} @@ -175,8 +207,8 @@ namespace scalfmm::low_rank { for(std::size_t i = 0; i < nnodes; ++i) { - U.at(i,j) /= weights[i]; - V.at(i,j) /= weights[i]; + U.at(i, j) /= weights[i]; + V.at(i, j) /= weights[i]; } } @@ -186,6 +218,6 @@ namespace scalfmm::low_rank return std::make_tuple(U_row, V_row); } -} +} // namespace scalfmm::low_rank -#endif //SCALFMM_UTILS_LOW_RANK_HPP +#endif //SCALFMM_UTILS_LOW_RANK_HPP diff --git a/include/scalfmm/utils/massert.hpp b/include/scalfmm/utils/massert.hpp index e2eb33ccf9f579ffdde94219d9001f561731e2be..2b0f970776f78771d7aae30f34d0045b5194ec96 100644 --- a/include/scalfmm/utils/massert.hpp +++ b/include/scalfmm/utils/massert.hpp @@ -1,12 +1,16 @@ // -------------------------------- // See LICENCE file at project root -// File : utils/massert.hpp +// File : scalfmm/utils/massert.hpp // -------------------------------- #ifndef SCALFMM_UTILS_MASSERT_HPP #define SCALFMM_UTILS_MASSERT_HPP #include <cassert> +/** + * @brief + * + */ #define assertm(exp, msg) assert(((void)msg, exp)); #endif // SCALFMM_UTILS_MASSERT_HPP diff --git a/include/scalfmm/utils/math.hpp b/include/scalfmm/utils/math.hpp index 9ce06dccf7bfe17c052f7d26d91e550d3c253a98..0199da1f6150349d2f0cf74b857b26a4a0132ae2 100644 --- a/include/scalfmm/utils/math.hpp +++ b/include/scalfmm/utils/math.hpp @@ -1,6 +1,6 @@ // -------------------------------- // See LICENCE file at project root -// File : utils/math.hpp +// File : scalfmm/utils/math.hpp // -------------------------------- #ifndef SCALFMM_UTILS_MATH_HPP #define SCALFMM_UTILS_MATH_HPP @@ -12,6 +12,13 @@ namespace scalfmm::math { + /** + * @brief + * + * @tparam T + * @param value + * @return T + */ template<typename T> inline auto factorial(int value) -> T { @@ -28,6 +35,14 @@ namespace scalfmm::math return T(result); } + /** + * @brief + * + * @tparam T + * @param a + * @param p + * @return T + */ template<typename T> inline auto pow(T a, int p) -> T { @@ -39,12 +54,36 @@ namespace scalfmm::math return result; } + /** + * @brief + * + * @tparam T + * @param a + * @param p + * @return T + */ template<typename T> inline constexpr auto pow(T a, std::size_t p) -> T { return p == 0 ? 1 : a * pow<T>(a, p - 1); } + /** + * @brief + * + * @tparam T + * @tparam U + * @tparam typename + * @tparam T> + * @tparam T>, + * typename + * @tparam U> + * @param a + * @param b + * @param epsilon + * @return true + * @return false + */ template<typename T, typename U, typename = std::enable_if_t<std::is_floating_point<T>::value, T>, typename = std::enable_if_t<std::is_floating_point<U>::value, U>> inline constexpr auto feq(const T& a, const U& b, T epsilon = std::numeric_limits<T>::epsilon()) -> bool @@ -52,6 +91,17 @@ namespace scalfmm::math return std::abs(a - b) < epsilon; } + /** + * @brief + * + * @tparam ValueType1 + * @tparam ValueType + * @param value + * @param range_begin + * @param range_end + * @return true + * @return false + */ template<typename ValueType1, typename ValueType> inline constexpr auto between(ValueType1 value, ValueType range_begin, ValueType range_end) -> bool { diff --git a/include/scalfmm/utils/parameters.hpp b/include/scalfmm/utils/parameters.hpp index 84cbed76cfd1d7fda1b3570d37efc98b1ee21074..68c48dda92ae62ea4baf1675649b3a25173f329c 100644 --- a/include/scalfmm/utils/parameters.hpp +++ b/include/scalfmm/utils/parameters.hpp @@ -1,10 +1,18 @@ -#ifndef EXAMPLES_PARAMETERS_HPP +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/utils/parameters.hpp +// -------------------------------- +#ifndef EXAMPLES_PARAMETERS_HPP #define EXAMPLES_PARAMETERS_HPP #include <cpp_tools/cl_parser/cl_parser.hpp> namespace args { + /** + * @brief + * + */ struct tree_height : cpp_tools::cl_parser::required_tag { cpp_tools::cl_parser::str_vec flags = {"--tree-height", "-th"}; @@ -13,6 +21,10 @@ namespace args type def = 2; }; + /** + * @brief + * + */ struct order : cpp_tools::cl_parser::required_tag { cpp_tools::cl_parser::str_vec flags = {"--order", "-o"}; @@ -21,6 +33,10 @@ namespace args type def = 3; }; + /** + * @brief + * + */ struct thread_count { cpp_tools::cl_parser::str_vec flags = {"--threads", "-t"}; @@ -29,6 +45,10 @@ namespace args type def = 1; }; + /** + * @brief + * + */ struct input_file : cpp_tools::cl_parser::required_tag { cpp_tools::cl_parser::str_vec flags = {"--input-file", "-fin"}; @@ -36,6 +56,10 @@ namespace args using type = std::string; }; + /** + * @brief + * + */ struct output_file { cpp_tools::cl_parser::str_vec flags = {"--output-file", "-fout"}; @@ -43,6 +67,10 @@ namespace args using type = std::string; }; + /** + * @brief + * + */ struct log_level { cpp_tools::cl_parser::str_vec flags = {"--log-level", "-llog"}; @@ -51,6 +79,10 @@ namespace args type def = "info"; }; + /** + * @brief + * + */ struct log_file { cpp_tools::cl_parser::str_vec flags = {"--log-file", "-flog"}; @@ -59,6 +91,10 @@ namespace args type def = ""; }; + /** + * @brief + * + */ struct block_size { cpp_tools::cl_parser::str_vec flags = {"--group-size", "--block-size", "-gs", "-bs"}; @@ -67,6 +103,10 @@ namespace args type def = 250; }; + /** + * @brief + * + */ struct pbc { cpp_tools::cl_parser::str_vec flags = {"--per", "--pbc"}; /*!< The flags */ @@ -76,6 +116,10 @@ namespace args using type = std::vector<bool>; }; + /** + * @brief + * + */ struct extended_tree_height { using type = int; @@ -87,6 +131,10 @@ namespace args type def = 0; // defaut no level for non periodic simulation }; + /** + * @brief + * + */ struct Dimension { cpp_tools::cl_parser::str_vec flags = {"--dimension", "-d"}; diff --git a/include/scalfmm/utils/periodicity.hpp b/include/scalfmm/utils/periodicity.hpp index 3ae0ae50b13716b9e057234fc3d165f162fcf782..a966e844552ab87fbda9d27076dc1832d30587c7 100644 --- a/include/scalfmm/utils/periodicity.hpp +++ b/include/scalfmm/utils/periodicity.hpp @@ -1,14 +1,30 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/utils/periodicity.hpp +// -------------------------------- #ifndef SCALFMM_UTILS_PERIODICITY_HPP #define SCALFMM_UTILS_PERIODICITY_HPP namespace scalfmm::utils { - template<typename Container, typename ValueType, typename Array> - auto replicated_distribution_grid_3x3(Container const& origin, ValueType width, Array const& pbc) -> Container + /** + * @brief + * + * @tparam ContainerType + * @tparam ValueType + * @tparam ArrayType + * @param origin + * @param width + * @param pbc + * @return ContainerType + */ + template<typename ContainerType, typename ValueType, typename ArrayType> + auto replicated_distribution_grid_3x3(ContainerType const& origin, ValueType width, + ArrayType const& pbc) -> ContainerType { - using container_type = Container; - using particle_type = typename Container::value_type; + using container_type = ContainerType; + using particle_type = typename container_type::value_type; using position_type = typename particle_type::position_type; static constexpr std::size_t dimension{particle_type::dimension}; @@ -98,11 +114,20 @@ namespace scalfmm::utils return replicated_dist; } - template<typename Container> - auto extract_particles_from_ref(Container const& container, const int& size_part, const int& ref) - -> std::vector<typename Container::particle_type> + /** + * @brief + * + * @tparam ContainerType + * @param container + * @param size_part + * @param ref + * @return std::vector<typename ContainerType::particle_type> + */ + template<typename ContainerType> + auto extract_particles_from_ref(ContainerType const& container, const int& size_part, + const int& ref) -> std::vector<typename ContainerType::particle_type> { - using particle_type = typename Container::particle_type; + using particle_type = typename ContainerType::particle_type; std::vector<particle_type> extracted_cont(size_part); diff --git a/include/scalfmm/utils/sort.hpp b/include/scalfmm/utils/sort.hpp index 2c0755f48981816fc86ef39107a33f96b60d72ae..d563a0bfecb05aed6a240f07f13f57c8e5a2a5dc 100644 --- a/include/scalfmm/utils/sort.hpp +++ b/include/scalfmm/utils/sort.hpp @@ -1,4 +1,8 @@ -#ifndef SCALFMM_UTILS_SORT_HPP +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/utils/sort.hpp +// -------------------------------- +#ifndef SCALFMM_UTILS_SORT_HPP #define SCALFMM_UTILS_SORT_HPP #include <array> @@ -14,207 +18,309 @@ #include "scalfmm/tree/for_each.hpp" #include "scalfmm/tree/utils.hpp" -namespace scalfmm +#include <xtensor/xview.hpp> + +namespace scalfmm::utils { - namespace utils + using indexing_structure = std::tuple<std::size_t, std::size_t>; + + /** + * @brief sort_container compute the permutation to obtain the sorted particles according to the morton index + * + * Each element of the returned vector is a tuple of std::size_t. The first element is the Morton index + * of the particle while the second element is original index + * + * @tparam BoxType + * @tparam ContainerType + * @tparam Int + * @param[in] box the box containing all the particles inside the container. + * @param[in] level the level of the d-tree to compute the morton index + * @param[in] particle_container the particles container + * @param[in] data_already_sorted specify if we only construct the identity permutation + * @return the permutation a vector of tuple containing the morton index and the original position of the + * particle + */ + template<class BoxType, class ContainerType, typename Int> //, class array_type> + inline auto get_morton_permutation(const BoxType& box, const Int& level, const ContainerType& particle_container, + const bool data_already_sorted = false) -> std::vector<indexing_structure> { - using indexing_structure = std::tuple<std::size_t, std::size_t>; - /// - /// - /// - /// \brief sort_container compute the permutation to obtain the sorted particles according to the morton index - /// - /// Each element of the returned vector is a tuple of std::size_t. The first element is the Morton index - /// of the particle while the second element is original index - /// - /// \param[in] box the box containing all the particles inside the container. - /// - /// \param[in] level the level of the d-tree to compute the morton index - /// - /// \param[in] particle_container the particles container - /// - /// \param[in] data_already_sorted specify if we only construct the identity permutation - /// - /// \return the permutation a vector of tuple containing the morton index and the original position of the - /// particle - /// - template<class BOX_T, class CONTAINER_T, typename Int> //, class array_type> - auto inline get_morton_permutation(const BOX_T& box, const Int& level, const CONTAINER_T& particle_container, - const bool data_already_sorted = false) -> std::vector<indexing_structure> + auto number_of_particles = particle_container.size(); + std::vector<indexing_structure> tuple_of_indexes(number_of_particles); + + for(std::size_t part = 0; part < number_of_particles; ++part) { - auto number_of_particles = particle_container.size(); - std::vector<indexing_structure> tuple_of_indexes(number_of_particles); + std::get<1>(tuple_of_indexes[part]) = part; + std::get<0>(tuple_of_indexes[part]) = + scalfmm::index::get_morton_index(particle_container.at(part).position(), box, level); + } + if(!data_already_sorted) + { + std::sort(std::begin(tuple_of_indexes), std::end(tuple_of_indexes), + [](auto& a, auto& b) { return std::get<0>(a) < std::get<0>(b); }); + } + return tuple_of_indexes; + } - for(std::size_t part = 0; part < number_of_particles; ++part) - { - std::get<1>(tuple_of_indexes[part]) = part; - std::get<0>(tuple_of_indexes[part]) = - scalfmm::index::get_morton_index(particle_container.at(part).position(), box, level); - } - if(!data_already_sorted) - { - std::sort(std::begin(tuple_of_indexes), std::end(tuple_of_indexes), - [](auto& a, auto& b) { return std::get<0>(a) < std::get<0>(b); }); - } - return tuple_of_indexes; + /** + * @brief sort a particles container + * + * Partial sort the particle_container according to the Morton index of the + * particle. The morton index is built at level level. At the end, the + * particle_container contains the ordered particles + * + * @tparam BoxType the type of the box + * @tparam ContainerType the type of the particles container + * @tparam Int the type of an integer + * @param[in] box the box containing all the particles inside the + * container. + * @param[in] level the level of the d-tree to compute the morton index + * @param[inout] particle_container the particles container + */ + template<class BoxType, class ParticleType, typename Int> + inline auto sort_container(const BoxType& box, const Int& level, + scalfmm::container::particle_container<ParticleType>*& particle_container) -> void + { + using ContainerType = scalfmm::container::particle_container<ParticleType>; + auto perm = get_morton_permutation(box, level, *particle_container); + const auto number_of_particles = particle_container->size(); + ContainerType* ordered_container = new ContainerType(number_of_particles); + for(std::size_t i = 0; i < number_of_particles; ++i) + { + ordered_container->insert_particle(i, particle_container->particle(std::get<1>(perm[i]))); } + delete particle_container; + particle_container = ordered_container; + } - /// - /// \brief sort a particles container - /// - /// Partial sort the particle_container according to the Morton index of the - /// particle. The morton index is built at level level. At the end, the - /// particle_container contains the ordered particles - /// - /// - /// \tparam BOX_T the type of the box - /// \tparam CONTAINER_T the type of the particles container - /// \tparam Int the type of an integer - /// - /// \param[in] box the box containing all the particles inside the - /// container. - /// - /// \param[in] level the level of the d-tree to compute the morton index - /// - /// \param[inout] particle_container the particles container - /// - /// - template<class BOX_T, class ParticleT, typename Int> - auto inline sort_container(const BOX_T& box, const Int& level, - scalfmm::container::particle_container<ParticleT>*& particle_container) -> void + /** + * @brief + * + * @tparam BoxType + * @tparam ParticleType + * @tparam Int + * @param box + * @param level + * @param particle_container + */ + template<class BoxType, class ParticleType, typename Int> + inline auto sort_container(const BoxType& box, const Int& level, + scalfmm::container::particle_container<ParticleType>& particle_container) -> void + { + using ContainerType = scalfmm::container::particle_container<ParticleType>; + auto perm = get_morton_permutation(box, level, particle_container); + const auto number_of_particles = particle_container.size(); + ContainerType tmp_container(number_of_particles); + // for(std::size_t i = 0; i < number_of_particles; ++i) + // { + // particle_container.insert_particle(i, particle_container.particle(std::get<1>(perm[i]))); + // } + std::copy(particle_container.begin(), particle_container.end(), tmp_container.begin()); + for(std::size_t i = 0; i < number_of_particles; ++i) { - using CONTAINER_T = scalfmm::container::particle_container<ParticleT>; - auto perm = get_morton_permutation(box, level, *particle_container); - const auto number_of_particles = particle_container->size(); - CONTAINER_T* ordered_container = new CONTAINER_T(number_of_particles); - for(std::size_t i = 0; i < number_of_particles; ++i) - { - ordered_container->insert_particle(i, particle_container->particle(std::get<1>(perm[i]))); - } - delete particle_container; - particle_container = ordered_container; + particle_container.insert_particle(i, tmp_container.particle(std::get<1>(perm[i]))); } - template<class BOX_T, class ParticleT, typename Int> - auto inline sort_container(const BOX_T& box, const Int& level, - scalfmm::container::particle_container<ParticleT>& particle_container) -> void + } + + /** + * @brief + * + * @tparam BoxType + * @tparam ContainerType + * @tparam Int + * @param box + * @param level + * @param particle_container + */ + template<class BoxType, class ContainerType, typename Int> + inline auto sort_container(const BoxType& box, const Int& level, ContainerType& particle_container) -> void + { + auto perm = get_morton_permutation(box, level, particle_container); + const auto number_of_particles = particle_container.size(); + ContainerType tmp_container(number_of_particles); + std::copy(particle_container.begin(), particle_container.end(), tmp_container.begin()); + for(std::size_t i = 0; i < number_of_particles; ++i) { - using CONTAINER_T = scalfmm::container::particle_container<ParticleT>; - auto perm = get_morton_permutation(box, level, particle_container); - const auto number_of_particles = particle_container.size(); - CONTAINER_T tmp_container(number_of_particles); - // for(std::size_t i = 0; i < number_of_particles; ++i) - // { - // particle_container.insert_particle(i, particle_container.particle(std::get<1>(perm[i]))); - // } - std::copy(particle_container.begin(), particle_container.end(), tmp_container.begin()); - for(std::size_t i = 0; i < number_of_particles; ++i) - { - particle_container.insert_particle(i, tmp_container.particle(std::get<1>(perm[i]))); - } + particle_container[i] = tmp_container[std::get<1>(perm[i])]; } - template<class BOX_T, class CONTAINER_T, typename Int> - auto inline sort_container(const BOX_T& box, const Int& level, CONTAINER_T& particle_container) -> void + } + /// \brief sort a vector particles + /// + /// Sort the vector of particles according to the Morton index of the + /// particle. The morton index is built at the maximum level depending of the dimension. + /// This level is int( sizeof(morton_type) / dimension). + /// The number of components (input + outputs values) associated to a particle is + /// array.size()/nbParticles - dimension. The type of all components is the same (float or double). + /// + /// \tparam BOX_T the type of the box + /// \tparam MatrixPos The type of Matrix containig the particles + /// \tparam VectorInt the type of the vector of permutation + /// \tparam VectorMorton the type of the element of Morton vector + /// + /// \param[in] box the box containing all the particles inside the + /// container. + /// + /// \param[in] nbParticles the number of particles stored in array. + /// + /// \param[in] pos the 2d-array of particles position (size nb_particles<dimension) + /// \param[inout] permutation the vector of permutation to apply on pos in order to have a sorted array + /// \param[out] morton the morton indexes of the particles(no sorted) + /// \param[in] level The level used to compute the Morton index of a particle + /// + /// + template<typename BoxType, typename MatrixPos, typename VectorInt, typename VectorMorton> + void morton_sort(const BoxType& box, MatrixPos const& pos, VectorInt& permutation, VectorMorton& morton, int level) + { + using point_type = typename BoxType::position_type; + constexpr std::size_t dimension = point_type::dimension; + using morton_type = typename VectorMorton::value_type; + // + struct PARTICLE_AT { - auto perm = get_morton_permutation(box, level, particle_container); - const auto number_of_particles = particle_container.size(); - CONTAINER_T tmp_container(number_of_particles); - std::copy(particle_container.begin(), particle_container.end(), tmp_container.begin()); - for(std::size_t i = 0; i < number_of_particles; ++i) + // + morton_type m{}; //< The morton index + int index{}; //< The old position + const morton_type& getMortonIndex() { return m; } + }; + + // meta::td<VectorInt> u; + // meta::td<VectorMorton> b; + + const int nbParticles = permutation.size(); + // const std::size_t max_level = sizeof(typename PARTICLE_AT::morton_type) * 8 / dimension - 1; + std::vector<PARTICLE_AT> vect(nbParticles); + point_type G; + for(int i = 0; i < nbParticles; ++i) + { + auto xyz = xt::row(pos, i); + for(int j = 0; j < dimension; ++j) { - particle_container[i] = tmp_container[std::get<1>(perm[i])]; + G[j] = xyz[j]; } + vect[i].index = i; // Pour le fortran + vect[i].m = scalfmm::index::get_morton_index(G, box, level); } + std::sort(vect.begin(), vect.end(), + [&](PARTICLE_AT& a, PARTICLE_AT& b) { return (a.getMortonIndex() < b.getMortonIndex()); }); - /// \brief sort a vector particles - /// - /// Sort the vector of particles according to the Morton index of the - /// particle. The morton index is built at the maximum level depending of the dimension. - /// This level is int( sizeof(morton_type) / dimension). - /// The number of components (input + outputs values) associated to a particle is - /// array.size()/nbParticles - dimension. The type of all components is the same (float or double). - /// - /// \tparam BOX_T the type of the box - /// \tparam Vector_T the type of the vector of particles - /// \tparam Int the type of an integer - /// - /// \param[in] box the box containing all the particles inside the - /// container. - /// - /// \param[in] nbParticles the number of particles stored in array. - /// - /// \param[inout] array the array of data (particles = position+inputs_values+outputs_values) - /// - /// - template<class BOX_T, class VECTOR_T> //, class array_type> - void sort_raw_array_with_morton_index(const BOX_T& box, const std::size_t& nbParticles, VECTOR_T& array) + for(int i = 0; i < nbParticles; ++i) { - const int nb_val = array.size() / nbParticles; - - using points_type = typename BOX_T::position_type; - using morton_type = std::size_t; - using value_type = typename VECTOR_T::value_type; - value_type* tmp_array = new value_type[array.size()]; - std::copy(array.begin(), array.end(), tmp_array); - constexpr static const std::size_t dimension = points_type::dimension; - // - const std::size_t max_level = sizeof(morton_type) * 8 / dimension - 1; - using pair_type = std::pair<morton_type, int>; - std::vector<pair_type> tosort(nbParticles); + permutation[i] = vect[i].index; + morton[i] = vect[i].m; + } + // std::vector<int> permback(3 * permutation.size(), -1); + // int idx{0}; + // for(int i = 0; i < permutation.size(); ++i, idx += 3) + // { + // auto index = permutation[i] - 1; + // permback[3 * index] = idx; + // permback[3 * index + 1] = idx + 1; + // permback[3 * index + 2] = idx + 2; + // } + // std::cout << "pb = [ " << permback[0]; + + // for(int i = 1; i < permback.size(); ++i) + // { + // std::cout << ", " << permback[i]; + // } + // std::cout << "]\n"; + + // std::cout << "p = [ " << permutation[0]; + + // for(int i = 1; i < permutation.size(); ++i) + // { + // std::cout << ", " << permutation[i]; + // } + // std::cout << "]\n"; + } + /// \brief sort a vector particles + /// + /// Sort the vector of particles according to the Morton index of the + /// particle. The morton index is built at the maximum level depending of the dimension. + /// This level is int( sizeof(morton_type) / dimension). + /// The number of components (input + outputs values) associated to a particle is + /// array.size()/nbParticles - dimension. The type of all components is the same (float or double). + /// + /// \tparam BoxType the type of the box + /// \tparam VectorType the type of the vector of particles + /// \tparam Int the type of an integer + /// + /// \param[in] box the box containing all the particles inside the + /// container. + /// + /// \param[in] nbParticles the number of particles stored in array. + /// + /// \param[inout] array the array of data (particles = position+inputs_values+outputs_values) + /// + /// + template<class BoxType, class VectorType> //, class array_type> + void sort_raw_array_with_morton_index(const BoxType& box, const std::size_t& nbParticles, VectorType& array) + { + const int nb_val = array.size() / nbParticles; + + using points_type = typename BoxType::position_type; + using morton_type = std::size_t; + using value_type = typename VectorType::value_type; + value_type* tmp_array = new value_type[array.size()]; + std::copy(array.begin(), array.end(), tmp_array); + constexpr static const std::size_t dimension = points_type::dimension; + // + const std::size_t max_level = 2; //sizeof(morton_type) * 8 / dimension - 1; + using pair_type = std::pair<morton_type, int>; + std::vector<pair_type> tosort(nbParticles); #pragma omp parallel for shared(tosort, nbParticles, box, max_level, array) - for(std::size_t i = 0; i < nbParticles; ++i) - { - points_type pos(&(array[i * nb_val])); - tosort[i].first = scalfmm::index::get_morton_index(pos, box, max_level); - tosort[i].second = i; - } + for(std::size_t i = 0; i < nbParticles; ++i) + { + points_type pos(&(array[i * nb_val])); + tosort[i].first = scalfmm::index::get_morton_index(pos, box, max_level); + tosort[i].second = i; + } - std::sort(tosort.begin(), tosort.end(), [&](pair_type& a, pair_type& b) { return (a.first > b.first); }); + std::sort(tosort.begin(), tosort.end(), [&](pair_type& a, pair_type& b) { return (a.first > b.first); }); - // - // We fill the sorted array + // + // We fill the sorted array #pragma omp parallel for shared(tosort, array, tmp_array) - for(std::size_t i = 0; i < nbParticles; ++i) - { - auto start = tmp_array + nb_val * tosort[i].second; - std::copy(start, start + nb_val, &(array[i * nb_val])); - } + for(std::size_t i = 0; i < nbParticles; ++i) + { + auto start = tmp_array + nb_val * tosort[i].second; + std::copy(start, start + nb_val, &(array[i * nb_val])); } + } - /// - /// \brief Print the position of the particles in the tree and in those in vector nodes - /// - /// Print the position of the particles in the tree and in those in vector nodes. We also - /// \param tree the tree containing the particles. - /// \param[i] perm permutation obtain after sorting the particles at the leaf level. - /// \param nodes a sorted vector of particles - /// - template<typename Tree_type, typename Permutation_type, typename Vector_type> - void check_positions(Tree_type& tree, Permutation_type const& perm, Vector_type const& nodes) - { - /// Set the fmm forces un vector the force structure - /// - auto box = tree.box(); - auto leaf_level = tree.height() - 1; - - std::size_t idx = 0; - scalfmm::component::for_each_leaf( - std::begin(tree), std::end(tree), - [&nodes, &perm, &idx, leaf_level, box](auto& leaf) + /** + * @brief Print the position of the particles in the tree and in those in vector nodes + * + * @tparam TreeType + * @tparam Permutation_type + * @tparam VectorType + * @param tree the tree containing the particles. + * @param perm permutation obtain after sorting the particles at the leaf level. + * @param nodes a sorted vector of particles + */ + template<typename TreeType, typename Permutation_type, typename VectorType> + inline auto check_positions(TreeType& tree, Permutation_type const& perm, VectorType const& nodes) -> void + { + // Set the fmm forces un vector the force structure + auto box = tree.box(); + auto leaf_level = tree.height() - 1; + + std::size_t idx = 0; + scalfmm::component::for_each_leaf( + std::begin(tree), std::end(tree), + [&nodes, &perm, &idx, leaf_level, box](auto& leaf) + { + std::cout << " -------------- leaf " << leaf.index() << " -------------- " << std::endl; + for(auto const p_tuple_ref: leaf) { - std::cout << " -------------- leaf " << leaf.index() << " -------------- " << std::endl; - for(auto const p_tuple_ref: leaf) - { - // We construct a particle type for classical acces - const auto part = typename Tree_type::leaf_type::const_proxy_type(p_tuple_ref); - auto p = part.position(); - std::cout << " tree pos " << p << " morton " - << scalfmm::index::get_morton_index(p, box, leaf_level) << " nodes " << idx << " " - << nodes[idx] << " perm: " << std::get<1>(perm[idx]) << " morton " - << std::get<0>(perm[idx]) << std::endl; - ++idx; - } - }); - } - } // namespace utils -} // namespace scalfmm + // We construct a particle type for classical acces + const auto part = typename TreeType::leaf_type::const_proxy_type(p_tuple_ref); + auto p = part.position(); + std::cout << " tree pos " << p << " morton " << scalfmm::index::get_morton_index(p, box, leaf_level) + << " nodes " << idx << " " << nodes[idx] << " perm: " << std::get<1>(perm[idx]) + << " morton " << std::get<0>(perm[idx]) << std::endl; + ++idx; + } + }); + } +} // namespace scalfmm::utils #endif diff --git a/include/scalfmm/utils/source_target.hpp b/include/scalfmm/utils/source_target.hpp index b5f990a5a1c384776c50d92dbbaaa206afc1af85..91feebaca1f4556dbd52ae10d31c0998fa007d57 100644 --- a/include/scalfmm/utils/source_target.hpp +++ b/include/scalfmm/utils/source_target.hpp @@ -1,3 +1,7 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/utils/source_target.hpp +// -------------------------------- #ifndef SCALFMM_UTILS_SOURCE_TARGET_HPP #define SCALFMM_UTILS_SOURCE_TARGET_HPP @@ -6,18 +10,18 @@ namespace scalfmm::utils { - /** * @brief Construct the geometric bounding box of box1 and box2 * + * @tparam BoxType * @param[in] box1 first box containing particles (sources) * @param[in] box2 second box containing particles (targets) * @return the box containing all particles (sources and targets) */ - template<typename box_type> - inline auto bounding_box(box_type const& box1, box_type const& box2) -> box_type + template<typename BoxType> + inline auto bounding_box(BoxType const& box1, BoxType const& box2) -> BoxType { - using position_type = typename box_type::position_type; + using position_type = typename BoxType::position_type; using value_type = typename position_type::value_type; constexpr static const std::size_t dimension = position_type::dimension; @@ -36,21 +40,22 @@ namespace scalfmm::utils value_type length{diff.max()}; c2 = c1 + length; - return box_type(c1, c2); + return BoxType(c1, c2); } /** * @brief Merge two containers of particles * + * @tparam ContainerType * @param container1 first container * @param container2 second container * @return the merge of the two containers */ - template<typename Container_type> - auto merge(Container_type const& container1, Container_type const& container2) -> Container_type + template<typename ContainerType> + inline auto merge(ContainerType const& container1, ContainerType const& container2) -> ContainerType { auto size = container1.size() + container2.size(); - Container_type output(size); + ContainerType output(size); std::size_t idx = 0; for(std::size_t i = 0; i < container1.size(); ++i, ++idx) { @@ -63,10 +68,23 @@ namespace scalfmm::utils return output; } - template<typename Treetype, typename Index_type> - inline auto find_cell_group(Treetype const& tree, Index_type index, int& level) + /** + * @brief + * + * @todo + * + * @tparam TreeType + * @tparam IndexType + * @param tree + * @param index + * @param level + * @return auto + */ + template<typename TreeType, typename IndexType> + inline auto find_cell_group(TreeType const& tree, IndexType index, int& level) { std::size_t id_group{0}; + throw("find_cell_group: not implemented yet !"); } } // namespace scalfmm::utils diff --git a/include/scalfmm/utils/static_assert_as_exception.hpp b/include/scalfmm/utils/static_assert_as_exception.hpp index 3ac9f22aae0c600e3abc1422e320c27e5a70208b..d6350504af0749ff8b27d4812fdd39c674f86b79 100644 --- a/include/scalfmm/utils/static_assert_as_exception.hpp +++ b/include/scalfmm/utils/static_assert_as_exception.hpp @@ -1,3 +1,7 @@ +// -------------------------------- +// See LICENCE file at project root +// File : scalfmm/utils/static_assert_as_exception.hpp +// -------------------------------- #ifndef SCALFMM_UTILS_STATIC_ASSERT_AS_EXCEPTION_H #define SCALFMM_UTILS_STATIC_ASSERT_AS_EXCEPTION_H @@ -13,22 +17,41 @@ namespace scalfmm::test { + /** + * @brief + * + */ struct exceptionalized_static_assert : std::logic_error { exceptionalized_static_assert(char const* what) - : std::logic_error(what){}; + : std::logic_error(what) {}; virtual ~exceptionalized_static_assert() noexcept {} }; + /** + * @brief + * + * @tparam Cond + */ template<bool Cond> struct exceptionalize_static_assert; + /** + * @brief + * + * @tparam + */ template<> struct exceptionalize_static_assert<true> { explicit exceptionalize_static_assert(char const* reason) { (void)reason; } }; + /** + * @brief + * + * @tparam + */ template<> struct exceptionalize_static_assert<false> { @@ -47,7 +70,7 @@ namespace scalfmm::test struct _1_test : scalfmm::test::exceptionalize_static_assert<cond> \ { \ _1_test() \ - : scalfmm::test::exceptionalize_static_assert<cond>(gripe){}; \ + : scalfmm::test::exceptionalize_static_assert<cond>(gripe) {}; \ }; \ _1_test _2_test diff --git a/include/scalfmm/utils/tensor.hpp b/include/scalfmm/utils/tensor.hpp index e9713b9fa19eaa67337f27f75ec16d5035cbee9d..1c09701b16b1b8dd21d316eea64b44c208d64bf5 100644 --- a/include/scalfmm/utils/tensor.hpp +++ b/include/scalfmm/utils/tensor.hpp @@ -1,20 +1,10 @@ // -------------------------------- // See LICENCE file at project root -// File : utils/tensor.hpp +// File : include/scalfmm/utils/tensor.hpp // -------------------------------- #ifndef SCALFMM_UTILS_TENSOR_HPP #define SCALFMM_UTILS_TENSOR_HPP -#include <scalfmm/meta/traits.hpp> -#include <utility> // for std::forward -#include <iterator> // std::begin -#include <numeric> // for std::iota -#include <type_traits> -#include <stdexcept> -#include <cstddef> -#include <tuple> -#include <complex> - #include "scalfmm/container/point.hpp" #include "scalfmm/utils/math.hpp" #include <scalfmm/meta/traits.hpp> @@ -42,6 +32,16 @@ #include <xtensor/xtensor_forward.hpp> #include <xtensor/xview.hpp> +#include <complex> +#include <cstddef> +#include <iterator> // std::begin +#include <numeric> // for std::iota +#include <scalfmm/meta/traits.hpp> +#include <stdexcept> +#include <tuple> +#include <type_traits> +#include <utility> // for std::forward + #ifndef XTENSOR_SELECT_ALIGN #define XTENSOR_SELECT_ALIGN(T) (XTENSOR_DEFAULT_ALIGNMENT != 0 ? XTENSOR_DEFAULT_ALIGNMENT : alignof(T)) #endif @@ -52,15 +52,37 @@ namespace scalfmm::tensor { + /** + * @brief + * + */ struct row { }; + + /** + * @brief + * + */ struct column { }; namespace details { + /** + * @brief Get the view object + * + * @tparam Tensor + * @tparam Range + * @tparam Is + * @param t + * @param indice + * @param range + * @param r + * @param seq + * @return constexpr auto + */ template<typename Tensor, typename Range, std::size_t... Is> [[nodiscard]] inline constexpr auto get_view(Tensor&& t, std::size_t indice, Range&& range, row r, std::index_sequence<Is...> seq) @@ -68,6 +90,19 @@ namespace scalfmm::tensor return xt::view(std::forward<Tensor>(t), indice, meta::id<Is>(std::forward<Range>(range))...); } + /** + * @brief Get the view object + * + * @tparam Tensor + * @tparam Range + * @tparam Is + * @param t + * @param indice + * @param range + * @param c + * @param seq + * @return constexpr auto + */ template<typename Tensor, typename Range, std::size_t... Is> [[nodiscard]] inline constexpr auto get_view(Tensor&& t, std::size_t indice, Range&& range, column c, std::index_sequence<Is...> seq) @@ -75,24 +110,65 @@ namespace scalfmm::tensor return xt::view(std::forward<Tensor>(t), meta::id<Is>(std::forward<Range>(range))..., indice); } + /** + * @brief Get the view object + * + * @tparam Tensor + * @tparam Slice + * @tparam Is + * @param t + * @param s + * @param seq + * @return constexpr auto + */ template<typename Tensor, typename Slice, std::size_t... Is> [[nodiscard]] inline constexpr auto get_view(Tensor&& t, Slice&& s, std::index_sequence<Is...> seq) { return xt::view(std::forward<Tensor>(t), meta::id<Is>(std::forward<Slice>(s))...); } + /** + * @brief Get the inner view object + * + * @tparam Tensor + * @tparam Slice + * @tparam Is + * @param t + * @param s + * @param seq + * @return constexpr auto + */ template<typename Tensor, typename Slice, std::size_t... Is> [[nodiscard]] inline constexpr auto get_inner_view(Tensor&& t, Slice&& s, std::index_sequence<Is...> seq) { return xt::view(std::forward<Tensor>(t), xt::all(), meta::id<Is>(std::forward<Slice>(s))...); } + /** + * @brief + * + * @tparam Tensor + * @tparam Is + * @param t + * @param indice + * @param seq + * @return constexpr auto + */ template<typename Tensor, std::size_t... Is> [[nodiscard]] inline constexpr auto gather(Tensor&& t, std::size_t indice, std::index_sequence<Is...> seq) { return xt::view(std::forward<Tensor>(t), xt::all(), indice, meta::id<Is>(xt::all())...); } + /** + * @brief + * + * @tparam Expression + * @tparam Is + * @param e + * @param s + * @return constexpr auto + */ template<typename Expression, std::size_t... Is> [[nodiscard]] inline constexpr auto generate_meshgrid(Expression const& e, std::index_sequence<Is...> s) { @@ -101,18 +177,51 @@ namespace scalfmm::tensor } // namespace details + /** + * @brief Get the view object + * + * @tparam N + * @tparam Tensor + * @tparam Slice + * @param t + * @param s + * @return constexpr auto + */ template<std::size_t N, typename Tensor, typename Slice> [[nodiscard]] inline constexpr auto get_view(Tensor&& t, Slice&& s) { return details::get_view(std::forward<Tensor>(t), std::forward<Slice>(s), std::make_index_sequence<N>{}); } + /** + * @brief Get the inner view object + * + * @tparam N + * @tparam Tensor + * @tparam Slice + * @param t + * @param s + * @return constexpr auto + */ template<std::size_t N, typename Tensor, typename Slice> [[nodiscard]] inline constexpr auto get_inner_view(Tensor&& t, Slice&& s) { return details::get_inner_view(std::forward<Tensor>(t), std::forward<Slice>(s), std::make_index_sequence<N>{}); } + /** + * @brief Get the view object + * + * @tparam N + * @tparam Tensor + * @tparam Range + * @tparam Tag + * @param t + * @param indice + * @param range + * @param tag + * @return constexpr auto + */ template<std::size_t N, typename Tensor, typename Range, typename Tag> [[nodiscard]] inline constexpr auto get_view(Tensor&& t, std::size_t indice, Range&& range, Tag tag) { @@ -120,6 +229,15 @@ namespace scalfmm::tensor std::make_index_sequence<N - 1>{}); } + /** + * @brief + * + * @tparam N + * @tparam Tensor + * @param t + * @param indice + * @return constexpr auto + */ template<std::size_t N, typename Tensor> [[nodiscard]] inline constexpr auto gather(Tensor&& t, std::size_t indice) { @@ -133,12 +251,30 @@ namespace scalfmm::tensor } } + /** + * @brief + * + * @tparam Dimension + * @tparam Expression + * @param e + * @return constexpr auto + */ template<std::size_t Dimension, typename Expression> [[nodiscard]] inline constexpr auto generate_meshgrid(Expression const& e) { return details::generate_meshgrid(e, std::make_index_sequence<Dimension>{}); } + /** + * @brief + * + * @tparam Dimension + * @tparam Expression + * @tparam Repeats + * @param e + * @param r + * @return auto + */ template<std::size_t Dimension, typename Expression, typename Repeats> [[nodiscard]] inline auto repeat(Expression&& e, Repeats&& r) { @@ -151,6 +287,15 @@ namespace scalfmm::tensor return t; } + /** + * @brief + * + * @tparam Tensor + * @param t + * @param source + * @param destination + * @return auto + */ template<typename Tensor> [[nodiscard]] inline auto moveaxis(Tensor&& t, std::size_t source, std::size_t destination) { @@ -184,6 +329,14 @@ namespace scalfmm::tensor return xt::transpose(std::forward<Tensor>(t), perm); } + /** + * @brief + * + * @tparam Tensor + * @param t + * @param axe + * @return auto + */ template<typename Tensor> [[nodiscard]] inline auto unfold(Tensor&& t, std::size_t axe) { @@ -209,6 +362,16 @@ namespace scalfmm::tensor {slice, leading_dim}); } + /** + * @brief + * + * @tparam Matrix + * @tparam Shape + * @param m + * @param axe + * @param shape + * @return auto + */ template<typename Matrix, typename Shape> [[nodiscard]] inline auto fold(Matrix&& m, std::size_t axe, Shape shape) { @@ -244,6 +407,17 @@ namespace scalfmm::tensor std::forward<result_of_reshaped>(xt::reshape_view(std::forward<Matrix>(m), std::move(new_shape))), 0, axe); } + /** + * @brief + * + * @tparam ValueType + * @tparam ScalarType + * @param accumulate + * @param t + * @param k + * @param scalar + * @return auto + */ template<typename ValueType, typename ScalarType> inline auto product(xt::xarray<ValueType>& accumulate, xt::xarray<ValueType> const& t, xt::xarray<ValueType> const& k, ScalarType scalar) @@ -255,30 +429,49 @@ namespace scalfmm::tensor const std::size_t vec_size = size - size % inc; value_type scale_cp(scalar); simd_type splat(scale_cp); - for(std::size_t i{0}; i<vec_size; i+=inc) + for(std::size_t i{0}; i < vec_size; i += inc) { auto t_ = xsimd::load_aligned(&t.data()[i]); auto k_ = xsimd::load_aligned(&k.data()[i]); auto acc_ = xsimd::load_aligned(&accumulate.data()[i]); - auto times = k_*splat; + auto times = k_ * splat; auto tmp = xsimd::fma(t_, times, acc_); //acc_ += t_ * k_ * splat; tmp.store_aligned(&accumulate.data()[i]); } - for(std::size_t i{vec_size}; i<size; ++i) + for(std::size_t i{vec_size}; i < size; ++i) { - accumulate.data()[i] += t.data()[i]*k.data()[i]*scale_cp; + accumulate.data()[i] += t.data()[i] * k.data()[i] * scale_cp; //auto tmp = accumulate.data()[i]; //accumulate.data()[i] = xsimd::fma(t.data()[i],k.data()[i]*scale_cp, tmp); } } + /** + * @brief + * + * @tparam T + * @param a + * @param b + * @return T + */ template<typename T> inline T simd_complex_prod(T a, T b) { - return T(a.real()*b.real() - a.imag()*b.imag(), a.real()*b.imag() + a.imag()*b.real()); + return T(a.real() * b.real() - a.imag() * b.imag(), a.real() * b.imag() + a.imag() * b.real()); } + /** + * @brief + * + * @tparam ValueType + * @tparam ScalarType + * @param accumulate + * @param t + * @param k + * @param scalar + * @return auto + */ template<typename ValueType, typename ScalarType> inline auto product(xt::xarray<std::complex<ValueType>>& accumulate, xt::xarray<std::complex<ValueType>> const& t, xt::xarray<std::complex<ValueType>> const& k, ScalarType scalar) @@ -304,7 +497,7 @@ namespace scalfmm::tensor acc_ += tmp2; acc_.store_aligned(&ptr_acc[i]); } - for(std::size_t i{vec_size}; i<size; ++i) + for(std::size_t i{vec_size}; i < size; ++i) { ptr_acc[i] += ptr_t[i] * ptr_k[i] * scale_cp; //auto tmp = accumulate.data()[i]; @@ -312,7 +505,14 @@ namespace scalfmm::tensor } } - // Generate a tensor of points from the interpolator roots + /** + * @brief Generate a tensor of points from the interpolator roots. + * + * @tparam Dimension + * @tparam RootsExpression + * @param roots + * @return auto + */ template<std::size_t Dimension, typename RootsExpression> inline auto generate_grid_of_points(RootsExpression const& roots) { @@ -337,14 +537,14 @@ namespace scalfmm::tensor // i.e p(x,y,z)[i] <- x[i], y[i], z[i] for(std::size_t i = 0; i < nnodes; ++i) { - X_points_flatten_views[i] = std::apply( - [&i](auto&&... xs) { - return container::point<value_type, dimension>{std::forward<decltype(xs)>(xs)[i]...}; - }, - X_flatten_views); + X_points_flatten_views[i] = + std::apply([&i](auto&&... xs) + { return container::point<value_type, dimension>{std::forward<decltype(xs)>(xs)[i]...}; }, + X_flatten_views); } return X_points; } + /** * @brief Perform matrix-vector product * @@ -378,13 +578,15 @@ namespace scalfmm::tensor static_cast<xt::blas_index_t>(1), (acc ? value_type(1.) : value_type(0.)), locals.data() + locals.data_offset(), static_cast<xt::blas_index_t>(1)); } + /** * @brief Perform matrix-vector product * * Perform the matrix-matrix product with gemm * C := alpha*A*B + beta*C * - * @tparam TensorType + * @tparam TensorType1 + * @tparam TensorType2 * @tparam InteractionMatrixType * @tparam ValueType * @param multipoles the B matrix @@ -393,10 +595,11 @@ namespace scalfmm::tensor * @param scale_factor the scale factor alpha * @param acc if true beta = 1 otherwise 0. */ - template<typename TensorType, typename TensorType1, typename InteractionMatrixType, typename value_type> - inline auto blas3_product(TensorType& B, TensorType1& C, InteractionMatrixType const& A, int nb_mult, - value_type scale_factor, bool acc, bool transA = false) -> void + template<typename TensorType1, typename TensorType2, typename InteractionMatrixType, typename ValueType> + inline auto blas3_product(TensorType1& B, TensorType2& C, InteractionMatrixType const& A, int nb_mult, + ValueType scale_factor, bool acc, bool transA = false) -> void { + using value_type = ValueType; // see // https://netlib.org/lapack/explore-html/d1/d54/group__double__blas__level3_gaeda3cbd99c8fb834a60a6412878226e1.html auto& shapeA = A.shape(); @@ -415,8 +618,8 @@ namespace scalfmm::tensor // << A.data_offset() << std::endl; // auto LDA = xt::get_leading_stride(A); // std::exit(-1); - // meta::td<TensorType> u; - // meta::td<TensorType1> u1; + // meta::td<TensorType1> u; + // meta::td<TensorType2> u1; // C = xt::linalg::dot(A, B); cxxblas::gemm<xt::blas_index_t>( xt::get_blas_storage_order(A), // locals.at(n) diff --git a/python_interface/CMakeLists.txt b/python_interface/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1de93a0cf747d1ab7874876e54a8f50bfe3e6e2d --- /dev/null +++ b/python_interface/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# Binding (pybind11) +# ------------------- + + +# List of source files +set(source_files + pyfmm.cpp +) +set(CMAKE_CXX_STANDARD 20) + +foreach(exec ${source_files}) + get_filename_component(execname ${exec} NAME_WE) + + list(APPEND ${CMAKE_PROJECT_NAME}_PYTHON_INTERFACE_TARGETS ${execname}) + pybind11_add_module(${execname} EXCLUDE_FROM_ALL ${exec}) + target_link_libraries(${execname} PRIVATE scalfmm) + target_include_directories(${execname} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + set_target_properties(${execname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BUILD_TYPE}) + + # Link-Time Optimization (LTO) + # The following option allows the compiler to determine the number of jobs to run during the Link-Time Optimization (LTO) phase automatically based on the available hardware resources + # if(CMAKE_BUILD_TYPE MATCHES Release) + # target_compile_options(${execname} PRIVATE -flto=auto) + # endif() +endforeach(exec) + +add_custom_target(python_interface DEPENDS ${${CMAKE_PROJECT_NAME}_PYTHON_INTERFACE_TARGETS}) +set_target_properties(python_interface PROPERTIES EXCLUDE_FROM_ALL True) \ No newline at end of file diff --git a/python_interface/README.md b/python_interface/README.md new file mode 100644 index 0000000000000000000000000000000000000000..90ad0472aa52fb866e02f5873336639f4253f20e --- /dev/null +++ b/python_interface/README.md @@ -0,0 +1,30 @@ +# Python binding for ScalFMM (experimental) + +To build the python module: +``` bash +cd ScalFMM +guix time-machine -C python_interface/guix-tools/channels.scm -- shell --pure -m python_interface/guix-tools/manifest.scm -- bash --norc +cmake -B build -DBLA_VENDOR=OpenBLAS -Dscalfmm_BUILD_PYFMM=ON +cmake --build build --target pyfmm +``` + +Do not forget to position the `PYTHONPATH` environment variable: +``` bash +export PYTHONPATH=build/python_interface/ +``` + +Finally, you can run the tutorial (this is a translation of `examples/tutorial.cpp` which is a simple minimalist 2D toy example): + - Run the sequential version (example with 50000) +``` bash +python3 python_interface/tutorial.py --group-size 1 --order 5 --tree-height 3 --random --number-particles 50000 --center 0 0 --width 2 -vv +``` + +``` bash +python3 python_interface/tutorial.py --group-size 1 --order 5 --tree-height 3 --input_file ../data/units/test_2d_ref.fma -vv +``` + - Run the OpenMP version (example with 50000) +``` bash +export OMP_MAX_TASK_PRIORITY=11 +python3 python_interface/tutorial.py --group-size 1 --order 5 --tree-height 3 --number-particles 50000 --random --center 0 0 --width 2 -vv --open-mp +``` + diff --git a/python_interface/fmmTools.py b/python_interface/fmmTools.py new file mode 100644 index 0000000000000000000000000000000000000000..de7c485573aba31837b700c2549efdcd2aaf44fa --- /dev/null +++ b/python_interface/fmmTools.py @@ -0,0 +1,120 @@ +import os +import sys +import numpy as np +from pyfmm import fmaloader + + + +def fmareader_ascii(filename,verbose=False): + ''' + Read an FMA file of scalfmmm + + Return + particules an array of three objects (position, inputs, ou tputs) + centre of the box + the size of the box + ''' + + # Read the two first line of the header + file = open(filename, "r") + line1 = file.readline() + lline = line1.split() + nval= int(lline[1]) + dimension = int(lline[2]) + nb_inputs = int(lline[3]) + nb_outputs = nval - dimension - nb_inputs + # + # + line2 = file.readline() + lline = line2.split() + npoints = int(lline[0]) + size = float(lline[1])*2.0 + centre = np.zeros(dimension, dtype = float) + for i in range(dimension): + centre[i] = float(lline[i+2]) + file.close() + # Read the particles + particles = np.empty((3), dtype=object) + particles[0] = np.loadtxt(filename, comments='#', skiprows=2, usecols=range(0,dimension)) + + particles[1] = np.loadtxt(filename, comments='#', skiprows=2, usecols=range(dimension,(dimension+nb_inputs))).reshape((npoints,nb_inputs)) + if nb_outputs == 0: + particles[2] = np.zeros((npoints,1),dtype=np.float64) + else: + start = dimension+nb_inputs + particles[2] = np.loadtxt(filename, comments='#', skiprows=2, usecols=range(start,(start+nb_outputs))).reshape(npoints,nb_outputs) + + if (verbose): + print(" dimension: ", dimension, " nb_inputs: ", nb_inputs," nb_outputs: ", nb_outputs) + print(" centre: ", centre," size: ", size) + + return particles,centre,size + +def fmareader_binary(filename,verbose=False): + ''' + Read an FMA file of scalfmmm + + Return + particules an array of three objects (position, inputs, ou tputs) + centre of the box + the size of the box + ''' + loader = fmaloader(name=filename, verbose=verbose) + print("fmareader_binary",filename) + nb_particles = loader.n() + nb_data = loader.nb_data() + dim = loader.dim() + nb_inputs = loader.nb_inputs() + nb_outputs = loader.nb_outputs() + data = loader.read() + particles = np.empty((3), dtype=object) + + particles[0] = data[:,0:dim] + particles[1] = data[:,dim:dim+nb_inputs] + if nb_outputs == 0: + particles[2] = np.zeros((nb_particles,1),dtype=np.float64) + else: + start = dim+nb_inputs + particles[2] = data[:,start:start+nb_outputs] + + centre = loader.centre() + width = loader.width() + return particles,centre,width + +def fmareader(filename,verbose=False): + ''' + Read an FMA file of scalfmmm + + Return + particules an array of three objects (position, inputs, ou tputs) + centre of the box + the size of the box + ''' + # Chemin du fichier + print("read file ",filename) + # Extraction de l'extension + extension = os.path.splitext(filename) + if extension[-1] == '.fma': + return fmareader_ascii(filename,verbose) + elif extension[-1] == '.bfma': + return fmareader_binary(filename,verbose) + else: + sys.exit("Wrong extention. only .fma (ascii) or .bfma (binary) files are avalaible") + + +######################################################## +if __name__ == '__main__': + filename = "../data/cubeSorted_10_PF.fma" + +# # Extraction de l'extension +# extension = os.path.splitext(filename) + +# # Affichage de l'extension +# print(f"L'extension du fichier est : {extension}") + + particles, centre, size = fmareader(filename) + + print("centre", centre) + print("size", size) + + print(particles) diff --git a/python_interface/guix-tools/channels.scm b/python_interface/guix-tools/channels.scm new file mode 100644 index 0000000000000000000000000000000000000000..c57d8314a2c9d468a709655fd2a196eb82281b52 --- /dev/null +++ b/python_interface/guix-tools/channels.scm @@ -0,0 +1,67 @@ +(list (channel + (name 'guix) + (url "https://git.savannah.gnu.org/git/guix.git") + (branch "master") + (commit + "cf74986eec8ffdcc12506870fb807a1ebb43e6e3") + (introduction + (make-channel-introduction + "9edb3f66fd807b096b48283debdcddccfea34bad" + (openpgp-fingerprint + "BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA")))) + (channel + (name 'guix-hpc-non-free) + (url "https://gitlab.inria.fr/guix-hpc/guix-hpc-non-free.git") + (branch "master") + (commit + "06f813adfe8d9e558c82d0114ead71dde3d59a0d")) + (channel + (name 'guix-hpc) + (url "https://gitlab.inria.fr/guix-hpc/guix-hpc.git") + (branch "master") + (commit + "9275bc4577611d41d2e8e9b995e0693e9f62a7b5")) + (channel + (name 'nonguix) + (url "https://gitlab.com/nonguix/nonguix") + (branch "master") + (commit + "6e864249c2025863e18e42587cb42764a99bec27") + (introduction + (make-channel-introduction + "897c1a470da759236cc11798f4e0a5f7d4d59fbc" + (openpgp-fingerprint + "2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5")))) + (channel + (name 'guix-science-nonfree) + (url "https://codeberg.org/guix-science/guix-science-nonfree.git") + (branch "master") + (commit + "446626ab1ca977b9278f431da8dde9ec8cf36457") + (introduction + (make-channel-introduction + "58661b110325fd5d9b40e6f0177cc486a615817e" + (openpgp-fingerprint + "CA4F 8CF4 37D7 478F DA05 5FD4 4213 7701 1A37 8446")))) + (channel + (name 'guix-science) + (url "https://codeberg.org/guix-science/guix-science.git") + (branch "master") + (commit + "e79f07d6ae81721e62d7cce78378f3ec49ff4efd") + (introduction + (make-channel-introduction + "b1fe5aaff3ab48e798a4cce02f0212bc91f423dc" + (openpgp-fingerprint + "CA4F 8CF4 37D7 478F DA05 5FD4 4213 7701 1A37 8446")))) + (channel + (name 'guix-past) + (url "https://codeberg.org/guix-science/guix-past.git") + (branch "master") + (commit + "f99ada4123de1eadf668d34dac2d726407634549") + (introduction + (make-channel-introduction + "0c119db2ea86a389769f4d2b9c6f5c41c027e336" + (openpgp-fingerprint + "3CE4 6455 8A84 FDC6 9DB4 0CFB 090B 1199 3D9A EBB5"))))) diff --git a/python_interface/guix-tools/manifest.scm b/python_interface/guix-tools/manifest.scm new file mode 100644 index 0000000000000000000000000000000000000000..a11a7daf7a3342ce4ac2d309be1e128da7a7e7b9 --- /dev/null +++ b/python_interface/guix-tools/manifest.scm @@ -0,0 +1,21 @@ +;; What follows is a "manifest" equivalent to the command line you gave. +;; You can store it in a file that you may then pass to any 'guix' command +;; that accepts a '--manifest' (or '-m') option. + +(concatenate-manifests + (list (specifications->manifest + (list "coreutils" + "cmake" + "make" + "ncurses" + "bash" + "openblas" + "pkg-config" + "fftw" + "fftwf" + "pybind11" + "python" + "python-numpy" + "python-colorama")) + (package->development-manifest + (specification->package "scalfmm")))) diff --git a/python_interface/py_interface.hpp b/python_interface/py_interface.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e7ab45d4c1940e232c70846445c2a181411d67e1 --- /dev/null +++ b/python_interface/py_interface.hpp @@ -0,0 +1,115 @@ +#pragma once + +// pybind11 +#include <pybind11/numpy.h> +#include <pybind11/pybind11.h> + +// xtensor +#include <xtensor/xadapt.hpp> +#include <xtensor/xtensor.hpp> + +#include <iostream> +#include <span> +// +#include "scalfmm/meta/utils.hpp" +#include "scalfmm/tree/utils.hpp" +#include "scalfmm/utils/sort.hpp" + +namespace scalfmm::python +{ + namespace py = pybind11; + + template<typename ValueType> + using py_array_t = py::array_t<ValueType, py::array::f_style>; // to enforce column-major storage + + using value_type = double; + using np_array = py_array_t<value_type>; + // + using np_array_64 = py_array_t<double>; + using np_array_32 = py_array_t<float>; + // + using py_vector_int = py::array_t<int>; + + // using matrix_type = xt::xtensor<value_type, 2 xt::layout_type::column_major>; + + namespace to_cpp + { + // template<typename T> + auto to_span(py_array_t<double> const& array) -> std::span<double> + { + // auto a = array.unchecked<1>(); + py::buffer_info buf_info = array.request(); + + return std::span<double>{static_cast<double*>(buf_info.ptr), static_cast<std::size_t>(buf_info.shape[0])}; + } + template<typename T> + auto to_ptr(py_array_t<T> const& array) -> T* + { + py::buffer_info buf_info = array.request(); + return static_cast<T*>(buf_info.ptr); + } + auto to_vector(py_array_t<double> const& array) -> xt::xtensor_pointer<double, 1> + { + // auto a = array.unchecked<1>(); + + py::buffer_info buf_info = array.request(); + std::array<size_t, 1> shape{static_cast<std::size_t>(buf_info.shape[0])}; + // xt::xtensor_pointer<double, 2> a = + return xt::adapt(static_cast<double*>(buf_info.ptr), static_cast<std::size_t>(buf_info.shape[0]), + xt::no_ownership(), shape); + } + auto matrix_to_vector(py_array_t<double> const& array) -> xt::xtensor_pointer<double, 1> + { + // auto a = array.unchecked<1>(); + + py::buffer_info buf_info = array.request(); + std::array<size_t, 1> shape{static_cast<std::size_t>(buf_info.shape[0] * buf_info.shape[1])}; + // xt::xtensor_pointer<double, 2> a = + return xt::adapt(static_cast<double*>(buf_info.ptr), + static_cast<std::size_t>(buf_info.shape[0] * buf_info.shape[1]), xt::no_ownership(), + shape); + } + auto to_matrix(py_array_t<double> const& array) // -> xt::xtensor_pointer<double, 2> + { + // auto a = array.unchecked<1>(); + + py::buffer_info buf_info = array.request(); + std::array<size_t, 2> shape = {static_cast<std::size_t>(buf_info.shape[0]), + static_cast<std::size_t>(buf_info.shape[1])}; + // xt::xtensor_pointer<double, 2> a = + return xt::adapt<xt::layout_type::column_major>(static_cast<double*>(buf_info.ptr), shape[0] * shape[1], + xt::no_ownership(), shape); + } + } // namespace to_cpp + /// @brief construct the Morton index and the permutation + /// + /// @param[in] box the simulation box containing the particles + /// @param[in] pos the Numpy array Nxdimension containing the particles + /// @param[in] level to sort the particles (leaf level) + /// + /// @return the morton index sorted ans the permutation to apply to the particles + /// + template<typename BoxType, typename value_type, typename Int_t> + auto morton_sort(BoxType const& box, py_array_t<value_type> const& pos, const Int_t level) + { + py::buffer_info buf_info = pos.request(); + Int_t N = buf_info.shape[0]; + auto dim = buf_info.shape[1]; + // auto pos_proxy = pos.template unchecked<2>(); + // const value_type* pos_ptr = pos_proxy.data(0); + std::vector<Int_t> permutation(N); + std::vector<std::size_t> morton(N); + + // set the numpy matrix in xtensor (column major) + auto xt_pos = to_cpp::to_matrix(pos); + // build the morton intex and the permutation + utils::morton_sort(box, xt_pos, permutation, morton, std::size_t(level)); + + return std::make_tuple(morton, py_array_t<Int_t>({static_cast<Int_t>(N)}, permutation.data())); + // py_array_t<Int_t>({static_cast<Int_t>(N)}, permutation.data())); + } + void morton2(int i) { std::cout << " coucou py::morton2\n"; } + + void add(int i, int j) { std::cout << i + j; } + +} // namespace scalfmm::python \ No newline at end of file diff --git a/python_interface/pyfmm.cpp b/python_interface/pyfmm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44bd61e74d101bcb20263bb6519649f0390fdeed --- /dev/null +++ b/python_interface/pyfmm.cpp @@ -0,0 +1,650 @@ +#include "scalfmm/algorithms/fmm.hpp" +#include "scalfmm/algorithms/full_direct.hpp" +#include "scalfmm/container/particle.hpp" +#include "scalfmm/container/particle_impl.hpp" +#include "scalfmm/container/point.hpp" +#include "scalfmm/interpolation/interpolation.hpp" +#include "scalfmm/matrix_kernels/laplace.hpp" +#include "scalfmm/operators/fmm_operators.hpp" +#include "scalfmm/tools/fma_loader.hpp" +#include "scalfmm/tree/box.hpp" +#include "scalfmm/tree/for_each.hpp" +#include "scalfmm/tree/group_tree_view.hpp" +#include "scalfmm/tree/leaf_view.hpp" +#include "scalfmm/utils/accurater.hpp" + +#include <pybind11/numpy.h> +#include <pybind11/pybind11.h> +#include <pybind11/stl.h> +#include <pybind11/stl_bind.h> + +#include <iostream> +#include <sstream> +#include <string> +#include <type_traits> +#include <vector> + +#include "py_interface.hpp" + +namespace py = pybind11; + +template<typename ValueType> +using py_array_t = py::array_t<ValueType, py::array::f_style>; // to enforce column-major storage + +namespace pyfmm +{ + + using value_type = double; + static constexpr std::size_t dimension{2}; + + // the far field matrix kernel + using far_matrix_kernel_type = scalfmm::matrix_kernels::laplace::one_over_r; + // the near field matrix kernel + using near_matrix_kernel_type = far_matrix_kernel_type; + // number of inputs and outputs. + static constexpr std::size_t nb_inputs_near{near_matrix_kernel_type::km}; + static constexpr std::size_t nb_outputs_near{near_matrix_kernel_type::kn}; + + // interpolator + using interpolator_type = scalfmm::interpolation::interpolator<value_type, dimension, far_matrix_kernel_type, + scalfmm::options::uniform_<scalfmm::options::fft_>>; + + // operators for the fmm algorithm + using far_field_type = scalfmm::operators::far_field_operator<interpolator_type>; + using near_field_type = scalfmm::operators::near_field_operator<near_matrix_kernel_type>; + using fmm_operator_type = scalfmm::operators::fmm_operators<near_field_type, far_field_type>; + + // particle + using particle_type = scalfmm::container::particle< + // position + value_type, dimension, + // inputs + value_type, nb_inputs_near, + // outputs + value_type, nb_outputs_near, + // variables + std::size_t // for storing the index in the original container. + >; + + // position point type + using position_type = typename particle_type::position_type; + + // simulation box + using box_type = scalfmm::component::box<position_type>; + + // particle container + using container_type = std::vector<particle_type>; + + // morton type + using morton_type = std::size_t; + + // group tree type + using cell_type = scalfmm::component::cell<typename interpolator_type::storage_type>; + using leaf_type = scalfmm::component::leaf_view<particle_type>; + using group_tree_type = scalfmm::component::group_tree_view<cell_type, leaf_type, box_type>; + + // numpy array object + using np_array = py_array_t<value_type>; + using np_array_int = py_array_t<int>; + +} // namespace pyfmm + +PYBIND11_MAKE_OPAQUE(pyfmm::container_type); + +PYBIND11_MAKE_OPAQUE(std::initializer_list<pyfmm::value_type>); + +PYBIND11_MAKE_OPAQUE(std::vector<pyfmm::morton_type>); + +PYBIND11_MODULE(pyfmm, m) +{ + m.doc() = "Fast Multipole Method python module"; // optional module docstring + + using value_type = pyfmm::value_type; + py::bind_vector<std::vector<pyfmm::morton_type>>(m, "VecMorton"); + // -------------- + // scalfmm point + // -------------- + using point = pyfmm::position_type; + py::class_<point>(m, "point", "multidimensional point") + .def(py::init<value_type, value_type>(), "main constructor") + .def("norm", &point::norm, "return the 2-norm of the point") + .def( + "__getitem__", [](const point& p, int i) -> point::value_type const& { return p[i]; }, + py::return_value_policy::reference, py::is_operator()) + .def( + "__setitem__", [](point& p, int i, value_type value) -> void { p[i] = value; }, + py::return_value_policy::reference, py::is_operator()) + .def("__repr__", + [](const point& p) -> std::string + { + std::ostringstream oss; + oss << p; + return oss.str(); + }); + + // ------------ + // scalfmm box + // ------------ + using box = pyfmm::box_type; + py::class_<box>(m, "box") + .def(py::init<point, value_type>(), py::arg("min_corner"), py::arg("side_length"), + "init box from min corner and its width") + .def(py::init<value_type, point>(), py::arg("width"), py::arg("box_center"), + "init box from its center and its width") + .def(py::init<point, point>(), py::arg("corner_1"), py::arg("corner_2"), "Init box from 2 corners") + .def("corner", static_cast<point (box::*)(std::size_t) const>(&box::corner), py::arg("idx"), + "accessor for the box corners") + .def("corner", static_cast<void (box::*)(std::size_t, const point&)>(&box::corner), py::arg("idx"), + py::arg("pos"), "Setter for the corners") + .def("width", &box::width, py::arg("dim"), "Returns the width for given dimension") + .def("center", &box::center, "Accessor for the box center") + .def("contains", static_cast<bool (box::*)(const point& p) const>(&box::contains), py::arg("point"), + "checks whether a position is within the box bounds") + .def("__repr__", + [](const box& b) -> std::string + { + std::ostringstream oss; + oss << b; + return oss.str(); + }); + + // ---------------- + // scalfmm particle + // ---------------- + using particle = pyfmm::particle_type; + py::class_<particle>(m, "particle") + .def(py::init<>(), "default constructor") + .def("__repr__", + [](const particle& part) -> std::string + { + std::ostringstream oss; + oss << part; + return oss.str(); + }) + .def("get_position", + [](const particle& part, std::size_t i) -> particle::base_type::position_value_type + { + if(i >= part.sizeof_dimension()) + throw py::index_error(); + return part.position(i); + }) + .def("set_position", + [](particle& part, std::size_t i, particle::base_type::position_value_type value) -> void + { + if(i >= part.sizeof_dimension()) + throw py::index_error(); + part.position(i) = value; + }) + .def("get_input", + [](const particle& part, std::size_t i) -> particle::base_type::inputs_value_type + { + if(i >= part.sizeof_inputs()) + throw py::index_error(); + return part.inputs(i); + }) + .def("set_input", + [](particle& part, std::size_t i, particle::base_type::inputs_value_type value) -> void + { + if(i >= part.sizeof_inputs()) + throw py::index_error(); + part.inputs(i) = value; + }) + .def("get_output", + [](const particle& part, std::size_t i) -> particle::base_type::outputs_value_type + { + if(i >= part.sizeof_outputs()) + throw py::index_error(); + return part.outputs(i); + }) + .def("set_output", + [](particle& part, std::size_t i, particle::base_type::outputs_value_type value) -> void + { + if(i >= part.sizeof_outputs()) + throw py::index_error(); + part.outputs(i) = value; + }) + .def("set_variable", [](particle& part, std::size_t value) -> void { part.variables(value); }) + .def("sizeof_dimension", &particle::sizeof_dimension) + .def("sizeof_inputs", &particle::sizeof_inputs) + .def("sizeof_outputs", &particle::sizeof_outputs) + .def("sizeof_variables", &particle::sizeof_variables); + + // -------------------------- + // scalfmm particle container + // -------------------------- + using container = pyfmm::container_type; + py::class_<container>(m, "container") + .def(py::init<>()) + .def(py::init<std::size_t>()) + .def("clear", &container::clear) + .def("pop_back", &container::pop_back) + .def("__len__", [](const container& c) -> container::size_type { return c.size(); }) + .def( + "__iter__", [](container& c) { return py::make_iterator(c.begin(), c.end()); }, + py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */ + .def( + "__getitem__", [](container& c, int i) -> particle& { return c.at(i); }, + py::return_value_policy::reference_internal, py::is_operator()) + .def( + "__setitem__", [](container& c, int i, const particle& part) -> void { c.at(i) = part; }, py::is_operator()) + .def("__repr__", + [](container& c) -> std::string + { + std::ostringstream os; + for(py::ssize_t idx{0}; idx < c.size(); ++idx) + { + os << c[idx] << std::endl; + } + return os.str(); + }) + .def("get_outputs", + [](container& c, pyfmm::np_array& out) -> void + { + static constexpr py::ssize_t outputs_size = container::value_type::base_type::outputs_size; + auto out_proxy = out.template mutable_unchecked<1>(); + + const py::ssize_t out_size = out_proxy.shape(0); + const std::size_t nb_particles{c.size()}; + + if(out_size != nb_particles * outputs_size) + throw std::runtime_error("Wrong size for output vector"); + + for(py::ssize_t idx{0}; idx < nb_particles; ++idx) + { + for(py::ssize_t i{0}; i < outputs_size; ++i) + { + out_proxy(idx + i * outputs_size) = c[idx].outputs(i); + } + } + }); + + // ------------ + // scalfmm tree + // ------------ + using tree = pyfmm::group_tree_type; + py::class_<tree>(m, "tree") + + // .def(py::init<std::size_t, std::size_t, std::size_t, box>(), py::arg("tree_height"), py::arg("order"), + // py::arg("group_size_leaves"), py::arg("box")) + + .def(py::init<std::size_t, std::size_t, std::size_t, std::size_t, box>(), py::arg("tree_height"), + py::arg("order"), py::arg("group_size_leaves"), py::arg("group_size_cells"), py::arg("box")) + + // .def(py::init<std::size_t, std::size_t, box, std::size_t, std::vector<pyfmm::morton_type>>(), + // py::arg("tree_height"), py::arg("order"), py::arg("box"), py::arg("group_size_leaves"), py::arg("morton")) + + .def(py::init<std::size_t, std::size_t, box, std::size_t, std::size_t, container, bool>(), py::arg("tree_height"), + py::arg("order"), py::arg("box"), py::arg("group_size_leaves"), py::arg("group_size_cells"), + py::arg("particle_container"), py::arg("particles_are_sorted")) + + .def(py::init<std::size_t, std::size_t, box, std::size_t, container, bool>(), py::arg("tree_height"), + py::arg("order"), py::arg("box"), py::arg("group_size"), py::arg("particle_container"), + py::arg("particles_are_sorted")) + + .def("leaf_level", &tree::leaf_level, " return the leaf level") + .def("top_level", &tree::top_level, "return the top level used in the algorithm") + .def("box_center", &tree::box_center, "return the center of the simulation box") + .def("box_width", &tree::box_width, "return the width of the simulation box") + .def("leaf_width", &tree::leaf_width, "return the width of a leaf") + .def("reset_outputs", &tree::reset_outputs, "reset all outputs") + .def("reset_far_field", &tree::reset_far_field, "reset far field") + .def("reset_multipoles", &tree::reset_multipoles, "reset multipoles") + .def("reset_locals", &tree::reset_locals, "reset locals") + // .def("build_with_morton", &tree::construct<pyfmm::morton_type>, "build tree with morton indexes") + .def("build_with_morton", &tree::build_with_morton, "build tree with morton indexes") + .def("fill_leaves_with_particles", &tree::fill_leaves_with_particles<container>, + "fill tree with particles containers") + // .def("get_outputs", + // [](tree& tree, pyfmm::np_array& out) -> void + // { + // static constexpr py::ssize_t outputs_size = container::value_type::base_type::outputs_size; + // auto out_proxy = out.template mutable_unchecked<1>(); + + // const py::ssize_t out_size = out_proxy.shape(0); + // py::ssize_t index; + + // for(auto const pg: tree.vector_of_leaf_groups()) + // { + // for(auto const& leaf: pg->components()) + // { + // for(auto const particle_tuple_ref: leaf) + // { + // auto p = typename tree::leaf_type::const_proxy_type(particle_tuple_ref); + + // const auto& idx = std::get<0>(p.variables()); + // auto const& output = p.outputs(); + + // for(py::ssize_t i{0}; i < outputs_size; ++i) + // { + // index = idx + i * outputs_size; + + // if(index >= out_size) + // throw std::runtime_error("index out of range"); + + // out_proxy(index) = output.at(i); + // } + // } + // } + // } + // }) + .def("get_outputs", + [](tree& tree, pyfmm::np_array& outputs_matrix) -> void + { + static constexpr py::ssize_t outputs_size = container::value_type::base_type::outputs_size; + using inputs_type = container::value_type::base_type::outputs_type::value_type; + + py::buffer_info buf_info = outputs_matrix.request(); + auto N = buf_info.shape[0]; + auto nb_inputs = buf_info.shape[1]; + // set the numpy matrix in xtensor (column major) + auto xt_matrix = scalfmm::python::to_cpp::to_matrix(outputs_matrix); + // + int idx{0}; + // loop through the groups of leaves + for(auto const& pg: tree.vector_of_leaf_groups()) + { + std::array<std::add_pointer_t<inputs_type>, outputs_size> output_storage; + // + auto nb_elements_in_group = pg->storage().size(); + // Get the storage of the inputs (of the current group) + output_storage[0] = pg->storage().ptr_on_output(); + for(int i = 1; i < outputs_size; ++i) + { + output_storage[i] = output_storage[i - 1] + nb_elements_in_group; + } + + // loop through the elements of the group + for(auto i(0); i < nb_elements_in_group; ++i, ++idx) + { + for(int k = 0; k < outputs_size; ++k) + { + xt_matrix(idx, k) = (output_storage[k])[i]; + } + } + } + }) + .def("fill_particles", + [](tree& tree, pyfmm::np_array& position_matrix) -> void + { + static constexpr py::ssize_t inputs_size = container::value_type::base_type::inputs_size; + using position_type = container::value_type::base_type::position_type::value_type; + + py::buffer_info buf_info = position_matrix.request(); + auto N = buf_info.shape[0]; + auto dimension = buf_info.shape[1]; + // set the numpy matrix in xtensor (column major) + auto xt_pos = scalfmm::python::to_cpp::to_matrix(position_matrix); + // + // loop through the groups of leaves + std::vector<std::add_pointer_t<position_type>> ptr(dimension, nullptr); + int idx{0}; + int cc{0}; + for(auto const& pg: tree.vector_of_leaf_groups()) + { + auto nb_particles = pg->storage().size(); + auto ptr_start = pg->storage().ptr_on_position(); + for(int j = 0; j < dimension; ++j) + { + auto ptr = ptr_start + nb_particles * j; + int idx1{idx}; + + for(auto i = 0; i < nb_particles; ++i, ++idx1) + { + ptr[i] = xt_pos(idx1, j); + } + } + idx += nb_particles; + } + }) + .def("set_inputs", + [](tree& tree, pyfmm::np_array& inputs_matrix) -> void + { + static constexpr py::ssize_t inputs_size = container::value_type::base_type::inputs_size; + using inputs_type = container::value_type::base_type::inputs_type::value_type; + + py::buffer_info buf_info = inputs_matrix.request(); + auto N = buf_info.shape[0]; + auto nb_inputs = buf_info.shape[1]; + // set the numpy matrix in xtensor (column major) + auto xt_inputs = scalfmm::python::to_cpp::to_matrix(inputs_matrix); + // + int idx{0}; + // loop through the groups of leaves + for(auto const& pg: tree.vector_of_leaf_groups()) + { + std::array<std::add_pointer_t<inputs_type>, inputs_size> input_storage; + // std::array<inputs_type*, inputs_size> input_storage; + auto nb_elements_in_group = pg->storage().size(); + // Get the storage of the inputs (of the current group) + input_storage[0] = pg->storage().ptr_on_input(); + for(int i = 1; i < inputs_size; ++i) + { + input_storage[i] = input_storage[i - 1] + nb_elements_in_group; + } + + // loop through the elements of the group + for(auto i(0); i < nb_elements_in_group; ++i, ++idx) + { + for(int k = 0; k < inputs_size; ++k) + { + (input_storage[k])[i] = xt_inputs(idx, k); + } + } + } + }) + + .def("__repr__", + [](tree& tree) -> std::string + { + std::stringstream os; + os << "tree | height = " << tree.height() << std::endl; + os << "tree | order = " << tree.order() << std::endl; + os << "tree | group size for leaves = " << tree.group_of_leaf_size() << std::endl; + os << "tree | group size for cells = " << tree.group_of_cell_size() << std::endl; + py::ssize_t group_index{0}; + for(auto const pg: tree.vector_of_leaf_groups()) + { + os << "group n°" << group_index << std::endl; + for(auto const& leaf: pg->components()) + { + os << "\tleaf n°" << leaf.index() << std::endl; + for(auto const particle_tuple_ref: leaf) + { + auto p = typename tree::leaf_type::const_proxy_type(particle_tuple_ref); + os << "\t\t" << p << std::endl; + } + } + ++group_index; + } + return os.str(); + }); + + // ------------------------------- + // get morton index + // ------------------------------- + + m.def("morton_sort", &scalfmm::python::morton_sort<pyfmm::box_type, double, int>, + pybind11::return_value_policy::move, + "Get the sorted morton index and the permutation to apply on the points"); + m.def("morton_sort", &scalfmm::python::morton_sort<pyfmm::box_type, float, int>, + pybind11::return_value_policy::move, + "Get the sorted morton index and the permutation to apply on the points"); + //pybind11::return_value_policy::automatic, + // --------------------- + // scalfmm matrix kernel + // --------------------- + using matrix_kernel = pyfmm::far_matrix_kernel_type; + py::class_<matrix_kernel>(m, "matrix_kernel") + .def(py::init<>(), "defaut constructor") + .def("name", &matrix_kernel::name, "return the name of the matrix kernel") + .def("__repr__", + [](matrix_kernel& mk) -> std::string + { + std::stringstream os; + os << mk.name(); + return os.str(); + }); + + // ------------------------------- + // scalfmm full direct computation + // ------------------------------- + m.def( + "full_direct", [](container& container, const matrix_kernel& matrix_kernel) -> void + { scalfmm::algorithms::full_direct(container, matrix_kernel); }, py::arg("container"), py::arg("matrix_kernel"), + "perform direct computation and store results in the container"); + + // ----------------- + // scalfmm operators + // ----------------- + using interpolator = pyfmm::interpolator_type; + using far_field = pyfmm::far_field_type; + using near_field = pyfmm::near_field_type; + using fmm_operator = pyfmm::fmm_operator_type; + + // interpolator + py::class_<interpolator>(m, "interpolator") + .def(py::init<std::size_t, std::size_t, value_type, value_type>(), py::arg("order"), py::arg("tree_height"), + py::arg("root_cell_width"), py::arg("cell_width_extension") = value_type(0.)); + + // far field + py::class_<far_field>(m, "far_field").def(py::init<const interpolator&>(), py::arg("interpolator")); + + // near field + py::class_<near_field>(m, "near_field") + .def(py::init<>(), "default constructor") + .def(py::init<bool>(), py::arg("mutual"), "first constructor") + .def(py::init<matrix_kernel>(), py::arg("matrix_kernel"), "second constructor") + .def(py::init<matrix_kernel, bool>(), py::arg("matrix_kernel"), py::arg("mutual"), "third constructor") + // .def("matrix_kernel", &near_field::matrix_kernel, py::return_value_policy::reference_internal, + // "underlying matrix kernel accessor") + .def_property( + "separation_criterion", [](const near_field& nf) -> int { return nf.separation_criterion(); }, + [](near_field& nf, int value) { nf.separation_criterion() = value; }) + .def_property( + "mutual", [](const near_field& nf) -> bool { return nf.mutual(); }, + [](near_field& nf, bool value) { nf.mutual() = value; }) + .def("__repr__", + [](near_field& nf) + { + return "near field[ mk: " + nf.matrix_kernel().name() + + ", separation criterion: " + std::to_string(nf.separation_criterion()) + + ", mutual: " + std::to_string(nf.mutual()) + " ]"; + }); + + // fmm operators + py::class_<fmm_operator>(m, "fmm_operator") + .def(py::init<const near_field&, const far_field&>(), py::arg("near_field"), py::arg("far_field")); + + // --------------------- + // scalfmm fmm algorithm + // --------------------- + m.def( + "fmm_seq", [](tree& tree, const fmm_operator& fmm_operator) -> void + { scalfmm::algorithms::fmm[scalfmm::options::_s(scalfmm::options::seq)](tree, fmm_operator); }, py::arg("tree"), + py::arg("fmm_operators"), "perform the fmm algorithm and store results in the tree (sequential version)"); + + m.def( + "fmm_omp", [](tree& tree, const fmm_operator& fmm_operator) -> void + { scalfmm::algorithms::fmm[scalfmm::options::_s(scalfmm::options::omp)](tree, fmm_operator); }, py::arg("tree"), + py::arg("fmm_operators"), "perform the fmm algorithm and store results in the tree (parallel version)"); + + // --------- + // accuracy + // --------- + m.def( + "compute_error", + [](container const& container, tree const& tree) + { + scalfmm::utils::accurater<value_type> error; + + scalfmm::component::for_each_leaf(std::cbegin(tree), std::cend(tree), + [&container, &error](auto const& leaf) + { + // loop on the particles of the leaf + for(auto const p_ref: leaf) + { + // build a particle + const auto p = typename tree::leaf_type::const_proxy_type(p_ref); + // + const auto& idx = std::get<0>(p.variables()); + + auto const& output_ref = container[idx].outputs(); + auto const& output = p.outputs(); + + for(int i{0}; i < pyfmm::nb_outputs_near; ++i) + { + error.add(output_ref.at(i), output.at(i)); + } + } + }); + return py::make_tuple(error.get_relative_l2_norm(), error.get_relative_infinity_norm(), + error.get_rms_error()); + }, + py::arg("container"), py::arg("tree"), "compute error between direct and fmm results"); + + m.def( + "compute_error", + [](pyfmm::np_array& out_direct, pyfmm::np_array& out_fmm) + { + scalfmm::utils::accurater<value_type> error; + auto xt_direct = scalfmm::python::to_cpp::to_matrix(out_direct); + auto xt_fmm = scalfmm::python::to_cpp::to_matrix(out_fmm); + + auto const& shape_direct = xt_direct.shape(); + auto const& shape_fmm = xt_fmm.shape(); + if(shape_direct[0] != shape_fmm[0] or shape_direct[1] != shape_fmm[1]) + { + std::cerr << " shape(data1) (" << shape_direct[0] << ", " << shape_direct[1] << ")\n " + << " shape(data2) (" << shape_fmm[0] << ", " << shape_fmm[1] << ")\n "; + throw std::runtime_error("Vectors have different size ()"); + } + if(shape_fmm[1] != 1) + { + throw std::runtime_error("only output_size== 1 is available"); + } + for(py::ssize_t i{0}; i < shape_direct[0]; ++i) + { + error.add(xt_direct(i, 1), xt_fmm(i, 1)); + } + + return py::make_tuple(error.get_relative_l2_norm(), error.get_relative_infinity_norm(), + error.get_rms_error()); + }, + py::arg("out_direct"), py::arg("out_fmm"), "compute error between direct and fmm results"); + + using fmaloader = scalfmm::io::FFmaGenericLoader<value_type, pyfmm::dimension>; + + py::class_<fmaloader>(m, "fmaloader") + .def(py::init<const std::string&, bool>(), py::arg("name"), py::arg("verbose")) + + // void + // fillParticles(vector_type & dataToRead, const std::size_t N) + .def("n", &fmaloader::getNumberOfParticles) + .def("nb_data", &fmaloader::getNbRecordPerline) + .def("dim", &fmaloader::get_dimension) + .def("nb_inputs", &fmaloader::get_number_of_input_per_record) + .def("nb_outputs", &fmaloader::get_number_of_output_per_record) + .def("data_type", &fmaloader::getDataType) + .def("centre", &fmaloader::getBoxCenter) + .def("width", &fmaloader::getBoxWidth) + .def( + "read", + [](fmaloader& loader) + { + int nb_line_record = loader.getNbRecordPerline(); + int nb_particles = loader.getNumberOfParticles(); + std::vector<value_type> data(nb_particles * nb_line_record); + std::cout << "n_mine_record " << nb_line_record << std::endl; + loader.fillParticles(data, nb_particles); + return py::array_t<value_type, py::array::c_style>({nb_particles, nb_line_record}, data.data()); + }, + pybind11::return_value_policy::move, " read the data (matrix C form)"); + + using fmawriter = scalfmm::io::FFmaGenericWriter<value_type>; + + py::class_<fmawriter>(m, "fmawriter") + .def(py::init<const std::string&>(), py::arg("name")) + .def(py::init<const std::string&, bool>(), py::arg("name"), py::arg("binary")) + + .def("write", &fmawriter::writeDataFromTree<typename pyfmm::group_tree_type>); +} \ No newline at end of file diff --git a/python_interface/tutorial.py b/python_interface/tutorial.py new file mode 100644 index 0000000000000000000000000000000000000000..1d46dcb54c8ebc98945d8ac5149e4ee920d12bb4 --- /dev/null +++ b/python_interface/tutorial.py @@ -0,0 +1,249 @@ +import random as rd +import numpy as np +import argparse +import time +import sys +from colorama import Fore, Back, Style +from argparse import Namespace + +import fmmTools +import pyfmm # make sure that the module is available + +# - Do not forget to update the PYTHONPATH environment variable as follows: +# export PYTHONPATH=/home/agicquel/Documents/Builds/build_scalfmm_last/python_interface:$PYTHONPATH +# +# - Run python script: +# python tutorial.py --help +# python tutorial.py +# python ../python_interface/tutorial.py --group-size 1 --order 5 --tree-height 3 --number-particles 1000 --center 0 0 --width 2 --input_file ../data/datal2p_10.fma +# python ../python_interface/tutorial.py --group-size 1 --order 5 --tree-height 3 --number-particles 1000 --center 0 0 --width 2 +def run(args): + """Run ScalFMM tutorial""" + + + dimension = 2 + _order = args.order + _group_size = args.group_size + _tree_height = args.tree_height + + _omp = args.open_mp + _mutual = args.mutual + + _center = args.center + _width = args.width + _file = None + if args.input_file != None: + _file = args.input_file + else: + _center = args.center + _width = args.width + _nb_particles = args.number_particles + + if _file: + data,_center,_width = fmmTools.fmareader(_file,verbose=True) + particles = data[0] + inputs = data[1] + outputs = data[2] + _nb_particles = particles.shape[0] + else: + # Init random seed + rd.seed(123) + data = np.empty((3), dtype=object) + data[0] = np.zeros((_nb_particles, dimension), dtype=np.float64) + data[1] = np.zeros((_nb_particles, 1), dtype=np.float64,) + data[2] = np.zeros((_nb_particles, 1), dtype=np.float64) + # particles in box + for i in range(dimension): + lower_bd = _center[i] - 0.5 * _width + upper_bd = _center[i] + 0.5 * _width + particles = data[0] + for i in range(_nb_particles): + particles[i,:] = rd.uniform(lower_bd, upper_bd) + data[1][i] = rd.uniform(1, dimension) + inputs = data[1] + outputs = data[2] + + if not (dimension == particles.shape[1]): + sys.exit("Dimension is not 2 ") + # ---------------------- + # Initialization + # ----------------------- + + print(Fore.LIGHTBLACK_EX + "\n\t--- initialization ---" + Style.RESET_ALL) + center = pyfmm.point(_center[0], _center[1]) + box = pyfmm.box(width=_width, box_center=center) + + print("\tsimulation box =", end="") + print(box) + print("\tcenter = ", end="") + print(center) + + level = _tree_height -1 + + + # construct the Morton index of the particles. The vector is sorted + # and we have the permutation to apply to the data + morton, perm = pyfmm.morton_sort(box, particles ,level ) + # Apply the permutation + for i in range(particles.shape[1]): + particles[:,i] = particles[perm[:], i ] + for i in range(inputs.shape[1]): + inputs[:,i] = inputs[perm[:], i ] + for i in range(outputs.shape[1]): + outputs[:,i] = outputs[perm[:], i ] + + # Tree + # ----------------------- + print(Fore.LIGHTBLACK_EX + "\n\t--- tree ---" + Style.RESET_ALL) + t = time.time() + # build and empty tree + tree = pyfmm.tree( + tree_height=_tree_height, + order=_order, + group_size_leaves=_group_size, + group_size_cells=_group_size, + box=box + ) + # set the tree structure (leaves/cells) + tree.build_with_morton( morton ) + # fill particles position in the leaves + tree.fill_particles(particles) + # fill in the positions of the particles in the leaves + tree.set_inputs(inputs) + t_tree = time.time() - t + print(f"\ttime required: {t_tree:.7} s") + + # --------------------------------------------- + # FMM computation + # --------------------------------------------- + + # --------------------------------------------- + # build FMM operator + # --------------------------------------------- + + print(Fore.LIGHTBLACK_EX + "\n\t--- fmm preprocessing ---" + Style.RESET_ALL) + t = time.time() + interpolator = pyfmm.interpolator( + order=_order, tree_height=_tree_height, root_cell_width=box.width(0) + ) + far_field = pyfmm.far_field(interpolator=interpolator) + near_field = pyfmm.near_field() + near_field.mutual = _mutual + fmm_operator = pyfmm.fmm_operator(near_field=near_field, far_field=far_field) + t_preproc = time.time() - t + print(f"\ttime required: {t_preproc:.7} s") + + # --------------------------------------------- + # computation + # --------------------------------------------- + + print(Fore.LIGHTBLACK_EX + "\n\t--- fmm computation ---" + Style.RESET_ALL) + if _omp: + print("\tusing parallel version (OpenMP)...") + t = time.time() + pyfmm.fmm_omp(tree=tree, fmm_operators=fmm_operator) + t_fmm = time.time() - t + print(f"\ttime required: {t_fmm:.7} s") + else: + print("\tusing sequential version...") + t = time.time() + pyfmm.fmm_seq(tree=tree, fmm_operators=fmm_operator) + t_fmm = time.time() - t + print(f"\ttime required: {t_fmm:.7} s") + + + # -------------------------------------------- + # Direct computation + # --------------------------------------------- + + # ---------------------- + # Particle container for direct computation + # ----------------------- + + print(Fore.LIGHTBLACK_EX + "\n\t--- particle container ---" + Style.RESET_ALL) + t = time.time() + container = pyfmm.container(_nb_particles) + idx = 0 + for part in container: + for i in range(2): + part.set_position(i, particles[idx,i]) + part.set_input(0, inputs[idx,0]) + part.set_output(0, outputs[idx,0]) + part.set_variable(idx) + idx += 1 + t_container = time.time() - t + print(f"\ttime required: {t_container:.7} s") + # ---------------------- + print(Fore.LIGHTBLACK_EX + "\n\t--- direct computation ---" + Style.RESET_ALL) + mk = pyfmm.matrix_kernel() + t = time.time() + pyfmm.full_direct(container, mk) + t_direct = time.time() - t + print(f"\ttime required: {t_direct:.7} s") + + # ---------------------- + # Postprocessing + # ----------------------- + + print(Fore.LIGHTBLACK_EX + "\n\t--- postprocessing ---" + Style.RESET_ALL) + + out_direct = np.zeros((_nb_particles)) + out_fmm = np.zeros((_nb_particles)) + + container.get_outputs(out_direct) + out_direct = out_direct.reshape(outputs.shape) + + tree.get_outputs(outputs) + l2_error, infty_error, _ = pyfmm.compute_error(out_direct=out_direct, out_fmm=outputs) + + speedup = t_direct / t_fmm + + print(Fore.GREEN + f"\t - speed-up = {speedup:.7}" + Style.RESET_ALL) + print(Fore.GREEN + f"\t - L2 relative error = {l2_error:.7}" + Style.RESET_ALL) + print(Fore.GREEN + f"\t - infinity relative error = {infty_error:.7}" + Style.RESET_ALL) + + # save data from tree in file ( fma (ascii) format or (bfma) binary) + writer = pyfmm.fmawriter(name="toto.bfma",binary=True) + writer.write(tree,_nb_particles) + +def parse_arguments(): + """Parse command-line arguments.""" + + # Create the parser + parser = argparse.ArgumentParser(description="Scalfmm tutorial") + + parser.add_argument("--open-mp","-omp", action="store_true", required=False, help="Use parallel version of the fmm algorithm") + parser.add_argument("--mutual", "-m", action="store_true", required=False, help="Enable full mutual computation for the p2p operator") + parser.add_argument("-gs", "--group-size", type=int, required=True, help="Group tree chunk size (granularity)", default=1) + parser.add_argument("-th", "--tree-height", type=int, required=True, help="Tree height (or initial height in case of an adaptive tree)", default=3) + parser.add_argument("-o", "--order", type=int, required=True, help="Precision setting (order of the interpolation method)", default=5) + parser.add_argument("-N", "--number-particles", type=int, help="Number of particles", default=1000) + parser.add_argument("-c", "--center", type=float, nargs="+", help="Center of the simulation box", default=[0.,0.]) + parser.add_argument("-w", "--width", type=float, help="Width of the simulation box", default=2.) + parser.add_argument("-v", "--verbose", action='count', default=0, help="Increase verbosity level.") + parser.add_argument("-fi", "--input_file", type=str, help="Particles file in fma format") + parser.add_argument("--random", action='store_true', required=False, help="use random particles then --center and -- width are required.") + + args = parser.parse_args() + if args.random and not args.center and not args.width: + parser.error("Args --random required args: --number-particles --centre and --width") + if args.random and args.input_file: + parser.error("Use either --random or --input_file") + return args + +def print_args(args): + """Print arguments form parser.""" + print(Fore.CYAN + "\n\t--- parameters ---" + Style.RESET_ALL) + for arg, value in vars(args).items(): + print(Fore.CYAN + f"<params> {arg} = {value}" + Style.RESET_ALL) + +def main(): + """Main function.""" + args = parse_arguments() + print_args(args) if args.verbose >= 2 else None + + + run(args) + +if __name__ == "__main__": + main() diff --git a/tools/compare_files.cpp b/tools/compare_files.cpp index 16109ffdbfb914862e6f469ceea81a47fefb3844..34f2caf1480247bd172b8dd6874bff73cf11b43b 100644 --- a/tools/compare_files.cpp +++ b/tools/compare_files.cpp @@ -172,8 +172,8 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int /// Parsing options /// auto parser = cpp_tools::cl_parser::make_parser(cpp_tools::cl_parser::help{}, local_args::input_file_one(), - local_args::input_file_two(), local_args::sort_particle(), - local_args::index_to_compare(), local_args::index2_to_compare()); + local_args::input_file_two(), local_args::sort_particle(), + local_args::index_to_compare(), local_args::index2_to_compare()); // Parameter handling parser.parse(argc, argv); @@ -183,13 +183,15 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int const auto filename1{parser.get<local_args::input_file_one>()}; if(!filename1.empty()) { - std::cout << cpp_tools::colors::blue << "<params> Input file 1: " << filename1 << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Input file 1: " << filename1 << cpp_tools::colors::reset + << '\n'; } const auto filename2{parser.get<local_args::input_file_two>()}; if(!filename2.empty()) { - std::cout << cpp_tools::colors::blue << "<params> Input file 2: " << filename2 << cpp_tools::colors::reset << '\n'; + std::cout << cpp_tools::colors::blue << "<params> Input file 2: " << filename2 << cpp_tools::colors::reset + << '\n'; } std::vector<int> index; index = parser.get<local_args::index_to_compare>(); diff --git a/tools/compare_trees.cpp b/tools/compare_trees.cpp index 1fa57b16cc544c97217ac2d32880031f16ad7487..984eb106a2e90368a31b26eb497f469b5b4ac5da 100644 --- a/tools/compare_trees.cpp +++ b/tools/compare_trees.cpp @@ -84,11 +84,11 @@ namespace local_args }; struct comp { - cpp_tools::cl_parser::str_vec flags = {"--compare", "-c"}; - std::string description = "1 multipoles, 2 locals, 3 both."; - using type = int; - type def = 3; - std::string input_hint = "std::string"; /*!< The input hint */ + cpp_tools::cl_parser::str_vec flags = {"--compare", "-c"}; + std::string description = "1 multipoles, 2 locals, 3 both."; + using type = int; + type def = 3; + std::string input_hint = "std::string"; /*!< The input hint */ }; } // namespace local_args ///////////////////////////////////////////////////////////// diff --git a/tools/define_symmetries.cpp b/tools/define_symmetries.cpp index c42869e068d406dbb414fb21c6684d1b6a32fd87..4998ecf99201fdb869e401fde6cb80badf1fb83f 100644 --- a/tools/define_symmetries.cpp +++ b/tools/define_symmetries.cpp @@ -117,7 +117,6 @@ auto diag_index(Point const& t, Int& axe) -> Point throw("wrong axe!"); return Point{}; } - } else { diff --git a/tools/direct_computation.cpp b/tools/direct_computation.cpp index 31568deb74b0960ddf7e8e077ad5c1a0d79d03ba..da470892279d84aaa4544a15a2c1bd7267c6f1f6 100644 --- a/tools/direct_computation.cpp +++ b/tools/direct_computation.cpp @@ -1,6 +1,5 @@ // @FUSE_OMP - #include "scalfmm/container/particle.hpp" #include "scalfmm/container/particle_container.hpp" #include "scalfmm/container/point.hpp" @@ -191,7 +190,7 @@ auto direct_run(const std::string& input_file, const std::string& output_file, c } return 0; } -template <typename value_type> +template<typename value_type> auto test_one_over_r2(const int dimension, const std::string& input_file, const std::string& output_file, const bool postreat) -> int { diff --git a/tools/tools_param.hpp b/tools/tools_param.hpp index 5db17191ec846c38a3d88dfadca9e884e8f9b408..d99157ae4e3cdadafbe5ecd842839c6a703e25e3 100644 --- a/tools/tools_param.hpp +++ b/tools/tools_param.hpp @@ -76,6 +76,6 @@ namespace args_tools std::string input_hint = "3"; type def = 0; // defaut no level for non periodic simulation }; - -} + +} // namespace args_tools #endif // TOOLS_PARAMETERS_HPP diff --git a/tools/trace_tree.cpp b/tools/trace_tree.cpp index c64fcbdcaf4924ee130d3390ae3790ed4694aa7d..133a04178808b7a91d2de89a83e8b78577c8b58e 100644 --- a/tools/trace_tree.cpp +++ b/tools/trace_tree.cpp @@ -242,7 +242,6 @@ auto run_trace(const std::string& input_file, std::string const& output_file, co if(Dimension == 2 && !output_file.empty()) { - std::string tikzName(output_file + ".tex"); scalfmm::tools::io::exportTIKZ(tikzName, tree, displayParticles, color, true); } @@ -266,7 +265,7 @@ auto main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) -> int auto parser = cpp_tools::cl_parser::make_parser( cpp_tools::cl_parser::help{}, args_tools::dimensionSpace(), args_tools::input_file_req(), local_args::output_file(), args_tools::tree_height{}, args_tools::pbc{}, args_tools::block_size{}, - local_args::Notmutual{}, /*local_args::grpInter{}, */local_args::logLevel{}, local_args::displayParticles{}, + local_args::Notmutual{}, /*local_args::grpInter{}, */ local_args::logLevel{}, local_args::displayParticles{}, local_args::color{}); parser.parse(argc, argv); diff --git a/units/container/particle.cpp b/units/container/particle.cpp index be41581ef2c17e1198cc0147eb24833f04895d1e..190135a93e263b9c4db5ee84e3adf2cc29903d06 100644 --- a/units/container/particle.cpp +++ b/units/container/particle.cpp @@ -95,7 +95,8 @@ TEST_CASE("particle-common", "[particle-common]") std::size_t i{0}; meta::repeat( - [&i, &ref, &proxy](auto const& e) { + [&i, &ref, &proxy](auto const& e) + { REQUIRE(ref.position(i) == e); REQUIRE(proxy.position(i) == e); ++i; @@ -103,7 +104,8 @@ TEST_CASE("particle-common", "[particle-common]") meta::sub_tuple(*it, typename particle_type::range_position_type{})); i = 0; meta::repeat( - [&i, &ref, &proxy](auto const& e) { + [&i, &ref, &proxy](auto const& e) + { REQUIRE(ref.inputs(i) == e); REQUIRE(proxy.inputs(i) == e); ++i; @@ -111,14 +113,16 @@ TEST_CASE("particle-common", "[particle-common]") meta::sub_tuple(*it, typename particle_type::range_inputs_type{})); i = 0; meta::repeat( - [&i, &ref, &proxy](auto const& e) { + [&i, &ref, &proxy](auto const& e) + { REQUIRE(ref.outputs(i) == e); REQUIRE(proxy.outputs(i) == e); ++i; }, meta::sub_tuple(*it, typename particle_type::range_outputs_type{})); meta::repeat( - [](auto const& e1, auto const& e2, auto const& e3) { + [](auto const& e1, auto const& e2, auto const& e3) + { REQUIRE(e1 == e2); REQUIRE(e1 == e3); }, @@ -140,7 +144,8 @@ TEST_CASE("particle-proxy", "[particle-proxy]") std::size_t i{0}; meta::repeat( - [&i, &proxy](auto const& e) { + [&i, &proxy](auto const& e) + { proxy.position(i) = 11.0; REQUIRE(11.0 == e); ++i; @@ -148,7 +153,8 @@ TEST_CASE("particle-proxy", "[particle-proxy]") meta::sub_tuple(*it, typename proxy_type::range_position_type{})); i = 0; meta::repeat( - [&i, &proxy](auto const& e) { + [&i, &proxy](auto const& e) + { proxy.inputs(i) = 22.0; REQUIRE(22.0 == e); ++i; @@ -156,14 +162,16 @@ TEST_CASE("particle-proxy", "[particle-proxy]") meta::sub_tuple(*it, typename proxy_type::range_inputs_type{})); i = 0; meta::repeat( - [&i, &proxy](auto const& e) { + [&i, &proxy](auto const& e) + { proxy.outputs(i) = 33.0; REQUIRE(33.0 == e); ++i; }, meta::sub_tuple(*it, typename proxy_type::range_outputs_type{})); meta::repeat( - [](auto const& e1, auto& e2) { + [](auto const& e1, auto& e2) + { e2 = 44.0; REQUIRE(44.0 == e1); }, @@ -182,16 +190,16 @@ TEST_CASE("reset-outputs", "[reset-outputs]") container::particle_container<particle_type> c(10); auto it = std::begin(c); - for(std::size_t i{0}; i<c.size(); ++i) + for(std::size_t i{0}; i < c.size(); ++i) { auto proxy = proxy_type(*it); - proxy.position() = container::point<float,3>{float(i), float(i), float(i)}; + proxy.position() = container::point<float, 3>{float(i), float(i), float(i)}; - for(std::size_t ii{0}; ii<proxy.sizeof_inputs(); ++ii) + for(std::size_t ii{0}; ii < proxy.sizeof_inputs(); ++ii) { proxy.inputs(ii) = float(i); } - for(std::size_t ii{0}; ii<proxy.sizeof_outputs(); ++ii) + for(std::size_t ii{0}; ii < proxy.sizeof_outputs(); ++ii) { proxy.outputs(ii) = float(i); } @@ -201,10 +209,11 @@ TEST_CASE("reset-outputs", "[reset-outputs]") } it = std::begin(c); - for(std::size_t i{0}; i<c.size(); ++i) + for(std::size_t i{0}; i < c.size(); ++i) { meta::repeat( - [i](auto const& e) { + [i](auto const& e) + { using type = std::decay_t<decltype(e)>; std::cout << e << ' '; REQUIRE(e == type(i)); @@ -215,17 +224,17 @@ TEST_CASE("reset-outputs", "[reset-outputs]") } it = std::begin(c); - for(std::size_t i{0}; i<c.size(); ++i) + for(std::size_t i{0}; i < c.size(); ++i) { auto proxy = proxy_type(*it); - for(std::size_t ii{0}; ii<proxy.sizeof_outputs(); ++ii) + for(std::size_t ii{0}; ii < proxy.sizeof_outputs(); ++ii) { proxy.outputs(ii) = 0.; } ++it; } it = std::begin(c); - for(std::size_t i{0}; i<c.size(); ++i) + for(std::size_t i{0}; i < c.size(); ++i) { meta::repeat( [i](auto const& e) @@ -238,7 +247,6 @@ TEST_CASE("reset-outputs", "[reset-outputs]") std::cout << '\n'; ++it; } - } } diff --git a/units/container/point_proxy.cpp b/units/container/point_proxy.cpp index e33482aa4573d5bcf108da7e11a6453186e287cb..90d09682a313e7dacfc841efaa195cea81779253 100644 --- a/units/container/point_proxy.cpp +++ b/units/container/point_proxy.cpp @@ -6,13 +6,13 @@ #include <functional> #include <type_traits> #define CATCH_CONFIG_RUNNER +#include "scalfmm/meta/utils.hpp" +#include <array> #include <catch2/catch.hpp> +#include <cpp_tools/colors/colorized.hpp> #include <string> #include <tuple> -#include <array> #include <valarray> -#include "scalfmm/meta/utils.hpp" -#include <cpp_tools/colors/colorized.hpp> //#define SCALFMM_TEST_EXCEPTIONALIZE_STATIC_ASSERT //#include <scalfmm/utils/static_assert_as_exception.hpp> @@ -48,13 +48,12 @@ TEST_CASE("Point proxy construction", "[particle-construction]") SECTION("Default construction", "[default]") { - std::array<float,3> t{0., 1., 2.}; + std::array<float, 3> t{0., 1., 2.}; container::point<float&, 3> proxy(t); std::cout << t.at(0) << '\n'; proxy.at(0) = 63.0f; std::cout << t.at(0) << '\n'; - // ______________________ // TODO: //container::point<float, 3> p{}; @@ -86,7 +85,7 @@ TEST_CASE("Point proxy construction", "[particle-construction]") container::particle_container<container::particle<float, 3, float, 0, float, 0>> c(1); auto it = std::begin(c); - *it = std::make_tuple(5.6,6.6,7.6); + *it = std::make_tuple(5.6, 6.6, 7.6); container::point<float&> proxy_it(*it); std::cout << meta::get<1>(*it) << '\n'; proxy_it[1] = 63.1f; @@ -96,7 +95,7 @@ TEST_CASE("Point proxy construction", "[particle-construction]") std::cout << proxy_it << '\n'; auto itc = std::cbegin(c); - *it = std::make_tuple(5.6,6.6,7.6); + *it = std::make_tuple(5.6, 6.6, 7.6); container::point<const float&> proxy_itc(*itc); std::cout << meta::get<1>(*itc) << '\n'; //proxy_itc.at(1) = 63.1f; @@ -112,10 +111,10 @@ TEST_CASE("Point proxy construction", "[particle-construction]") // Comparison functions template<typename T, typename U, std::size_t D> -bool against(scalfmm::container::point<T,D> const& p1, scalfmm::container::point<U,D> const& p2) +bool against(scalfmm::container::point<T, D> const& p1, scalfmm::container::point<U, D> const& p2) { bool ok{true}; - for(std::size_t i{0}; i<D; ++i) + for(std::size_t i{0}; i < D; ++i) { ok &= (p1.at(i) == p2.at(i)); } @@ -123,10 +122,10 @@ bool against(scalfmm::container::point<T,D> const& p1, scalfmm::container::point } template<typename T, typename U, std::size_t D> -bool against(std::array<T,D> const& p1, scalfmm::container::point<U,D> const& p2) +bool against(std::array<T, D> const& p1, scalfmm::container::point<U, D> const& p2) { bool ok{true}; - for(std::size_t i{0}; i<D; ++i) + for(std::size_t i{0}; i < D; ++i) { ok &= (p1.at(i) == p2.at(i)); } @@ -134,10 +133,10 @@ bool against(std::array<T,D> const& p1, scalfmm::container::point<U,D> const& p2 } template<typename T, typename U, std::size_t D> -bool against(std::valarray<T> const& p1, scalfmm::container::point<U,D> const& p2) +bool against(std::valarray<T> const& p1, scalfmm::container::point<U, D> const& p2) { bool ok{true}; - for(std::size_t i{0}; i<D; ++i) + for(std::size_t i{0}; i < D; ++i) { ok &= (p1[i] == p2.at(i)); } @@ -145,19 +144,19 @@ bool against(std::valarray<T> const& p1, scalfmm::container::point<U,D> const& p } template<typename T, std::size_t D> -bool against(std::valarray<T> const& p1, std::array<T,D> const& p2) +bool against(std::valarray<T> const& p1, std::array<T, D> const& p2) { bool ok{true}; - for(std::size_t i{0}; i<D; ++i) + for(std::size_t i{0}; i < D; ++i) { std::cout << p1[i] << ' ' << p2.at(i) << '\n'; - ok &= ( p1[i] == p2.at(i)); + ok &= (p1[i] == p2.at(i)); } return ok; } template<typename U, typename... Ts, std::size_t D> -bool against(std::tuple<Ts...> const& p1, scalfmm::container::point<U,D> const& p2) +bool against(std::tuple<Ts...> const& p1, scalfmm::container::point<U, D> const& p2) { bool ok{true}; scalfmm::meta::repeat([&ok](auto const& e_p1, auto const& e_p2) { ok &= (e_p1 == e_p2); }, p1, p2); @@ -169,8 +168,8 @@ template<typename ValueType, typename Operator> void apply_operator_unary(Operator&& f) { using value_type = ValueType; - std::array<float, 3> a {1., 2., 3.}; - std::array<float, 3> b {1., 2., 3.}; + std::array<float, 3> a{1., 2., 3.}; + std::array<float, 3> b{1., 2., 3.}; std::valarray<float> a_{1., 2., 3.}; std::valarray<float> b_{1., 2., 3.}; value_type p_a(a); @@ -179,7 +178,7 @@ void apply_operator_unary(Operator&& f) std::invoke(std::forward<Operator>(f), a_, b_); REQUIRE(against(a_, p_a)); // if TestType is a proxy - if constexpr (std::is_same_v<value_type, scalfmm::container::point<float&>>) + if constexpr(std::is_same_v<value_type, scalfmm::container::point<float&>>) { REQUIRE(against(a, p_a)); } @@ -190,9 +189,9 @@ template<typename ValueType, typename Operator> void apply_operator_binary(Operator&& f) { using value_type = ValueType; - std::array<float, 3> a {1., 2., 3.}; - std::array<float, 3> b {1., 2., 3.}; - std::array<float, 3> c {0., 0., 0.}; + std::array<float, 3> a{1., 2., 3.}; + std::array<float, 3> b{1., 2., 3.}; + std::array<float, 3> c{0., 0., 0.}; std::valarray<float> a_{1., 2., 3.}; std::valarray<float> b_{1., 2., 3.}; value_type p_a(a); @@ -200,7 +199,7 @@ void apply_operator_binary(Operator&& f) std::valarray<float> res_{}; std::invoke(std::forward<Operator>(f), a_, b_, res_); // if TestType is a proxy - if constexpr (std::is_same_v<value_type, scalfmm::container::point<float&>>) + if constexpr(std::is_same_v<value_type, scalfmm::container::point<float&>>) { value_type res_proxy(c); std::invoke(std::forward<Operator>(f), p_a, p_b, res_proxy); @@ -223,30 +222,36 @@ TEMPLATE_TEST_CASE("point-construction-common", "[point-construction-common]", s SECTION("from-array", "[from-array]") { - std::array<float,3> t{0., 1., 2.}; + std::array<float, 3> t{0., 1., 2.}; value_type p(t); REQUIRE(against(t, p)); } SECTION("arithmetic-operator", "[arithmetic-operator]") { - apply_operator_unary<value_type>([](auto& a, auto const& b){ a += b; }); - apply_operator_unary<value_type>([](auto& a, auto const& b){ a -= b; }); - apply_operator_unary<value_type>([](auto& a, auto const& b){ a *= b; }); - apply_operator_unary<value_type>([](auto& a, auto const& b){ a /= b; }); - apply_operator_unary<value_type>([](auto& a, [[maybe_unused]] auto const& b){ a += float(8.); }); - apply_operator_unary<value_type>([](auto& a, [[maybe_unused]] auto const& b){ a -= float(8.); }); - apply_operator_unary<value_type>([](auto& a, [[maybe_unused]] auto const& b){ a *= float(8.); }); - apply_operator_unary<value_type>([](auto& a, [[maybe_unused]] auto const& b){ a /= float(8.); }); - apply_operator_binary<value_type>([](auto const& a, auto const& b, auto& r){ r = a + b; }); - apply_operator_binary<value_type>([](auto const& a, auto const& b, auto& r){ r = a - b; }); - apply_operator_binary<value_type>([](auto const& a, auto const& b, auto& r){ r = a * b; }); - apply_operator_binary<value_type>([](auto const& a, auto const& b, auto& r){ r = a / b; }); - apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r){ r = a + float(8.); }); - apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r){ r = a - float(8.); }); - apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r){ r = a * float(8.); }); - apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r){ r = a / float(8.); }); - apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r){ r = a + float(8.); }); - apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r){ r = float(8.) * a; }); + apply_operator_unary<value_type>([](auto& a, auto const& b) { a += b; }); + apply_operator_unary<value_type>([](auto& a, auto const& b) { a -= b; }); + apply_operator_unary<value_type>([](auto& a, auto const& b) { a *= b; }); + apply_operator_unary<value_type>([](auto& a, auto const& b) { a /= b; }); + apply_operator_unary<value_type>([](auto& a, [[maybe_unused]] auto const& b) { a += float(8.); }); + apply_operator_unary<value_type>([](auto& a, [[maybe_unused]] auto const& b) { a -= float(8.); }); + apply_operator_unary<value_type>([](auto& a, [[maybe_unused]] auto const& b) { a *= float(8.); }); + apply_operator_unary<value_type>([](auto& a, [[maybe_unused]] auto const& b) { a /= float(8.); }); + apply_operator_binary<value_type>([](auto const& a, auto const& b, auto& r) { r = a + b; }); + apply_operator_binary<value_type>([](auto const& a, auto const& b, auto& r) { r = a - b; }); + apply_operator_binary<value_type>([](auto const& a, auto const& b, auto& r) { r = a * b; }); + apply_operator_binary<value_type>([](auto const& a, auto const& b, auto& r) { r = a / b; }); + apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r) + { r = a + float(8.); }); + apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r) + { r = a - float(8.); }); + apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r) + { r = a * float(8.); }); + apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r) + { r = a / float(8.); }); + apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r) + { r = a + float(8.); }); + apply_operator_binary<value_type>([](auto const& a, [[maybe_unused]] auto const& b, auto& r) + { r = float(8.) * a; }); } } diff --git a/units/fmm/non_mutual.cpp b/units/fmm/non_mutual.cpp index b265a0faea58274f17c41d62c272a7e32474c37c..ac239a7a70f56753aae38949a10fef8b208d7021 100644 --- a/units/fmm/non_mutual.cpp +++ b/units/fmm/non_mutual.cpp @@ -32,7 +32,7 @@ TEMPLATE_TEST_CASE("test-non-mutual", "[test-non-mutual]", scalfmm::matrix_kerne using far_field_type = scalfmm::operators::far_field_operator<interpolator_type>; path.append("test_1d_ref.fma"); REQUIRE(run<dim, scalfmm::operators::fmm_operators<near_field_type, far_field_type>, value_type>( - path, 4, 2, 5, false, scalfmm::options::seq)); + path, 4, 2, 5, false, scalfmm::options::seq)); } SECTION("fmm test 2D", "[fmm-test-2D]") { diff --git a/units/operators/interp.cpp b/units/operators/interp.cpp index 45a03e1fe2cdc33092daccf6e33b2f83e5be131e..c4d2272063968fb9f7daec9c5a1c448ae61d6014 100644 --- a/units/operators/interp.cpp +++ b/units/operators/interp.cpp @@ -3,8 +3,8 @@ #include <iostream> #include "scalfmm/container/point.hpp" -#include "scalfmm/interpolation/uniform.hpp" #include "scalfmm/interpolation/chebyshev.hpp" +#include "scalfmm/interpolation/uniform.hpp" #include "scalfmm/matrix_kernels/laplace.hpp" using namespace scalfmm; @@ -18,7 +18,7 @@ int test_interp(const std::size_t order) // using interpolator_type = scalfmm::interpolation::uniform_interpolator<value_type, dimension, matrix_kernel_type>; std::string sinterp("_cheb.txt"); using interpolator_type = scalfmm::interpolation::chebyshev_interpolator<value_type, dimension, matrix_kernel_type>; - + using point_type = container::point<value_type, dimension>; // const std::size_t tree_height{3}; @@ -34,22 +34,20 @@ int test_interp(const std::size_t order) std::vector<value_type> poly_of_part(N); std::vector<value_type> der_poly_of_part(N); // generate function atp = 3 - std::size_t p = order/2; + std::size_t p = order / 2; // generate the pth lagrange polynomial and its derivative for(std::size_t part = 0; part < points.size(); ++part) { - poly_of_part[part] = interpolator.polynomials(points[part], p); der_poly_of_part[part] = interpolator.derivative(points[part], p); - std::clog << part << " " << points[part] << " " << poly_of_part[part] - << std::endl; + std::clog << part << " " << points[part] << " " << poly_of_part[part] << std::endl; } // // Save the roots in a file - std::ofstream out("roots"+sinterp); + std::ofstream out("roots" + sinterp); for(std::size_t part = 0; part < roots.size(); ++part) { @@ -60,7 +58,7 @@ int test_interp(const std::size_t order) out.close(); // save basis function p and its derivative in a file - std::ofstream out_p("points"+sinterp); + std::ofstream out_p("points" + sinterp); for(std::size_t part = 0; part < points.size(); ++part) { @@ -68,14 +66,16 @@ int test_interp(const std::size_t order) } out_p.close(); - std::ofstream out_p1("lagrange"+sinterp); + std::ofstream out_p1("lagrange" + sinterp); - for (std::size_t part = 0; part < points.size(); ++part) { - out_p1 << points[part] << " "; - for (int n = 0; n < order; ++n) { - out_p1 << interpolator.polynomials(points[part], n) << " "; - } - out_p1 << std::endl; + for(std::size_t part = 0; part < points.size(); ++part) + { + out_p1 << points[part] << " "; + for(int n = 0; n < order; ++n) + { + out_p1 << interpolator.polynomials(points[part], n) << " "; + } + out_p1 << std::endl; } out_p1.close(); diff --git a/units/operators/l2p.cpp b/units/operators/l2p.cpp index ae032be452a7bbe032bc76232617cb9a5319836f..4ddeb0019416bb2c38334aa903e3ee8fbb59fc8e 100644 --- a/units/operators/l2p.cpp +++ b/units/operators/l2p.cpp @@ -2,6 +2,7 @@ // Units for l2p operator // ---------------------- // @FUSE_FFTW + #include "scalfmm/operators/l2p.hpp" #include "scalfmm/container/particle.hpp" #include "scalfmm/container/point.hpp" @@ -12,8 +13,14 @@ #include "scalfmm/meta/utils.hpp" #include "scalfmm/operators/fmm_operators.hpp" #include "scalfmm/tree/cell.hpp" -#include "scalfmm/tree/leaf.hpp" +#include "scalfmm/tree/group_of_views.hpp" +#include "scalfmm/tree/leaf_view.hpp" +// #include "scalfmm/tree/leaf.hpp" #include "scalfmm/utils/generate.hpp" + +#include "xtensor/xeval.hpp" +#include "xtensor/xmanipulation.hpp" + #include <algorithm> #include <array> #include <iostream> @@ -22,16 +29,26 @@ #include <string> #include <tuple> #include <utility> -#include <xtensor/xeval.hpp> -#include <xtensor/xmanipulation.hpp> #define CATCH_CONFIG_RUNNER #include <catch2/catch.hpp> using namespace scalfmm; +template<typename LeafType, typename ContainerType> +auto init_leaf(LeafType& leaf, ContainerType const& container) -> void +{ + auto leaf_container_begin = leaf.particles().first; + + for(std::size_t index_part = 0; index_part < leaf.size(); ++index_part) + { + *leaf_container_begin = container.at(index_part).as_tuple(); + ++leaf_container_begin; + } +} + template<typename FmmOperator, typename ValueType> -void test_l2p(std::size_t order) +auto test_l2p(std::size_t order) -> void { using value_type = ValueType; // @@ -48,7 +65,10 @@ void test_l2p(std::size_t order) // using particle_type = scalfmm::container::particle<value_type, dimension, value_type, nb_inputs, value_type, nb_outputs, std::size_t>; - using leaf_type = scalfmm::component::leaf<particle_type>; + // using leaf_type = scalfmm::component::leaf<particle_type>; + using leaf_type = scalfmm::component::leaf_view<particle_type>; + using storage_type = typename leaf_type::storage_type; + using cell_type = scalfmm::component::cell<typename interpolator_type::storage_type>; const std::size_t tree_height{3}; @@ -62,7 +82,6 @@ void test_l2p(std::size_t order) // container::point<value_type, dimension> center{utils::get_center<value_type, dimension>()}; - //Dimension of the test box const value_type width{0.25}; const auto half_width{width * 0.5}; @@ -74,7 +93,13 @@ void test_l2p(std::size_t order) // outputs are set to zero in the function. auto container{utils::generate_particles<particle_type>(nb_particles, center, width)}; // We create the leaf corresponding to the box. - leaf_type leaf(std::cbegin(container), std::cend(container), container.size(), center, width, 0); + // leaf_type leaf(std::cbegin(container), std::cend(container), container.size(), center, width, 0); + + storage_type storage(nb_particles, 1); + leaf_type leaf(std::make_pair(std::begin(storage), std::begin(storage) + nb_particles), storage.symbolics()); + leaf.center() = center; + leaf.width() = width; + init_leaf(leaf, container); // Then the cell cell_type cell(center, width, order); @@ -100,11 +125,11 @@ void test_l2p(std::size_t order) auto& locals = cell.locals(); // Here We take a simple function to test the l2p operator, it just sums all the coordinates of a point and multily by 2. - auto func = [](auto const&... e) { return 2*(e + ...); }; + auto func = [](auto const&... e) { return 2 * (e + ...); }; // We take the flatten view of our multidimensional grid and apply the function func to the grid. // We store it as the local expansion of the cell. - for(std::size_t n{0}; n<nb_outputs; ++n) + for(std::size_t n{0}; n < nb_outputs; ++n) { if constexpr(dimension == 1) { @@ -119,8 +144,10 @@ void test_l2p(std::size_t order) // we call the operator operators::l2p(far_field, cell, leaf); - auto const& particles = leaf.cparticles(); - auto particle_begin = particles.begin(); + // auto const& particles = leaf.cparticles(); + // auto particle_begin = particles.begin(); + + auto particle_begin = leaf.particles().first; // Now for each outputs in our leaf, we will apply the same function func to the position of the particle // and compare this result to the one store in the outputs of the leaf. @@ -132,19 +159,17 @@ void test_l2p(std::size_t order) auto result_of_func = std::apply(func, std::array<value_type, dimension>(p.position())); // for each output we compare it the result of func(x_i) meta::repeat( - [&result_of_func](auto const& output) { - REQUIRE(utils::almost_equal(result_of_func, output, std::numeric_limits<value_type>::digits10)); - }, + [&result_of_func](auto const& output) + { REQUIRE(utils::almost_equal(result_of_func, output, std::numeric_limits<value_type>::digits10)); }, p.outputs()); ++particle_begin; } } -TEMPLATE_TEST_CASE("l2p-test-3D-1PV-double", "[l2p-test-3D-1PV-double]" - , scalfmm::options::chebyshev_<scalfmm::options::dense_> - , scalfmm::options::uniform_<scalfmm::options::dense_> - , scalfmm::options::uniform_<scalfmm::options::fft_> - ) +TEMPLATE_TEST_CASE("l2p-test-3D-1PV-double", "[l2p-test-3D-1PV-double]", + scalfmm::options::chebyshev_<scalfmm::options::dense_>, + scalfmm::options::uniform_<scalfmm::options::dense_>, + scalfmm::options::uniform_<scalfmm::options::fft_>) { using options_type = TestType; using value_type = double; @@ -174,11 +199,10 @@ TEMPLATE_TEST_CASE("l2p-test-3D-1PV-double", "[l2p-test-3D-1PV-double]" } } -TEMPLATE_TEST_CASE("l2p-test-3D-1PV-float", "[l2p-test-3D-1PV-float]" - , scalfmm::options::chebyshev_<scalfmm::options::dense_> - , scalfmm::options::uniform_<scalfmm::options::dense_> - , scalfmm::options::uniform_<scalfmm::options::fft_> - ) +TEMPLATE_TEST_CASE("l2p-test-3D-1PV-float", "[l2p-test-3D-1PV-float]", + scalfmm::options::chebyshev_<scalfmm::options::dense_>, + scalfmm::options::uniform_<scalfmm::options::dense_>, + scalfmm::options::uniform_<scalfmm::options::fft_>) { using options_type = TestType; using value_type = float; diff --git a/units/operators/p2p_full_mutual.cpp b/units/operators/p2p_full_mutual.cpp index 98a4e7ae72608151c525ee041303ecf4f0193047..934657a687c3a95dfef2b99c07522cc46763270d 100644 --- a/units/operators/p2p_full_mutual.cpp +++ b/units/operators/p2p_full_mutual.cpp @@ -83,16 +83,20 @@ auto test_p2p_full_mutual(std::size_t order) -> void auto const& outputs_f2 = leaf_f2[0]->coutputs(); auto container_it = std::cbegin(outputs_direct_f0); - std::for_each(std::begin(outputs_f1), std::end(outputs_f1), [&container_it](auto const& p) { - REQUIRE(utils::meta_compare(p, *container_it, - [](auto const& a, auto const& b) { return utils::almost_equal(a, b, 7); })); - ++container_it; - }); - std::for_each(std::begin(outputs_f2), std::end(outputs_f2), [&container_it](auto const& p) { - REQUIRE(utils::meta_compare(p, *container_it, - [](auto const& a, auto const& b) { return utils::almost_equal(a, b, 7); })); - ++container_it; - }); + std::for_each(std::begin(outputs_f1), std::end(outputs_f1), + [&container_it](auto const& p) + { + REQUIRE(utils::meta_compare(p, *container_it, [](auto const& a, auto const& b) + { return utils::almost_equal(a, b, 7); })); + ++container_it; + }); + std::for_each(std::begin(outputs_f2), std::end(outputs_f2), + [&container_it](auto const& p) + { + REQUIRE(utils::meta_compare(p, *container_it, [](auto const& a, auto const& b) + { return utils::almost_equal(a, b, 7); })); + ++container_it; + }); } TEMPLATE_TEST_CASE("p2p-inner-test-3D-1PV", "[p2p-inner-test-3D-1PV]", double) //, float) diff --git a/units/operators/p2p_inner.cpp b/units/operators/p2p_inner.cpp index 603ca01a00cfca191dcbdaba68f12a4769d96030..dc75797518b54091bd917f7247aa452926e4c715 100644 --- a/units/operators/p2p_inner.cpp +++ b/units/operators/p2p_inner.cpp @@ -60,13 +60,18 @@ void test_p2p_inner(std::size_t order) auto const& output = leaf.coutputs(); auto container_it = std::cbegin(outputs); - std::for_each(std::begin(output), std::end(output), [&container_it](auto const& p) { - REQUIRE(utils::meta_compare(p, *container_it, [](auto const& a, auto const& b) { - return std::fabs(a - b) <= std::numeric_limits<value_type>::epsilon() * 5; - // return utils::almost_equal(a, b, std::numeric_limits<value_type>::digits10); - })); - ++container_it; - }); + std::for_each(std::begin(output), std::end(output), + [&container_it](auto const& p) + { + REQUIRE(utils::meta_compare(p, *container_it, + [](auto const& a, auto const& b) + { + return std::fabs(a - b) <= + std::numeric_limits<value_type>::epsilon() * 5; + // return utils::almost_equal(a, b, std::numeric_limits<value_type>::digits10); + })); + ++container_it; + }); } TEMPLATE_TEST_CASE("p2p-inner-test-3D-1PV", "[p2p-inner-test-3D-1PV]", float) diff --git a/units/tree/interaction_list.cpp b/units/tree/interaction_list.cpp index 5325ac504cc142b322d561ae64a647e8b68f1b16..7b7d8b5fc4afa90785b5c27c96ded63d3b150f43 100644 --- a/units/tree/interaction_list.cpp +++ b/units/tree/interaction_list.cpp @@ -376,11 +376,11 @@ TEST_CASE("interaction list p2p", "[interaction-list-p2p]") const int neighbour_separation = 1; const bool with_depend = true; - scalfmm::list::sequential::build_interaction_lists( tree, tree, neighbour_separation, with_depend); - if(with_depend) - { - scalfmm::list::reconstruct_p2p_mutual_interaction_lists(tree); - } + scalfmm::list::sequential::build_interaction_lists(tree, tree, neighbour_separation, with_depend); + if(with_depend) + { + scalfmm::list::reconstruct_p2p_mutual_interaction_lists(tree); + } tree.trace(std::cout, 4); scalfmm::component::for_each_leaf( tree.begin(), tree.end(), @@ -388,40 +388,40 @@ TEST_CASE("interaction list p2p", "[interaction-list-p2p]") { auto const& indexes = leaf.csymbolics().interaction_indexes; auto interaction_morton_index = leaf.csymbolics().interaction_iterators; - auto my_index = leaf.index(); + auto my_index = leaf.index(); auto ref1{get_symbolic_list_p2p(leaf.index())}; - std::vector<std::size_t> ref(ref1.size()); - int dim = 0; + std::vector<std::size_t> ref(ref1.size()); + int dim = 0; - if(with_depend) - { - for(auto v: ref1) + if(with_depend) { - if(v < my_index) + for(auto v: ref1) { - ref[dim] = v; - ++dim; - } - else - { - break; + if(v < my_index) + { + ref[dim] = v; + ++dim; + } + else + { + break; + } } + ref.resize(dim); } - ref.resize(dim); - } - else - { - for(auto v: ref1) + else { - ref[dim] = v; - ++dim; + for(auto v: ref1) + { + ref[dim] = v; + ++dim; + } } - } - for(std::size_t i{0}; i < leaf.csymbolics().existing_neighbors_in_group; ++i) - { - REQUIRE(ref.at(i) == indexes.at(i)); + for(std::size_t i{0}; i < leaf.csymbolics().existing_neighbors_in_group; ++i) + { + REQUIRE(ref.at(i) == indexes.at(i)); } for(std::size_t i{0}; i < leaf.csymbolics().existing_neighbors_in_group; ++i) { @@ -455,12 +455,12 @@ TEST_CASE("interaction list p2p", "[interaction-list-p2p]") const int neighbour_separation = 1; const bool with_depend = false; - scalfmm::list::sequential::build_interaction_lists( tree, tree, neighbour_separation, with_depend); + scalfmm::list::sequential::build_interaction_lists(tree, tree, neighbour_separation, with_depend); - if(with_depend) - { - scalfmm::list::reconstruct_p2p_mutual_interaction_lists(tree); - } + if(with_depend) + { + scalfmm::list::reconstruct_p2p_mutual_interaction_lists(tree); + } scalfmm::component::for_each_leaf( tree.begin(), tree.end(), [](auto const& leaf)