From 690b53a939088b448e60cd853c1a96156ee2e35c Mon Sep 17 00:00:00 2001
From: Berenger Bramas <Berenger.Bramas@inria.fr>
Date: Thu, 17 Oct 2019 15:30:37 +0200
Subject: [PATCH 1/4] First version where particles that collide can be removed

---
 cpp/particles/p2p_computer.hpp            |   4 +-
 cpp/particles/p2p_computer_empty.hpp      |   4 +-
 cpp/particles/p2p_cylinder_collisions.hpp |   4 +-
 cpp/particles/p2p_distr_mpi.hpp           | 163 +++++++++++++---------
 cpp/particles/p2p_ghost_collisions.hpp    |   4 +-
 cpp/particles/p2p_merge_collisions.hpp    |  76 ++++++++++
 cpp/particles/particles_system.hpp        |  96 +++++++++++++
 7 files changed, 283 insertions(+), 68 deletions(-)
 create mode 100644 cpp/particles/p2p_merge_collisions.hpp

diff --git a/cpp/particles/p2p_computer.hpp b/cpp/particles/p2p_computer.hpp
index 5fd2aef8..e5ddda47 100644
--- a/cpp/particles/p2p_computer.hpp
+++ b/cpp/particles/p2p_computer.hpp
@@ -70,7 +70,9 @@ public:
     }
 
     template <int size_particle_positions, int size_particle_rhs>
-    void compute_interaction(const real_number pos_part1[], real_number rhs_part1[],
+    void compute_interaction(const partsize_t /*idx_part1*/,
+                             const real_number pos_part1[], real_number rhs_part1[],
+                             const partsize_t /*idx_part2*/,
                              const real_number pos_part2[], real_number rhs_part2[],
                              const real_number dist_pow2, const real_number cutoff,
                              const real_number /*xshift_coef*/, const real_number /*yshift_coef*/, const real_number /*zshift_coef*/) const{
diff --git a/cpp/particles/p2p_computer_empty.hpp b/cpp/particles/p2p_computer_empty.hpp
index c7c0057f..cb9710cc 100644
--- a/cpp/particles/p2p_computer_empty.hpp
+++ b/cpp/particles/p2p_computer_empty.hpp
@@ -40,7 +40,9 @@ public:
     }
 
     template <int size_particle_positions, int size_particle_rhs>
-    void compute_interaction(const real_number /*pos_part1*/[], real_number /*rhs_part1*/[],
+    void compute_interaction(const partsize_t /*idx_part1*/,
+                             const real_number /*pos_part1*/[], real_number /*rhs_part1*/[],
+                             const partsize_t /*idx_part2*/,
                              const real_number /*pos_part2*/[], real_number /*rhs_part2*/[],
                              const real_number /*dist_pow2*/,  const real_number /*cutoff*/,
                              const real_number /*xshift_coef*/, const real_number /*yshift_coef*/, const real_number /*zshift_coef*/) const{
diff --git a/cpp/particles/p2p_cylinder_collisions.hpp b/cpp/particles/p2p_cylinder_collisions.hpp
index 46763ddd..c1fc4b97 100644
--- a/cpp/particles/p2p_cylinder_collisions.hpp
+++ b/cpp/particles/p2p_cylinder_collisions.hpp
@@ -44,7 +44,9 @@ public:
     }
 
     template <int size_particle_positions, int size_particle_rhs>
-    void compute_interaction(const real_number /*pos_part1*/[], real_number /*rhs_part1*/[],
+    void compute_interaction(const partsize_t /*idx_part1*/,
+                             const real_number /*pos_part1*/[], real_number /*rhs_part1*/[],
+                             const partsize_t /*idx_part2*/,
                              const real_number /*pos_part2*/[], real_number /*rhs_part2*/[],
                              const real_number /*dist_pow2*/,  const real_number /*cutoff*/,
                              const real_number /*xshift_coef*/, const real_number /*yshift_coef*/, const real_number /*zshift_coef*/){
diff --git a/cpp/particles/p2p_distr_mpi.hpp b/cpp/particles/p2p_distr_mpi.hpp
index 64b7b605..e567e86f 100644
--- a/cpp/particles/p2p_distr_mpi.hpp
+++ b/cpp/particles/p2p_distr_mpi.hpp
@@ -49,6 +49,7 @@ protected:
     enum MpiTag{
         TAG_NB_PARTICLES,
         TAG_POSITION_PARTICLES,
+        TAG_INDEX_PARTICLES,
         TAG_RESULT_PARTICLES,
     };
 
@@ -57,10 +58,12 @@ protected:
         int destProc;
         int nbLevelsToExchange;
         bool isRecv;
+        int nbReceived;
 
         std::unique_ptr<real_number[]> toRecvAndMerge;
         std::unique_ptr<real_number[]> toCompute;
         std::unique_ptr<real_number[]> results;
+        std::unique_ptr<partsize_t[]> indexes;
     };
 
     enum Action{
@@ -416,6 +419,7 @@ public:
                 descriptor.nbLevelsToExchange = nb_levels_to_send;
                 descriptor.nbParticlesToExchange = particles_offset_layers[my_nb_cell_levels] - particles_offset_layers[my_nb_cell_levels-nb_levels_to_send];
                 descriptor.isRecv = false;
+                descriptor.nbReceived = 0;
 
                 neigDescriptors.emplace_back(std::move(descriptor));
 
@@ -438,6 +442,7 @@ public:
                 descriptor.nbLevelsToExchange = nb_levels_to_recv;
                 descriptor.nbParticlesToExchange = -1;
                 descriptor.isRecv = true;
+                descriptor.nbReceived = 0;
 
                 neigDescriptors.emplace_back(std::move(descriptor));
 
@@ -479,6 +484,14 @@ public:
                               descriptor.destProc, TAG_POSITION_PARTICLES,
                               current_com, &mpiRequests.back()));
 
+                    whatNext.emplace_back(std::pair<Action,int>{NOTHING_TODO, -1});
+                    mpiRequests.emplace_back();
+                    assert(descriptor.nbParticlesToExchange*size_particle_positions < std::numeric_limits<int>::max());
+                    AssertMpi(MPI_Isend(const_cast<partsize_t*>(&inout_index_particles[particles_offset_layers[my_nb_cell_levels-descriptor.nbLevelsToExchange]]),
+                              int(descriptor.nbParticlesToExchange), particles_utils::GetMpiType(partsize_t()),
+                              descriptor.destProc, TAG_INDEX_PARTICLES,
+                              current_com, &mpiRequests.back()));
+
                     assert(descriptor.toRecvAndMerge == nullptr);
                     descriptor.toRecvAndMerge.reset(new real_number[descriptor.nbParticlesToExchange*size_particle_rhs]);
                     whatNext.emplace_back(std::pair<Action,int>{MERGE_PARTICLES, idxDescr});
@@ -576,6 +589,14 @@ public:
                             AssertMpi(MPI_Irecv(descriptor.toCompute.get(), int(NbParticlesToReceive*size_particle_positions),
                                                 particles_utils::GetMpiType(real_number()), destProc, TAG_POSITION_PARTICLES,
                                                 current_com, &mpiRequests.back()));
+
+                            descriptor.toCompute.reset(new real_number[NbParticlesToReceive*size_particle_positions]);
+                            whatNext.emplace_back(std::pair<Action,int>{COMPUTE_PARTICLES, releasedAction.second});
+                            mpiRequests.emplace_back();
+                            assert(NbParticlesToReceive*size_particle_positions < std::numeric_limits<int>::max());
+                            AssertMpi(MPI_Irecv(descriptor.indexes.get(), int(NbParticlesToReceive),
+                                                particles_utils::GetMpiType(partsize_t()), destProc, TAG_INDEX_PARTICLES,
+                                                current_com, &mpiRequests.back()));
                         }
                     }
 
