From 99d5fe727a8825da6f17693196e2e4f3e9866d77 Mon Sep 17 00:00:00 2001
From: Berenger Bramas <berenger.bramas@mpcdf.mpg.de>
Date: Fri, 22 Sep 2017 15:53:10 +0200
Subject: [PATCH] Update BFPS interface to include the new P2P classes

---
 bfps/cpp/full_code/NSVEparticlesP2P.cpp       | 113 ++++++++++++++++++
 bfps/cpp/full_code/NSVEparticlesP2P.hpp       |  82 +++++++++++++
 .../particles/abstract_particles_system.hpp   |   2 +
 bfps/cpp/particles/lock_free_bool_array.hpp   |   4 +-
 bfps/cpp/particles/p2p_computer.hpp           |   6 +
 bfps/cpp/particles/p2p_computer_empty.hpp     |  31 +++++
 bfps/cpp/particles/p2p_distr_mpi.hpp          |   6 +-
 bfps/cpp/particles/particles_distr_mpi.hpp    | 112 ++++++++++++++---
 bfps/cpp/particles/particles_system.hpp       |  37 +++++-
 .../particles/particles_system_builder.hpp    |  30 ++++-
 setup.py                                      |   3 +-
 11 files changed, 393 insertions(+), 33 deletions(-)
 create mode 100644 bfps/cpp/full_code/NSVEparticlesP2P.cpp
 create mode 100644 bfps/cpp/full_code/NSVEparticlesP2P.hpp
 create mode 100644 bfps/cpp/particles/p2p_computer_empty.hpp

