diff --git a/bfps/cpp/distributed_particles.cpp b/bfps/cpp/distributed_particles.cpp
index b0a445af03ab51d79dd1aded6ba230fc4b80adbf..bc19ea9417ca0ee943fb3db41928d07e66c2c04a 100644
--- a/bfps/cpp/distributed_particles.cpp
+++ b/bfps/cpp/distributed_particles.cpp
@@ -56,6 +56,9 @@ distributed_particles<particle_type, rnumber, interp_neighbours>::distributed_pa
     this->vel = FIELD;
     this->rhs.resize(INTEGRATION_STEPS);
     this->integration_steps = INTEGRATION_STEPS;
+    this->state.reserve(2*this->nparticles / this->nprocs);
+    for (unsigned int i=0; i<this->rhs.size(); i++)
+        this->rhs[i].reserve(2*this->nparticles / this->nprocs);
 }
 
 template <int particle_type, class rnumber, int interp_neighbours>
@@ -66,11 +69,12 @@ distributed_particles<particle_type, rnumber, interp_neighbours>::~distributed_p
 template <int particle_type, class rnumber, int interp_neighbours>
 void distributed_particles<particle_type, rnumber, interp_neighbours>::sample(
         interpolator<rnumber, interp_neighbours> *field,
+        const std::unordered_map<int, single_particle_state<particle_type>> &x,
         std::unordered_map<int, single_particle_state<POINT3D>> &y)
 {
     double *yy = new double[3];
     y.clear();
-    for (auto &pp: this->state)
+    for (auto &pp: x)
     {
         (*field)(pp.second.data, yy);
         y[pp.first] = yy;
@@ -83,14 +87,16 @@ void distributed_particles<particle_type, rnumber, interp_neighbours>::get_rhs(
         const std::unordered_map<int, single_particle_state<particle_type>> &x,
         std::unordered_map<int, single_particle_state<particle_type>> &y)
 {
-    double *yy = new double[this->ncomponents];
-    y.clear();
-    for (auto &pp: x)
+    std::unordered_map<int, single_particle_state<POINT3D>> yy;
+    switch(particle_type)
     {
-        (*this->vel)(pp.second.data, yy);
-        y[pp.first] = yy;
+        case VELOCITY_TRACER:
+            this->sample(this->vel, this->state, yy);
+            y.clear();
+            for (auto &pp: x)
+                y[pp.first] = yy[pp.first].data;
+            break;
     }
-    delete[] yy;
 }
 
 template <int particle_type, class rnumber, int interp_neighbours>
@@ -99,7 +105,7 @@ void distributed_particles<particle_type, rnumber, interp_neighbours>::sample(
         const char *dset_name)
 {
     std::unordered_map<int, single_particle_state<POINT3D>> y;
-    this->sample(field, y);
+    this->sample(field, this->state, y);
     this->write(dset_name, y);
 }
 
diff --git a/bfps/cpp/distributed_particles.hpp b/bfps/cpp/distributed_particles.hpp
index e972f69943814823e8b5527422b9727ab7331a8d..63a7ce35d68f7c01866008883c9443d27bc28241 100644
--- a/bfps/cpp/distributed_particles.hpp
+++ b/bfps/cpp/distributed_particles.hpp
@@ -74,6 +74,7 @@ class distributed_particles: public particles_io_base<particle_type>
                 const char *dset_name);
         void sample(
                 interpolator<rnumber, interp_neighbours> *field,
+                const std::unordered_map<int, single_particle_state<particle_type>> &x,
                 std::unordered_map<int, single_particle_state<POINT3D>> &y);
         void get_rhs(
                 const std::unordered_map<int, single_particle_state<particle_type>> &x,
diff --git a/bfps/cpp/interpolator.hpp b/bfps/cpp/interpolator.hpp
index d20ee6ee1b5023d6a5e177d15e8ddc92a339d7ca..7e56ebe159fd24ed7cf623f0a869e1d262d4aadb 100644
--- a/bfps/cpp/interpolator.hpp
+++ b/bfps/cpp/interpolator.hpp
@@ -42,6 +42,7 @@ class interpolator:public interpolator_base<rnumber, interp_neighbours>
         rnumber *field;
 
     public:
+        using interpolator_base<rnumber, interp_neighbours>::operator();
         ptrdiff_t buffer_size;
 
         /* descriptor for buffered field */
@@ -67,17 +68,6 @@ class interpolator:public interpolator_base<rnumber, interp_neighbours>
                 const double *__restrict__ x,
                 double *__restrict__ y,
                 const int *deriv = NULL);
-        /* interpolate 1 point */
-        inline void operator()(
-                const double *__restrict__ x,
-                double *__restrict__ dest,
-                const int *deriv = NULL)
-        {
-            int xg[3];
-            double xx[3];
-            this->get_grid_coordinates(x, xg, xx);
-            (*this)(xg, xx, dest, deriv);
-        }
         void operator()(
                 const int *__restrict__ xg,
                 const double *__restrict__ xx,
diff --git a/bfps/cpp/interpolator_base.hpp b/bfps/cpp/interpolator_base.hpp
index 1f29a5c05e2afa2dda45f45e5f87a71a63d2b25d..7dda7fb08319bf2a044bcc220e204b748d6336d6 100644
--- a/bfps/cpp/interpolator_base.hpp
+++ b/bfps/cpp/interpolator_base.hpp
@@ -87,6 +87,18 @@ class interpolator_base
                 const double *__restrict__ xx,
                 double *__restrict__ dest,
                 const int *deriv = NULL) = 0;
+
+        /* interpolate 1 point */
+        inline void operator()(
+                const double *__restrict__ x,
+                double *__restrict__ dest,
+                const int *deriv = NULL)
+        {
+            int xg[3];
+            double xx[3];
+            this->get_grid_coordinates(x, xg, xx);
+            (*this)(xg, xx, dest, deriv);
+        }
 };
 
 #endif//INTERPOLATOR_BASE