@@ -583,80 +604,88 @@ public:
                     /// Computation
                     //////////////////////////////////////////////////////////////////////
                     if(releasedAction.first == COMPUTE_PARTICLES){
-                        TIMEZONE("compute-particles");
                         NeighborDescriptor& descriptor = neigDescriptors[releasedAction.second];
-                        assert(descriptor.isRecv);
-                        const partsize_t NbParticlesToReceive = descriptor.nbParticlesToExchange;
-
-                        assert(descriptor.toCompute != nullptr);
-                        descriptor.results.reset(new real_number[NbParticlesToReceive*size_particle_rhs]);
-                        computer_thread.template init_result_array<size_particle_rhs>(descriptor.results.get(), NbParticlesToReceive);
-
-                        // Compute
-                        partsize_t idxPart = 0;
-                        while(idxPart != NbParticlesToReceive){
-                            const long int current_cell_idx = get_cell_idx(descriptor.toCompute[idxPart*size_particle_positions + IDXC_X],
-                                                                           descriptor.toCompute[idxPart*size_particle_positions + IDXC_Y],
-                                                                           descriptor.toCompute[idxPart*size_particle_positions + IDXC_Z]);
-                            partsize_t nb_parts_in_cell = 1;
-                            while(idxPart+nb_parts_in_cell != NbParticlesToReceive
-                                  && current_cell_idx == get_cell_idx(descriptor.toCompute[(idxPart+nb_parts_in_cell)*size_particle_positions + IDXC_X],
-                                                                     descriptor.toCompute[(idxPart+nb_parts_in_cell)*size_particle_positions + IDXC_Y],
-                                                                     descriptor.toCompute[(idxPart+nb_parts_in_cell)*size_particle_positions + IDXC_Z])){
-                                nb_parts_in_cell += 1;
-                            }
+                        descriptor.nbReceived += 1;
+                        assert(descriptor.nbReceived <= 2);
+
+                        if(descriptor.nbReceived == 2){
+                            TIMEZONE("compute-particles");
+                            NeighborDescriptor& descriptor = neigDescriptors[releasedAction.second];
+                            assert(descriptor.isRecv);
+                            const partsize_t NbParticlesToReceive = descriptor.nbParticlesToExchange;
+
+                            assert(descriptor.toCompute != nullptr);
+                            descriptor.results.reset(new real_number[NbParticlesToReceive*size_particle_rhs]);
+                            computer_thread.template init_result_array<size_particle_rhs>(descriptor.results.get(), NbParticlesToReceive);
+
+                            // Compute
+                            partsize_t idxPart = 0;
+                            while(idxPart != NbParticlesToReceive){
+                                const long int current_cell_idx = get_cell_idx(descriptor.toCompute[idxPart*size_particle_positions + IDXC_X],
+                                                                               descriptor.toCompute[idxPart*size_particle_positions + IDXC_Y],
+                                                                               descriptor.toCompute[idxPart*size_particle_positions + IDXC_Z]);
+                                partsize_t nb_parts_in_cell = 1;
+                                while(idxPart+nb_parts_in_cell != NbParticlesToReceive
+                                      && current_cell_idx == get_cell_idx(descriptor.toCompute[(idxPart+nb_parts_in_cell)*size_particle_positions + IDXC_X],
+                                                                         descriptor.toCompute[(idxPart+nb_parts_in_cell)*size_particle_positions + IDXC_Y],
+                                                                         descriptor.toCompute[(idxPart+nb_parts_in_cell)*size_particle_positions + IDXC_Z])){
+                                    nb_parts_in_cell += 1;
+                                }
 
-                            #pragma omp task default(shared) firstprivate(idxPart, nb_parts_in_cell, current_cell_idx)
-                            {
-                                const std::vector<std::pair<partsize_t,partsize_t>>* neighbors[27];
-                                long int neighbors_indexes[27];
-                                std::array<real_number,3> shift[27];
-                                const int nbNeighbors = my_tree.getNeighbors(current_cell_idx, neighbors, neighbors_indexes, shift, true);
-
-                                // with other interval
-                                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){
-                                        for(partsize_t idx_p1 = 0 ; idx_p1 < nb_parts_in_cell ; ++idx_p1){
-                                            for(partsize_t idx_p2 = 0 ; idx_p2 < (*neighbors[idx_neighbor])[idx_2].second ; ++idx_p2){
-                                                const real_number dist_r2 = compute_distance_r2(descriptor.toCompute[(idxPart+idx_p1)*size_particle_positions + IDXC_X],
-                                                                                                descriptor.toCompute[(idxPart+idx_p1)*size_particle_positions + IDXC_Y],
-                                                                                                descriptor.toCompute[(idxPart+idx_p1)*size_particle_positions + IDXC_Z],
-                                                                                                particles_positions[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_positions + IDXC_X],
-                                                                                                particles_positions[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_positions + IDXC_Y],
-                                                                                                particles_positions[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_positions + IDXC_Z],
-                                                                                                shift[idx_neighbor][IDXC_X], shift[idx_neighbor][IDXC_Y], shift[idx_neighbor][IDXC_Z]);
-                                                if(dist_r2 < cutoff_radius_compute*cutoff_radius_compute){
-                                                    computer_thread.template compute_interaction<size_particle_positions, size_particle_rhs>(
-                                                                        &descriptor.toCompute[(idxPart+idx_p1)*size_particle_positions],
-                                                                        &descriptor.results[(idxPart+idx_p1)*size_particle_rhs],
-                                                                        &particles_positions[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_positions],
-                                                                        &particles_current_rhs[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_rhs],
-                                                                        dist_r2, cutoff_radius_compute, shift[idx_neighbor][IDXC_X], shift[idx_neighbor][IDXC_Y], shift[idx_neighbor][IDXC_Z]);
+                                #pragma omp task default(shared) firstprivate(idxPart, nb_parts_in_cell, current_cell_idx)
+                                {
+                                    const std::vector<std::pair<partsize_t,partsize_t>>* neighbors[27];
+                                    long int neighbors_indexes[27];
+                                    std::array<real_number,3> shift[27];
+                                    const int nbNeighbors = my_tree.getNeighbors(current_cell_idx, neighbors, neighbors_indexes, shift, true);
+
+                                    // with other interval
+                                    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){
+                                            for(partsize_t idx_p1 = 0 ; idx_p1 < nb_parts_in_cell ; ++idx_p1){
+                                                for(partsize_t idx_p2 = 0 ; idx_p2 < (*neighbors[idx_neighbor])[idx_2].second ; ++idx_p2){
+                                                    const real_number dist_r2 = compute_distance_r2(descriptor.toCompute[(idxPart+idx_p1)*size_particle_positions + IDXC_X],
+                                                                                                    descriptor.toCompute[(idxPart+idx_p1)*size_particle_positions + IDXC_Y],
+                                                                                                    descriptor.toCompute[(idxPart+idx_p1)*size_particle_positions + IDXC_Z],
+                                                                                                    particles_positions[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_positions + IDXC_X],
+                                                                                                    particles_positions[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_positions + IDXC_Y],
+                                                                                                    particles_positions[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_positions + IDXC_Z],
+                                                                                                    shift[idx_neighbor][IDXC_X], shift[idx_neighbor][IDXC_Y], shift[idx_neighbor][IDXC_Z]);
+                                                    if(dist_r2 < cutoff_radius_compute*cutoff_radius_compute){
+                                                        computer_thread.template compute_interaction<size_particle_positions, size_particle_rhs>(
+                                                                            descriptor.indexes[(idxPart+idx_p1)],
+                                                                            &descriptor.toCompute[(idxPart+idx_p1)*size_particle_positions],
+                                                                            &descriptor.results[(idxPart+idx_p1)*size_particle_rhs],
+                                                                            inout_index_particles[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)],
+                                                                            &particles_positions[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_positions],
+                                                                            &particles_current_rhs[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_rhs],
+                                                                            dist_r2, cutoff_radius_compute, shift[idx_neighbor][IDXC_X], shift[idx_neighbor][IDXC_Y], shift[idx_neighbor][IDXC_Z]);
+                                                    }
                                                 }
                                             }
                                         }
-                                    }
 
-                                    cells_locker.unlock(neighbors_indexes[idx_neighbor]);
+                                        cells_locker.unlock(neighbors_indexes[idx_neighbor]);
+                                    }
                                 }
-                            }
 
-                            idxPart += nb_parts_in_cell;
-                        }
+                                idxPart += nb_parts_in_cell;
+                            }
 
-                        #pragma omp taskwait
+                            #pragma omp taskwait
 
-                        // Send back
-                        const int destProc = descriptor.destProc;
-                        whatNext.emplace_back(std::pair<Action,int>{RELEASE_BUFFER_PARTICLES, releasedAction.second});
-                        mpiRequests.emplace_back();
-                        assert(NbParticlesToReceive*size_particle_rhs < std::numeric_limits<int>::max());
-                        AssertMpi(MPI_Isend(descriptor.results.get(), int(NbParticlesToReceive*size_particle_rhs),
-                                            particles_utils::GetMpiType(real_number()), destProc, TAG_RESULT_PARTICLES,
-                                            current_com, &mpiRequests.back()));
-                        delete[] descriptor.toCompute.release();
+                            // Send back
+                            const int destProc = descriptor.destProc;
+                            whatNext.emplace_back(std::pair<Action,int>{RELEASE_BUFFER_PARTICLES, releasedAction.second});
+                            mpiRequests.emplace_back();
+                            assert(NbParticlesToReceive*size_particle_rhs < std::numeric_limits<int>::max());
+                            AssertMpi(MPI_Isend(descriptor.results.get(), int(NbParticlesToReceive*size_particle_rhs),
+                                                particles_utils::GetMpiType(real_number()), destProc, TAG_RESULT_PARTICLES,
+                                                current_com, &mpiRequests.back()));
+                            delete[] descriptor.toCompute.release();
+                        }
                     }
                     //////////////////////////////////////////////////////////////////////
                     /// Release memory that was sent back
