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, &gtree);
@@ -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 = [&center, &width, &random_r]() {
+    auto make_particle = [&center, &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)