diff --git a/bfps/cpp/rFFTW_distributed_particles.cpp b/bfps/cpp/rFFTW_distributed_particles.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..38f0bc6575bb84350264406b88362dc3f57fca9e
--- /dev/null
+++ b/bfps/cpp/rFFTW_distributed_particles.cpp
@@ -0,0 +1,516 @@
+/**********************************************************************
+*                                                                     *
+*  Copyright 2015 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                                 *
+*                                                                     *
+**********************************************************************/
+
+
+
+//#define NDEBUG
+
+#include <cmath>
+#include <cassert>
+#include <cstring>
+#include <string>
+#include <sstream>
+
+#include "base.hpp"
+#include "rFFTW_distributed_particles.hpp"
+#include "fftw_tools.hpp"
+
+
+extern int myrank, nprocs;
+
+template <int particle_type, class rnumber, int interp_neighbours>
+rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::rFFTW_distributed_particles(
+        const char *NAME,
+        const hid_t data_file_id,
+        rFFTW_interpolator<rnumber, interp_neighbours> *FIELD,
+        const int TRAJ_SKIP,
+        const int INTEGRATION_STEPS) : particles_io_base<particle_type>(
+            NAME,
+            TRAJ_SKIP,
+            data_file_id,
+            FIELD->descriptor->comm)
+{
+    assert((INTEGRATION_STEPS <= 6) &&
+           (INTEGRATION_STEPS >= 1));
+    this->vel = FIELD;
+    this->rhs.resize(INTEGRATION_STEPS);
+    this->integration_steps = INTEGRATION_STEPS;
+    this->state.reserve(2*this->nparticles / this->nprocs);
+    for (unsigned int i=0; i<this->rhs.size(); i++)
+        this->rhs[i].reserve(2*this->nparticles / this->nprocs);
+
+    this->interp_comm.resize(this->vel->descriptor->sizes[0]);
+    this->interp_nprocs.resize(this->vel->descriptor->sizes[0]);
+    int rmaxz, rminz;
+    int color, key;
+    for (int zg=0; zg<this->vel->descriptor->sizes[0]; zg++)
+    {
+        color = (this->vel->get_rank_info(
+                    (zg+.5)*this->vel->dz, rminz, rmaxz) ? zg : MPI_UNDEFINED);
+        key = zg - this->vel->descriptor->starts[0] + interp_neighbours;
+        MPI_Comm_split(this->comm, color, key, &this->interp_comm[zg]);
+        if (this->interp_comm[zg] != MPI_COMM_NULL)
+            MPI_Comm_size(this->interp_comm[zg], &this->interp_nprocs[zg]);
+        else
+            this->interp_nprocs[zg] = 0;
+    }
+}
+
+template <int particle_type, class rnumber, int interp_neighbours>
+rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::~rFFTW_distributed_particles()
+{
+}
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::sample(
+        rFFTW_interpolator<rnumber, interp_neighbours> *field,
+        const std::unordered_map<int, single_particle_state<particle_type>> &x,
+        std::unordered_map<int, single_particle_state<POINT3D>> &y)
+{
+    double *yyy = new double[3];
+    double *yy = new double[3];
+    std::fill_n(yy, 3, 0);
+    y.clear();
+    int xg[3];
+    double xx[3];
+    for (int p=0; p<this->nparticles; p++)
+    {
+        auto pp = x.find(p);
+        if (pp != x.end())
+        {
+            field->get_grid_coordinates(pp->second.data, xg, xx);
+            (*field)(xg, xx, yy);
+            if (this->interp_nprocs[xg[2]]>1)
+            {
+                DEBUG_MSG(
+                        "iteration %d, zg is %d, nprocs is %d\n",
+                        this->iteration, xg[2], this->interp_nprocs[xg[2]]);
+                MPI_Allreduce(
+                        yy,
+                        yyy,
+                        3,
+                        MPI_DOUBLE,
+                        MPI_SUM,
+                        this->interp_comm[xg[2]]);
+                y[p] = yyy;
+            }
+            else
+                y[p] = yy;
+        }
+    }
+    delete[] yy;
+    delete[] yyy;
+}
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::get_rhs(
+        const std::unordered_map<int, single_particle_state<particle_type>> &x,
+        std::unordered_map<int, single_particle_state<particle_type>> &y)
+{
+    std::unordered_map<int, single_particle_state<POINT3D>> yy;
+    switch(particle_type)
+    {
+        case VELOCITY_TRACER:
+            this->sample(this->vel, this->state, yy);
+            y.clear();
+            for (auto &pp: x)
+                y[pp.first] = yy[pp.first].data;
+            break;
+    }
+}
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::sample(
+        rFFTW_interpolator<rnumber, interp_neighbours> *field,
+        const char *dset_name)
+{
+    std::unordered_map<int, single_particle_state<POINT3D>> y;
+    this->sample(field, this->state, y);
+    this->write(dset_name, y);
+}
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::roll_rhs()
+{
+    for (int i=this->integration_steps-2; i>=0; i--)
+        rhs[i+1] = rhs[i];
+}
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::redistribute(
+        std::unordered_map<int, single_particle_state<particle_type>> &x,
+        std::vector<std::unordered_map<int, single_particle_state<particle_type>>> &vals)
+{
+    //DEBUG_MSG("entered redistribute\n");
+    /* neighbouring rank offsets */
+    int ro[2];
+    ro[0] = -1;
+    ro[1] = 1;
+    /* neighbouring ranks */
+    int nr[2];
+    nr[0] = MOD(this->myrank+ro[0], this->nprocs);
+    nr[1] = MOD(this->myrank+ro[1], this->nprocs);
+    /* particles to send, particles to receive */
+    std::vector<int> ps[2], pr[2];
+    /* number of particles to send, number of particles to receive */
+    int nps[2], npr[2];
+    int rsrc, rdst;
+    /* get list of id-s to send */
+    for (auto &pp: x)
+    {
+        int rminz, rmaxz;
+        bool is_here = this->vel->get_rank_info(pp.second.data[2], rminz, rmaxz);
+        //for (int i=0; i<2; i++)
+        //{
+        //    if (this->vel->get_rank() == nr[i])
+        //        ps[i].push_back(pp.first);
+        //}
+    }
+    /* prepare data for send recv */
+    for (int i=0; i<2; i++)
+        nps[i] = ps[i].size();
+    for (rsrc = 0; rsrc<this->nprocs; rsrc++)
+        for (int i=0; i<2; i++)
+        {
+            rdst = MOD(rsrc+ro[i], this->nprocs);
+            if (this->myrank == rsrc)
+                MPI_Send(
+                        nps+i,
+                        1,
+                        MPI_INTEGER,
+                        rdst,
+                        2*(rsrc*this->nprocs + rdst)+i,
+                        this->comm);
+            if (this->myrank == rdst)
+                MPI_Recv(
+                        npr+1-i,
+                        1,
+                        MPI_INTEGER,
+                        rsrc,
+                        2*(rsrc*this->nprocs + rdst)+i,
+                        this->comm,
+                        MPI_STATUS_IGNORE);
+        }
+    //DEBUG_MSG("I have to send %d %d particles\n", nps[0], nps[1]);
+    //DEBUG_MSG("I have to recv %d %d particles\n", npr[0], npr[1]);
+    for (int i=0; i<2; i++)
+        pr[i].resize(npr[i]);
+
+    int buffer_size = (nps[0] > nps[1]) ? nps[0] : nps[1];
+    buffer_size = (buffer_size > npr[0])? buffer_size : npr[0];
+    buffer_size = (buffer_size > npr[1])? buffer_size : npr[1];
+    //DEBUG_MSG("buffer size is %d\n", buffer_size);
+    double *buffer = new double[buffer_size*this->ncomponents*(1+vals.size())];
+    for (rsrc = 0; rsrc<this->nprocs; rsrc++)
+        for (int i=0; i<2; i++)
+        {
+            rdst = MOD(rsrc+ro[i], this->nprocs);
+            if (this->myrank == rsrc && nps[i] > 0)
+            {
+                MPI_Send(
+                        &ps[i].front(),
+                        nps[i],
+                        MPI_INTEGER,
+                        rdst,
+                        2*(rsrc*this->nprocs + rdst),
+                        this->comm);
+                int pcounter = 0;
+                for (int p: ps[i])
+                {
+                    std::copy(x[p].data,
+                              x[p].data + this->ncomponents,
+                              buffer + pcounter*(1+vals.size())*this->ncomponents);
+                    x.erase(p);
+                    for (int tindex=0; tindex<vals.size(); tindex++)
+                    {
+                        std::copy(vals[tindex][p].data,
+                                  vals[tindex][p].data + this->ncomponents,
+                                  buffer + (pcounter*(1+vals.size()) + tindex+1)*this->ncomponents);
+                        vals[tindex].erase(p);
+                    }
+                    pcounter++;
+                }
+                MPI_Send(
+                        buffer,
+                        nps[i]*(1+vals.size())*this->ncomponents,
+                        MPI_DOUBLE,
+                        rdst,
+                        2*(rsrc*this->nprocs + rdst)+1,
+                        this->comm);
+            }
+            if (this->myrank == rdst && npr[1-i] > 0)
+            {
+                MPI_Recv(
+                        &pr[1-i].front(),
+                        npr[1-i],
+                        MPI_INTEGER,
+                        rsrc,
+                        2*(rsrc*this->nprocs + rdst),
+                        this->comm,
+                        MPI_STATUS_IGNORE);
+                MPI_Recv(
+                        buffer,
+                        npr[1-i]*(1+vals.size())*this->ncomponents,
+                        MPI_DOUBLE,
+                        rsrc,
+                        2*(rsrc*this->nprocs + rdst)+1,
+                        this->comm,
+                        MPI_STATUS_IGNORE);
+                int pcounter = 0;
+                for (int p: pr[1-i])
+                {
+                    x[p] = buffer + (pcounter*(1+vals.size()))*this->ncomponents;
+                    for (int tindex=0; tindex<vals.size(); tindex++)
+                    {
+                        vals[tindex][p] = buffer + (pcounter*(1+vals.size()) + tindex+1)*this->ncomponents;
+                    }
+                    pcounter++;
+                }
+            }
+        }
+    delete[] buffer;
+
+
+#ifndef NDEBUG
+    /* check that all particles at x are local */
+    //for (auto &pp: x)
+    //    if (this->vel->get_rank(pp.second.data[2]) != this->myrank)
+    //    {
+    //        DEBUG_MSG("found particle %d with rank %d\n",
+    //                pp.first,
+    //                this->vel->get_rank(pp.second.data[2]));
+    //        assert(false);
+    //    }
+#endif
+    //DEBUG_MSG("exiting redistribute\n");
+}
+
+
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::AdamsBashforth(
+        const int nsteps)
+{
+    this->get_rhs(this->state, this->rhs[0]);
+    for (auto &pp: this->state)
+        for (int i=0; i<this->ncomponents; i++)
+            switch(nsteps)
+            {
+                case 1:
+                    pp.second[i] += this->dt*this->rhs[0][pp.first][i];
+                    break;
+                case 2:
+                    pp.second[i] += this->dt*(3*this->rhs[0][pp.first][i]
+                                            -   this->rhs[1][pp.first][i])/2;
+                    break;
+                case 3:
+                    pp.second[i] += this->dt*(23*this->rhs[0][pp.first][i]
+                                            - 16*this->rhs[1][pp.first][i]
+                                            +  5*this->rhs[2][pp.first][i])/12;
+                    break;
+                case 4:
+                    pp.second[i] += this->dt*(55*this->rhs[0][pp.first][i]
+                                            - 59*this->rhs[1][pp.first][i]
+                                            + 37*this->rhs[2][pp.first][i]
+                                            -  9*this->rhs[3][pp.first][i])/24;
+                    break;
+                case 5:
+                    pp.second[i] += this->dt*(1901*this->rhs[0][pp.first][i]
+                                            - 2774*this->rhs[1][pp.first][i]
+                                            + 2616*this->rhs[2][pp.first][i]
+                                            - 1274*this->rhs[3][pp.first][i]
+                                            +  251*this->rhs[4][pp.first][i])/720;
+                    break;
+                case 6:
+                    pp.second[i] += this->dt*(4277*this->rhs[0][pp.first][i]
+                                            - 7923*this->rhs[1][pp.first][i]
+                                            + 9982*this->rhs[2][pp.first][i]
+                                            - 7298*this->rhs[3][pp.first][i]
+                                            + 2877*this->rhs[4][pp.first][i]
+                                            -  475*this->rhs[5][pp.first][i])/1440;
+                    break;
+            }
+    //this->redistribute(this->state, this->rhs);
+    this->roll_rhs();
+}
+
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::step()
+{
+    this->AdamsBashforth((this->iteration < this->integration_steps) ?
+                            this->iteration+1 :
+                            this->integration_steps);
+    this->iteration++;
+}
+
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::read()
+{
+    double *temp = new double[this->chunk_size*this->ncomponents];
+    int tmpint1, tmpint2;
+    for (int cindex=0; cindex<this->get_number_of_chunks(); cindex++)
+    {
+        //read state
+        if (this->myrank == 0)
+            this->read_state_chunk(cindex, temp);
+        MPI_Bcast(
+                temp,
+                this->chunk_size*this->ncomponents,
+                MPI_DOUBLE,
+                0,
+                this->comm);
+        for (int p=0; p<this->chunk_size; p++)
+        {
+            if (this->vel->get_rank_info(temp[this->ncomponents*p+2], tmpint1, tmpint2))
+                this->state[p+cindex*this->chunk_size] = temp + this->ncomponents*p;
+        }
+        //read rhs
+        if (this->iteration > 0)
+            for (int i=0; i<this->integration_steps; i++)
+            {
+                if (this->myrank == 0)
+                    this->read_rhs_chunk(cindex, i, temp);
+                MPI_Bcast(
+                        temp,
+                        this->chunk_size*this->ncomponents,
+                        MPI_DOUBLE,
+                        0,
+                        this->comm);
+                for (int p=0; p<this->chunk_size; p++)
+                {
+                    auto pp = this->state.find(p+cindex*this->chunk_size);
+                    if (pp != this->state.end())
+                        this->rhs[i][p+cindex*this->chunk_size] = temp + this->ncomponents*p;
+                }
+            }
+    }
+    DEBUG_MSG("%s->state.size = %ld\n", this->name.c_str(), this->state.size());
+    delete[] temp;
+}
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::write(
+        const char *dset_name,
+        std::unordered_map<int, single_particle_state<POINT3D>> &y)
+{
+    double *data = new double[this->nparticles*3];
+    double *yy = new double[this->nparticles*3];
+    for (int cindex=0; cindex<this->get_number_of_chunks(); cindex++)
+    {
+        std::fill_n(yy, this->chunk_size*3, 0);
+        for (int p=0; p<this->chunk_size; p++)
+        {
+            auto pp = y.find(p+cindex*this->chunk_size);
+            if (pp != y.end())
+                std::copy(pp->second.data,
+                          pp->second.data + 3,
+                          yy + pp->first*3);
+        }
+        MPI_Allreduce(
+                yy,
+                data,
+                3*this->nparticles,
+                MPI_DOUBLE,
+                MPI_SUM,
+                this->comm);
+        if (this->myrank == 0)
+            this->write_point3D_chunk(dset_name, cindex, data);
+    }
+    delete[] yy;
+    delete[] data;
+}
+
+template <int particle_type, class rnumber, int interp_neighbours>
+void rFFTW_distributed_particles<particle_type, rnumber, interp_neighbours>::write(
+        const bool write_rhs)
+{
+    double *temp0 = new double[this->chunk_size*this->ncomponents];
+    double *temp1 = new double[this->chunk_size*this->ncomponents];
+    for (int cindex=0; cindex<this->get_number_of_chunks(); cindex++)
+    {
+        //write state
+        std::fill_n(temp0, this->ncomponents*this->chunk_size, 0);
+        for (int p=0; p<this->chunk_size; p++)
+        {
+            auto pp = this->state.find(p + cindex*this->chunk_size);
+            if (pp != this->state.end())
+                std::copy(pp->second.data,
+                          pp->second.data + this->ncomponents,
+                          temp0 + pp->first*this->ncomponents);
+        }
+        MPI_Allreduce(
+                temp0,
+                temp1,
+                this->ncomponents*this->chunk_size,
+                MPI_DOUBLE,
+                MPI_SUM,
+                this->comm);
+        if (this->myrank == 0)
+            this->write_state_chunk(cindex, temp1);
+        //write rhs
+        if (write_rhs)
+            for (int i=0; i<this->integration_steps; i++)
+            {
+                std::fill_n(temp0, this->ncomponents*this->chunk_size, 0);
+                for (int p=0; p<this->chunk_size; p++)
+                {
+                    auto pp = this->rhs[i].find(p + cindex*this->chunk_size);
+                    if (pp != this->rhs[i].end())
+                        std::copy(pp->second.data,
+                                  pp->second.data + this->ncomponents,
+                                  temp0 + pp->first*this->ncomponents);
+                }
+                MPI_Allreduce(
+                        temp0,
+                        temp1,
+                        this->ncomponents*this->chunk_size,
+                        MPI_DOUBLE,
+                        MPI_SUM,
+                        this->comm);
+                if (this->myrank == 0)
+                    this->write_rhs_chunk(cindex, i, temp1);
+            }
+    }
+    delete[] temp0;
+    delete[] temp1;
+}
+
+
+/*****************************************************************************/
+template class rFFTW_distributed_particles<VELOCITY_TRACER, float, 1>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, float, 2>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, float, 3>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, float, 4>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, float, 5>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, float, 6>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, double, 1>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, double, 2>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, double, 3>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, double, 4>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, double, 5>;
+template class rFFTW_distributed_particles<VELOCITY_TRACER, double, 6>;
+/*****************************************************************************/
+
diff --git a/bfps/cpp/rFFTW_distributed_particles.hpp b/bfps/cpp/rFFTW_distributed_particles.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..18d43c8057b406e17744e587291b97cca1730a7b
--- /dev/null
+++ b/bfps/cpp/rFFTW_distributed_particles.hpp
@@ -0,0 +1,107 @@
+/**********************************************************************
+*                                                                     *
+*  Copyright 2015 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 <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+#include <unordered_map>
+#include <vector>
+#include <hdf5.h>
+#include "base.hpp"
+#include "particles_base.hpp"
+#include "fluid_solver_base.hpp"
+#include "rFFTW_interpolator.hpp"
+
+#ifndef RFFTW_DISTRIBUTED_PARTICLES
+
+#define RFFTW_DISTRIBUTED_PARTICLES
+
+template <int particle_type, class rnumber, int interp_neighbours>
+class rFFTW_distributed_particles: public particles_io_base<particle_type>
+{
+    private:
+        std::unordered_map<int, single_particle_state<particle_type> > state;
+        std::vector<std::unordered_map<int, single_particle_state<particle_type>>> rhs;
+        std::vector<MPI_Comm> interp_comm;
+        std::vector<int> interp_nprocs;
+
+    public:
+        int integration_steps;
+        // this class only works with rFFTW interpolator
+        rFFTW_interpolator<rnumber, interp_neighbours> *vel;
+
+        /* simulation parameters */
+        double dt;
+
+        /* methods */
+
+        /* constructor and destructor.
+         * allocate and deallocate:
+         *  this->state
+         *  this->rhs
+         * */
+        rFFTW_distributed_particles(
+                const char *NAME,
+                const hid_t data_file_id,
+                rFFTW_interpolator<rnumber, interp_neighbours> *FIELD,
+                const int TRAJ_SKIP,
+                const int INTEGRATION_STEPS = 2);
+        ~rFFTW_distributed_particles();
+
+        void sample(
+                rFFTW_interpolator<rnumber, interp_neighbours> *field,
+                const char *dset_name);
+        void sample(
+                rFFTW_interpolator<rnumber, interp_neighbours> *field,
+                const std::unordered_map<int, single_particle_state<particle_type>> &x,
+                std::unordered_map<int, single_particle_state<POINT3D>> &y);
+        void get_rhs(
+                const std::unordered_map<int, single_particle_state<particle_type>> &x,
+                std::unordered_map<int, single_particle_state<particle_type>> &y);
+
+        void redistribute(
+                std::unordered_map<int, single_particle_state<particle_type>> &x,
+                std::vector<std::unordered_map<int, single_particle_state<particle_type>>> &vals);
+
+
+        /* input/output */
+        void read();
+        void write(
+                const char *dset_name,
+                std::unordered_map<int, single_particle_state<POINT3D>> &y);
+        void write(
+                const char *dset_name,
+                std::unordered_map<int, single_particle_state<particle_type>> &y);
+        void write(const bool write_rhs = true);
+
+        /* solvers */
+        void step();
+        void roll_rhs();
+        void AdamsBashforth(const int nsteps);
+};
+
+#endif//RFFTW_DISTRIBUTED_PARTICLES
+
diff --git a/bfps/cpp/rFFTW_interpolator.cpp b/bfps/cpp/rFFTW_interpolator.cpp
index facc09c42164d44478ffa01b47354a05b0decce0..bffae44f5986f9873a231442e92cba6cf005d3a4 100644
--- a/bfps/cpp/rFFTW_interpolator.cpp
+++ b/bfps/cpp/rFFTW_interpolator.cpp
@@ -54,6 +54,24 @@ rFFTW_interpolator<rnumber, interp_neighbours>::~rFFTW_interpolator()
     delete[] this->compute;
 }
 