@@ -713,8 +742,10 @@ public:
                                                                                 0, 0, 0);
                                 if(dist_r2 < cutoff_radius_compute*cutoff_radius_compute){
                                     computer_thread.template compute_interaction<size_particle_positions,size_particle_rhs>(
+                                                        inout_index_particles[(intervals[idx_1].first+idx_p1)],
                                                         &particles_positions[(intervals[idx_1].first+idx_p1)*size_particle_positions],
                                                         &particles_current_rhs[(intervals[idx_1].first+idx_p1)*size_particle_rhs],
+                                                        inout_index_particles[(intervals[idx_1].first+idx_p2)],
                                                         &particles_positions[(intervals[idx_1].first+idx_p2)*size_particle_positions],
                                                         &particles_current_rhs[(intervals[idx_1].first+idx_p2)*size_particle_rhs],
                                                         dist_r2, cutoff_radius_compute, 0, 0, 0);
@@ -735,8 +766,10 @@ public:
                                                                                     0, 0, 0);
                                     if(dist_r2 < cutoff_radius_compute*cutoff_radius_compute){
                                         computer_thread.template compute_interaction<size_particle_positions,size_particle_rhs>(
+                                                            inout_index_particles[(intervals[idx_1].first+idx_p1)],
                                                             &particles_positions[(intervals[idx_1].first+idx_p1)*size_particle_positions],
                                                             &particles_current_rhs[(intervals[idx_1].first+idx_p1)*size_particle_rhs],
+                                                            inout_index_particles[(intervals[idx_2].first+idx_p2)],
                                                             &particles_positions[(intervals[idx_2].first+idx_p2)*size_particle_positions],
                                                             &particles_current_rhs[(intervals[idx_2].first+idx_p2)*size_particle_rhs],
                                                             dist_r2, cutoff_radius_compute, 0, 0, 0);
@@ -769,8 +802,10 @@ public:
                                                                                             shift[idx_neighbor][IDXC_X], shift[idx_neighbor][IDXC_Y], shift[idx_neighbor][IDXC_Z]);
                                             if(dist_r2 < cutoff_radius_compute*cutoff_radius_compute){
                                                 computer_thread.template compute_interaction<size_particle_positions,size_particle_rhs>(
+                                                                    inout_index_particles[(intervals[idx_1].first+idx_p1)],
                                                                     &particles_positions[(intervals[idx_1].first+idx_p1)*size_particle_positions],
                                                                     &particles_current_rhs[(intervals[idx_1].first+idx_p1)*size_particle_rhs],
+                                                                    inout_index_particles[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)],
                                                                     &particles_positions[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_positions],
                                                                     &particles_current_rhs[((*neighbors[idx_neighbor])[idx_2].first+idx_p2)*size_particle_rhs],
                                                                     dist_r2, cutoff_radius_compute, shift[idx_neighbor][IDXC_X], shift[idx_neighbor][IDXC_Y], shift[idx_neighbor][IDXC_Z]);
