diff --git a/projects/sedimenting_rods/GCDNS.py b/projects/sedimenting_rods/GCDNS.py
new file mode 100644
index 0000000000000000000000000000000000000000..670c910d270d3b5fb60aaf255b1b8f438cd0c77a
--- /dev/null
+++ b/projects/sedimenting_rods/GCDNS.py
@@ -0,0 +1,228 @@
+import os
+import sys
+import argparse
+import getpass
+from packaging import version
+
+username = getpass.getuser()
+homefolder = os.path.expanduser('~')
+
+here = os.path.normcase(__file__)
+
+cpp_location = os.path.dirname(here)
+
+import numpy as np
+import h5py
+
+import TurTLE
+#assert(version.parse(TurTLE.__version__) > version.parse('3.16'))
+import TurTLE._base
+import TurTLE.DNS
+from TurTLE._code import _code
+
+
+class GCDNS(TurTLE.DNS):
+    def __init__(
+            self,
+            **kwargs):
+        self.dns_type = 'many_NSVEcylinder_collisions'
+        TurTLE.DNS.__init__(self, **kwargs)
+        self.extra_parameters = {} #Added this here to get it to work
+        self.extra_parameters[self.dns_type] = {
+            'niter_part' : int(10),
+            'niter_part_fine_period' : int(10),
+            'niter_part_fine_duration' : int(0),
+            'nparticles' : np.array([100],dtype=int),
+            'tracers0_integration_steps' : int(2),
+            'tracers0_neighbours' : int(3),
+            'tracers0_smoothness' : int(2),
+            'tracers0_enable_p2p' : int(1),
+            'tracers0_enable_inner' : int(0),
+            'tracers0_enable_vorticity_omega' : int(0),
+            'tracers0_cutoff' : np.array([0.5],dtype='float64'),
+            'tracers0_lambda' : np.array([100.0],dtype='float64'),
+            'tracers0_beta0' : np.array([0.0],dtype='float64'),
+            'tracers0_beta1' : np.array([0.0],dtype='float64'),
+            'tracers0_D_rho' : np.array([1.0],dtype='float64'),
+            'tracers0_u_sed' : np.array([1.0],dtype='float64'),
+            'l_factor' : float(1.0),
+            't_factor' : float(1.0),
+            'many_integration_steps' : np.array([2]).astype(np.int),
+                }
+        return None
+    def write_src(
+            self):
+        self.version_message = (
+                '/***********************************************************************\n' +
+                '* this code automatically generated by TurTLE\n' +
+                '* version {0}\n'.format(TurTLE.__version__) +
+                '***********************************************************************/\n\n\n')
+        self.include_list = [
+                '"base.hpp"',
+                '"scope_timer.hpp"',
+                '"fftw_interface.hpp"',
+                '"full_code/main_code.hpp"',
+                '"full_code/NSVEparticles.hpp"',
+                '<cmath>',
+                '<iostream>',
+                '<hdf5.h>',
+                '<string>',
+                '<cstring>',
+                '<fftw3-mpi.h>',
+                '<omp.h>',
+                '<cfenv>',
+                '<cstdlib>']
+        self.main = """
+            int main(int argc, char *argv[])
+            {{
+                bool fpe = (
+                    (getenv("BFPS_FPE_OFF") == nullptr) ||
+                    (getenv("BFPS_FPE_OFF") != std::string("TRUE")));
+                return main_code< {0} >(argc, argv, fpe);
+            }}
+            """.format(self.dns_type + '<{0}>'.format(self.C_field_dtype))
+        self.includes = '\n'.join(
+                ['#include ' + hh
+                 for hh in self.include_list])
+        self.definitions += open(
+                os.path.join(
+                    cpp_location, 'particles_inner_computer_sedimenting_rods.hpp'), 'r').read()
+        self.definitions += open(
+                os.path.join(
+                    cpp_location, 'particles_inner_computer_sedimenting_rods.cpp'), 'r').read()
+        self.definitions += open(
+            os.path.join(
+                cpp_location, 'many_NSVEcylinder_collisions.hpp'), 'r').read()
+        self.definitions += open(
+            os.path.join(
+                cpp_location, 'many_NSVEcylinder_collisions.cpp'), 'r').read()
+        with open(self.name + '.cpp', 'w') as outfile:
+            outfile.write(self.version_message + '\n\n')
+            outfile.write(self.includes + '\n\n')
+            outfile.write(self.definitions + '\n\n')
+            outfile.write(self.main + '\n')
+        self.check_current_vorticity_exists = True
+        return None
+
+    def generate_tracer_state(
+            self,
+            rseed = None,
+            species = 0,
+            integration_steps = None,
+            ncomponents = 3
+            ):
+        try:
+            if type(integration_steps) == type(None):
+                integration_steps = self.NSVEp_extra_parameters['tracers0_integration_steps']
+            if 'tracers{0}_integration_steps'.format(species) in self.parameters.keys():
+                integration_steps = self.parameters['tracers{0}_integration_steps'.format(species)]
+            if self.dns_type == 'static_field_with_cylinder_collisions' or self.dns_type == 'NSVEcylinder_collisions' or self.dns_type == 'many_NSVEcylinder_collisions':
+                ncomponents = 6
+            with h5py.File(self.get_checkpoint_0_fname(), 'a') as data_file:
+                nn = self.parameters['nparticles'][species]
+                if not 'tracers{0}'.format(species) in data_file.keys():
+                    data_file.create_group('tracers{0}'.format(species))
+                    data_file.create_group('tracers{0}/rhs'.format(species))
+                    data_file.create_group('tracers{0}/state'.format(species))
+                data_file['tracers{0}/rhs'.format(species)].create_dataset(
+                        '0',
+                        shape = (integration_steps, nn, ncomponents,),
+                        dtype = np.float)
+                dset = data_file['tracers{0}/state'.format(species)].create_dataset(
+                        '0',
+                        shape = (nn, ncomponents,),
+                        dtype = np.float)
+                if not type(rseed) == type(None):
+                    np.random.seed(rseed)
+                cc = int(0)
+                batch_size = int(1e6)
+                def get_random_phases(npoints):
+                    return np.random.random(
+                                    (npoints, 3))*2*np.pi
+                def get_random_versors(npoints):
+                    if self.parallel_rods:
+                        bla = np.zeros((npoints, 3),dtype='float64')
+                        bla[:,1]=1.0
+                    else:
+                        bla = np.random.normal(
+                                size = (npoints, 3))
+                        bla  /= np.sum(bla**2, axis = 1)[:, None]**.5
+                    return bla
+                while nn > 0:
+                    if nn > batch_size:
+                        dset[cc*batch_size:(cc+1)*batch_size, :3] = get_random_phases(batch_size)
+                        if dset.shape[1] == 6:
+                            dset[cc*batch_size:(cc+1)*batch_size, 3:] = get_random_versors(batch_size)
+                        nn -= batch_size
+                    else:
+                        dset[cc*batch_size:cc*batch_size+nn, :3] = get_random_phases(nn)
+                        if dset.shape[1] == 6:
+                            dset[cc*batch_size:cc*batch_size+nn, 3:] = get_random_versors(nn)
+                        nn = 0
+                    cc += 1
+        except Exception as e:
+            print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!WARNING')
+            print('WARNING: when generating particles, caught the exception ', e)
+            print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!WARNING')
+        return None
+
+    def launch(
+            self,
+            args = [],
+            test_collisions = False,
+            parallel_rods = False,
+            **kwargs
+            ):
+        self.parallel_rods = parallel_rods
+        opt = self.prepare_launch(args = args)
+        if not os.path.exists(self.get_data_file_name()):
+            self.write_par()
+            self.generate_initial_condition(opt = opt)
+            for species in range(self.parameters['many_integration_steps'].shape[0]):
+                self.generate_tracer_state(
+                        species = species,
+                        ncomponents = 6,
+                        integration_steps = self.parameters['many_integration_steps'][species-1]
+                        )
+                with h5py.File(self.get_particle_file_name(), 'a') as particle_file:
+                    particle_file.create_group('tracers{0}/position'.format(species))
+                    particle_file.create_group('tracers{0}/orientation'.format(species))
+                    particle_file.create_group('tracers{0}/velocity'.format(species))
+                    particle_file.create_group('tracers{0}/velocity_gradient'.format(species))
+                    particle_file.create_group('tracers{0}/acceleration'.format(species))
+                with h5py.File(os.path.join(self.work_dir, self.simname + '_colliding_particle_subset.h5'), 'a') as subset_file:
+                    subset_file.create_group('subset_tracers{0}/labels'.format(species))
+                    subset_file.create_group('subset_tracers{0}/position'.format(species))
+                    subset_file.create_group('subset_tracers{0}/orientation'.format(species))
+                    subset_file.create_group('subset_tracers{0}/velocity'.format(species))
+                    subset_file.create_group('subset_tracers{0}/velocity_gradient'.format(species))
+                    subset_file.create_group('subset_tracers{0}/acceleration'.format(species))
+        # Create collisions file
+        if not os.path.exists(os.path.join(self.work_dir, self.simname + '_collisions.h5')):
+            with h5py.File(os.path.join(self.work_dir, self.simname + '_collisions.h5'), 'w') as ofile:
+                for i in range(self.parameters['many_integration_steps'].size):
+                    ofile.create_group('/collisions{0}'.format(i))
+                    ofile.create_group('/new_collisions{0}'.format(i))
+                    ofile.create_group('/same_collisions{0}'.format(i))
+                    ofile.create_group('/old_collisions{0}'.format(i))
+        if not os.path.exists(os.path.join(self.work_dir, self.simname + '_collisions_PP.h5')):
+            with h5py.File(os.path.join(self.work_dir, self.simname + '_collisions_PP.h5'), 'w') as ofile:
+                for i in range(self.parameters['many_integration_steps'].size):
+                    ofile.create_group('/new_collisions{0}'.format(i))
+        self.run(
+                nb_processes = opt.nb_processes,
+                nb_threads_per_process = opt.nb_threads_per_process,
+                njobs = opt.njobs,
+                hours = opt.minutes // 60,
+                minutes = opt.minutes % 60,
+                no_submit = opt.no_submit,
+                no_debug = opt.no_debug)
+        return None
+
+def main():
+    bla = GCDNS()
+    bla.launch(['many_NSVEcylinder_collisions'] + sys.argv[1:])
+    return None
+
+if __name__ == '__main__':
+    main()
diff --git a/projects/sedimenting_rods/README.rst b/projects/sedimenting_rods/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b5dc14efcf3835a7cfe04e59c1a60adc05d04dbd
--- /dev/null
+++ b/projects/sedimenting_rods/README.rst
@@ -0,0 +1,9 @@
+Collision counter for sedimenting rods
+--------------------------------------
+
+Please see the PNAS publication
+José-Agustı́n Arguedas-Leiva, Jonasz Słomka, Cristian C. Lalescu, Roman Stocker, and Michael Wilczek
+*Elongation enhances encounter rates betweenin turbulence*
+
+In this folder we include the code used to generate the results
+presented in the paper above.
diff --git a/projects/sedimenting_rods/many_NSVEcylinder_collisions.cpp b/projects/sedimenting_rods/many_NSVEcylinder_collisions.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c231f2d3bf61b3ad8533f691025223551b65536d
--- /dev/null
+++ b/projects/sedimenting_rods/many_NSVEcylinder_collisions.cpp
@@ -0,0 +1,663 @@
+/**********************************************************************
+*                                                                     *
+*  Copyright 2020 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                                 *
+*                                                                     *
+**********************************************************************/
+
+#include <string>
+#include <cmath>
+#include "scope_timer.hpp"
+#include "particles/particles_sampling.hpp"
+#include "particles/p2p/p2p_computer.hpp"
+#include "particles/inner/particles_inner_computer.hpp"
+#include "particles/interpolation/field_tinterpolator.hpp"
+#include "particles/interpolation/particle_set.hpp"
+#include "particles/p2p/p2p_ghost_collisions.hpp"
+
+template <typename rnumber>
+int many_NSVEcylinder_collisions<rnumber>::initialize(void)
+{
+    TIMEZONE("many_NSVEcylinder_collisions::initialize");
+    this->NSVE<rnumber>::initialize();
+    this->many_ps.resize(this->many_integration_steps.size());
+    this->particles_output_writer_mpi.resize(this->many_integration_steps.size());
+    this->particles_sample_writer_mpi.resize(this->many_integration_steps.size());
+
+    // initialize particles
+    //force inner term to true.
+    //not sure why, since it's set to true in the constructor prototype
+    this->enable_inner = true;
+
+    for (unsigned int index = 0; index < this->many_ps.size(); index++)
+    {
+        DEBUG_MSG("in many_ps loop %d\n", index);
+        p2p_ghost_collisions<double, long long int> p2p_cc;
+        particles_inner_computer_sedimenting_rods<double, long long int> current_particles_inner_computer(this->tracers0_lambda[index], this->tracers0_D_rho[index], this->tracers0_u_sed[index], this->tracers0_beta0[index], this->tracers0_beta1[index]);
+        current_particles_inner_computer.setEnable(enable_inner);
+
+        DEBUG_MSG("Settling velocity is %g\n", current_particles_inner_computer.get_sedimenting_velocity() );
+        DEBUG_MSG("D_rho is %g\n", current_particles_inner_computer.get_D_rho() );
+        DEBUG_MSG("beta0 is %g\n", current_particles_inner_computer.get_beta0() );
+        DEBUG_MSG("beta1 is %g\n", current_particles_inner_computer.get_beta1() );
+        DEBUG_MSG("Is enable is %d\n", current_particles_inner_computer.isEnable() );
+
+        this->many_ps[index] = Template_double_for_if::evaluate<std::unique_ptr<abstract_particles_system_with_p2p<long long int, double, p2p_ghost_collisions<double, long long int>>>,
+                        int, 1, 11, 1, // interpolation_size
+                        int, 0, 3, 1, // spline_mode
+                        particles_system_build_container<long long int, rnumber,FFTW,THREE,double,
+                                                            p2p_ghost_collisions<double, long long int>,
+                                                            particles_inner_computer_sedimenting_rods<double,long long int>,
+                                                            6,6>>(
+                            this->tracers0_neighbours, // template iterator 1
+                            this->tracers0_smoothness, // template iterator 2
+                            this->fs->cvelocity,
+                            this->fs->kk,
+                            tracers0_integration_steps,
+                            (long long int)nparticles[index],
+                            this->get_current_fname(),
+                            std::string("/tracers" + std::to_string(index) + "/state/") + std::to_string(this->iteration),
+                            std::string("/tracers" + std::to_string(index) + "/rhs/") + std::to_string(this->iteration),
+                            this->comm,
+                            this->iteration+1,
+                            std::move(p2p_cc),
+                            std::move(current_particles_inner_computer),
+                            this->tracers0_cutoff[index]);
+
+        //DEBUG_MSG("Settling velocity is %g\n", this->ps->getInnerComputer().get_sedimenting_velocity() );
+        //DEBUG_MSG("Is enable is %d\n", this->ps->getInnerComputer().isEnable() );
+
+        if (this->tracers0_lambda[index]>1.0){
+        this->many_ps[index]->getP2PComputer().setEnable(true);
+        this->many_ps[index]->getP2PComputer().set_cylinder();
+        this->many_ps[index]->getP2PComputer().set_cylinder_length(this->tracers0_cutoff[index]*(1.0-1.0/this->tracers0_lambda[index]));
+        this->many_ps[index]->getP2PComputer().set_cylinder_width(this->tracers0_cutoff[index] / this->tracers0_lambda[index]);
+        } else if (this->tracers0_lambda[index]==1.0){
+        this->many_ps[index]->getP2PComputer().set_sphere();
+        }
+
+        // initialize output objects
+        this->particles_output_writer_mpi[index] = new particles_output_hdf5<
+            long long int, double, 6>(
+                    MPI_COMM_WORLD,
+                    "tracers" + std::to_string(index),
+                    nparticles[index],
+                    this->many_integration_steps[index]);
+        this->particles_output_writer_mpi[index]->setParticleFileLayout(this->many_ps[index]->getParticleFileLayout());
+
+    }
+
+    /// allocate grad vel field
+    this->nabla_u = new field<rnumber, FFTW, THREExTHREE>(
+            this->nx, this->ny, this->nz,
+            this->comm,
+            this->fs->cvorticity->fftw_plan_rigor);
+	/// open collisions file
+    if (this->myrank == 0){
+        // set caching parameters
+        hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
+        herr_t cache_err = H5Pset_cache(fapl, 0, 521, 134217728, 1.0);
+        variable_used_only_in_assert(cache_err);
+        DEBUG_MSG("when setting cols_file cache I got %d\n", cache_err);
+        this->cols_file = H5Fopen(
+                (this->simname + "_collisions.h5").c_str(),
+                H5F_ACC_RDWR,
+                fapl);
+        this->cols_file_PP = H5Fopen(
+                (this->simname + "_collisions_PP.h5").c_str(),
+                H5F_ACC_RDWR,
+                fapl);
+        this->collision_pairs_old.resize(this->many_ps.size());
+        this->collision_pairs_new.resize(this->many_ps.size());
+        this->collision_pairs_same.resize(this->many_ps.size());
+        this->collision_pairs_time_0.resize(this->many_ps.size());
+        this->collision_pairs_time_1.resize(this->many_ps.size());
+        for (unsigned int index = 0; index < this->many_ps.size(); index++)
+            {
+            DEBUG_MSG("index %d\n", index);
+            reset_collision_lists(index);
+            }
+    }
+
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber>
+int many_NSVEcylinder_collisions<rnumber>::step(void)
+{
+    TIMEZONE("many_NSVEcylinder_collisions::step");
+    this->fs->compute_velocity(this->fs->cvorticity);
+
+    compute_gradient(
+        this->fs->kk,
+        this->fs->cvelocity,
+        this->nabla_u);
+    this->nabla_u->ift();
+    this->fs->cvelocity->ift(); // needed before completeloop
+    //std::unique_ptr<double[]> sampled_vorticity(new double[9*this->ps->getLocalNbParticles()]);
+    //std::fill_n(sampled_vorticity.get(), 9*this->ps->getLocalNbParticles(), 0);
+    //this->ps->sample_compute_field(*this->nabla_u, sampled_vorticity.get());
+    //*this->tmp_vec_field = this->fs->cvorticity->get_cdata();
+    //this->tmp_vec_field->ift();
+
+    for (unsigned int index = 0; index < this->many_ps.size(); index++)
+    {
+        DEBUG_MSG(("step for tracers"+std::to_string(index)+"\n").c_str());
+        this->many_ps[index]->getP2PComputer().reset_collision_pairs();
+        DEBUG_MSG(("step for tracers"+std::to_string(index)+", after reset_collision_pairs\n").c_str());
+        this->many_ps[index]->completeLoopWithExtraField(this->dt, *this->nabla_u);
+        DEBUG_MSG(("step for tracers"+std::to_string(index)+", after completeLoopWithExtraField\n").c_str());
+    }
+
+    this->NSVE<rnumber>::step();
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber>
+int many_NSVEcylinder_collisions<rnumber>::write_checkpoint(void)
+{
+    TIMEZONE("many_NSVEcylinder_collisions::write_checkpoint");
+    this->NSVE<rnumber>::write_checkpoint();
+    for (unsigned int index = 0; index < this->many_ps.size(); index++)
+    {
+        this->particles_output_writer_mpi[index]->open_file(this->fs->get_current_fname());
+        // TODO P2P write particle data too
+        this->particles_output_writer_mpi[index]->template save<6>(
+                this->many_ps[index]->getParticlesState(),
+                this->many_ps[index]->getParticlesRhs(),
+                this->many_ps[index]->getParticlesIndexes(),
+                this->many_ps[index]->getLocalNbParticles(),
+                this->fs->iteration);
+        this->particles_output_writer_mpi[index]->close_file();
+    }
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber>
+int many_NSVEcylinder_collisions<rnumber>::finalize(void)
+{
+    TIMEZONE("many_NSVEcylinder_collisions::finalize");
+    if (this->myrank == 0){
+         H5Fclose(this->cols_file);
+         H5Fclose(this->cols_file_PP);
+         }
+    delete this->nabla_u;
+    for (unsigned int index = 0; index < this->many_ps.size(); index++)
+    {
+        delete this->particles_output_writer_mpi[index];
+    }
+    this->NSVE<rnumber>::finalize();
+
+    return EXIT_SUCCESS;
+}
+
+/** \brief Compute fluid stats and sample particle data.
+ */
+
+template <typename rnumber>
+int many_NSVEcylinder_collisions<rnumber>::do_stats()
+{
+    bool save_pair;
+    long long int indx_0_0, indx_0_1, indx_1_0, indx_1_1;
+    TIMEZONE("many_NSVEcylinder_collisions::do_stats");
+    /// perform fluid stats
+    this->NSVE<rnumber>::do_stats();
+
+    // compute velocity, copy to tmp field
+    //this->fs->compute_velocity(this->fs->cvorticity);
+    //*(this->tmp_vec_field) = *this->fs->cvelocity;
+
+     /// sample velocity gradient
+     /// fs->cvelocity should contain the velocity in Fourier space
+     this->fs->compute_velocity(this->fs->cvorticity);
+     compute_gradient(
+             this->fs->kk,
+             this->fs->cvelocity,
+             this->nabla_u);
+     this->nabla_u->ift();
+     // Dont need acceleration in the moment //
+     ///// compute acceleration and sample it
+     //this->fs->compute_Lagrangian_acceleration(this->tmp_vec_field);
+    // move velocity to real space
+    this->fs->cvelocity->ift();
+
+    // Carry out collision measurement
+    if (this->enable_p2p) {
+        for (unsigned int index = 0; index < this->many_ps.size(); index++)
+        {
+            /* DEBUG_MSG("Saving collisions at index %d\n",this->many_ps[index]->get_step_idx()-1); */
+            /* Here the collisions are saved both at the end and at the beginning of a subsequent simulation. This produces an error message. */
+            DEBUG_MSG(("merge for tracers"+std::to_string(index)+"\n").c_str());
+            this->many_ps[index]->getP2PComputer().MPI_merge(
+                    this->comm,
+                    this->myrank,
+                    this->nprocs);
+
+            if (this->myrank == 0){
+                /* Reset the collision lists old, same, and new */
+                this->reset_collision_lists(index);
+                /* Copy the collision list from the current time step onto collision_pairs_time_1 */
+                this->collision_pairs_time_1[index] = this->many_ps[index]->getP2PComputer().get_collision_pairs(
+                                this->comm,
+                                this->myrank,
+                                this->nprocs);
+                /* Get the collisions that dissapeared in this time step and those that remain the same */
+                for (unsigned int pair_index_0 = 0; pair_index_0 < this->collision_pairs_time_0[index].size()/2; pair_index_0++)
+                    {
+                    indx_0_0 = this->collision_pairs_time_0[index][pair_index_0*2];
+                    indx_0_1 = this->collision_pairs_time_0[index][pair_index_0*2+1];
+                    save_pair = true;
+                    for (unsigned int pair_index_1 = 0; pair_index_1 < this->collision_pairs_time_1[index].size()/2; pair_index_1++)
+                        {
+                        indx_1_0 = this->collision_pairs_time_1[index][pair_index_1*2];
+                        indx_1_1 = this->collision_pairs_time_1[index][pair_index_1*2+1];
+                        if (indx_0_0==indx_1_0 and indx_0_1==indx_1_1)
+                            {
+                            save_pair = false;
+                            this->collision_pairs_same[index].push_back(indx_0_0);
+                            this->collision_pairs_same[index].push_back(indx_0_1);
+                            break;
+                            }
+                        }
+                    if (save_pair){
+                    this->collision_pairs_old[index].push_back(indx_0_0);
+                    this->collision_pairs_old[index].push_back(indx_0_1);
+                    }
+                }
+                /* Get the new collisions from this time step */
+                for (unsigned int pair_index_0 = 0; pair_index_0 < this->collision_pairs_time_1[index].size()/2; pair_index_0++)
+                    {
+                    indx_0_0 = this->collision_pairs_time_1[index][pair_index_0*2];
+                    indx_0_1 = this->collision_pairs_time_1[index][pair_index_0*2+1];
+                    save_pair = true;
+                    for (unsigned int pair_index_1 = 0; pair_index_1 < this->collision_pairs_time_0[index].size()/2; pair_index_1++)
+                        {
+                        indx_1_0 = this->collision_pairs_time_0[index][pair_index_1*2];
+                        indx_1_1 = this->collision_pairs_time_0[index][pair_index_1*2+1];
+                        if (indx_0_0==indx_1_0 and indx_0_1==indx_1_1)
+                            {
+                            save_pair = false;
+                            break;
+                            }
+                        }
+                    if (save_pair){
+                    this->collision_pairs_new[index].push_back(indx_0_0);
+                    this->collision_pairs_new[index].push_back(indx_0_1);
+                    }
+                }
+                DEBUG_MSG(("save cols for tracers"+std::to_string(index)+"\n").c_str());
+                // this->cols_file is only defined for rank 0,
+                // so only rank 0 can write the pairs.
+                // this is also a reason for calling MPI_merge beforehand.
+                std::string dset_name = (
+                        "/collisions"
+                         + std::to_string(index)
+                         + "/pair_list_"
+                         + std::to_string(this->many_ps[index]->get_step_idx()-1));
+                // if the dataset already exists, it's because we're doing the statistics for
+                // first iteration in current run. this is fine for spectra etc (what happens for example
+                // in NSVE::do_stats), where the same element of some big array gets rewritten.
+                // However, it's not fine for "write_particle_ID_pairs", because this method creates
+                // a new dataset. Which raises an error when the dataset already exists.
+                // Hence I chose to write the current data with "extra", for redundancy checks.
+                // Although I assume the "extra" dataset will be empty since timestep has not run
+                // and no collisions have been counted.
+                if (H5Lexists(this->cols_file, dset_name.c_str(), H5P_DEFAULT))
+                    dset_name += "_extra";
+                hdf5_tools::write_particle_ID_pairs_with_single_rank<long long>(
+                        this->collision_pairs_time_1[index],
+                        this->cols_file,
+                        dset_name.c_str());
+                /* Save the new, old, and same collisions */
+                DEBUG_MSG(("save new cols for tracers"+std::to_string(index)+"\n").c_str());
+                dset_name = (
+                        "/new_collisions"
+                         + std::to_string(index)
+                         + "/pair_list_"
+                         + std::to_string(this->many_ps[index]->get_step_idx()-1));
+                if (H5Lexists(this->cols_file, dset_name.c_str(), H5P_DEFAULT))
+                    dset_name += "_extra";
+                hdf5_tools::write_particle_ID_pairs_with_single_rank<long long>(
+                        this->collision_pairs_new[index],
+                        this->cols_file,
+                        dset_name.c_str());
+                hdf5_tools::write_particle_ID_pairs_with_single_rank<long long>(
+                        this->collision_pairs_new[index],
+                        this->cols_file_PP,
+                        dset_name.c_str());
+                DEBUG_MSG(("save old cols for tracers"+std::to_string(index)+"\n").c_str());
+                dset_name = (
+                        "/old_collisions"
+                         + std::to_string(index)
+                         + "/pair_list_"
+                         + std::to_string(this->many_ps[index]->get_step_idx()-1));
+                if (H5Lexists(this->cols_file, dset_name.c_str(), H5P_DEFAULT))
+                    dset_name += "_extra";
+                hdf5_tools::write_particle_ID_pairs_with_single_rank<long long>(
+                        this->collision_pairs_old[index],
+                        this->cols_file,
+                        dset_name.c_str());
+                DEBUG_MSG(("save same cols for tracers"+std::to_string(index)+"\n").c_str());
+                dset_name = (
+                        "/same_collisions"
+                         + std::to_string(index)
+                         + "/pair_list_"
+                         + std::to_string(this->many_ps[index]->get_step_idx()-1));
+                if (H5Lexists(this->cols_file, dset_name.c_str(), H5P_DEFAULT))
+                    dset_name += "_extra";
+                hdf5_tools::write_particle_ID_pairs_with_single_rank<long long>(
+                        this->collision_pairs_same[index],
+                        this->cols_file,
+                        dset_name.c_str());
+                DEBUG_MSG(("save stats for tracers"+std::to_string(index)+"\n").c_str());
+                /* Copy the collision list from the current time step onto collision_pairs_time_0 */
+                this->collision_pairs_time_0[index]=this->collision_pairs_time_1[index];
+                DEBUG_MSG(("done saving stats for tracers"+std::to_string(index)+"\n").c_str());
+            }
+
+            {
+                // this block for subsampling
+                // declare temporary particle set for subset sampling
+                DEBUG_MSG_WAIT(this->comm, "00 hello from subsample block\n");
+                particle_set<6, 3, 2> psubset(
+                        this->fs->cvelocity->rlayout,
+                        this->dkx,
+                        this->dky,
+                        this->dkz,
+                        this->tracers0_cutoff[index]);
+                DEBUG_MSG_WAIT(this->comm, "01 hello from subsample block\n");
+
+                // first extract unique IDs of colliding particles
+                int nparticles_to_sample;
+                std::vector<long long int> indices_to_sample;
+                if (this->myrank == 0)
+                {
+                    indices_to_sample.resize(this->collision_pairs_new[index].size());
+                    // direct copy
+                    for (unsigned ii = 0; ii < indices_to_sample.size(); ii++)
+                        indices_to_sample[ii] = this->collision_pairs_new[index][ii];
+                    // sort in place
+                    std::sort(indices_to_sample.begin(), indices_to_sample.end());
+                    // remove adjacent duplicates
+                    auto last = std::unique(indices_to_sample.begin(), indices_to_sample.end());
+                    // clean up undefined values
+                    indices_to_sample.erase(last, indices_to_sample.end());
+                    // now indices_to_sample contains unique particle labels.
+                    nparticles_to_sample = indices_to_sample.size();
+                }
+                DEBUG_MSG_WAIT(this->comm, "02 hello from subsample block\n");
+
+                // tell all processes how many particles there are
+                AssertMpi(MPI_Bcast(
+                            &nparticles_to_sample,
+                            1,
+                            MPI_INT,
+                            0,
+                            this->comm));
+                DEBUG_MSG_WAIT(this->comm, "03 hello from subsample block\n");
+                if (nparticles_to_sample > 0)
+                {
+                    DEBUG_MSG_WAIT(this->comm, "hello, there are %d particles to sample\n", nparticles_to_sample);
+                    // tell all processes which particles are in the subset
+                    if (this->myrank != 0)
+                        indices_to_sample.resize(nparticles_to_sample);
+                    AssertMpi(MPI_Bcast(
+                                &indices_to_sample.front(),
+                                nparticles_to_sample,
+                                MPI_LONG,
+                                0,
+                                this->comm));
+                    // now initialize particle set
+                    psubset.init_as_subset_of(
+                            *this->many_ps[index],
+                            indices_to_sample);
+                    DEBUG_MSG_WAIT(this->comm, "hello, psubset has %ld particles out of %ld\n",
+                            psubset.getLocalNumberOfParticles(),
+                            psubset.getTotalNumberOfParticles());
+                    // write labels of colliding particles
+                    // open subset file only with rank 0
+                    // START WARNING: subset file opens "colliding_particle_subset"
+                    std::string file_name = this->simname + "_colliding_particle_subset.h5";
+                    if (this->myrank == 0)
+                        this->subset_file = H5Fopen(
+                                file_name.c_str(),
+                                H5F_ACC_RDWR,
+                                H5P_DEFAULT);
+                    else
+                        this->subset_file = -1;
+                    DEBUG_MSG_WAIT(this->comm, "psubset check 00\n");
+                    hdf5_tools::gather_and_write_with_single_rank(
+                            this->myrank,
+                            0,
+                            this->comm,
+                            psubset.getParticleIndices(),
+                            psubset.getLocalNumberOfParticles(),
+                            this->subset_file,
+                            "subset_tracers"+ std::to_string(index) + "/labels/" + std::to_string(this->iteration));
+                    // write positions of colliding particles
+                    DEBUG_MSG_WAIT(this->comm, "psubset check 01\n");
+                    std::unique_ptr<double[]> position = psubset.extractFromParticleState(0, 3);
+                    hdf5_tools::gather_and_write_with_single_rank(
+                            this->myrank,
+                            0,
+                            this->comm,
+                            position.get(),
+                            psubset.getLocalNumberOfParticles()*3,
+                            this->subset_file,
+                            "subset_tracers"+ std::to_string(index) + "/position/" + std::to_string(this->iteration));
+                    delete[] position.release();
+                    // write orientations of colliding particles
+                    DEBUG_MSG_WAIT(this->comm, "psubset check 02\n");
+                    std::unique_ptr<double[]> orientation = psubset.extractFromParticleState(3, 6);
+                    hdf5_tools::gather_and_write_with_single_rank(
+                            this->myrank,
+                            0,
+                            this->comm,
+                            orientation.get(),
+                            psubset.getLocalNumberOfParticles()*3,
+                            this->subset_file,
+                            "subset_tracers"+ std::to_string(index) + "/orientation/" + std::to_string(this->iteration));
+                    if (this->myrank == 0)
+                        H5Fclose(this->subset_file);
+                    DEBUG_MSG_WAIT(this->comm, "psubset check 03\n");
+                    MPI_Barrier(this->comm);
+                    // END WARNING: subset file "colliding_particle_subset"
+                    delete[] orientation.release();
+                    // START WARNING these calls use "colliding_particle_subset"
+                    // now write velocity sample
+                    psubset.writeSample(
+                        this->fs->cvelocity,
+                        this->simname + "_colliding_particle_subset.h5",
+                        "subset_tracers"+ std::to_string(index),
+                        "velocity",
+                        this->iteration);
+                    // now write gradient sample
+                    psubset.writeSample(
+                        this->nabla_u,
+                        this->simname + "_colliding_particle_subset.h5",
+                        "subset_tracers"+ std::to_string(index),
+                        "velocity_gradient",
+                        this->iteration);
+                    // END WARNING
+                }
+                DEBUG_MSG_WAIT(this->comm, "04 hello from subsample block\n");
+            }
+        if (this->myrank == 0)
+        {
+            /* Reset the collision lists old, same, and new */
+            this->reset_collision_lists(index);
+        }
+        }
+    }
+
+    /// check if particle stats should be performed now;
+    /// if not, exit method.
+    if (!(this->iteration % this->niter_part == 0))
+    {
+        return EXIT_SUCCESS;
+    }
+
+
+    DEBUG_MSG_WAIT(this->comm, "before sample write\n");
+    for (unsigned int index = 0; index < this->many_ps.size(); index++)
+    {
+        this->particles_sample_writer_mpi[index] = new particles_output_sampling_hdf5<
+        long long int, double, double, 3>(
+            MPI_COMM_WORLD,
+            this->many_ps[index]->getGlobalNbParticles(),
+            (this->simname + "_particles.h5"),
+            "tracers" + std::to_string(index),
+            "position/0");
+
+        this->particles_sample_writer_mpi[index]->setParticleFileLayout(this->many_ps[index]->getParticleFileLayout());
+        DEBUG_MSG(("sampling tracers"+std::to_string(index)+"\n").c_str());
+        /// allocate temporary data array
+        /// initialize pdata0 with the positions, and pdata1 with the orientations
+        std::unique_ptr<double[]> pdata0 = this->many_ps[index]->extractParticlesState(0, 3);
+        std::unique_ptr<double[]> pdata1 = this->many_ps[index]->extractParticlesState(3, 6);
+        std::unique_ptr<double[]> pdata2(new double[9*this->many_ps[index]->getLocalNbParticles()]);
+        DEBUG_MSG(("sampling position for tracers"+std::to_string(index)+"\n").c_str());
+        /// sample position
+        this->particles_sample_writer_mpi[index]->template save_dataset<3>(
+                "tracers" + std::to_string(index),
+                "position",
+                pdata0.get(), // we need to use pdata0.get() here, because it's 3D, whereas getParticlesState may give something else
+                &pdata0,
+                this->many_ps[index]->getParticlesIndexes(),
+                this->many_ps[index]->getLocalNbParticles(),
+                this->many_ps[index]->get_step_idx()-1);
+        DEBUG_MSG(("sampling orientation for tracers"+std::to_string(index)+"\n").c_str());
+        /// sample orientation
+        this->particles_sample_writer_mpi[index]->template save_dataset<3>(
+                "tracers" + std::to_string(index),
+                "orientation",
+                pdata0.get(),
+                &pdata1,
+                this->many_ps[index]->getParticlesIndexes(),
+                this->many_ps[index]->getLocalNbParticles(),
+                this->many_ps[index]->get_step_idx()-1);
+        DEBUG_MSG(("sampling velocity for tracers"+std::to_string(index)+"\n").c_str());
+        /// sample velocity
+        /// from now on, we need to clean up data arrays before interpolation
+        std::fill_n(pdata1.get(), 3*this->many_ps[index]->getLocalNbParticles(), 0);
+        DEBUG_MSG(("filled pdata1 for tracers"+std::to_string(index)+"\n").c_str());
+        this->many_ps[index]->sample_compute_field(*this->fs->cvelocity, pdata1.get());
+        DEBUG_MSG(("sampled for tracers"+std::to_string(index)+"\n").c_str());
+        this->particles_sample_writer_mpi[index]->template save_dataset<3>(
+                "tracers" + std::to_string(index),
+                "velocity",
+                pdata0.get(),
+                &pdata1,
+                this->many_ps[index]->getParticlesIndexes(),
+                this->many_ps[index]->getLocalNbParticles(),
+                this->many_ps[index]->get_step_idx()-1);
+        DEBUG_MSG(("sampling velocity gradient for tracers"+std::to_string(index)+"\n").c_str());
+        std::fill_n(pdata2.get(), 9*this->many_ps[index]->getLocalNbParticles(), 0);
+        this->many_ps[index]->sample_compute_field(*this->nabla_u, pdata2.get());
+        this->particles_sample_writer_mpi[index]->template save_dataset<9>(
+                "tracers" + std::to_string(index),
+                "velocity_gradient",
+                pdata0.get(),
+                &pdata2,
+                this->many_ps[index]->getParticlesIndexes(),
+                this->many_ps[index]->getLocalNbParticles(),
+                this->many_ps[index]->get_step_idx()-1);
+
+    //Dont need accelaration at the moment.
+//          std::fill_n(pdata1.get(), 3*this->many_ps[index]->getLocalNbParticles(), 0);
+//         this->many_ps[index]->sample_compute_field(*this->tmp_vec_field, pdata1.get());
+//         this->particles_sample_writer_mpi[index]->template save_dataset<3>(
+//                 "tracers" + std::to_string(index),
+//                 "acceleration",
+//                 pdata0.get(),
+//                 &pdata1,
+//                 this->many_ps[index]->getParticlesIndexes(),
+//                 this->many_ps[index]->getLocalNbParticles(),
+//                 this->many_ps[index]->get_step_idx()-1);
+        delete this->particles_sample_writer_mpi[index];
+        // deallocate temporary data array
+        delete[] pdata0.release();
+        delete[] pdata1.release();
+        delete[] pdata2.release();
+    }
+    DEBUG_MSG_WAIT(this->comm, "after sample write\n");
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber>
+int many_NSVEcylinder_collisions<rnumber>::read_parameters(void)
+{
+    TIMEZONE("many_NSVEcylinder_collisions::read_parameters");
+    this->NSVE<rnumber>::read_parameters();
+    hid_t parameter_file = H5Fopen((this->simname + ".h5").c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
+    this->niter_part = hdf5_tools::read_value<int>(parameter_file, "parameters/niter_part");
+    DEBUG_MSG("Loading nparticles");
+    this->nparticles = hdf5_tools::read_vector<int>(
+            parameter_file,
+            "parameters/nparticles");
+    //this->nparticles = hdf5_tools::read_value<int>(parameter_file,"parameters/nparticles");
+    this->tracers0_integration_steps = hdf5_tools::read_value<int>(parameter_file, "parameters/tracers0_integration_steps");
+    this->tracers0_neighbours = hdf5_tools::read_value<int>(parameter_file, "parameters/tracers0_neighbours");
+    this->tracers0_smoothness = hdf5_tools::read_value<int>(parameter_file, "parameters/tracers0_smoothness");
+    this->enable_p2p = hdf5_tools::read_value<int>(parameter_file, "parameters/tracers0_enable_p2p");
+    this->enable_inner = hdf5_tools::read_value<int>(parameter_file, "parameters/tracers0_enable_inner");
+    int tval = hdf5_tools::read_value<int>(parameter_file, "parameters/tracers0_enable_vorticity_omega");
+    this->enable_vorticity_omega = tval;
+    DEBUG_MSG("tracers0_enable_vorticity_omega = %d, this->enable_vorticity_omega = %d\n",
+              tval, this->enable_vorticity_omega);
+    DEBUG_MSG("Loading tracers0_cutoff \n");
+    this->tracers0_cutoff = hdf5_tools::read_vector<double>(
+            parameter_file,
+            "parameters/tracers0_cutoff");
+    DEBUG_MSG("Loading tracers0_lambda \n");
+    this->tracers0_lambda = hdf5_tools::read_vector<double>(
+            parameter_file,
+            "parameters/tracers0_lambda");
+    DEBUG_MSG("Loading tracers0_D_rho \n");
+    this->tracers0_D_rho = hdf5_tools::read_vector<double>(
+            parameter_file,
+            "parameters/tracers0_D_rho");
+    DEBUG_MSG("Loading many_integration_steps \n");
+    this->many_integration_steps = hdf5_tools::read_vector<int>(
+            parameter_file,
+            "parameters/many_integration_steps");
+    DEBUG_MSG("Loading tracers0_u_sed \n");
+    this->tracers0_u_sed = hdf5_tools::read_vector<double>(
+            parameter_file,
+            "parameters/tracers0_u_sed");
+    DEBUG_MSG("Loading tracers0_beta0 \n");
+    this->tracers0_beta0 = hdf5_tools::read_vector<double>(
+            parameter_file,
+            "parameters/tracers0_beta0");
+    DEBUG_MSG("Loading tracers0_beta1 \n");
+    this->tracers0_beta1 = hdf5_tools::read_vector<double>(
+            parameter_file,
+            "parameters/tracers0_beta1");
+    this->niter_part_fine_period = hdf5_tools::read_value<int>(parameter_file, "parameters/niter_part_fine_period");
+    this->niter_part_fine_duration = hdf5_tools::read_value<int>(parameter_file, "parameters/niter_part_fine_duration");
+    H5Fclose(parameter_file);
+    MPI_Barrier(this->comm);
+    assert(this->many_integration_steps.size() == this->tracers0_D_rho.size());
+    return EXIT_SUCCESS;
+}
+
+
+template class many_NSVEcylinder_collisions<float>;
+template class many_NSVEcylinder_collisions<double>;
diff --git a/projects/sedimenting_rods/many_NSVEcylinder_collisions.hpp b/projects/sedimenting_rods/many_NSVEcylinder_collisions.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d89526c969c931a61a204d6365e406bee93fc83a
--- /dev/null
+++ b/projects/sedimenting_rods/many_NSVEcylinder_collisions.hpp
@@ -0,0 +1,122 @@
+/**********************************************************************
+*                                                                     *
+*  Copyright 2020 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 many_NSVEcylinder_collisions_HPP
+#define many_NSVEcylinder_collisions_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"
+#include "particles/particles_sampling.hpp"
+#include "particles/p2p/p2p_computer.hpp"
+#include "particles/p2p/p2p_ghost_collisions.hpp"
+
+/** \brief Navier-Stokes solver that includes cylinders sedimenting in gravity with a ghost collision counter.
+ *
+ *  Child of Navier Stokes vorticity equation solver, this class calls all the
+ *  methods from `NSVE`, and in addition integrates `sedimenting rods
+ *  in the resulting velocity field.
+ *  Sedimenting rods are very small particles with a density offset to the
+ *  fluid flow. They have an orientation and are idealized as very thin ellongated
+ *  ellipsoids, whose orientation evolves accordint to Jeffery's equations.
+ */
+
+template <typename rnumber>
+class many_NSVEcylinder_collisions: public NSVE<rnumber>
+{
+    public:
+
+        /* parameters that are read in read_parameters */
+        std::vector<int> many_integration_steps;
+        int niter_part;
+        int niter_part_fine_period;//NEW
+        int niter_part_fine_duration;//NEW
+        std::vector<int> nparticles;
+        int tracers0_integration_steps;
+        int tracers0_neighbours;
+        int tracers0_smoothness;
+        std::vector<double> tracers0_cutoff;
+        std::vector<double> tracers0_lambda;
+        std::vector<double> tracers0_D_rho;
+        std::vector<double> tracers0_u_sed;
+        std::vector<double> tracers0_beta0;
+        std::vector<double> tracers0_beta1;
+
+        bool enable_p2p;
+        bool enable_inner;
+        bool enable_vorticity_omega;
+
+        /* other stuff */
+        std::vector<std::unique_ptr<abstract_particles_system_with_p2p<long long int, double, p2p_ghost_collisions<double, long long int>>>> many_ps;
+
+        std::vector<particles_output_hdf5<long long int, double,6>*> particles_output_writer_mpi;
+        std::vector<particles_output_sampling_hdf5<long long int, double, double, 3>*> particles_sample_writer_mpi;
+        // field for sampling velocity gradient
+        field<rnumber, FFTW, THREExTHREE> *nabla_u;
+
+        hid_t cols_file;
+        hid_t cols_file_PP;
+        hid_t subset_file;
+
+        many_NSVEcylinder_collisions(
+                const MPI_Comm COMMUNICATOR,
+                const std::string &simulation_name):
+            NSVE<rnumber>(
+                    COMMUNICATOR,
+                    simulation_name),
+                    tracers0_cutoff(0.314),
+                    tracers0_lambda(100.0),
+                    tracers0_D_rho(1.0),
+                    tracers0_u_sed(1.0),
+                    enable_p2p(true),//interaction has to be turned off by hand in p2p_ghost_collision_base
+                    enable_inner(true),
+                    enable_vorticity_omega(true){}//PUT TRUE HERE AS IN NSVE
+        ~many_NSVEcylinder_collisions(){}
+
+        int initialize(void);
+        int step(void);
+        int finalize(void);
+
+        virtual int read_parameters(void);
+        int write_checkpoint(void);
+        int do_stats(void);
+        
+        std::vector <std::vector <long long int>> collision_pairs_old;
+        std::vector <std::vector <long long int>> collision_pairs_new;
+        std::vector <std::vector <long long int>> collision_pairs_same;
+        std::vector <std::vector <long long int>> collision_pairs_time_0;
+        std::vector <std::vector <long long int>> collision_pairs_time_1;
+  
+void reset_collision_lists(int index){
+        this->collision_pairs_old[index].resize(0);
+        this->collision_pairs_new[index].resize(0);
+        this->collision_pairs_same[index].resize(0);
+        }
+        
+};
+
+#endif//many_NSVEcylinder_collisions_HPP
diff --git a/projects/sedimenting_rods/particles_inner_computer_sedimenting_rods.cpp b/projects/sedimenting_rods/particles_inner_computer_sedimenting_rods.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..101eaf3631550898d4a97bfa1380205e25a76e08
--- /dev/null
+++ b/projects/sedimenting_rods/particles_inner_computer_sedimenting_rods.cpp
@@ -0,0 +1,239 @@
+/******************************************************************************
+*                                                                             *
+*  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                                         *
+*                                                                             *
+******************************************************************************/
+
+
+
+#include "base.hpp"
+#include "particles/particles_utils.hpp"
+#include <cmath>
+
+template <class real_number, class partsize_t>
+template <int size_particle_positions, int size_particle_rhs>
+void particles_inner_computer_sedimenting_rods<real_number, partsize_t>::compute_interaction(
+        const partsize_t nb_particles,
+        const real_number pos_part[],
+        real_number rhs_part[]) const{
+    static_assert(size_particle_positions == 6, "This kernel works only with 6 values for one particle's position");
+    static_assert(size_particle_rhs == 6, "This kernel works only with 6 values per particle's rhs");
+
+    #pragma omp parallel for
+    for(partsize_t idx_part = 0 ; idx_part < nb_particles ; ++idx_part)
+    {
+        //if (fabs(rhs_part[idx_part*size_particle_rhs + IDXC_X]) > 0 ||
+        //    fabs(rhs_part[idx_part*size_particle_rhs + IDXC_Y]) > 0 ||
+        //    fabs(rhs_part[idx_part*size_particle_rhs + IDXC_Z]) > 0){
+        //    DEBUG_MSG("just entered loop, nonzero sampled velocities for particle %ld are %g %g %g\n",
+        //            idx_part,
+        //            rhs_part[idx_part*size_particle_rhs + IDXC_X],
+        //            rhs_part[idx_part*size_particle_rhs + IDXC_Y],
+        //            rhs_part[idx_part*size_particle_rhs + IDXC_Z]);
+        //            }
+		const double px = pos_part[idx_part*size_particle_positions + 3+IDXC_X];
+		const double py = pos_part[idx_part*size_particle_positions + 3+IDXC_Y];
+		const double pz = pos_part[idx_part*size_particle_positions + 3+IDXC_Z];
+
+        rhs_part[idx_part*size_particle_rhs + IDXC_X] -= D_rho * u_sed * beta1 * px * py;
+        rhs_part[idx_part*size_particle_rhs + IDXC_Y] -= D_rho * u_sed * ( beta0 + beta1 * py*py );
+        rhs_part[idx_part*size_particle_rhs + IDXC_Z] -= D_rho * u_sed * beta1 * pz * py;
+        //if (fabs(rhs_part[idx_part*size_particle_rhs + IDXC_X]) > 0 ||
+        //    fabs(rhs_part[idx_part*size_particle_rhs + IDXC_Y]) > 0 ||
+        //    fabs(rhs_part[idx_part*size_particle_rhs + IDXC_Z]) > 0){
+        //    DEBUG_MSG("computed settling velocity for particle %ld are %g %g %g\n",
+        //            idx_part,
+        //            rhs_part[idx_part*size_particle_rhs + IDXC_X],
+        //            rhs_part[idx_part*size_particle_rhs + IDXC_Y],
+        //            rhs_part[idx_part*size_particle_rhs + IDXC_Z]);
+        //            }
+        //if (idx_part == 0)
+        //{
+        //    DEBUG_MSG("particle %ld, moving with %g %g %g, D_rho = %g, u_sed = %g, beta0 = %g, beta1 = %g\n",
+        //            idx_part,
+        //              rhs_part[idx_part*size_particle_rhs + IDXC_X],
+        //              rhs_part[idx_part*size_particle_rhs + IDXC_Y],
+        //              rhs_part[idx_part*size_particle_rhs + IDXC_Z],
+        //              D_rho, u_sed, beta1, beta0);
+        //    DEBUG_MSG("particle %ld, orientation is %g %g %g\n",
+        //            idx_part,
+        //              px,
+        //              py,
+        //              pz);
+        //    DEBUG_MSG("particle %ld, location is %g %g %g\n",
+        //              idx_part,
+        //              pos_part[idx_part*size_particle_positions + IDXC_X],
+        //              pos_part[idx_part*size_particle_positions + IDXC_Y],
+        //              pos_part[idx_part*size_particle_positions + IDXC_Z]);
+        //}
+    }
+}
+    // for given orientation and right-hand-side, recompute right-hand-side such
+    // that it is perpendicular to the current orientation.
+    // this is the job of the Lagrange multiplier terms, hence the
+    // "add_Lagrange_multipliers" name of the method.
+template <>
+template <>
+void particles_inner_computer_sedimenting_rods<double, long long>::add_Lagrange_multipliers<6,6>(
+        const long long nb_particles,
+        const double pos_part[],
+        double rhs_part[]) const{
+
+    //DEBUG_MSG("hello from add_lagrange_multipliers\n");
+
+        #pragma omp parallel for
+        for(long long idx_part = 0 ; idx_part < nb_particles ; ++idx_part){
+            const long long idx0 = idx_part*6 + 3;
+            const long long idx1 = idx_part*6 + 3;
+            // check that orientation is unit vector:
+            double orientation_size = sqrt(
+                    pos_part[idx0+IDXC_X]*pos_part[idx0+IDXC_X] +
+                    pos_part[idx0+IDXC_Y]*pos_part[idx0+IDXC_Y] +
+                    pos_part[idx0+IDXC_Z]*pos_part[idx0+IDXC_Z]);
+            variable_used_only_in_assert(orientation_size);
+            assert(orientation_size > 0.99);
+            assert(orientation_size < 1.01);
+            // I call "rotation" to be the right hand side of the orientation part of the ODE
+            // project rotation on orientation:
+            double projection = (
+                    pos_part[idx0+IDXC_X]*rhs_part[idx1+IDXC_X] +
+                    pos_part[idx0+IDXC_Y]*rhs_part[idx1+IDXC_Y] +
+                    pos_part[idx0+IDXC_Z]*rhs_part[idx1+IDXC_Z]);
+
+            // now remove parallel bit.
+            rhs_part[idx1+IDXC_X] -= pos_part[idx0+IDXC_X]*projection;
+            rhs_part[idx1+IDXC_Y] -= pos_part[idx0+IDXC_Y]*projection;
+            rhs_part[idx1+IDXC_Z] -= pos_part[idx0+IDXC_Z]*projection;
+            //if (idx_part == 0){
+             //   DEBUG_MSG("add_Lagrange_multipliers particles 0 projection is %g\n", projection);
+            //}
+
+            // DEBUG
+            // sanity check, for debugging purposes
+            // compute dot product between orientation and orientation change
+            //double dotproduct = (
+            //        rhs_part[idx1 + IDXC_X]*pos_part[idx0 + IDXC_X] +
+            //        rhs_part[idx1 + IDXC_Y]*pos_part[idx0 + IDXC_Y] +
+            //        rhs_part[idx1 + IDXC_Z]*pos_part[idx0 + IDXC_Z]);
+            //if (dotproduct > 0.1)
+            //{
+            //    DEBUG_MSG("dotproduct = %g, projection = %g\n"
+            //              "pos_part[%d] = %g, pos_part[%d] = %g, pos_part[%d] = %g\n"
+            //              "rhs_part[%d] = %g, rhs_part[%d] = %g, rhs_part[%d] = %g\n",
+            //            dotproduct,
+            //            projection,
+            //            IDXC_X, pos_part[idx0 + IDXC_X],
+            //            IDXC_Y, pos_part[idx0 + IDXC_Y],
+            //            IDXC_Z, pos_part[idx0 + IDXC_Z],
+            //            IDXC_X, rhs_part[idx1 + IDXC_X],
+            //            IDXC_Y, rhs_part[idx1 + IDXC_Y],
+            //            IDXC_Z, rhs_part[idx1 + IDXC_Z]);
+            //    assert(false);
+            //}
+            //assert(dotproduct <= 0.1);
+        }
+    }
+
+template <>
+template <>
+void particles_inner_computer_sedimenting_rods<double, long long>::compute_interaction_with_extra<6,6,3>(
+        const long long nb_particles,
+        const double pos_part[],
+        double rhs_part[],
+        const double rhs_part_extra[]) const{
+    // call plain compute_interaction first
+    //DEBUG_MSG("hello from compute_interaction_with_extra 3\n");
+    compute_interaction<6, 6>(nb_particles, pos_part, rhs_part);
+
+    // now add vorticity term
+    #pragma omp parallel for
+    for(long long idx_part = 0 ; idx_part < nb_particles ; ++idx_part){
+        // Cross product vorticity/orientation
+        rhs_part[idx_part*6 + 3+IDXC_X] += 0.5*(rhs_part_extra[idx_part*3 + IDXC_Y]*pos_part[idx_part*6 + 3+IDXC_Z] -
+                                                rhs_part_extra[idx_part*3 + IDXC_Z]*pos_part[idx_part*6 + 3+IDXC_Y]);
+        rhs_part[idx_part*6 + 3+IDXC_Y] += 0.5*(rhs_part_extra[idx_part*3 + IDXC_Z]*pos_part[idx_part*6 + 3+IDXC_X] -
+                                                rhs_part_extra[idx_part*3 + IDXC_X]*pos_part[idx_part*6 + 3+IDXC_Z]);
+        rhs_part[idx_part*6 + 3+IDXC_Z] += 0.5*(rhs_part_extra[idx_part*3 + IDXC_X]*pos_part[idx_part*6 + 3+IDXC_Y] -
+                                                rhs_part_extra[idx_part*3 + IDXC_Y]*pos_part[idx_part*6 + 3+IDXC_X]);
+    }
+}
+
+template <> //Work here
+template <>
+void particles_inner_computer_sedimenting_rods<double, long long>::compute_interaction_with_extra<6,6,9>(
+        const long long nb_particles,
+        const double pos_part[],
+        double rhs_part[],
+        const double rhs_part_extra[]) const{
+    //DEBUG_MSG("hello from compute_interaction_with_extra 9\n");
+    // call plain compute_interaction first
+    compute_interaction<6, 6>(nb_particles, pos_part, rhs_part);
+    const double ll2 = lambda*lambda;
+
+    // now add vorticity term
+    #pragma omp parallel for
+    for(long long idx_part = 0 ; idx_part < nb_particles ; ++idx_part){
+        const long long idx_part6 = idx_part*6 + 3;
+        const long long idx_part9 = idx_part*9;
+        rhs_part[idx_part6+IDXC_X] += (
+                pos_part[idx_part6+IDXC_Z]*(ll2*rhs_part_extra[idx_part9 + IDXC_DZ_X]-rhs_part_extra[idx_part9 + IDXC_DX_Z])
+              + pos_part[idx_part6+IDXC_Y]*(ll2*rhs_part_extra[idx_part9 + IDXC_DY_X]-rhs_part_extra[idx_part9 + IDXC_DX_Y])
+              + pos_part[idx_part6+IDXC_X]*(ll2-1)*rhs_part_extra[idx_part9 + IDXC_DX_X]) / (ll2+1);
+        rhs_part[idx_part6+IDXC_Y] += (
+                pos_part[idx_part6+IDXC_X]*(ll2*rhs_part_extra[idx_part9 + IDXC_DX_Y]-rhs_part_extra[idx_part9 + IDXC_DY_X])
+              + pos_part[idx_part6+IDXC_Z]*(ll2*rhs_part_extra[idx_part9 + IDXC_DZ_Y]-rhs_part_extra[idx_part9 + IDXC_DY_Z])
+              + pos_part[idx_part6+IDXC_Y]*(ll2-1)*rhs_part_extra[idx_part9 + IDXC_DY_Y]) / (ll2+1);
+        rhs_part[idx_part6+IDXC_Z] += (
+                pos_part[idx_part6+IDXC_Y]*(ll2*rhs_part_extra[idx_part9 + IDXC_DY_Z]-rhs_part_extra[idx_part9 + IDXC_DZ_Y])
+              + pos_part[idx_part6+IDXC_X]*(ll2*rhs_part_extra[idx_part9 + IDXC_DX_Z]-rhs_part_extra[idx_part9 + IDXC_DZ_X])
+              + pos_part[idx_part6+IDXC_Z]*(ll2-1)*rhs_part_extra[idx_part9 + IDXC_DZ_Z]) / (ll2+1);
+    }
+}
+
+
+// meant to be called AFTER executing the time-stepping operation.
+// once the particles have been moved, ensure that the orientation is a unit vector.
+template <>
+template <>
+void particles_inner_computer_sedimenting_rods<double, long long>::enforce_unit_orientation<6>(
+        const long long nb_particles,
+        double pos_part[]) const{
+
+    //DEBUG_MSG("hello from enforce_unit_orientation\n");
+    #pragma omp parallel for
+    for(long long idx_part = 0 ; idx_part < nb_particles ; ++idx_part){
+        const long long idx0 = idx_part*6 + 3;
+        // compute orientation size:
+        const double orientation_size = sqrt(
+                pos_part[idx0+IDXC_X]*pos_part[idx0+IDXC_X] +
+                pos_part[idx0+IDXC_Y]*pos_part[idx0+IDXC_Y] +
+                pos_part[idx0+IDXC_Z]*pos_part[idx0+IDXC_Z]);
+        // now renormalize
+        pos_part[idx0 + IDXC_X] /= orientation_size;
+        pos_part[idx0 + IDXC_Y] /= orientation_size;
+        pos_part[idx0 + IDXC_Z] /= orientation_size;
+    }
+}
+
+template
+void particles_inner_computer_sedimenting_rods<double, long long>::compute_interaction<6, 6>(
+        const long long nb_particles,
+        const double pos_part[],
+        double rhs_part[]) const;
\ No newline at end of file
diff --git a/projects/sedimenting_rods/particles_inner_computer_sedimenting_rods.hpp b/projects/sedimenting_rods/particles_inner_computer_sedimenting_rods.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6fa808ff7cc0c663088a66cc2cd8f8cff0ac544d
--- /dev/null
+++ b/projects/sedimenting_rods/particles_inner_computer_sedimenting_rods.hpp
@@ -0,0 +1,123 @@
+/******************************************************************************
+*                                                                             *
+*  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 PARTICLES_INNER_COMPUTER_SEDIMENTING_RODS_HPP
+#define PARTICLES_INNER_COMPUTER_SEDIMENTING_RODS_HPP
+
+#include <cstring>
+#include <cassert>
+#include <iostream>
+
+template <class real_number, class partsize_t>
+class particles_inner_computer_sedimenting_rods{
+    bool isActive;
+    const real_number D_rho, u_sed;
+    const real_number beta0, beta1;
+    const real_number lambda;
+    const real_number lambda1;
+    const real_number lambda2;
+    const real_number lambda3;
+
+public:
+    explicit particles_inner_computer_sedimenting_rods(const real_number inLambda, const real_number inD_rho, const real_number inu_sed, const real_number in_beta0, const real_number in_beta1):
+        isActive(true),
+        D_rho(inD_rho),
+        u_sed(inu_sed),
+        beta0(in_beta0),
+        beta1(in_beta1),
+        lambda(inLambda),
+        lambda1(0),
+        lambda2(0),
+        lambda3(0)
+    {}
+
+    template <int size_particle_positions, int size_particle_rhs>
+    void compute_interaction(
+            const partsize_t nb_particles,
+            const real_number pos_part[],
+            real_number rhs_part[]) const;
+    // for given orientation and right-hand-side, recompute right-hand-side such
+    // that it is perpendicular to the current orientation.
+    // this is the job of the Lagrange multiplier terms, hence the
+    // "add_Lagrange_multipliers" name of the method.
+    template <int size_particle_positions, int size_particle_rhs>
+    void add_Lagrange_multipliers(
+            const partsize_t nb_particles,
+            const real_number pos_part[],
+            real_number rhs_part[]) const;
+    template <int size_particle_positions, int size_particle_rhs, int size_particle_rhs_extra>
+    void compute_interaction_with_extra(
+            const partsize_t nb_particles,
+            const real_number pos_part[],
+            real_number rhs_part[],
+            const real_number rhs_part_extra[]) const;
+    // meant to be called AFTER executing the time-stepping operation.
+    // once the particles have been moved, ensure that the orientation is a unit vector.
+    template <int size_particle_positions>
+    void enforce_unit_orientation(
+            const partsize_t nb_particles,
+            real_number pos_part[]) const;
+
+    bool isEnable() const {
+        return isActive;
+    }
+
+    void setEnable(const bool inIsActive) {
+        isActive = inIsActive;
+    }
+
+    double get_sedimenting_velocity () {
+        return u_sed;
+    }
+
+    void set_sedimenting_velocity (const double bla) {
+        this->u_sed = bla;
+    }
+
+    void set_D_rho (const double DRHO) {
+        this->D_rho = DRHO;
+    }
+
+    void set_beta0 (const double bla) {
+        this->beta0 = bla;
+    }
+
+    void set_beta1 (const double bla) {
+        this->beta1 = bla;
+    }
+
+    double get_D_rho () {
+        return D_rho;
+    }
+
+    double get_beta0 () {
+        return beta0;
+    }
+    double get_beta1 () {
+        return beta1;
+    }
+};
+
+#endif
\ No newline at end of file