+template <class rnumber, int interp_neighbours>
+bool rFFTW_interpolator<rnumber, interp_neighbours>::get_rank_info(double z, int &maxz_rank, int &minz_rank)
+{
+    int zg = int(floor(z/this->dz));
+    minz_rank = this->descriptor->rank[MOD(
+             zg - interp_neighbours,
+            this->descriptor->sizes[0])];
+    maxz_rank = this->descriptor->rank[MOD(
+            zg + 1 + interp_neighbours,
+            this->descriptor->sizes[0])];
+    bool is_here = false;
+    for (int iz = -interp_neighbours; iz <= interp_neighbours+1; iz++)
+        is_here = (is_here ||
+                   (this->descriptor->myrank ==
+                    this->descriptor->rank[MOD(zg+iz, this->descriptor->sizes[0])]));
+    return is_here;
+}
+
 template <class rnumber, int interp_neighbours>
 void rFFTW_interpolator<rnumber, interp_neighbours>::sample(
         const int nparticles,
diff --git a/bfps/cpp/rFFTW_interpolator.hpp b/bfps/cpp/rFFTW_interpolator.hpp
index fb96963eee8dd1361a59edf00b38b6460a27d9e9..795257d2744e432d9c346b93848cadfbd8cc85dc 100644
--- a/bfps/cpp/rFFTW_interpolator.hpp
+++ b/bfps/cpp/rFFTW_interpolator.hpp
@@ -37,6 +37,7 @@ template <class rnumber, int interp_neighbours>
 class rFFTW_interpolator:public interpolator_base<rnumber, interp_neighbours>
 {
     public:
+        using interpolator_base<rnumber, interp_neighbours>::operator();
         /* size of field to interpolate */
         ptrdiff_t field_size;
 
@@ -62,6 +63,8 @@ class rFFTW_interpolator:public interpolator_base<rnumber, interp_neighbours>
             return EXIT_SUCCESS;
         }
 
+        bool get_rank_info(double z, int &maxz_rank, int &minz_rank);
+
         /* interpolate field at an array of locations */
         void sample(
                 const int nparticles,
diff --git a/bfps/tools.py b/bfps/tools.py
index 0f94a98ace7b849ae5f112ca7bd517829cdf4eca..fefb7819cf96b4f9b178252dd444ff5eabe01238 100644
--- a/bfps/tools.py
+++ b/bfps/tools.py
@@ -232,13 +232,16 @@ def particle_finite_diff_test(
     if interp_name not in pars.keys():
         # old format
         interp_name = 'tracers{0}_field'.format(species)
+    interp_name = pars[interp_name].value
+    if type(interp_name) == bytes:
+        interp_name = str(interp_name, 'ASCII')
     to_print = (
             'steps={0}, interp={1}, neighbours={2}, '.format(
                 pars['tracers{0}_integration_steps'.format(species)].value,
-                pars[str(pars[interp_name].value, 'ASCII') + '_type'].value,
-                pars[str(pars[interp_name].value, 'ASCII') + '_neighbours'].value))
-    if 'spline' in str(pars[interp_name].value, 'ASCII'):
-        to_print += 'smoothness = {0}, '.format(pars[str(pars[interp_name].value, 'ASCII') + '_smoothness'].value)
+                pars[interp_name + '_type'].value,
+                pars[interp_name + '_neighbours'].value))
+    if 'spline' in interp_name:
+        to_print += 'smoothness = {0}, '.format(pars[interp_name + '_smoothness'].value)
     to_print += (
             'SNR d1p-vel={0:.3f}'.format(np.mean(snr_vel1)))
     if acc_on:
diff --git a/setup.py b/setup.py
index 8f982144e9188dbc74eea1bb4d6ef1f32508b45c..8cb0e225fb8518645fb6bddef753ae8973f0d43d 100644
--- a/setup.py
+++ b/setup.py
@@ -92,6 +92,7 @@ print('This is bfps version ' + VERSION)
 ### lists of files and MANIFEST.in
 src_file_list = ['field_descriptor',
                  'interpolator_base',
+                 'rFFTW_distributed_particles',
                  'distributed_particles',
                  'particles_base',
                  'interpolator',
diff --git a/tests/test_particle_classes.py b/tests/test_particle_classes.py
index 6f38859a22c4f2e5a0ae71627899cd580af740d4..c14145c72fc57a886368e1a0a8cf9744486bf0ef 100644
--- a/tests/test_particle_classes.py
+++ b/tests/test_particle_classes.py
@@ -47,7 +47,7 @@ def scaling(opt):
     c1.compute_statistics()
     compare_stats(opt, c0, c1)
     opt.work_dir = wd + '/N{0:0>3x}_3'.format(opt.n)
-    c2 = launch(opt, dt = c0.parameters['dt'], particle_class = 'particles', interpolator_class = 'interpolator')
+    c2 = launch(opt, dt = c0.parameters['dt'], particle_class = 'rFFTW_distributed_particles')
     c2.compute_statistics()
     compare_stats(opt, c0, c2)
     compare_stats(opt, c1, c2)