diff --git a/cpp/particles/p2p_ghost_collisions.hpp b/cpp/particles/p2p_ghost_collisions.hpp
index 604ac120..63c46981 100644
--- a/cpp/particles/p2p_ghost_collisions.hpp
+++ b/cpp/particles/p2p_ghost_collisions.hpp
@@ -44,7 +44,9 @@ public:
     }
 
     template <int size_particle_positions, int size_particle_rhs>
-    void compute_interaction(const real_number /*pos_part1*/[], real_number /*rhs_part1*/[],
+    void compute_interaction(const partsize_t /*idx_part1*/,
+                             const real_number /*pos_part1*/[], real_number /*rhs_part1*/[],
+                             const partsize_t /*idx_part2*/,
                              const real_number /*pos_part2*/[], real_number /*rhs_part2*/[],
                              const real_number /*dist_pow2*/,  const real_number /*cutoff*/,
                              const real_number /*xshift_coef*/, const real_number /*yshift_coef*/, const real_number /*zshift_coef*/){
diff --git a/cpp/particles/p2p_merge_collisions.hpp b/cpp/particles/p2p_merge_collisions.hpp
new file mode 100644
index 00000000..96fff9c2
--- /dev/null
+++ b/cpp/particles/p2p_merge_collisions.hpp
@@ -0,0 +1,76 @@
+/******************************************************************************
+*                                                                             *
+*  Copyright 2019 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 P2P_MERGE_COLLISIONS_HPP
+#define P2P_MERGE_COLLISIONS_HPP
+
+#include <cstring>
+
+template <class real_number, class partsize_t>
+class p2p_merge_collisions{
+    long int collision_counter;
+
+    std::vector<partsize_t> mergedParticles;
+
+public:
+    p2p_merge_collisions() {}
+
+    // Copy constructor use a counter set to zero
+    p2p_merge_collisions(const p2p_merge_collisions&){}
+
+    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_rhs>
+    void compute_interaction(const partsize_t idx_part1,
+                             const real_number /*pos_part1*/[], real_number /*rhs_part1*/[],
+                             const partsize_t idx_part2,
+                             const real_number /*pos_part2*/[], real_number /*rhs_part2*/[],
+                             const real_number /*dist_pow2*/,  const real_number /*cutoff*/,
+                             const real_number /*xshift_coef*/, const real_number /*yshift_coef*/, const real_number /*zshift_coef*/){
+        mergedParticles.emplace_back(std::max(idx_part1,idx_part2));
+    }
+
+    void merge(const p2p_merge_collisions& other){
+        collision_counter.insert(collision_counter.end(), other.collision_counter.begin(), other.collision_counter.end());
+    }
+
+    constexpr static bool isEnable() {
+        return true;
+    }
+
+    auto& get_merge_list() const{
+        return collision_counter;
+    }
+
+    void reset_merge_list(){
+        mergedParticles.clear();
+    }
+};
+
+
+#endif // P2P_GHOST_COLLISIONS_HPP
diff --git a/cpp/particles/particles_system.hpp b/cpp/particles/particles_system.hpp
index cdb523bb..8756aec8 100644
--- a/cpp/particles/particles_system.hpp
+++ b/cpp/particles/particles_system.hpp
@@ -27,6 +27,7 @@
 #define PARTICLES_SYSTEM_HPP
 
 #include <array>