diff --git a/bfps/cpp/full_code/NSVEparticlesP2P.cpp b/bfps/cpp/full_code/NSVEparticlesP2P.cpp
new file mode 100644
index 00000000..08326119
--- /dev/null
+++ b/bfps/cpp/full_code/NSVEparticlesP2P.cpp
@@ -0,0 +1,113 @@
+#include <string>
+#include <cmath>
+#include "NSVEparticlesP2P.hpp"
+#include "scope_timer.hpp"
+#include "particles/particles_sampling.hpp"
+#include "particles/p2p_computer.hpp"
+
+template <typename rnumber>
+int NSVEparticlesP2P<rnumber>::initialize(void)
+{
+    this->NSVE<rnumber>::initialize();
+
+    this->ps = particles_system_builder(
+                this->fs->cvelocity,              // (field object)
+                this->fs->kk,                     // (kspace object, contains dkx, dky, dkz)
+                tracers0_integration_steps, // to check coherency between parameters and hdf input file (nb rhs)
+                (long long int)nparticles,  // to check coherency between parameters and hdf input file
+                this->fs->get_current_fname(),    // particles input filename
+                std::string("/tracers0/state/") + std::to_string(this->fs->iteration), // dataset name for initial input
+                std::string("/tracers0/rhs/")  + std::to_string(this->fs->iteration),  // dataset name for initial input
+                tracers0_neighbours,        // parameter (interpolation no neighbours)
+                tracers0_smoothness,        // parameter
+                this->comm,
+                this->fs->iteration+1);
+    // TODO P2P write particle data too
+    this->particles_output_writer_mpi = new particles_output_hdf5<
+        long long int, double, 3, 3>(
+                MPI_COMM_WORLD,
+                "tracers0",
+                nparticles,
+                tracers0_integration_steps);
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber>
+int NSVEparticlesP2P<rnumber>::step(void)
+{
+    this->fs->compute_velocity(this->fs->cvorticity);
+    this->fs->cvelocity->ift();
+    this->ps->completeLoop(this->dt);
+    this->NSVE<rnumber>::step();
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber>
+int NSVEparticlesP2P<rnumber>::write_checkpoint(void)
+{
+    this->NSVE<rnumber>::write_checkpoint();
+    this->particles_output_writer_mpi->open_file(this->fs->get_current_fname());
+    // TODO P2P write particle data too
+    this->particles_output_writer_mpi->save(
+            this->ps->getParticlesPositions(),
+            this->ps->getParticlesRhs(),
+            this->ps->getParticlesIndexes(),
+            this->ps->getLocalNbParticles(),
+            this->fs->iteration);
+    this->particles_output_writer_mpi->close_file();
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber>
+int NSVEparticlesP2P<rnumber>::finalize(void)
+{
+    this->ps.release();
+    delete this->particles_output_writer_mpi;
+    this->NSVE<rnumber>::finalize();
+    return EXIT_SUCCESS;
+}
+
+/** \brief Compute fluid stats and sample fields at particle locations.
+ */
+
+template <typename rnumber>
+int NSVEparticlesP2P<rnumber>::do_stats()
+{
+    /// fluid stats go here
+    this->NSVE<rnumber>::do_stats();
+
+
+    if (!(this->iteration % this->niter_part == 0))
+        return EXIT_SUCCESS;
+
+    /// sample position
+    sample_particles_system_position(
+                                 this->ps,
+                                 (this->simname + "_particles.h5"), // filename
+                                 "tracers0",                        // hdf5 parent group
+                                 "position"                         // dataset basename TODO
+                                 );
+
+    /// sample velocity
+    sample_from_particles_system(*this->tmp_vec_field,              // field to save
+                                 this->ps,
+                                 (this->simname + "_particles.h5"), // filename
+                                 "tracers0",                        // hdf5 parent group
+                                 "velocity"                         // dataset basename TODO
+                                 );
+
+    /// compute acceleration and sample it
+    this->fs->compute_Lagrangian_acceleration(this->tmp_vec_field);
+    this->tmp_vec_field->ift();
+    sample_from_particles_system(*this->tmp_vec_field,
+                                 this->ps,
+                                 (this->simname + "_particles.h5"),
+                                 "tracers0",
+                                 "acceleration");
+
+    return EXIT_SUCCESS;
+}
+
+template class NSVEparticlesP2P<float>;
+template class NSVEparticlesP2P<double>;
+
diff --git a/bfps/cpp/full_code/NSVEparticlesP2P.hpp b/bfps/cpp/full_code/NSVEparticlesP2P.hpp
new file mode 100644
index 00000000..9b015659
--- /dev/null
+++ b/bfps/cpp/full_code/NSVEparticlesP2P.hpp
@@ -0,0 +1,82 @@
+/**********************************************************************
+*                                                                     *
+*  Copyright 2017 Max Planck Institute                                *
+*                 for Dynamics and Self-Organization                  *
+*                                                                     *
+*  This file is part of bfps.                                         *
+*                                                                     *
+*  bfps is free software: you can redistribute it and/or modify       *
+*  it under the terms of the GNU General Public License as published  *
+*  by the Free Software Foundation, either version 3 of the License,  *
+*  or (at your option) any later version.                             *
+*                                                                     *
+*  bfps is distributed in the hope that it will be useful,            *
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the      *
+*  GNU General Public License for more details.                       *
+*                                                                     *
+*  You should have received a copy of the GNU General Public License  *
+*  along with bfps.  If not, see <http://www.gnu.org/licenses/>       *
+*                                                                     *
+* Contact: Cristian.Lalescu@ds.mpg.de                                 *
+*                                                                     *
+**********************************************************************/
+
+
+
+#ifndef NSVEPARTICLESP2P_HPP
+#define NSVEPARTICLESP2P_HPP
+
+
+
+#include <cstdlib>
+#include "base.hpp"
+#include "vorticity_equation.hpp"
+#include "full_code/NSVE.hpp"
+#include "particles/particles_system_builder.hpp"
+#include "particles/particles_output_hdf5.hpp"
+
+/** \brief Navier-Stokes solver that includes simple Lagrangian tracers.
+ *
+ *  Child of Navier Stokes vorticity equation solver, this class calls all the
+ *  methods from `NSVE`, and in addition integrates simple Lagrangian tracers
+ *  in the resulting velocity field.
+ */
+
+template <typename rnumber>
+class NSVEparticlesP2P: public NSVE<rnumber>
+{
+    public:
+
+        /* parameters that are read in read_parameters */
+        int niter_part;
+        int nparticles;
+        int tracers0_integration_steps;
+        int tracers0_neighbours;
+        int tracers0_smoothness;
+
+        /* other stuff */
+        std::unique_ptr<abstract_particles_system<long long int, double>> ps;
+        // TODO P2P use a reader with particle data
+        particles_output_hdf5<long long int, double,3,3> *particles_output_writer_mpi;
+
+
+        NSVEparticlesP2P(
+                const MPI_Comm COMMUNICATOR,
+                const std::string &simulation_name):
+            NSVE<rnumber>(
+                    COMMUNICATOR,
+                    simulation_name){}
+        ~NSVEparticlesP2P(){}
+
+        int initialize(void);
+        int step(void);
+        int finalize(void);
+
+        int read_parameters(void);
+        int write_checkpoint(void);
+        int do_stats(void);
+};
+
+#endif//NSVEPARTICLESP2P_HPP
+
diff --git a/bfps/cpp/particles/abstract_particles_system.hpp b/bfps/cpp/particles/abstract_particles_system.hpp
index 1c8592f3..26ce7198 100644
--- a/bfps/cpp/particles/abstract_particles_system.hpp
+++ b/bfps/cpp/particles/abstract_particles_system.hpp
@@ -14,6 +14,8 @@ class abstract_particles_system {
 public:
     virtual void compute() = 0;
 
+    virtual void compute_p2p() = 0;
+
     virtual void move(const real_number dt) = 0;
 
     virtual void redistribute() = 0;
diff --git a/bfps/cpp/particles/lock_free_bool_array.hpp b/bfps/cpp/particles/lock_free_bool_array.hpp
index d0b2aa2b..928c1753 100644
--- a/bfps/cpp/particles/lock_free_bool_array.hpp
+++ b/bfps/cpp/particles/lock_free_bool_array.hpp
@@ -15,7 +15,7 @@ public:
         }
     }
 
-    void lock(const int inKey){
+    void lock(const long int inKey){
         volatile long int* k = keys[inKey%keys.size()].get();
         long int res = 1;
         while(res == 1){
@@ -23,7 +23,7 @@ public:
         }
     }
 
-    void unlock(const int inKey){
+    void unlock(const long int inKey){
         volatile long int* k = keys[inKey%keys.size()].get();
         assert(k && *k);
         (*k) = 0;
diff --git a/bfps/cpp/particles/p2p_computer.hpp b/bfps/cpp/particles/p2p_computer.hpp
index cb384950..de024ff7 100644
--- a/bfps/cpp/particles/p2p_computer.hpp
+++ b/bfps/cpp/particles/p2p_computer.hpp
@@ -6,6 +6,8 @@
 template <class real_number, class partsize_t>
 class p2p_computer{
 public:
+    constexpr static int size_data = 3;
+
     template <int size_particle_rhs>
     void init_result_array(real_number rhs[], const partsize_t nbParticles) const{
         memset(rhs, 0, sizeof(real_number)*nbParticles*size_particle_rhs);
@@ -28,6 +30,10 @@ public:
         // TODO put the kernel here
         static_assert(size_particle_positions == 3, "This kernel works only with 3 values for one position");
     }
+
+    constexpr static bool isEmpty() {
+        return false;
+    }
 };
 
 #endif
diff --git a/bfps/cpp/particles/p2p_computer_empty.hpp b/bfps/cpp/particles/p2p_computer_empty.hpp
new file mode 100644
index 00000000..7076061e
--- /dev/null
+++ b/bfps/cpp/particles/p2p_computer_empty.hpp
@@ -0,0 +1,31 @@
+#ifndef P2P_COMPUTER_EMPTY_HPP
+#define P2P_COMPUTER_EMPTY_HPP
+
+#include <cstring>
+
+template <class real_number, class partsize_t>
+class p2p_computer_empty{
+public:
+    constexpr int static size_data = 0;
+
+    template <int size_particle_rhs>
+    void init_result_array(real_number /*rhs*/[], const partsize_t /*nbParticles*/) const{
+    }
+
+    template <int size_particle_rhs>
+    void reduce_particles_rhs(real_number /*rhs_dst*/[], const real_number /*rhs_src*/[], const partsize_t /*nbParticles*/) const{
+    }
+
+    template <int size_particle_positions, int size_particle_data, int size_particle_rhs>
+    void compute_interaction(const real_number /*pos_part1*/[], const real_number /*data_part1*/[], real_number /*rhs_part1*/[],
+                             const real_number /*pos_part2*/[], const real_number /*data_part2*/[], real_number /*rhs_part2*/[],
+                             const real_number /*dist_pow2*/,
+                             const real_number /*xshift_coef*/, const real_number /*yshift_coef*/, const real_number /*zshift_coef*/) const{
+    }
+
+    constexpr static bool isEmpty() {
+        return true;
+    }
+};
+
+#endif
diff --git a/bfps/cpp/particles/p2p_distr_mpi.hpp b/bfps/cpp/particles/p2p_distr_mpi.hpp
index 12b349dc..ccc236d6 100644
--- a/bfps/cpp/particles/p2p_distr_mpi.hpp
+++ b/bfps/cpp/particles/p2p_distr_mpi.hpp
@@ -300,7 +300,7 @@ public:
                     part_to_sort.back().second = idxPart;
                 }
 
-                assert(part_to_sort.size() == (current_my_nb_particles_per_partition[idxPartition]));
+                assert(partsize_t(part_to_sort.size()) == (current_my_nb_particles_per_partition[idxPartition]));
 
                 std::sort(part_to_sort.begin(), part_to_sort.end(),
                           [](const std::pair<long int,partsize_t>& p1,
@@ -575,7 +575,7 @@ public:
                                 const int nbNeighbors = my_tree.getNeighbors(current_cell_idx, neighbors, neighbors_indexes, shift, true);
 
                                 // with other interval
-                                for(size_t idx_neighbor = 0 ; idx_neighbor < nbNeighbors ; ++idx_neighbor){
+                                for(int idx_neighbor = 0 ; idx_neighbor < nbNeighbors ; ++idx_neighbor){
                                     cells_locker.lock(neighbors_indexes[idx_neighbor]);
 
                                     for(size_t idx_2 = 0 ; idx_2 < (*neighbors[idx_neighbor]).size() ; ++idx_2){
@@ -726,7 +726,7 @@ public:
 
                 for(size_t idx_1 = 0 ; idx_1 < intervals.size() ; ++idx_1){
                     // with other interval
-                    for(size_t idx_neighbor = 0 ; idx_neighbor < nbNeighbors ; ++idx_neighbor){
+                    for(int idx_neighbor = 0 ; idx_neighbor < nbNeighbors ; ++idx_neighbor){
                         if(currenct_cell_idx < neighbors_indexes[idx_neighbor]){
                             cells_locker.lock(neighbors_indexes[idx_neighbor]);
 
diff --git a/bfps/cpp/particles/particles_distr_mpi.hpp b/bfps/cpp/particles/particles_distr_mpi.hpp
index 48559518..f0c09fd9 100644
--- a/bfps/cpp/particles/particles_distr_mpi.hpp
+++ b/bfps/cpp/particles/particles_distr_mpi.hpp
@@ -35,6 +35,9 @@ protected:
         TAG_LOW_UP_MOVED_PARTICLES_INDEXES,
         TAG_UP_LOW_MOVED_PARTICLES_INDEXES,
 
+        TAG_LOW_UP_MOVED_PARTICLES_DATA,
+        TAG_UP_LOW_MOVED_PARTICLES_DATA,
+
         TAG_LOW_UP_MOVED_PARTICLES_RHS,
         TAG_LOW_UP_MOVED_PARTICLES_RHS_MAX = TAG_LOW_UP_MOVED_PARTICLES_RHS+MaxNbRhs,
 
@@ -503,13 +506,14 @@ public:
 
     ////////////////////////////////////////////////////////////////////////////
 
-    template <class computer_class, int size_particle_positions, int size_particle_rhs, int size_particle_index>
+    template <class computer_class, int size_particle_positions, int size_particle_data, int size_particle_rhs, int size_particle_index>
     void redistribute(computer_class& in_computer,
                       partsize_t current_my_nb_particles_per_partition[],
                       partsize_t* nb_particles,
                       std::unique_ptr<real_number[]>* inout_positions_particles,
                       std::unique_ptr<real_number[]> inout_rhs_particles[], const int in_nb_rhs,
-                      std::unique_ptr<partsize_t[]>* inout_index_particles){
+                      std::unique_ptr<partsize_t[]>* inout_index_particles,
+                      std::unique_ptr<real_number[]>* inout_data_particles){
         TIMEZONE("redistribute");
 
         // Some latest processes might not be involved
@@ -537,7 +541,13 @@ public:
         },
                     [&](const partsize_t idx1, const partsize_t idx2){
             for(int idx_val = 0 ; idx_val < size_particle_index ; ++idx_val){
-                std::swap((*inout_index_particles)[idx1], (*inout_index_particles)[idx2]);
+                std::swap((*inout_index_particles)[size_particle_index*idx1+idx_val],
+                          (*inout_index_particles)[size_particle_index*idx2+idx_val]);
+            }
+
+            for(int idx_val = 0 ; idx_val < size_particle_data ; ++idx_val){
+                std::swap((*inout_data_particles)[size_particle_data*idx1+idx_val],
+                          (*inout_data_particles)[size_particle_data*idx2+idx_val]);
             }
 
             for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
@@ -562,7 +572,13 @@ public:
         },
                     [&](const partsize_t idx1, const partsize_t idx2){
             for(int idx_val = 0 ; idx_val < size_particle_index ; ++idx_val){
-                std::swap((*inout_index_particles)[idx1], (*inout_index_particles)[idx2]);
+                std::swap((*inout_index_particles)[size_particle_index*idx1+idx_val],
+                          (*inout_index_particles)[size_particle_index*idx2+idx_val]);
+            }
+
+            for(int idx_val = 0 ; idx_val < size_particle_data ; ++idx_val){
+                std::swap((*inout_data_particles)[size_particle_data*idx1+idx_val],
+                          (*inout_data_particles)[size_particle_data*idx2+idx_val]);
             }
 
             for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
@@ -581,6 +597,8 @@ public:
         std::unique_ptr<real_number[]> newParticlesUp;
         std::unique_ptr<partsize_t[]> newParticlesLowIndexes;
         std::unique_ptr<partsize_t[]> newParticlesUpIndexes;
+        std::unique_ptr<real_number[]> newParticlesLowData;
+        std::unique_ptr<real_number[]> newParticlesUpData;
         std::vector<std::unique_ptr<real_number[]>> newParticlesLowRhs(in_nb_rhs);
         std::vector<std::unique_ptr<real_number[]>> newParticlesUpRhs(in_nb_rhs);
 
@@ -607,13 +625,24 @@ public:
                 assert(nbOutLower*size_particle_positions < std::numeric_limits<int>::max());
                 AssertMpi(MPI_Isend(&(*inout_positions_particles)[0], int(nbOutLower*size_particle_positions), particles_utils::GetMpiType(real_number()), (my_rank-1+nb_processes_involved)%nb_processes_involved, TAG_LOW_UP_MOVED_PARTICLES,
                           MPI_COMM_WORLD, &mpiRequests.back()));
+
                 whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
                 mpiRequests.emplace_back();
-                assert(nbOutLower < std::numeric_limits<int>::max());
-                AssertMpi(MPI_Isend(&(*inout_index_particles)[0], int(nbOutLower), particles_utils::GetMpiType(partsize_t()),
+                assert(nbOutLower*size_particle_index < std::numeric_limits<int>::max());
+                AssertMpi(MPI_Isend(&(*inout_index_particles)[0], int(nbOutLower*size_particle_index), particles_utils::GetMpiType(partsize_t()),
                           (my_rank-1+nb_processes_involved)%nb_processes_involved, TAG_LOW_UP_MOVED_PARTICLES_INDEXES,
                           MPI_COMM_WORLD, &mpiRequests.back()));
 
+                if(size_particle_data){
+                    whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
+                    mpiRequests.emplace_back();
+                    assert(nbOutLower*size_particle_data < std::numeric_limits<int>::max());
+                    AssertMpi(MPI_Isend(&(*inout_data_particles)[0], int(nbOutLower*size_particle_data),
+                              particles_utils::GetMpiType(partsize_t()),
+                              (my_rank-1+nb_processes_involved)%nb_processes_involved, TAG_LOW_UP_MOVED_PARTICLES_DATA,
+                              MPI_COMM_WORLD, &mpiRequests.back()));
+                }
+
                 for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
                     whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
                     mpiRequests.emplace_back();
@@ -643,13 +672,23 @@ public:
                 AssertMpi(MPI_Isend(&(*inout_positions_particles)[(myTotalNbParticles-nbOutUpper)*size_particle_positions],
                           int(nbOutUpper*size_particle_positions), particles_utils::GetMpiType(real_number()), (my_rank+1)%nb_processes_involved, TAG_UP_LOW_MOVED_PARTICLES,
                           MPI_COMM_WORLD, &mpiRequests.back()));
+
                 whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
                 mpiRequests.emplace_back();
-                assert(nbOutUpper < std::numeric_limits<int>::max());
-                AssertMpi(MPI_Isend(&(*inout_index_particles)[(myTotalNbParticles-nbOutUpper)], int(nbOutUpper),
+                assert(nbOutUpper*size_particle_index < std::numeric_limits<int>::max());
+                AssertMpi(MPI_Isend(&(*inout_index_particles)[(myTotalNbParticles-nbOutUpper)*size_particle_index], int(nbOutUpper*size_particle_index),
                           particles_utils::GetMpiType(partsize_t()), (my_rank+1)%nb_processes_involved, TAG_UP_LOW_MOVED_PARTICLES_INDEXES,
                           MPI_COMM_WORLD, &mpiRequests.back()));
 
+                if(size_particle_data){
+                    whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
+                    mpiRequests.emplace_back();
+                    assert(nbOutUpper*size_particle_data < std::numeric_limits<int>::max());
+                    AssertMpi(MPI_Isend(&(*inout_data_particles)[(myTotalNbParticles-nbOutUpper)*size_particle_data],
+                              int(nbOutUpper*size_particle_data),
+                              particles_utils::GetMpiType(partsize_t()), (my_rank+1)%nb_processes_involved, TAG_UP_LOW_MOVED_PARTICLES_DATA,
+                              MPI_COMM_WORLD, &mpiRequests.back()));
+                }
 
                 for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
                     whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
@@ -684,14 +723,26 @@ public:
                                   (my_rank-1+nb_processes_involved)%nb_processes_involved, TAG_UP_LOW_MOVED_PARTICLES,
                                   MPI_COMM_WORLD, &mpiRequests.back()));
 
-                        newParticlesLowIndexes.reset(new partsize_t[nbNewFromLow]);
+                        newParticlesLowIndexes.reset(new partsize_t[nbNewFromLow*size_particle_index]);
                         whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
                         mpiRequests.emplace_back();
-                        assert(nbNewFromLow < std::numeric_limits<int>::max());
-                        AssertMpi(MPI_Irecv(&newParticlesLowIndexes[0], int(nbNewFromLow), particles_utils::GetMpiType(partsize_t()),
+                        assert(nbNewFromLow*size_particle_index < std::numeric_limits<int>::max());
+                        AssertMpi(MPI_Irecv(&newParticlesLowIndexes[0], int(nbNewFromLow*size_particle_index),
+                                  particles_utils::GetMpiType(partsize_t()),
                                   (my_rank-1+nb_processes_involved)%nb_processes_involved, TAG_UP_LOW_MOVED_PARTICLES_INDEXES,
                                   MPI_COMM_WORLD, &mpiRequests.back()));
 
+                        if(size_particle_data){
+                            newParticlesLowData.reset(new real_number[nbNewFromLow*size_particle_data]);
+                            whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
+                            mpiRequests.emplace_back();
+                            assert(nbNewFromLow*size_particle_data < std::numeric_limits<int>::max());
+                            AssertMpi(MPI_Irecv(&newParticlesLowData[0], int(nbNewFromLow*size_particle_data),
+                                      particles_utils::GetMpiType(real_number()),
+                                      (my_rank-1+nb_processes_involved)%nb_processes_involved, TAG_UP_LOW_MOVED_PARTICLES_DATA,
+                                      MPI_COMM_WORLD, &mpiRequests.back()));
+                        }
+
                         for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
                             newParticlesLowRhs[idx_rhs].reset(new real_number[nbNewFromLow*size_particle_rhs]);
                             whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
@@ -713,14 +764,26 @@ public:
                         AssertMpi(MPI_Irecv(&newParticlesUp[0], int(nbNewFromUp*size_particle_positions), particles_utils::GetMpiType(real_number()), (my_rank+1)%nb_processes_involved, TAG_LOW_UP_MOVED_PARTICLES,
                                   MPI_COMM_WORLD, &mpiRequests.back()));
 
-                        newParticlesUpIndexes.reset(new partsize_t[nbNewFromUp]);
+                        newParticlesUpIndexes.reset(new partsize_t[nbNewFromUp*size_particle_index]);
                         whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
                         mpiRequests.emplace_back();
-                        assert(nbNewFromUp < std::numeric_limits<int>::max());
-                        AssertMpi(MPI_Irecv(&newParticlesUpIndexes[0], int(nbNewFromUp), particles_utils::GetMpiType(partsize_t()),
+                        assert(nbNewFromUp*size_particle_index < std::numeric_limits<int>::max());
+                        AssertMpi(MPI_Irecv(&newParticlesUpIndexes[0], int(nbNewFromUp*size_particle_index),
+                                  particles_utils::GetMpiType(partsize_t()),
                                   (my_rank+1)%nb_processes_involved, TAG_LOW_UP_MOVED_PARTICLES_INDEXES,
                                   MPI_COMM_WORLD, &mpiRequests.back()));
 
+                        if(size_particle_data){
+                            newParticlesUpData.reset(new real_number[nbNewFromUp*size_particle_data]);
+                            whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
+                            mpiRequests.emplace_back();
+                            assert(nbNewFromUp*size_particle_data < std::numeric_limits<int>::max());
+                            AssertMpi(MPI_Irecv(&newParticlesUpData[0], int(nbNewFromUp*size_particle_data),
+                                      particles_utils::GetMpiType(real_number()),
+                                      (my_rank+1)%nb_processes_involved, TAG_LOW_UP_MOVED_PARTICLES_DATA,
+                                      MPI_COMM_WORLD, &mpiRequests.back()));
+                        }
+
                         for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
                             newParticlesUpRhs[idx_rhs].reset(new real_number[nbNewFromUp*size_particle_rhs]);
                             whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
@@ -750,7 +813,8 @@ public:
             const partsize_t myTotalNewNbParticles = nbOldParticlesInside + nbNewFromLow + nbNewFromUp;
 
             std::unique_ptr<real_number[]> newArray(new real_number[myTotalNewNbParticles*size_particle_positions]);
-            std::unique_ptr<partsize_t[]> newArrayIndexes(new partsize_t[myTotalNewNbParticles]);
+            std::unique_ptr<partsize_t[]> newArrayIndexes(new partsize_t[myTotalNewNbParticles*size_particle_index]);
+            std::unique_ptr<real_number[]> newArrayData(new real_number[myTotalNewNbParticles*size_particle_data]);
             std::vector<std::unique_ptr<real_number[]>> newArrayRhs(in_nb_rhs);
             for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
                 newArrayRhs[idx_rhs].reset(new real_number[myTotalNewNbParticles*size_particle_rhs]);
@@ -760,7 +824,8 @@ public:
             if(nbNewFromLow){
                 const particles_utils::fixed_copy fcp(0, 0, nbNewFromLow);
                 fcp.copy(newArray, newParticlesLow, size_particle_positions);
-                fcp.copy(newArrayIndexes, newParticlesLowIndexes);
+                fcp.copy(newArrayIndexes, newParticlesLowIndexes, size_particle_index);
+                fcp.copy(newArrayData, newParticlesLowData, size_particle_data);
                 for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
                     fcp.copy(newArrayRhs[idx_rhs], newParticlesLowRhs[idx_rhs], size_particle_rhs);
                 }
@@ -770,7 +835,8 @@ public:
             {
                 const particles_utils::fixed_copy fcp(nbNewFromLow, nbOutLower, nbOldParticlesInside);
                 fcp.copy(newArray, (*inout_positions_particles), size_particle_positions);
-                fcp.copy(newArrayIndexes, (*inout_index_particles));
+                fcp.copy(newArrayIndexes, (*inout_index_particles), size_particle_index);
+                fcp.copy(newArrayData, (*inout_data_particles), size_particle_data);
                 for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
                     fcp.copy(newArrayRhs[idx_rhs], inout_rhs_particles[idx_rhs], size_particle_rhs);
                 }
@@ -780,7 +846,8 @@ public:
             if(nbNewFromUp){
                 const particles_utils::fixed_copy fcp(nbNewFromLow+nbOldParticlesInside, 0, nbNewFromUp);
                 fcp.copy(newArray, newParticlesUp, size_particle_positions);
-                fcp.copy(newArrayIndexes, newParticlesUpIndexes);
+                fcp.copy(newArrayIndexes, newParticlesUpIndexes, size_particle_index);
+                fcp.copy(newArrayData, newParticlesUpData, size_particle_data);
                 for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
                     fcp.copy(newArrayRhs[idx_rhs], newParticlesUpRhs[idx_rhs], size_particle_rhs);
                 }
@@ -788,6 +855,7 @@ public:
 
             (*inout_positions_particles) = std::move(newArray);
             (*inout_index_particles) = std::move(newArrayIndexes);
+            (*inout_data_particles) = std::move(newArrayData);
             for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
                 inout_rhs_particles[idx_rhs] = std::move(newArrayRhs[idx_rhs]);
             }
@@ -808,7 +876,13 @@ public:
             },
             [&](const partsize_t idx1, const partsize_t idx2){
                 for(int idx_val = 0 ; idx_val < size_particle_index ; ++idx_val){
-                    std::swap((*inout_index_particles)[idx1], (*inout_index_particles)[idx2]);
+                    std::swap((*inout_index_particles)[size_particle_index*idx1 + idx_val],
+                              (*inout_index_particles)[size_particle_index*idx2 + idx_val]);
+                }
+
+                for(int idx_val = 0 ; idx_val < size_particle_data ; ++idx_val){
+                    std::swap((*inout_data_particles)[size_particle_data*idx1 + idx_val],
+                              (*inout_data_particles)[size_particle_data*idx2 + idx_val]);
                 }
 
                 for(int idx_rhs = 0 ; idx_rhs < in_nb_rhs ; ++idx_rhs){
diff --git a/bfps/cpp/particles/particles_system.hpp b/bfps/cpp/particles/particles_system.hpp
index 02767a8b..12bf6c29 100644
--- a/bfps/cpp/particles/particles_system.hpp
+++ b/bfps/cpp/particles/particles_system.hpp
@@ -12,8 +12,10 @@
 #include "particles_adams_bashforth.hpp"
 #include "scope_timer.hpp"
 
+#include "p2p_distr_mpi.hpp"
+
 template <class partsize_t, class real_number, class field_rnumber, class field_class, class interpolator_class, int interp_neighbours,
-          int size_particle_rhs>
+          int size_particle_rhs, class p2p_computer_class, int size_particle_data>
 class particles_system : public abstract_particles_system<partsize_t, real_number> {
     MPI_Comm mpi_com;
 
@@ -47,6 +49,10 @@ class particles_system : public abstract_particles_system<partsize_t, real_numbe
 
     int step_idx;
 
+    p2p_distr_mpi<partsize_t, real_number> distr_p2p;
+    p2p_computer_class computer_p2p;
+    std::unique_ptr<real_number[]> my_particles_data;
+
 public:
     particles_system(const std::array<size_t,3>& field_grid_dim, const std::array<real_number,3>& in_spatial_box_width,
                      const std::array<real_number,3>& in_spatial_box_offset,
@@ -57,7 +63,8 @@ public:
                      const field_class& in_field,
                      MPI_Comm in_mpi_com,
                      const partsize_t in_total_nb_particles,
-                     const int in_current_iteration = 1)
+                     const int in_current_iteration = 1,
+                     const real_number in_cutoff = 1.)
         : mpi_com(in_mpi_com),
           current_partition_interval({in_local_field_offset[IDX_Z], in_local_field_offset[IDX_Z] + in_local_field_dims[IDX_Z]}),
           partition_interval_size(current_partition_interval.second - current_partition_interval.first),
@@ -69,7 +76,8 @@ public:
           default_field(in_field),
           spatial_box_width(in_spatial_box_width), spatial_partition_width(in_spatial_partition_width),
           my_spatial_low_limit(in_my_spatial_low_limit), my_spatial_up_limit(in_my_spatial_up_limit),
-          my_nb_particles(0), total_nb_particles(in_total_nb_particles), step_idx(in_current_iteration){
+          my_nb_particles(0), total_nb_particles(in_total_nb_particles), step_idx(in_current_iteration),
+          distr_p2p(in_mpi_com, current_partition_interval,field_grid_dim, spatial_box_width, in_spatial_box_offset, in_cutoff){
 
         current_my_nb_particles_per_partition.reset(new partsize_t[partition_interval_size]);
         current_offset_particles_for_partition.reset(new partsize_t[partition_interval_size+1]);
@@ -85,6 +93,8 @@ public:
         my_particles_positions_indexes = particles_input.getMyParticlesIndexes();
         my_particles_rhs = particles_input.getMyRhs();
         my_nb_particles = particles_input.getLocalNbParticles();
+        // TODO P2P get it from loader
+        my_particles_data.reset(new real_number[my_nb_particles*size_particle_data]());
 
         for(partsize_t idx_part = 0 ; idx_part < my_nb_particles ; ++idx_part){ // TODO remove me
             const int partition_level = computer.pbc_field_layer(my_particles_positions[idx_part*3+IDX_Z], IDX_Z);
@@ -107,6 +117,10 @@ public:
                               my_particles_rhs[idx_rhs][idx2*size_particle_rhs + idx_val]);
                 }
             }
+            for(int idx_val = 0 ; idx_val < size_particle_data ; ++idx_val){
+                std::swap(my_particles_data[idx1*size_particle_data + idx_val],
+                          my_particles_data[idx2*size_particle_data + idx_val]);
+            }
         });
 
         {// TODO remove
@@ -131,6 +145,17 @@ public:
                                interp_neighbours);
     }
 
+    void compute_p2p() final {
+        // TODO P2P
+        if(p2p_computer_class::isEmpty() == false){
+            TIMEZONE("particles_system::compute_p2p");
+            distr_p2p.template compute_distr<p2p_computer_class, 3, size_particle_data, size_particle_rhs>(
+                            computer_p2p, current_my_nb_particles_per_partition.get(),
+                            my_particles_positions.get(), my_particles_data.get(), my_particles_rhs.front().get(),
+                            my_particles_positions_indexes.get());
+        }
+    }
+
     template <class sample_field_class, int sample_size_particle_rhs>
     void sample_compute(const sample_field_class& sample_field,
                         real_number sample_rhs[]) {
@@ -179,13 +204,14 @@ public:
 
     void redistribute() final {
         TIMEZONE("particles_system::redistribute");
-        particles_distr.template redistribute<computer_class, 3, size_particle_rhs, 1>(
+        particles_distr.template redistribute<computer_class, 3, size_particle_data, size_particle_rhs, 1>(
                               computer,
                               current_my_nb_particles_per_partition.get(),
                               &my_nb_particles,
                               &my_particles_positions,
                               my_particles_rhs.data(), int(my_particles_rhs.size()),
-                              &my_particles_positions_indexes);
+                              &my_particles_positions_indexes,
+                              &my_particles_data);
     }
 
     void inc_step_idx() final {
@@ -210,6 +236,7 @@ public:
     void completeLoop(const real_number dt) final {
         TIMEZONE("particles_system::completeLoop");
         compute();
+        compute_p2p();
         move(dt);
         redistribute();
         inc_step_idx();
diff --git a/bfps/cpp/particles/particles_system_builder.hpp b/bfps/cpp/particles/particles_system_builder.hpp
index a3bc689d..f9ce512d 100644
--- a/bfps/cpp/particles/particles_system_builder.hpp
+++ b/bfps/cpp/particles/particles_system_builder.hpp
@@ -8,6 +8,7 @@
 #include "particles_system.hpp"
 #include "particles_input_hdf5.hpp"
 #include "particles_generic_interp.hpp"
+#include "p2p_computer_empty.hpp"
 
 #include "field.hpp"
 #include "kspace.hpp"
@@ -109,7 +110,7 @@ inline RetType evaluate(IterType1 value1, IterType2 value2, Args... args){
 ///
 //////////////////////////////////////////////////////////////////////////////
 
-template <class partsize_t, class field_rnumber, field_backend be, field_components fc, class particles_rnumber>
+template <class partsize_t, class field_rnumber, field_backend be, field_components fc, class particles_rnumber, class p2p_computer_class>
 struct particles_system_build_container {
     template <const int interpolation_size, const int spline_mode>
     static std::unique_ptr<abstract_particles_system<partsize_t, particles_rnumber>> instanciate(
@@ -196,7 +197,8 @@ struct particles_system_build_container {
         using particles_system_type = particles_system<partsize_t, particles_rnumber, field_rnumber,
                                                        field<field_rnumber, be, fc>,
                                                        particles_generic_interp<particles_rnumber, interpolation_size,spline_mode>,
-                                                       interpolation_size, ncomp(fc)>;
+                                                       interpolation_size, ncomp(fc),
+                                                       p2p_computer_class, p2p_computer_class::size_data>;
         particles_system_type* part_sys = new particles_system_type(field_grid_dim,
                                                spatial_box_width,
                                                spatial_box_offset,
@@ -210,6 +212,7 @@ struct particles_system_build_container {
                                                nparticles,
                                                in_current_iteration);
 
+        // TODO P2P load particle data too
         // Load particles from hdf5
         particles_input_hdf5<partsize_t, particles_rnumber, 3,3> generator(mpi_comm, fname_input,
                                             inDatanameState, inDatanameRhs, my_spatial_low_limit_z, my_spatial_up_limit_z);
@@ -251,7 +254,28 @@ inline std::unique_ptr<abstract_particles_system<partsize_t, particles_rnumber>>
     return Template_double_for_if::evaluate<std::unique_ptr<abstract_particles_system<partsize_t, particles_rnumber>>,
                        int, 1, 11, 1, // interpolation_size
                        int, 0, 3, 1, // spline_mode
-                       particles_system_build_container<partsize_t, field_rnumber,be,fc,particles_rnumber>>(
+                       particles_system_build_container<partsize_t, field_rnumber,be,fc,particles_rnumber, p2p_computer_empty<particles_rnumber,partsize_t>>>(
+                           interpolation_size, // template iterator 1
+                           spline_mode, // template iterator 2
+                           fs_field,fs_kk, nsteps, nparticles, fname_input, inDatanameState, inDatanameRhs, mpi_comm, in_current_iteration);
+}
+
+template <class partsize_t, class field_rnumber, field_backend be, field_components fc, class p2p_computer_class, class particles_rnumber = double>
+inline std::unique_ptr<abstract_particles_system<partsize_t, particles_rnumber>> particles_system_builder_with_p2p(
+        const field<field_rnumber, be, fc>* fs_field, // (field object)
+        const kspace<be, SMOOTH>* fs_kk, // (kspace object, contains dkx, dky, dkz)
+        const int nsteps, // to check coherency between parameters and hdf input file (nb rhs)
+        const partsize_t nparticles, // to check coherency between parameters and hdf input file
+        const std::string& fname_input, // particles input filename
+        const std::string& inDatanameState, const std::string& inDatanameRhs, // input dataset names
+        const int interpolation_size,
+        const int spline_mode,
+        MPI_Comm mpi_comm,
+        const int in_current_iteration){
+    return Template_double_for_if::evaluate<std::unique_ptr<abstract_particles_system<partsize_t, particles_rnumber>>,
+                       int, 1, 11, 1, // interpolation_size
+                       int, 0, 3, 1, // spline_mode
+                       particles_system_build_container<partsize_t, field_rnumber,be,fc,particles_rnumber,p2p_computer_class>>(
                            interpolation_size, // template iterator 1
                            spline_mode, // template iterator 2
                            fs_field,fs_kk, nsteps, nparticles, fname_input, inDatanameState, inDatanameRhs, mpi_comm, in_current_iteration);
diff --git a/setup.py b/setup.py
index 9bba1701..f257fd7f 100644
--- a/setup.py
+++ b/setup.py
@@ -127,7 +127,8 @@ src_file_list = ['full_code/joint_acc_vel_stats',
                  'spline_n10',
                  'Lagrange_polys',
                  'scope_timer',
-                 'full_code/NSVEparticles']
+                 'full_code/NSVEparticles',
+                 'full_code/NSVEparticlesP2P']
 
 particle_headers = [
         'cpp/particles/particles_distr_mpi.hpp',
-- 
GitLab