+#include <set>
 
 #include "abstract_particles_system.hpp"
 #include "abstract_particles_system_with_p2p.hpp"
@@ -161,6 +162,101 @@ public:
         }
     }
 
+    void delete_particles_from_indexes(const std::vector<partsize_t>& inIndexToDelete){
+        // We consider that the given indexes are here or in our neighbors,
+        // so we exchange them
+        int my_rank;
+        AssertMpi(MPI_Comm_rank(mpi_com, &my_rank));
+        int nb_processes;
+        AssertMpi(MPI_Comm_size(mpi_com, &nb_processes));
+
+        if(nb_processes > 1){
+            const int TopToBottom = 0;
+            const int BottomToTop = 1;
+
+            partsize_t nbIndexesFromTop = 0;
+            partsize_t nbIndexesFromBottom = 0;
+            partsize_t myNbIndexes = inIndexToDelete.size();
+            {
+                MPI_Request requests[4];
+                AssertMpi(MPI_Isend(&myNbIndexes, 1, particles_utils::GetMpiType(partsize_t()),
+                              (my_rank+1)%nb_processes, BottomToTop, mpi_com, &requests[0]));
+                AssertMpi(MPI_Isend(&myNbIndexes, 1, particles_utils::GetMpiType(partsize_t()),
+                              (my_rank+nb_processes-1)%nb_processes, TopToBottom, mpi_com, &requests[1]));
+
+                AssertMpi(MPI_Irecv(&nbIndexesFromTop, 1, particles_utils::GetMpiType(partsize_t()),
+                              (my_rank+1)%nb_processes, TopToBottom, mpi_com, &requests[2]));
+                AssertMpi(MPI_Irecv(&nbIndexesFromBottom, 1, particles_utils::GetMpiType(partsize_t()),
+                              (my_rank+nb_processes-1)%nb_processes, BottomToTop, mpi_com, &requests[3]));
+
+                AssertMpi(MPI_Waitall(4, requests, MPI_STATUSES_IGNORE));
+            }
+            inIndexToDelete.resize(inIndexToDelete.size() + nbIndexesFromTop + nbIndexesFromBottom);
+            {
+                MPI_Request requests[4];
+                int nbRequests = 0;
+
+                if(myNbIndexes){
+                    AssertMpi(MPI_Isend(&inIndexToDelete[0], myNbIndexes, particles_utils::GetMpiType(partsize_t()),
+                                  (my_rank+1)%nb_processes, BottomToTop, mpi_com, &requests[nbRequests++]));
+                    AssertMpi(MPI_Isend(&inIndexToDelete[0], myNbIndexes, particles_utils::GetMpiType(partsize_t()),
+                                  (my_rank+nb_processes-1)%nb_processes, TopToBottom, mpi_com, &requests[nbRequests++]));
+                }
+
+                if(nbIndexesFromTop){
+                    AssertMpi(MPI_Irecv(&inIndexToDelete[myNbIndexes], int(nbIndexesFromTop), particles_utils::GetMpiType(partsize_t()),
+                              (my_rank+1)%nb_processes, TopToBottom, mpi_com, &requests[nbRequests++]));
+                }
+                if(nbIndexesFromBottom){
+                    AssertMpi(MPI_Irecv(&inIndexToDelete[myNbIndexes+nbIndexesFromTop], int(nbIndexesFromTop), particles_utils::GetMpiType(partsize_t()),
+                              (my_rank+nb_processes-1)%nb_processes, BottomToTop, mpi_com, &requests[nbRequests++]));
+                }
+
+                AssertMpi(MPI_Waitall(nbRequests, requests, MPI_STATUSES_IGNORE));
+            }
+        }
+
+        if(inIndexToDelete.size()){
+            partsize_t nbDeletedParticles = 0;
+
+            std::set<partsize_t> setIndexToDelete(inIndexToDelete.begin(), inIndexToDelete.end());
+
+            for(int idxPartition = 0 ; idxPartition < partition_interval_size ; ++idxPartition){
+                const partsize_t nbDeletedParticlesBefore = nbDeletedParticles;
+                for(partsize_t idx = current_offset_particles_for_partition[idxPartition] ; idx < current_offset_particles_for_partition[idxPartition+1] ; ++idx){
+                    if(setIndexToDelete.find(my_particles_positions_indexes[idx]) != setIndexToDelete.end()){
+                        nbDeletedParticles += 1;
+                    }
+                    else if(nbDeletedParticles){
+                        my_particles_positions_indexes[idx-nbDeletedParticles] = my_particles_positions_indexes[idx];
+
+                        for(int idx_pos = 0 ; idx_pos < size_particle_positions ; ++idx_pos){
+                            my_particles_positions[(idx-nbDeletedParticles)*size_particle_positions+idx_pos] =
+                                    my_particles_positions[idx*size_particle_positions+idx_pos];
+                        }
+
+                        for(int idx_rhs = 0 ; idx_rhs < int(my_particles_rhs.size()) ; ++idx_rhs){
+                            for(int idx_val = 0 ; idx_val < size_particle_rhs ; ++idx_val){
+                                my_particles_rhs[idx_rhs][(idx-nbDeletedParticles)*size_particle_rhs + idx_val] =
+                                        my_particles_rhs[idx_rhs][idx*size_particle_rhs + idx_val];
+                            }
+                        }
+                    }
+                }
+
+                current_offset_particles_for_partition[idxPartition] -= nbDeletedParticlesBefore;
+            }
+
+            current_offset_particles_for_partition[partition_interval_size] -= nbDeletedParticles;
+
+            my_nb_particles -= nbDeletedParticles;
+            assert(my_nb_particles == current_offset_particles_for_partition[partition_interval_size]);
+        }
+
+        AssertMpi(MPI_Allreduce(&my_nb_particles, &total_nb_particles, 1,
+                          particles_utils::GetMpiType(partsize_t()), MPI_SUM, mpi_com));
+    }
+
     void compute() final {
         TIMEZONE("particles_system::compute");
         particles_distr.template compute_distr<computer_class, field_class, size_particle_positions, size_particle_rhs>(
-- 
GitLab


From 922db8baa992d70c179a7b4ecc76900b34706930 Mon Sep 17 00:00:00 2001
From: Berenger Bramas <Berenger.Bramas@inria.fr>
Date: Thu, 17 Oct 2019 15:44:49 +0200
Subject: [PATCH 2/4] Update the interface and set use

---
 cpp/particles/abstract_particles_system.hpp |  3 +++
 cpp/particles/particles_system.hpp          | 24 +++++++++++++--------
 2 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/cpp/particles/abstract_particles_system.hpp b/cpp/particles/abstract_particles_system.hpp
index 2f2f510f..8a1be218 100644
--- a/cpp/particles/abstract_particles_system.hpp
+++ b/cpp/particles/abstract_particles_system.hpp
@@ -27,6 +27,7 @@
 #define ABSTRACT_PARTICLES_SYSTEM_HPP
 
 #include <memory>
+#include <vector>
 
 //- Not generic to enable sampling begin
 #include "field.hpp"
@@ -39,6 +40,8 @@ class abstract_particles_system {
 public:
     virtual ~abstract_particles_system(){}
 
+    virtual void delete_particles_from_indexes(const std::vector<partsize_t>& inIndexToDelete) = 0;
+
     virtual void compute() = 0;
 
     virtual void compute_p2p() = 0;
diff --git a/cpp/particles/particles_system.hpp b/cpp/particles/particles_system.hpp
index 8756aec8..7c746965 100644
--- a/cpp/particles/particles_system.hpp
+++ b/cpp/particles/particles_system.hpp
@@ -28,6 +28,7 @@
 
 #include <array>
 #include <set>
+#include <algorithm>
 
 #include "abstract_particles_system.hpp"
 #include "abstract_particles_system_with_p2p.hpp"
@@ -73,7 +74,7 @@ class particles_system : public abstract_particles_system_with_p2p<partsize_t, r
     std::unique_ptr<real_number[]> my_particles_positions;
     std::unique_ptr<partsize_t[]> my_particles_positions_indexes;
     partsize_t my_nb_particles;
-    const partsize_t total_nb_particles;
+    partsize_t total_nb_particles;
     std::vector<std::unique_ptr<real_number[]>> my_particles_rhs;
     std::vector<hsize_t> particle_file_layout;
 
@@ -162,7 +163,7 @@ public:
         }
     }
 
-    void delete_particles_from_indexes(const std::vector<partsize_t>& inIndexToDelete){
+    void delete_particles_from_indexes(const std::vector<partsize_t>& inIndexToDelete) final {
         // We consider that the given indexes are here or in our neighbors,
         // so we exchange them
         int my_rank;
@@ -170,6 +171,8 @@ public:
         int nb_processes;
         AssertMpi(MPI_Comm_size(mpi_com, &nb_processes));
 
+        std::set<partsize_t> setIndexToDelete(inIndexToDelete.begin(), inIndexToDelete.end());
+
         if(nb_processes > 1){
             const int TopToBottom = 0;
             const int BottomToTop = 1;
@@ -191,7 +194,6 @@ public:
 
                 AssertMpi(MPI_Waitall(4, requests, MPI_STATUSES_IGNORE));
             }
-            inIndexToDelete.resize(inIndexToDelete.size() + nbIndexesFromTop + nbIndexesFromBottom);
             {
                 MPI_Request requests[4];
                 int nbRequests = 0;
@@ -203,24 +205,28 @@ public:
                                   (my_rank+nb_processes-1)%nb_processes, TopToBottom, mpi_com, &requests[nbRequests++]));
                 }
 
+                std::vector<partsize_t> indexesFromTop(nbIndexesFromTop);
+                std::vector<partsize_t> indexesFromBottom(nbIndexesFromTop);
+
                 if(nbIndexesFromTop){
-                    AssertMpi(MPI_Irecv(&inIndexToDelete[myNbIndexes], int(nbIndexesFromTop), particles_utils::GetMpiType(partsize_t()),
+                    AssertMpi(MPI_Irecv(&indexesFromTop[0], int(nbIndexesFromTop), particles_utils::GetMpiType(partsize_t()),
                               (my_rank+1)%nb_processes, TopToBottom, mpi_com, &requests[nbRequests++]));
                 }
                 if(nbIndexesFromBottom){
-                    AssertMpi(MPI_Irecv(&inIndexToDelete[myNbIndexes+nbIndexesFromTop], int(nbIndexesFromTop), particles_utils::GetMpiType(partsize_t()),
+                    AssertMpi(MPI_Irecv(&indexesFromBottom[0], int(nbIndexesFromTop), particles_utils::GetMpiType(partsize_t()),
                               (my_rank+nb_processes-1)%nb_processes, BottomToTop, mpi_com, &requests[nbRequests++]));
                 }
 
                 AssertMpi(MPI_Waitall(nbRequests, requests, MPI_STATUSES_IGNORE));
+
+                std::copy( indexesFromTop.begin(), indexesFromTop.end(), std::inserter( setIndexToDelete, setIndexToDelete.end() ) );
+                std::copy( indexesFromBottom.begin(), indexesFromBottom.end(), std::inserter( setIndexToDelete, setIndexToDelete.end() ) );
             }
         }
 
-        if(inIndexToDelete.size()){
+        if(setIndexToDelete.size()){
             partsize_t nbDeletedParticles = 0;
 
-            std::set<partsize_t> setIndexToDelete(inIndexToDelete.begin(), inIndexToDelete.end());
-
             for(int idxPartition = 0 ; idxPartition < partition_interval_size ; ++idxPartition){
                 const partsize_t nbDeletedParticlesBefore = nbDeletedParticles;
                 for(partsize_t idx = current_offset_particles_for_partition[idxPartition] ; idx < current_offset_particles_for_partition[idxPartition+1] ; ++idx){
@@ -253,7 +259,7 @@ public:
             assert(my_nb_particles == current_offset_particles_for_partition[partition_interval_size]);
         }
 
-        AssertMpi(MPI_Allreduce(&my_nb_particles, &total_nb_particles, 1,
+        AssertMpi(MPI_Allreduce(const_cast<partsize_t*>(&my_nb_particles), &total_nb_particles, 1,
                           particles_utils::GetMpiType(partsize_t()), MPI_SUM, mpi_com));
     }
 
-- 
GitLab


From 630e7e8aed890ef03f3c1c4ae723b9acfbda2a2a Mon Sep 17 00:00:00 2001
From: Berenger Bramas <Berenger.Bramas@inria.fr>
Date: Mon, 4 Nov 2019 16:46:12 +0100
Subject: [PATCH 3/4] Debug (forgot to allocate the right array for indexes)

---
 cpp/particles/p2p_distr_mpi.hpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/cpp/particles/p2p_distr_mpi.hpp b/cpp/particles/p2p_distr_mpi.hpp
index e567e86f..274dc8a6 100644
--- a/cpp/particles/p2p_distr_mpi.hpp
+++ b/cpp/particles/p2p_distr_mpi.hpp
@@ -580,6 +580,7 @@ public:
                         const partsize_t NbParticlesToReceive = descriptor.nbParticlesToExchange;
                         assert(NbParticlesToReceive != -1);
                         assert(descriptor.toCompute == nullptr);
+                        assert(descriptor.indexes == nullptr);
 
                         if(NbParticlesToReceive){
                             descriptor.toCompute.reset(new real_number[NbParticlesToReceive*size_particle_positions]);
@@ -590,7 +591,7 @@ public:
                                                 particles_utils::GetMpiType(real_number()), destProc, TAG_POSITION_PARTICLES,
                                                 current_com, &mpiRequests.back()));
 
-                            descriptor.toCompute.reset(new real_number[NbParticlesToReceive*size_particle_positions]);
+                            descriptor.indexes.reset(new partsize_t[NbParticlesToReceive]);
                             whatNext.emplace_back(std::pair<Action,int>{COMPUTE_PARTICLES, releasedAction.second});
                             mpiRequests.emplace_back();
                             assert(NbParticlesToReceive*size_particle_positions < std::numeric_limits<int>::max());
@@ -615,6 +616,7 @@ public:
                             const partsize_t NbParticlesToReceive = descriptor.nbParticlesToExchange;
 
                             assert(descriptor.toCompute != nullptr);
+                            assert(descriptor.indexes != nullptr);
                             descriptor.results.reset(new real_number[NbParticlesToReceive*size_particle_rhs]);
                             computer_thread.template init_result_array<size_particle_rhs>(descriptor.results.get(), NbParticlesToReceive);
 
@@ -685,6 +687,7 @@ public:
                                                 particles_utils::GetMpiType(real_number()), destProc, TAG_RESULT_PARTICLES,
                                                 current_com, &mpiRequests.back()));
                             delete[] descriptor.toCompute.release();
+                            delete[] descriptor.indexes.release();
                         }
                     }
                     //////////////////////////////////////////////////////////////////////
-- 
GitLab


From 395528bd2b244b5812ce0adf8bd1f5148a89cccf Mon Sep 17 00:00:00 2001
From: Cristian C Lalescu <Cristian.Lalescu@ds.mpg.de>
Date: Tue, 5 Nov 2019 16:05:31 +0100
Subject: [PATCH 4/4] adds hdf5 single value write method

---
 cpp/hdf5_tools.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++
 cpp/hdf5_tools.hpp |  6 ++++++
 2 files changed, 48 insertions(+)

diff --git a/cpp/hdf5_tools.cpp b/cpp/hdf5_tools.cpp
index 5a3aef39..9300f7ef 100644
--- a/cpp/hdf5_tools.cpp
+++ b/cpp/hdf5_tools.cpp
@@ -169,6 +169,36 @@ number hdf5_tools::read_value(
     return result;
 }
 
+template <typename number>
+int hdf5_tools::write_value_with_single_rank(
+        const hid_t group,
+        const std::string dset_name,
+        const number value)
+{
+    hid_t dset, mem_dtype;
+    if (typeid(number) == typeid(int))
+        mem_dtype = H5Tcopy(H5T_NATIVE_INT);
+    else if (typeid(number) == typeid(double))
+        mem_dtype = H5Tcopy(H5T_NATIVE_DOUBLE);
+    if (H5Lexists(group, dset_name.c_str(), H5P_DEFAULT))
+    {
+        dset = H5Dopen(group, dset_name.c_str(), H5P_DEFAULT);
+    }
+    else
+    {
+        hid_t fspace;
+        hsize_t count[1];
+        count[0] = 1;
+        fspace = H5Screate_simple(1, count, NULL);
+        dset = H5Dcreate(group, dset_name.c_str(), mem_dtype, fspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+        H5Sclose(fspace);
+    }
+    H5Dwrite(dset, mem_dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
+    H5Dclose(dset);
+    H5Tclose(mem_dtype);
+    return EXIT_SUCCESS;
+}
+
 template <typename dtype>
 std::vector<dtype> hdf5_tools::read_vector_with_single_rank(
         const int myrank,
@@ -269,3 +299,15 @@ double hdf5_tools::read_value<double>(
         const hid_t,
         const std::string);
 
+template
+int hdf5_tools::write_value_with_single_rank<int>(
+        const hid_t group,
+        const std::string dset_name,
+        const int value);
+
+template
+int hdf5_tools::write_value_with_single_rank<double>(
+        const hid_t group,
+        const std::string dset_name,
+        const double value);
+
diff --git a/cpp/hdf5_tools.hpp b/cpp/hdf5_tools.hpp
index 99ba45a1..82c00486 100644
--- a/cpp/hdf5_tools.hpp
+++ b/cpp/hdf5_tools.hpp
@@ -84,6 +84,12 @@ namespace hdf5_tools
     number read_value(
             const hid_t group,
             const std::string dset_name);
+
+    template <typename number>
+    int write_value_with_single_rank(
+            const hid_t group,
+            const std::string dset_name,
+            const number value);
 }
 
 #endif//HDF5_TOOLS_HPP
-- 
GitLab