diff --git a/bfps/NavierStokes.py b/bfps/NavierStokes.py
index f3fa39d04c76ac5b400b481ecf423095b4135bcd..70a4426d6f81c0610aff559b7cb8620194f74a0a 100644
--- a/bfps/NavierStokes.py
+++ b/bfps/NavierStokes.py
@@ -119,80 +119,76 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
         self.fluid_includes += '#include "fftw_tools.hpp"\n'
         self.stat_src += """
                 //begincpp
+                double *velocity_moments  = new double[10*4];
+                double *vorticity_moments = new double[10*4];
+                ptrdiff_t *hist_velocity  = new ptrdiff_t[histogram_bins*4];
+                ptrdiff_t *hist_vorticity = new ptrdiff_t[histogram_bins*4];
+                double max_estimates[4];
+                fs->compute_velocity(fs->cvorticity);
+                double *spec_velocity  = new double[fs->nshells*9];
+                double *spec_vorticity = new double[fs->nshells*9];
+                fs->cospectrum(fs->cvelocity, fs->cvelocity, spec_velocity);
+                fs->cospectrum(fs->cvorticity, fs->cvorticity, spec_vorticity);
                 //endcpp
                 """
-        self.stat_src += """
-                //begincpp
-                    double *velocity_moments  = new double[10*4];
-                    double *vorticity_moments = new double[10*4];
-                    ptrdiff_t *hist_velocity  = new ptrdiff_t[histogram_bins*4];
-                    ptrdiff_t *hist_vorticity = new ptrdiff_t[histogram_bins*4];
-                    double max_estimates[4];
-                    fs->compute_velocity(fs->cvorticity);
-                    double *spec_velocity  = new double[fs->nshells*9];
-                    double *spec_vorticity = new double[fs->nshells*9];
-                    fs->cospectrum(fs->cvelocity, fs->cvelocity, spec_velocity);
-                    fs->cospectrum(fs->cvorticity, fs->cvorticity, spec_vorticity);
-                    //endcpp
-                    """
         if self.QR_stats_on:
             self.stat_src += """
                 //begincpp
-                    double *trS2_Q_R_moments  = new double[10*3];
-                    double *gradu_moments     = new double[10*9];
-                    ptrdiff_t *hist_trS2_Q_R  = new ptrdiff_t[histogram_bins*3];
-                    ptrdiff_t *hist_gradu     = new ptrdiff_t[histogram_bins*9];
-                    ptrdiff_t *hist_QR2D      = new ptrdiff_t[QR2D_histogram_bins*QR2D_histogram_bins];
-                    double trS2QR_max_estimates[3];
-                    double gradu_max_estimates[9];
-                    trS2QR_max_estimates[0] = max_trS2_estimate;
-                    trS2QR_max_estimates[1] = max_Q_estimate;
-                    trS2QR_max_estimates[2] = max_R_estimate;
-                    std::fill_n(gradu_max_estimates, 9, sqrt(3*max_trS2_estimate));
-                    fs->compute_gradient_statistics(
-                        fs->cvelocity,
-                        gradu_moments,
-                        trS2_Q_R_moments,
-                        hist_gradu,
-                        hist_trS2_Q_R,
-                        hist_QR2D,
-                        trS2QR_max_estimates,
-                        gradu_max_estimates,
-                        histogram_bins,
-                        QR2D_histogram_bins);
-                    //endcpp
-                    """
+                double *trS2_Q_R_moments  = new double[10*3];
+                double *gradu_moments     = new double[10*9];
+                ptrdiff_t *hist_trS2_Q_R  = new ptrdiff_t[histogram_bins*3];
+                ptrdiff_t *hist_gradu     = new ptrdiff_t[histogram_bins*9];
+                ptrdiff_t *hist_QR2D      = new ptrdiff_t[QR2D_histogram_bins*QR2D_histogram_bins];
+                double trS2QR_max_estimates[3];
+                double gradu_max_estimates[9];
+                trS2QR_max_estimates[0] = max_trS2_estimate;
+                trS2QR_max_estimates[1] = max_Q_estimate;
+                trS2QR_max_estimates[2] = max_R_estimate;
+                std::fill_n(gradu_max_estimates, 9, sqrt(3*max_trS2_estimate));
+                fs->compute_gradient_statistics(
+                    fs->cvelocity,
+                    gradu_moments,
+                    trS2_Q_R_moments,
+                    hist_gradu,
+                    hist_trS2_Q_R,
+                    hist_QR2D,
+                    trS2QR_max_estimates,
+                    gradu_max_estimates,
+                    histogram_bins,
+                    QR2D_histogram_bins);
+                //endcpp
+                """
         self.stat_src += """
                 //begincpp
-                    fs->ift_velocity();
-                    max_estimates[0] = max_velocity_estimate/sqrt(3);
-                    max_estimates[1] = max_estimates[0];
-                    max_estimates[2] = max_estimates[0];
-                    max_estimates[3] = max_velocity_estimate;
-                    fs->compute_rspace_stats4(fs->rvelocity,
-                                             velocity_moments,
-                                             hist_velocity,
-                                             max_estimates,
-                                             histogram_bins);
-                    fs->ift_vorticity();
-                    max_estimates[0] = max_vorticity_estimate/sqrt(3);
-                    max_estimates[1] = max_estimates[0];
-                    max_estimates[2] = max_estimates[0];
-                    max_estimates[3] = max_vorticity_estimate;
-                    fs->compute_rspace_stats4(fs->rvorticity,
-                                             vorticity_moments,
-                                             hist_vorticity,
-                                             max_estimates,
-                                             histogram_bins);
-                    if (fs->cd->myrank == 0)
-                    {{
-                        hid_t Cdset, wspace, mspace;
-                        int ndims;
-                        hsize_t count[4], offset[4], dims[4];
-                        offset[0] = fs->iteration/niter_stat;
-                        offset[1] = 0;
-                        offset[2] = 0;
-                        offset[3] = 0;
+                fs->ift_velocity();
+                max_estimates[0] = max_velocity_estimate/sqrt(3);
+                max_estimates[1] = max_estimates[0];
+                max_estimates[2] = max_estimates[0];
+                max_estimates[3] = max_velocity_estimate;
+                fs->compute_rspace_stats4(fs->rvelocity,
+                                         velocity_moments,
+                                         hist_velocity,
+                                         max_estimates,
+                                         histogram_bins);
+                fs->ift_vorticity();
+                max_estimates[0] = max_vorticity_estimate/sqrt(3);
+                max_estimates[1] = max_estimates[0];
+                max_estimates[2] = max_estimates[0];
+                max_estimates[3] = max_vorticity_estimate;
+                fs->compute_rspace_stats4(fs->rvorticity,
+                                         vorticity_moments,
+                                         hist_vorticity,
+                                         max_estimates,
+                                         histogram_bins);
+                if (fs->cd->myrank == 0)
+                {{
+                    hid_t Cdset, wspace, mspace;
+                    int ndims;
+                    hsize_t count[4], offset[4], dims[4];
+                    offset[0] = fs->iteration/niter_stat;
+                    offset[1] = 0;
+                    offset[2] = 0;
+                    offset[3] = 0;
                 //endcpp
                 """.format(self.C_dtype)
         if self.dtype == np.float32:
@@ -300,23 +296,23 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
                         """)
         self.stat_src += """
                 //begincpp
-                    }
-                    delete[] spec_velocity;
-                    delete[] spec_vorticity;
-                    delete[] velocity_moments;
-                    delete[] vorticity_moments;
-                    delete[] hist_velocity;
-                    delete[] hist_vorticity;
+                }
+                delete[] spec_velocity;
+                delete[] spec_vorticity;
+                delete[] velocity_moments;
+                delete[] vorticity_moments;
+                delete[] hist_velocity;
+                delete[] hist_vorticity;
                 //endcpp
                 """
         if self.QR_stats_on:
             self.stat_src += """
                 //begincpp
-                    delete[] trS2_Q_R_moments;
-                    delete[] gradu_moments;
-                    delete[] hist_trS2_Q_R;
-                    delete[] hist_gradu;
-                    delete[] hist_QR2D;
+                delete[] trS2_Q_R_moments;
+                delete[] gradu_moments;
+                delete[] hist_trS2_Q_R;
+                delete[] hist_gradu;
+                delete[] hist_QR2D;
                 //endcpp
                 """
         return None
@@ -378,16 +374,27 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
                           '}\n' +
                           'delete fs;\n')
         return None
-    def add_particle_fields(
+    def add_3D_rFFTW_field(
+            self,
+            name = 'rFFTW_acc'):
+        if self.dtype == np.float32:
+            FFTW = 'fftwf'
+        elif self.dtype == np.float64:
+            FFTW = 'fftw'
+        self.fluid_variables += '{0} *{1};\n'.format(self.C_dtype, name)
+        self.fluid_start += '{0} = {1}_alloc_real(2*fs->cd->local_size);\n'.format(name, FFTW)
+        self.fluid_end   += '{0}_free({1});\n'.format(FFTW, name)
+        return None
+    def add_interpolator(
             self,
             interp_type = 'spline',
-            kcut = None,
             neighbours = 1,
             smoothness = 1,
-            name = 'particle_field',
-            field_class = 'rFFTW_interpolator'):
-        self.fluid_includes += '#include "{0}.hpp"\n'.format(field_class)
-        self.fluid_variables += field_class + '<{0}, {1}> *vel_{2}, *acc_{2};\n'.format(self.C_dtype, neighbours, name)
+            name = 'field_interpolator',
+            field_name = 'fs->rvelocity'):
+        self.fluid_includes += '#include "rFFTW_interpolator.hpp"\n'
+        self.fluid_variables += 'rFFTW_interpolator <{0}, {1}> *{2};\n'.format(
+                self.C_dtype, neighbours, name)
         self.parameters[name + '_type'] = interp_type
         self.parameters[name + '_neighbours'] = neighbours
         if interp_type == 'spline':
@@ -395,41 +402,66 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
             beta_name = 'beta_n{0}_m{1}'.format(neighbours, smoothness)
         elif interp_type == 'Lagrange':
             beta_name = 'beta_Lagrange_n{0}'.format(neighbours)
-        self.fluid_start += ('vel_{0} = new {1}<{2}, {3}>(fs, {4});\n' +
-                             'acc_{0} = new {1}<{2}, {3}>(fs, {4});\n').format(name,
-                                                                               field_class,
-                                                                               self.C_dtype,
-                                                                               neighbours,
-                                                                               beta_name)
-        self.fluid_end += ('delete vel_{0};\n' +
-                           'delete acc_{0};\n').format(name)
-        update_fields = 'fs->compute_velocity(fs->cvorticity);\n'
-        if not type(kcut) == type(None):
-            update_fields += 'fs->low_pass_Fourier(fs->cvelocity, 3, {0});\n'.format(kcut)
-        update_fields += ('fs->ift_velocity();\n' +
-                          'vel_{0}->read_rFFTW(fs->rvelocity);\n' +
-                          'fs->compute_Lagrangian_acceleration(acc_{0}->field);\n').format(name)
-        self.fluid_start += update_fields
-        self.fluid_loop += update_fields
+        self.fluid_start += '{0} = new rFFTW_interpolator<{1}, {2}>(fs, {3}, {4});\n'.format(
+                name,
+                self.C_dtype,
+                neighbours,
+                beta_name,
+                field_name)
+        self.fluid_end += 'delete {0};\n'.format(name)
         return None
     def add_particles(
             self,
-            integration_method = 'AdamsBashforth',
             integration_steps = 2,
-            kcut = 'fs->kM',
+            kcut = None,
+            interpolator = 'field_interpolator',
             frozen_particles = False,
-            fields_name = None,
-            particle_class = 'rFFTW_particles'):
-        if integration_method == 'cRK4':
-            integration_steps = 4
-        elif integration_method == 'Heun':
-            integration_steps = 2
-        neighbours = self.parameters[fields_name + '_neighbours']
-        self.parameters['tracers{0}_field'.format(self.particle_species)] = fields_name
-        self.parameters['tracers{0}_integration_method'.format(self.particle_species)] = integration_method
-        self.parameters['tracers{0}_kcut'.format(self.particle_species)] = kcut
-        self.parameters['tracers{0}_integration_steps'.format(self.particle_species)] = integration_steps
-        self.file_datasets_grow += """
+            acc_name = None):
+        """Adds code for tracking a series of particle species, each
+        consisting of `nparticles` particles.
+
+        :type integration_steps: int, list of int
+        :type kcut: None (default), str, list of str
+        :type interpolator: str, list of str
+        :type frozen_particles: bool
+        :type acc_name: str
+
+        .. warning :: if not None, kcut must be a list of decreasing
+        wavenumbers, since filtering is done on the same field...
+        """
+        if self.dtype == np.float32:
+            FFTW = 'fftwf'
+        elif self.dtype == np.float64:
+            FFTW = 'fftw'
+        s0 = self.particle_species
+        if type(integration_steps) == int:
+            integration_steps = [integration_steps]
+        if type(kcut) == str:
+            kcut = [kcut]
+        if type(interpolator) == str:
+            interpolator = [interpolator]
+        nspecies = max(len(integration_steps), len(interpolator))
+        if type(kcut) == list:
+            nspecies = max(nspecies, len(kcut))
+        if len(integration_steps) == 1:
+            integration_steps = [integration_steps[0] for s in range(nspecies)]
+        if len(interpolator) == 1:
+            interpolator = [interpolator[0] for s in range(nspecies)]
+        if type(kcut) == list:
+            if len(kcut) == 1:
+                kcut = [kcut[0] for s in range(nspecies)]
+        assert(len(integration_steps) == nspecies)
+        assert(len(interpolator) == nspecies)
+        if type(kcut) == list:
+            assert(len(kcut) == nspecies)
+        for s in range(nspecies):
+            neighbours = self.parameters[interpolator[s] + '_neighbours']
+            if type(kcut) == list:
+                self.parameters['tracers{0}_kcut'.format(s0 + s)] = kcut[s]
+            self.parameters['tracers{0}_acc_on'.format(s0 + s)] = not type(acc_name) == type(None)
+            self.parameters['tracers{0}_interpolator'.format(s0 + s)] = interpolator[s]
+            self.parameters['tracers{0}_integration_steps'.format(s0 + s)] = integration_steps[s]
+            self.file_datasets_grow += """
                         //begincpp
                         temp_string = (std::string("/particles/") +
                                        std::string(ps{0}->name));
@@ -437,110 +469,118 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
                         grow_particle_datasets(group, temp_string.c_str(), NULL, NULL);
                         H5Gclose(group);
                         //endcpp
-                        """.format(self.particle_species)
-        if self.dtype == np.float32:
-            FFTW = 'fftwf'
-        elif self.dtype == np.float64:
-            FFTW = 'fftw'
-        output_vel_acc =  """
-                          //begincpp
-                          {{
-                              double *acceleration = new double[ps{0}->array_size];
-                              double *velocity     = new double[ps{0}->array_size];
-                              ps{0}->sample_vec_field(vel_{1}, velocity);
-                              ps{0}->sample_vec_field(acc_{1}, acceleration);
-                              if (ps{0}->myrank == 0)
-                              {{
-                                  //VELOCITY begin
-                                  std::string temp_string = (std::string("/particles/") +
-                                                             std::string(ps{0}->name) +
-                                                             std::string("/velocity"));
-                                  hid_t Cdset = H5Dopen(stat_file, temp_string.c_str(), H5P_DEFAULT);
-                                  hid_t mspace, wspace;
-                                  int ndims;
-                                  hsize_t count[3], offset[3];
-                                  wspace = H5Dget_space(Cdset);
-                                  ndims = H5Sget_simple_extent_dims(wspace, count, NULL);
-                                  count[0] = 1;
-                                  offset[0] = ps{0}->iteration / ps{0}->traj_skip;
-                                  offset[1] = 0;
-                                  offset[2] = 0;
-                                  mspace = H5Screate_simple(ndims, count, NULL);
-                                  H5Sselect_hyperslab(wspace, H5S_SELECT_SET, offset, NULL, count, NULL);
-                                  H5Dwrite(Cdset, H5T_NATIVE_DOUBLE, mspace, wspace, H5P_DEFAULT, velocity);
-                                  H5Dclose(Cdset);
-                                  //VELOCITY end
-                                  //ACCELERATION begin
-                                  temp_string = (std::string("/particles/") +
-                                                 std::string(ps{0}->name) +
-                                                 std::string("/acceleration"));
-                                  Cdset = H5Dopen(stat_file, temp_string.c_str(), H5P_DEFAULT);
-                                  H5Dwrite(Cdset, H5T_NATIVE_DOUBLE, mspace, wspace, H5P_DEFAULT, acceleration);
-                                  H5Sclose(mspace);
-                                  H5Sclose(wspace);
-                                  H5Dclose(Cdset);
-                                  //ACCELERATION end
-                              }}
-                              delete[] acceleration;
-                              delete[] velocity;
-                          }}
-                          //endcpp
-                          """.format(self.particle_species, fields_name)
-        self.particle_start += 'sprintf(fname, "tracers{0}");\n'.format(self.particle_species)
-        self.particle_end += ('ps{0}->write(stat_file);\n' +
-                              'delete ps{0};\n').format(self.particle_species)
-        self.particle_includes += '#include "{0}.hpp"\n'.format(particle_class)
-        if particle_class == 'particles':
-            if integration_method == 'AdamsBashforth':
-                multistep = 'true'
-            else:
-                multistep = 'false'
-            self.particle_variables += 'particles<VELOCITY_TRACER, {0}, {1}, {2}> *ps{3};\n'.format(
-                    self.C_dtype,
-                    multistep,
-                    neighbours,
-                    self.particle_species)
-            self.particle_start += ('ps{0} = new particles<VELOCITY_TRACER, {1}, {2},{3}>(\n' +
-                                    'fname, fs, vel_{4},\n' +
-                                    'nparticles,\n' +
-                                    'niter_part, tracers{0}_integration_steps);\n').format(
-                                            self.particle_species, self.C_dtype, multistep, neighbours, fields_name)
+                        """.format(s0 + s)
+
+        #### code that outputs statistics
+        output_vel_acc = '{\n'
+        # array for putting sampled velocity in
+        # must compute velocity, just in case it was messed up by some
+        # other particle species before the stats
+        output_vel_acc += ('double *velocity = new double[3*nparticles];\n' +
+                           'fs->compute_velocity(fs->cvorticity);\n')
+        if not type(kcut) == list:
+            output_vel_acc += 'fs->ift_velocity();\n'
+        if not type(acc_name) == type(None):
+            # array for putting sampled acceleration in
+            # must compute acceleration
+            output_vel_acc += 'double *acceleration = new double[3*nparticles];\n'
+            output_vel_acc += 'fs->compute_Lagrangian_acceleration({0});\n'.format(acc_name)
+        for s in range(nspecies):
+            if type(kcut) == list:
+                output_vel_acc += 'fs->low_pass_Fourier(fs->cvelocity, 3, {0});\n'.format(kcut[s])
+                output_vel_acc += 'fs->ift_velocity();\n'
+            output_vel_acc += """
+                {0}->field = fs->rvelocity;
+                ps{1}->sample_vec_field({0}, velocity);
+                """.format(interpolator[s], s0 + s)
+            if not type(acc_name) == type(None):
+                output_vel_acc += """
+                    {0}->field = {1};
+                    ps{2}->sample_vec_field({0}, acceleration);
+                    """.format(interpolator[s], acc_name, s0 + s)
+            output_vel_acc += """
+                if (myrank == 0)
+                {{
+                //VELOCITY begin
+                std::string temp_string = (std::string("/particles/") +
+                                           std::string(ps{0}->name) +
+                                           std::string("/velocity"));
+                hid_t Cdset = H5Dopen(stat_file, temp_string.c_str(), H5P_DEFAULT);
+                hid_t mspace, wspace;
+                int ndims;
+                hsize_t count[3], offset[3];
+                wspace = H5Dget_space(Cdset);
+                ndims = H5Sget_simple_extent_dims(wspace, count, NULL);
+                count[0] = 1;
+                offset[0] = ps{0}->iteration / ps{0}->traj_skip;
+                offset[1] = 0;
+                offset[2] = 0;
+                mspace = H5Screate_simple(ndims, count, NULL);
+                H5Sselect_hyperslab(wspace, H5S_SELECT_SET, offset, NULL, count, NULL);
+                H5Dwrite(Cdset, H5T_NATIVE_DOUBLE, mspace, wspace, H5P_DEFAULT, velocity);
+                H5Dclose(Cdset);
+                //VELOCITY end\n""".format(s0 + s)
+            if not type(acc_name) == type(None):
+                output_vel_acc += """
+                    //ACCELERATION begin
+                    temp_string = (std::string("/particles/") +
+                                   std::string(ps{0}->name) +
+                                   std::string("/acceleration"));
+                    Cdset = H5Dopen(stat_file, temp_string.c_str(), H5P_DEFAULT);
+                    H5Dwrite(Cdset, H5T_NATIVE_DOUBLE, mspace, wspace, H5P_DEFAULT, acceleration);
+                    H5Sclose(mspace);
+                    H5Sclose(wspace);
+                    H5Dclose(Cdset);
+                    //ACCELERATION end\n""".format(s0 + s)
+            output_vel_acc += '}\n'
+        output_vel_acc += 'delete[] velocity;\n'
+        if not type(acc_name) == type(None):
+            output_vel_acc += 'delete[] acceleration;\n'
+        output_vel_acc += '}\n'
+
+        #### initialize, stepping and finalize code
+        if not type(kcut) == list:
+            update_fields = ('fs->compute_velocity(fs->cvorticity);\n' +
+                             'fs->ift_velocity();\n')
+            self.particle_start += update_fields
+            self.particle_loop  += update_fields
         else:
+            self.particle_loop += 'fs->compute_velocity(fs->cvorticity);\n'
+        self.particle_includes += '#include "rFFTW_particles.hpp"\n'
+        self.particle_stat_src += (
+                'if (ps0->iteration % niter_part == 0)\n' +
+                '{\n')
+        for s in range(nspecies):
+            self.particle_start += 'sprintf(fname, "tracers{0}");\n'.format(s0 + s)
+            self.particle_end += ('ps{0}->write(stat_file);\n' +
+                                  'delete ps{0};\n').format(s0 + s)
             self.particle_variables += 'rFFTW_particles<VELOCITY_TRACER, {0}, {1}> *ps{2};\n'.format(
                     self.C_dtype,
                     neighbours,
-                    self.particle_species)
+                    s0 + s)
             self.particle_start += ('ps{0} = new rFFTW_particles<VELOCITY_TRACER, {1}, {2}>(\n' +
-                                    'fname, vel_{3},\n' +
+                                    'fname, {3},\n' +
                                     'nparticles,\n' +
                                     'niter_part, tracers{0}_integration_steps);\n').format(
-                                            self.particle_species, self.C_dtype, neighbours, fields_name)
-        self.particle_start += ('ps{0}->dt = dt;\n' +
-                                'ps{0}->iteration = iteration;\n' +
-                                'ps{0}->read(stat_file);\n').format(self.particle_species)
+                                            s0 + s,
+                                            self.C_dtype,
+                                            neighbours,
+                                            interpolator[s])
+            self.particle_start += ('ps{0}->dt = dt;\n' +
+                                    'ps{0}->iteration = iteration;\n' +
+                                    'ps{0}->read(stat_file);\n').format(s0 + s)
+            if not frozen_particles:
+                if type(kcut) == list:
+                    update_field = ('fs->low_pass_Fourier(fs->cvelocity, 3, {0});\n'.format(kcut[s]) +
+                                    'fs->ift_velocity();\n')
+                    self.particle_loop += update_field
+                self.particle_loop += '{0}->field = fs->rvelocity;\n'.format(interpolator[s])
+                self.particle_loop += 'ps{0}->step();\n'.format(s0 + s)
+            self.particle_stat_src += 'ps{0}->write(stat_file, false);\n'.format(s0 + s)
         self.particle_start += output_vel_acc
-        if not frozen_particles:
-            if particle_class == 'particles':
-                if integration_method == 'AdamsBashforth':
-                    self.particle_loop += 'ps{0}->AdamsBashforth((ps{0}->iteration < ps{0}->integration_steps) ? ps{0}->iteration+1 : ps{0}->integration_steps);\n'.format(self.particle_species)
-                elif integration_method == 'Euler':
-                    self.particle_loop += 'ps{0}->Euler();\n'.format(self.particle_species)
-                elif integration_method == 'Heun':
-                    assert(integration_steps == 2)
-                    self.particle_loop += 'ps{0}->Heun();\n'.format(self.particle_species)
-                elif integration_method == 'cRK4':
-                    assert(integration_steps == 4)
-                    self.particle_loop += 'ps{0}->cRK4();\n'.format(self.particle_species)
-                self.particle_loop += 'ps{0}->iteration++;\n'.format(self.particle_species)
-                self.particle_loop += 'ps{0}->synchronize();\n'.format(self.particle_species)
-            elif particle_class == 'rFFTW_particles':
-                self.particle_loop += 'ps{0}->step();\n'.format(self.particle_species)
-        self.particle_stat_src += (
-                ('if (ps{0}->iteration % niter_part == 0)\n' +
-                 '{{\n' +
-                 'ps{0}->write(stat_file, false);\n').format(self.particle_species) +
-                output_vel_acc + '}\n')
-        self.particle_species += 1
+        self.particle_stat_src += output_vel_acc
+        self.particle_stat_src += '}\n'
+        self.particle_species += nspecies
         return None
     def get_data_file(self):
         return h5py.File(os.path.join(self.work_dir, self.simname + '.h5'), 'r')
@@ -553,7 +593,8 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
         with self.get_data_file() as data_file:
             if 'moments' not in data_file['statistics'].keys():
                 return None
-            iter0 = min(data_file['statistics/moments/velocity'].shape[0]*self.parameters['niter_stat']-1,
+            iter0 = min((data_file['statistics/moments/velocity'].shape[0] *
+                         self.parameters['niter_stat']-1),
                         iter0)
             if type(iter1) == type(None):
                 iter1 = data_file['iteration'].value
@@ -565,9 +606,9 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
             self.statistics['kM'] = data_file['kspace/kM'].value
             self.statistics['dk'] = data_file['kspace/dk'].value
             if self.particle_species > 0:
-                self.trajectories = [
-                        data_file['particles/' + key + '/state'][
-                            iter0//self.parameters['niter_part']:iter1//self.parameters['niter_part']+1]
+                self.trajectories = [data_file['particles/' + key + '/state'][
+                                        iter0//self.parameters['niter_part'] :
+                                        iter1//self.parameters['niter_part']+1]
                                      for key in data_file['particles'].keys()]
             computation_needed = True
             pp_file = h5py.File(self.get_postprocess_file_name(), 'a')
@@ -612,7 +653,8 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
             self.statistics[key + '(t)'] = (self.statistics['dk'] *
                                             np.sum(self.statistics[key + '(t, k)'], axis = 1))
         self.statistics['Uint(t)'] = np.sqrt(2*self.statistics['energy(t)'] / 3)
-        self.statistics['Lint(t)'] = ((self.statistics['dk']*np.pi / (2*self.statistics['Uint(t)']**2)) *
+        self.statistics['Lint(t)'] = ((self.statistics['dk']*np.pi /
+                                       (2*self.statistics['Uint(t)']**2)) *
                                       np.nansum(self.statistics['energy(t, k)'] /
                                                 self.statistics['kshell'][None, :], axis = 1))
         for key in ['energy',
@@ -639,11 +681,12 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
             self.statistics['Rlambda' + suffix] = (self.statistics['Uint' + suffix] *
                                                    self.statistics['lambda' + suffix] /
                                                    self.parameters['nu'])
+            self.statistics['kMeta' + suffix] = (self.statistics['kM'] *
+                                                 self.statistics['etaK' + suffix])
+            if self.parameters['dealias_type'] == 1:
+                self.statistics['kMeta' + suffix] *= 0.8
         self.statistics['Tint'] = self.statistics['Lint'] / self.statistics['Uint']
         self.statistics['Taylor_microscale'] = self.statistics['lambda']
-        self.statistics['kMeta'] = self.statistics['kM']*self.statistics['etaK']
-        if self.parameters['dealias_type'] == 1:
-            self.statistics['kMeta'] *= 0.8
         return None
     def set_plt_style(
             self,
@@ -731,6 +774,51 @@ class NavierStokes(bfps.fluid_base.fluid_particle_base):
                                      dtype = np.int64,
                                      compression = 'gzip')
         return None
+    def add_particle_fields(
+            self,
+            interp_type = 'spline',
+            kcut = None,
+            neighbours = 1,
+            smoothness = 1,
+            name = 'particle_field',
+            field_class = 'rFFTW_interpolator',
+            acc_field_name = 'rFFTW_acc'):
+        self.fluid_includes += '#include "{0}.hpp"\n'.format(field_class)
+        self.fluid_variables += field_class + '<{0}, {1}> *vel_{2}, *acc_{2};\n'.format(
+                self.C_dtype, neighbours, name)
+        self.parameters[name + '_type'] = interp_type
+        self.parameters[name + '_neighbours'] = neighbours
+        if interp_type == 'spline':
+            self.parameters[name + '_smoothness'] = smoothness
+            beta_name = 'beta_n{0}_m{1}'.format(neighbours, smoothness)
+        elif interp_type == 'Lagrange':
+            beta_name = 'beta_Lagrange_n{0}'.format(neighbours)
+        if field_class == 'rFFTW_interpolator':
+            self.fluid_start += ('vel_{0} = new {1}<{2}, {3}>(fs, {4}, fs->rvelocity);\n' +
+                                 'acc_{0} = new {1}<{2}, {3}>(fs, {4}, {5});\n').format(name,
+                                                                                   field_class,
+                                                                                   self.C_dtype,
+                                                                                   neighbours,
+                                                                                   beta_name,
+                                                                                   acc_field_name)
+        elif field_class == 'interpolator':
+            self.fluid_start += ('vel_{0} = new {1}<{2}, {3}>(fs, {4});\n' +
+                                 'acc_{0} = new {1}<{2}, {3}>(fs, {4});\n').format(name,
+                                                                                   field_class,
+                                                                                   self.C_dtype,
+                                                                                   neighbours,
+                                                                                   beta_name,
+                                                                                   acc_field_name)
+        self.fluid_end += ('delete vel_{0};\n' +
+                           'delete acc_{0};\n').format(name)
+        update_fields = 'fs->compute_velocity(fs->cvorticity);\n'
+        if not type(kcut) == type(None):
+            update_fields += 'fs->low_pass_Fourier(fs->cvelocity, 3, {0});\n'.format(kcut)
+        update_fields += ('fs->ift_velocity();\n' +
+                          'fs->compute_Lagrangian_acceleration(acc_{0}->field);\n').format(name)
+        self.fluid_start += update_fields
+        self.fluid_loop += update_fields
+        return None
 
 def launch(
         opt,
diff --git a/bfps/cpp/interpolator.hpp b/bfps/cpp/interpolator.hpp
index 299fef4910693b878d3f2734636b43dbf8f98a6d..e083bd56c6c27c1cc048d0ecfdfb7ee416082651 100644
--- a/bfps/cpp/interpolator.hpp
+++ b/bfps/cpp/interpolator.hpp
@@ -27,23 +27,12 @@
 #include "field_descriptor.hpp"
 #include "fftw_tools.hpp"
 #include "fluid_solver_base.hpp"
-#include "spline_n1.hpp"
-#include "spline_n2.hpp"
-#include "spline_n3.hpp"
-#include "spline_n4.hpp"
-#include "spline_n5.hpp"
-#include "spline_n6.hpp"
-#include "Lagrange_polys.hpp"
+#include "interpolator_base.hpp"
 
 #ifndef INTERPOLATOR
 
 #define INTERPOLATOR
 
-typedef void (*base_polynomial_values)(
-        const int derivative,
-        const double fraction,
-        double *__restrict__ destination);
-
 template <class rnumber, int interp_neighbours>
 class interpolator
 {
diff --git a/bfps/cpp/interpolator_base.hpp b/bfps/cpp/interpolator_base.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..69c2d4f27bb3b029fc5fd4c4e85d6160b23fe32c
--- /dev/null
+++ b/bfps/cpp/interpolator_base.hpp
@@ -0,0 +1,45 @@
+/**********************************************************************
+*                                                                     *
+*  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 "spline_n1.hpp"
+#include "spline_n2.hpp"
+#include "spline_n3.hpp"
+#include "spline_n4.hpp"
+#include "spline_n5.hpp"
+#include "spline_n6.hpp"
+#include "Lagrange_polys.hpp"
+
+#ifndef INTERPOLATOR_BASE
+
+#define INTERPOLATOR_BASE
+
+typedef void (*base_polynomial_values)(
+        const int derivative,
+        const double fraction,
+        double *__restrict__ destination);
+
+#endif//INTERPOLATOR_BASE
+
diff --git a/bfps/cpp/particles.hpp b/bfps/cpp/particles.hpp
index 4b743a006c15c1c83b65d66b9fd8056caee6e2e9..6fa379c458f38d364765efa4d7e58bb25e9daff5 100644
--- a/bfps/cpp/particles.hpp
+++ b/bfps/cpp/particles.hpp
@@ -29,6 +29,7 @@
 #include <iostream>
 #include <hdf5.h>
 #include "base.hpp"
+#include "particles_base.hpp"
 #include "fluid_solver_base.hpp"
 #include "interpolator.hpp"
 
@@ -36,9 +37,6 @@
 
 #define PARTICLES
 
-/* particle types */
-enum particle_types {VELOCITY_TRACER};
-
 template <int particle_type, class rnumber, bool multistep, int interp_neighbours>
 class particles
 {
diff --git a/bfps/cpp/particles_base.hpp b/bfps/cpp/particles_base.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cf61f38efadf0030a67a10ff94c21a92e79bdd64
--- /dev/null
+++ b/bfps/cpp/particles_base.hpp
@@ -0,0 +1,37 @@
+/**********************************************************************
+*                                                                     *
+*  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 "interpolator_base.hpp"
+
+#ifndef PARTICLES_BASE
+
+#define PARTICLES_BASE
+
+/* particle types */
+enum particle_types {VELOCITY_TRACER};
+
+#endif//PARTICLES_BASE
+
diff --git a/bfps/cpp/rFFTW_interpolator.cpp b/bfps/cpp/rFFTW_interpolator.cpp
index a858eb104abf19837f7ccd30ae673caaf841a493..c701fbd64bba8e1c68ab2de953eae0795baead2f 100644
--- a/bfps/cpp/rFFTW_interpolator.cpp
+++ b/bfps/cpp/rFFTW_interpolator.cpp
@@ -32,15 +32,13 @@
 template <class rnumber, int interp_neighbours>
 rFFTW_interpolator<rnumber, interp_neighbours>::rFFTW_interpolator(
         fluid_solver_base<rnumber> *fs,
-        base_polynomial_values BETA_POLYS)
+        base_polynomial_values BETA_POLYS,
+        rnumber *FIELD)
 {
     this->descriptor = fs->rd;
     this->field_size = 2*fs->cd->local_size;
     this->compute_beta = BETA_POLYS;
-    if (sizeof(rnumber) == 4)
-        this->field = (rnumber*)((void*)fftwf_alloc_real(this->field_size));
-    else if (sizeof(rnumber) == 8)
-        this->field = (rnumber*)((void*)fftw_alloc_real(this->field_size));
+    this->field = FIELD;
 
     // compute dx, dy, dz;
     this->dx = 4*acos(0) / (fs->dkx*this->descriptor->sizes[2]);
@@ -59,24 +57,9 @@ rFFTW_interpolator<rnumber, interp_neighbours>::rFFTW_interpolator(
 template <class rnumber, int interp_neighbours>
 rFFTW_interpolator<rnumber, interp_neighbours>::~rFFTW_interpolator()
 {
-    if (sizeof(rnumber) == 4)
-        fftwf_free((float*)((void*)this->field));
-    else if (sizeof(rnumber) == 8)
-        fftw_free((double*)((void*)this->field));
     delete[] this->compute;
 }
 
-template <class rnumber, int interp_neighbours>
-int rFFTW_interpolator<rnumber, interp_neighbours>::read_rFFTW(void *void_src)
-{
-    rnumber *src = (rnumber*)void_src;
-    /* do big copy of middle stuff */
-    std::copy(src,
-              src + this->field_size,
-              this->field);
-    return EXIT_SUCCESS;
-}
-
 template <class rnumber, int interp_neighbours>
 void rFFTW_interpolator<rnumber, interp_neighbours>::get_grid_coordinates(
         const int nparticles,
diff --git a/bfps/cpp/rFFTW_interpolator.hpp b/bfps/cpp/rFFTW_interpolator.hpp
index 0864bd964b77b5b3527a58898b8c22da63c33c2e..3dccd7150d17bea612128bf6d70b37033d281db8 100644
--- a/bfps/cpp/rFFTW_interpolator.hpp
+++ b/bfps/cpp/rFFTW_interpolator.hpp
@@ -27,14 +27,7 @@
 #include "field_descriptor.hpp"
 #include "fftw_tools.hpp"
 #include "fluid_solver_base.hpp"
-#include "spline_n1.hpp"
-#include "spline_n2.hpp"
-#include "spline_n3.hpp"
-#include "spline_n4.hpp"
-#include "spline_n5.hpp"
-#include "spline_n6.hpp"
-#include "Lagrange_polys.hpp"
-#include "interpolator.hpp"
+#include "interpolator_base.hpp"
 
 #ifndef RFFTW_INTERPOLATOR
 
@@ -67,7 +60,8 @@ class rFFTW_interpolator
 
         rFFTW_interpolator(
                 fluid_solver_base<rnumber> *FSOLVER,
-                base_polynomial_values BETA_POLYS);
+                base_polynomial_values BETA_POLYS,
+                rnumber *FIELD_DATA);
         ~rFFTW_interpolator();
 
         /* map real locations to grid coordinates */
@@ -90,7 +84,6 @@ class rFFTW_interpolator
                 const double *__restrict__ xx,
                 double *__restrict__ dest,
                 const int *deriv = NULL);
-        int read_rFFTW(void *src);
 };
 
 #endif//RFFTW_INTERPOLATOR
diff --git a/bfps/cpp/rFFTW_particles.hpp b/bfps/cpp/rFFTW_particles.hpp
index 68f8badea0359436d3aaf3f06b02543ee9ff0d89..b577da055b77f58703cbf175c8215c6178cd7600 100644
--- a/bfps/cpp/rFFTW_particles.hpp
+++ b/bfps/cpp/rFFTW_particles.hpp
@@ -29,9 +29,9 @@
 #include <iostream>
 #include <hdf5.h>
 #include "base.hpp"
+#include "particles_base.hpp"
 #include "fluid_solver_base.hpp"
 #include "rFFTW_interpolator.hpp"
-#include "particles.hpp"
 
 #ifndef RFFTW_PARTICLES
 
diff --git a/bfps/fluid_base.py b/bfps/fluid_base.py
index ba44f306bce344d73ef39c51618e51b48bf98a6c..ba0be9ed8e3660dc66c9fa0a89a2eb2b8979a99f 100644
--- a/bfps/fluid_base.py
+++ b/bfps/fluid_base.py
@@ -130,9 +130,10 @@ class fluid_particle_base(bfps.code):
                              'hsize_t dset;\n')
         for key in ['state', 'velocity', 'acceleration']:
             self.definitions += ('full_name = (std::string(name) + std::string("/{0}"));\n'.format(key) +
+                                 'if (H5Lexists(g_id, full_name.c_str(), H5P_DEFAULT))\n{\n' +
                                  'dset = H5Dopen(g_id, full_name.c_str(), H5P_DEFAULT);\n' +
                                  'grow_single_dataset(dset, niter_todo/niter_part);\n' +
-                                 'H5Dclose(dset);\n')
+                                 'H5Dclose(dset);\n}\n')
         self.definitions += ('full_name = (std::string(name) + std::string("/rhs"));\n' +
                              'if (H5Lexists(g_id, full_name.c_str(), H5P_DEFAULT))\n{\n' +
                              'dset = H5Dopen(g_id, full_name.c_str(), H5P_DEFAULT);\n' +
@@ -445,25 +446,24 @@ class fluid_particle_base(bfps.code):
                                      dtype = np.int64,
                                      compression = 'gzip')
             for s in range(self.particle_species):
-                if self.parameters['tracers{0}_integration_method'.format(s)] == 'AdamsBashforth':
-                    time_chunk = 2**20 // (8*3*
-                                           self.parameters['nparticles']*
-                                           self.parameters['tracers{0}_integration_steps'.format(s)])
-                    time_chunk = max(time_chunk, 1)
-                    ofile.create_dataset('particles/tracers{0}/rhs'.format(s),
-                                         (1,
-                                          self.parameters['tracers{0}_integration_steps'.format(s)],
-                                          self.parameters['nparticles'],
-                                          3),
-                                         maxshape = (None,
-                                                     self.parameters['tracers{0}_integration_steps'.format(s)],
-                                                     self.parameters['nparticles'],
-                                                     3),
-                                         chunks =  (time_chunk,
-                                                    self.parameters['tracers{0}_integration_steps'.format(s)],
-                                                    self.parameters['nparticles'],
-                                                    3),
-                                         dtype = np.float64)
+                time_chunk = 2**20 // (8*3*
+                                       self.parameters['nparticles']*
+                                       self.parameters['tracers{0}_integration_steps'.format(s)])
+                time_chunk = max(time_chunk, 1)
+                ofile.create_dataset('particles/tracers{0}/rhs'.format(s),
+                                     (1,
+                                      self.parameters['tracers{0}_integration_steps'.format(s)],
+                                      self.parameters['nparticles'],
+                                      3),
+                                     maxshape = (None,
+                                                 self.parameters['tracers{0}_integration_steps'.format(s)],
+                                                 self.parameters['nparticles'],
+                                                 3),
+                                     chunks =  (time_chunk,
+                                                self.parameters['tracers{0}_integration_steps'.format(s)],
+                                                self.parameters['nparticles'],
+                                                3),
+                                     dtype = np.float64)
                 time_chunk = 2**20 // (8*3*self.parameters['nparticles'])
                 time_chunk = max(time_chunk, 1)
                 ofile.create_dataset(
@@ -474,14 +474,15 @@ class fluid_particle_base(bfps.code):
                     chunks = (time_chunk, self.parameters['nparticles'], 3),
                     maxshape = (None, self.parameters['nparticles'], 3),
                     dtype = np.float64)
-                ofile.create_dataset(
-                    '/particles/tracers{0}/acceleration'.format(s),
-                    (1,
-                     self.parameters['nparticles'],
-                     3),
-                    chunks = (time_chunk, self.parameters['nparticles'], 3),
-                    maxshape = (None, self.parameters['nparticles'], 3),
-                    dtype = np.float64)
+                if self.parameters['tracers{0}_acc_on'.format(s)]:
+                    ofile.create_dataset(
+                        '/particles/tracers{0}/acceleration'.format(s),
+                        (1,
+                         self.parameters['nparticles'],
+                         3),
+                        chunks = (time_chunk, self.parameters['nparticles'], 3),
+                        maxshape = (None, self.parameters['nparticles'], 3),
+                        dtype = np.float64)
             ofile.close()
         return None
 
diff --git a/done.txt b/done.txt
index 344832ae21046d40f47702000ab8b31d0128114b..79d3a56f93a3ca8227643655bae6e46bfa4b2ee0 100644
--- a/done.txt
+++ b/done.txt
@@ -3,3 +3,10 @@ x 2015-12-23 decide on versioning system
 x 2015-12-24 move get_grid coords to interpolator                    @optimization +v1.0
 x 2015-12-25 get rid of temporal interpolation                       @optimization +v1.0
 x 2015-12-26 call interpolation only when needed                     @optimization +v1.0
+x 2015-12-26 clean up tox files, make sure all tests run             @tests        +v1.0
+x 2016-01-03 check divfree function
+x 2016-01-03 compute kMeta(t) as well
+x 2016-01-03 split library into core and extra                                       @optimization +v1.0
+x 2016-01-07 FFTW interpolator doesn't need its own field                            @optimization +v1.0 +particle_api
+x 2016-01-08 simplify tracer/field addition mechanism                                @design +v1.0 +particle_api
+x 2016-01-08 add stat choice parameter to add_particles                              @design +v1.0 +particle_api
diff --git a/setup.py b/setup.py
index 3fdf80d57f41f6620de62d62ce25c00f6603e3b4..c60f568f5a4f0baa5b14b6548f13bc3d89c2321e 100644
--- a/setup.py
+++ b/setup.py
@@ -68,9 +68,7 @@ print(VERSION)
 src_file_list = ['field_descriptor',
                  'fluid_solver_base',
                  'fluid_solver',
-                 'interpolator',
                  'rFFTW_interpolator',
-                 'particles',
                  'rFFTW_particles',
                  'fftw_tools',
                  'spline_n1',
@@ -81,7 +79,11 @@ src_file_list = ['field_descriptor',
                  'spline_n6',
                  'Lagrange_polys']
 
-header_list = ['cpp/base.hpp'] + ['cpp/' + fname + '.hpp' for fname in src_file_list]
+header_list = (['cpp/base.hpp',
+                'cpp/particles_base.hpp',
+                'cpp/interpolator_base.hpp'] +
+               ['cpp/' + fname + '.hpp'
+                for fname in src_file_list])
 
 with open('MANIFEST.in', 'w') as manifest_in_file:
     for fname in ['bfps/cpp/' + fname + '.cpp' for fname in src_file_list] + header_list:
diff --git a/tests/base.py b/tests/base.py
old mode 100755
new mode 100644
index c050245b85910bd35e2c62bf937945ba8d897e52..abe5cf861ecc35b9211b76bd32a7f43e84a82148
--- a/tests/base.py
+++ b/tests/base.py
@@ -102,29 +102,19 @@ def launch(
     c.parameters['famplitude'] = 0.2
     c.fill_up_fluid_code()
     if c.parameters['nparticles'] > 0:
-        c.add_particle_fields(
-                name = 'regular',
+        c.add_3D_rFFTW_field(name = 'rFFTW_acc')
+        c.add_interpolator(
+                name = 'spline',
                 neighbours = opt.neighbours,
                 smoothness = opt.smoothness)
-        c.add_particle_fields(kcut = 'fs->kM/2', name = 'filtered', neighbours = opt.neighbours)
         c.add_particles(
-                kcut = 'fs->kM/2',
-                integration_steps = 1,
-                fields_name = 'filtered')
-        #for integr_steps in range(1, 7):
-        #    c.add_particles(
-        #            integration_steps = integr_steps,
-        #            neighbours = opt.neighbours,
-        #            smoothness = opt.smoothness,
-        #            fields_name = 'regular')
-        for info in [(2, 'AdamsBashforth'),
-                     (3, 'AdamsBashforth'),
-                     (4, 'AdamsBashforth'),
-                     (6, 'AdamsBashforth')]:
-            c.add_particles(
-                    integration_steps = info[0],
-                    integration_method = info[1],
-                    fields_name = 'regular')
+                kcut = ['fs->kM/2', 'fs->kM/3'],
+                integration_steps = 3,
+                interpolator = 'spline')
+        c.add_particles(
+                integration_steps = [2, 3, 4, 6],
+                interpolator = 'spline',
+                acc_name = 'rFFTW_acc')
     c.finalize_code()
     c.write_src()
     c.write_par()
@@ -158,6 +148,8 @@ def launch(
 
 
 def acceleration_test(c, m = 3, species = 0):
+    if not c.parameters['tracers{0}_acc_on'.format(species)]:
+        return None
     import numpy as np
     import matplotlib.pyplot as plt
     from bfps.tools import get_fornberg_coeffs
@@ -182,13 +174,12 @@ def acceleration_test(c, m = 3, species = 0):
     pid = np.argmin(SNR(num_acc1, acc[n+1:-n-1]))
     pars = d['parameters']
     to_print = (
-            'integration={0}, steps={1}, interp={2}, neighbours={3}, '.format(
-                pars['tracers{0}_integration_method'.format(species)].value,
+            'steps={0}, interp={1}, neighbours={2}, '.format(
                 pars['tracers{0}_integration_steps'.format(species)].value,
-                pars[str(pars['tracers{0}_field'.format(species)].value) + '_type'].value,
-                pars[str(pars['tracers{0}_field'.format(species)].value) + '_neighbours'].value))
-    if 'spline' in pars['tracers{0}_field'.format(species)].value:
-        to_print += 'smoothness = {0}, '.format(pars[str(pars['tracers{0}_field'.format(species)].value) + '_smoothness'].value)
+                pars[str(pars['tracers{0}_interpolator'.format(species)].value) + '_type'].value,
+                pars[str(pars['tracers{0}_interpolator'.format(species)].value) + '_neighbours'].value))
+    if 'spline' in pars['tracers{0}_interpolator'.format(species)].value:
+        to_print += 'smoothness = {0}, '.format(pars[str(pars['tracers{0}_interpolator'.format(species)].value) + '_smoothness'].value)
     to_print += (
             'SNR d1p-vel={0:.3f}, d1v-acc={1:.3f}, d2p-acc={2:.3f}'.format(
                 np.mean(SNR(num_vel1, vel[n+1:-n-1])),
diff --git a/tests/test_convergence.py b/tests/test_convergence.py
old mode 100755
new mode 100644
index 636dea9bc9487241b62166d35ac7367272e513b3..f00e8df7e46388c461b59c8f5b34c62936344baf
--- a/tests/test_convergence.py
+++ b/tests/test_convergence.py
@@ -1,4 +1,3 @@
-#! /usr/bin/env python2
 #######################################################################
 #                                                                     #
 #  Copyright 2015 Max Planck Institute                                #
@@ -30,7 +29,7 @@ import h5py
 from mpl_toolkits.mplot3d import axes3d
 import matplotlib.pyplot as plt
 
-from test_base import *
+from base import *
 
 def convergence_test(
         opt,
@@ -224,6 +223,15 @@ def convergence_test(
     return c0, c1, c2
 
 if __name__ == '__main__':
-    opt = parser.parse_args()
+    opt = parser.parse_args(
+            ['-n', '16',
+             '--run',
+             '--initialize',
+             '--ncpu', '2',
+             '--nparticles', '1000',
+             '--niter_todo', '32',
+             '--precision', 'single',
+             '--wd', 'data/single'] +
+            sys.argv[1:])
     convergence_test(opt, launch)
 
diff --git a/tests/test_frozen_field.py b/tests/test_frozen_field.py
old mode 100755
new mode 100644
index ca42527c977f16477faeca651202f23cd3f4589c..4bf10325a348f59533ac309d8407a1eecbbeb989
--- a/tests/test_frozen_field.py
+++ b/tests/test_frozen_field.py
@@ -1,4 +1,3 @@
-#! /usr/bin/env python2
 #######################################################################
 #                                                                     #
 #  Copyright 2015 Max Planck Institute                                #
@@ -25,7 +24,7 @@
 
 
 
-from test_base import *
+from base import *
 
 class FrozenFieldParticles(bfps.NavierStokes):
     def __init__(
@@ -33,12 +32,16 @@ class FrozenFieldParticles(bfps.NavierStokes):
             name = 'FrozenFieldParticles',
             work_dir = './',
             simname = 'test',
-            fluid_precision = 'single'):
+            frozen_fields = True,
+            fluid_precision = 'single',
+            use_fftw_wisdom = False):
         super(FrozenFieldParticles, self).__init__(
                 name = name,
                 work_dir = work_dir,
                 simname = simname,
-                fluid_precision = fluid_precision)
+                fluid_precision = fluid_precision,
+                frozen_fields = frozen_fields,
+                use_fftw_wisdom = use_fftw_wisdom)
     def fill_up_fluid_code(self):
         self.fluid_includes += '#include <cstring>\n'
         self.fluid_variables += 'fluid_solver<{0}> *fs;\n'.format(self.C_dtype)
diff --git a/tests/test_interpolation.py b/tests/test_interpolation.py
old mode 100755
new mode 100644
index 928ae115370019c6b32a8720efddb77376d86a09..29755bd6a5207c322c7e442df89be28b7a996543
--- a/tests/test_interpolation.py
+++ b/tests/test_interpolation.py
@@ -1,4 +1,3 @@
-#! /usr/bin/env python2
 #######################################################################
 #                                                                     #
 #  Copyright 2015 Max Planck Institute                                #
@@ -26,35 +25,44 @@
 
 
 import numpy as np
-from test_base import *
+from base import *
 import matplotlib.pyplot as plt
 
-class test_interpolation(bfps.NavierStokes):
-    def __init__(
-            self,
-            name = 'test_interpolation',
-            work_dir = './',
-            simname = 'test'):
-        super(test_interpolation, self).__init__(
-                work_dir = work_dir,
-                simname = simname,
-                name = name,
-                frozen_fields = True)
-        return None
-
 if __name__ == '__main__':
-    opt = parser.parse_args()
-    c = test_interpolation(work_dir = opt.work_dir + '/io')
+    opt = parser.parse_args(
+            ['-n', '32',
+             '--run',
+             '--ncpu', '2',
+             '--initialize',
+             '--neighbours', '4',
+             '--smoothness', '1',
+             '--nparticles', '4225',
+             '--niter_todo', '1',
+             '--niter_stat', '1',
+             '--niter_part', '1',
+             '--niter_out', '1',
+             '--wd', 'interp'] +
+            sys.argv[1:])
+    c = bfps.NavierStokes(
+            name = 'test_interpolation',
+            use_fftw_wisdom = False,
+            frozen_fields = True)
     c.pars_from_namespace(opt)
+    c.fill_up_fluid_code()
+    c.add_particle_fields(
+            name = 'n{0}m{1}'.format(opt.neighbours, opt.smoothness),
+            neighbours = opt.neighbours,
+            smoothness = opt.smoothness)
+    c.add_particle_fields(
+            name = 'n{0}'.format(opt.neighbours),
+            neighbours = opt.neighbours,
+            interp_type = 'Lagrange')
     c.add_particles(
             integration_steps = 1,
-            neighbours = 4,
-            smoothness = 1)
+            fields_name = 'n{0}m{1}'.format(opt.neighbours, opt.smoothness))
     c.add_particles(
             integration_steps = 1,
-            neighbours = 4,
-            interp_type = 'Lagrange')
-    c.fill_up_fluid_code()
+            fields_name = 'n{0}'.format(opt.neighbours))
     c.finalize_code()
     c.write_src()
     c.write_par()
diff --git a/tests/test_io.py b/tests/test_io.py
old mode 100755
new mode 100644
index 0d8b8c055eae1deea8fbaef0644f050d8a900e3c..447cb405406a61573cc7d7b2b5942d012e6f55f6
--- a/tests/test_io.py
+++ b/tests/test_io.py
@@ -1,4 +1,3 @@
-#! /usr/bin/env python2
 #######################################################################
 #                                                                     #
 #  Copyright 2015 Max Planck Institute                                #
@@ -25,7 +24,7 @@
 
 
 
-from test_base import *
+from base import *
 
 class test_io(bfps.code):
     def __init__(
@@ -43,7 +42,12 @@ class test_io(bfps.code):
         return None
 
 if __name__ == '__main__':
-    opt = parser.parse_args()
+    opt = parser.parse_args(
+            ['-n', '32',
+             '--run',
+             '--initialize',
+             '--ncpu', '2'] +
+            sys.argv[1:])
     c = test_io(work_dir = opt.work_dir + '/io')
     c.write_src()
     c.write_par()
diff --git a/tests/test_particles.py b/tests/test_particles.py
old mode 100755
new mode 100644
index 93801df1c7263964c8bf1ae2a189a826b4cc2763..92f82b901624ad0b48cf9ba5e3661de7f5071f03
--- a/tests/test_particles.py
+++ b/tests/test_particles.py
@@ -28,7 +28,7 @@
 import numpy as np
 import matplotlib.pyplot as plt
 
-from test_base import *
+from base import *
 
 from test_frozen_field import FrozenFieldParticles
 from test_convergence import convergence_test
@@ -147,7 +147,17 @@ class err_finder:
         return errlist
 
 if __name__ == '__main__':
-    opt = parser.parse_args()
+    opt = parser.parse_args(
+            ['-n', '16',
+             '--run',
+             '--initialize',
+             '--frozen',
+             '--ncpu', '2',
+             '--nparticles', '128',
+             '--niter_todo', '16',
+             '--precision', 'single',
+             '--wd', 'data/single'] +
+            sys.argv[1:])
     if opt.precision == 'single':
         dtype = np.complex64
     elif opt.precision == 'double':
diff --git a/tests/test_plain.py b/tests/test_plain.py
old mode 100755
new mode 100644
index 8ada988ad741d9a2fe177c8e2ea09369a9e69d39..5d526cfd3412b097068f9ab1b93b2ca86523b34d
--- a/tests/test_plain.py
+++ b/tests/test_plain.py
@@ -65,6 +65,7 @@ if __name__ == '__main__':
     opt = parser.parse_args(
             ['-n', '32',
              '--run',
+             '--initialize',
              '--ncpu', '2',
              '--nparticles', '1000',
              '--niter_todo', '48',
diff --git a/tests/test_scaling.py b/tests/test_scaling.py
old mode 100755
new mode 100644
diff --git a/tests/test_time_step.py b/tests/test_time_step.py
old mode 100755
new mode 100644
index fd1662c8d812f59dd9faa9e4bcf9f60ef289350b..9a24619b94bc06d77b712018544aa663e32665c0
--- a/tests/test_time_step.py
+++ b/tests/test_time_step.py
@@ -1,4 +1,3 @@
-#! /usr/bin/env python2
 #######################################################################
 #                                                                     #
 #  Copyright 2015 Max Planck Institute                                #
@@ -30,7 +29,7 @@ import h5py
 from mpl_toolkits.mplot3d import axes3d
 import matplotlib.pyplot as plt
 
-from test_base import *
+from base import *
 
 def convergence_test(
         opt,
@@ -93,10 +92,19 @@ def convergence_test(
     a.set_xscale('log')
     a.set_yscale('log')
     a.set_xlabel('$\\|u\\|_\\infty \\frac{\\Delta t}{\\Delta x}$')
-    fig.savefig('spec_err_vs_dt_{0}.pdf'.format(opt.precision))
+    fig.savefig('vel_err_vs_dt_{0}.pdf'.format(opt.precision))
     return None
 
 if __name__ == '__main__':
-    opt = parser.parse_args()
+    opt = parser.parse_args(
+            ['-n', '32',
+             '--run',
+             '--initialize',
+             '--ncpu', '2',
+             '--nparticles', '1000',
+             '--niter_todo', '16',
+             '--precision', 'single',
+             '--wd', 'data/single'] +
+            sys.argv[1:])
     convergence_test(opt, launch)
 
diff --git a/todo.txt b/todo.txt
index 2356510f3170e2841465fa2231af6b11411fcd5b..41ab0f009a43dd95179363bdb499aab43624983c 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,16 +1,18 @@
-(B) FFTW interpolator doesn't need its own field            @optimization +v1.0
-(B) clean up tox files, make sure all tests run             @tests        +v1.0
-(B) compute z polynomials only when needed                  @optimization +v1.0
-(B) use less memory                                         @optimization
-(B) split library into core and extra                       @optimization +v1.0
-(C) clean up machine_settings mess                          @design @documentation +v2.0
-(C) code overview                                           @documentation
-(C) move stat I/O to cpp lib                                @design @HDF5
-(C) set up mechanism for adding in new PDEs                 @design +v2.0 +alternate_algorithms
-(C) test involving hydrodynamic similarity                  @tests
-(C) use HDF5 io for fields                                  @design @HDF5 +I/O
-(D) test anisotropic grids                                  @tests
-(D) test non-cubic domains                                  @tests
-(E) add u-equation algorithm for testing purposes           @tests +alternate_algorithms
-(E) pure python DNS addon: pros and cons                    @tests +alternate_algorithms
+(B) compute z polynomials only when needed                                  @optimization +v1.0
+(B) read https://www.xsede.org/documents/271087/369161/ExtScale-Koziol.pdf  @optimization @HDF5 +I/O
+(B) set up mechanism for adding in new PDEs                                 @design +v2.0 +alternate_algorithms
+(B) tweak HDF5 settings                                                     @optimization @HDF5 +I/O
+(B) use less memory                                                         @optimization
+(C) clean up machine_settings mess                                          @design @documentation +v2.0
+(C) code overview                                                           @documentation
+(C) move stat I/O to cpp lib                                                @design @HDF5
+(C) test involving hydrodynamic similarity                                  @tests
+(C) use HDF5 io for fields                                                  @design @HDF5 +I/O
+(D) generalize interpolation comparison test                                @tests
+(D) generate separate lib(s) with extra classes                             @tests +alternate_algorithms
+(D) test anisotropic grids                                                  @tests
+(D) test non-cubic domains                                                  @tests
+(D) tests should not overwrite other tests (tox_full)                       @tests
+(E) add u-equation algorithm for testing purposes                           @tests +alternate_algorithms
+(E) pure python DNS addon: pros and cons                                    @tests +alternate_algorithms
 (F) add switch to turn off simulation
diff --git a/tox_convergence.ini b/tox_convergence.ini
deleted file mode 100644
index 39a368ff0d1954cb841cbaab41e5a8d98bb68031..0000000000000000000000000000000000000000
--- a/tox_convergence.ini
+++ /dev/null
@@ -1,31 +0,0 @@
-[tox]
-envlist = py34
-[testenv]
-deps = matplotlib
-whitelist_externals =
-    echo
-    cp
-passenv = LD_LIBRARY_PATH
-changedir =
-    {envtmpdir}
-commands =
-    cp -r {toxinidir}/tests {envtmpdir}
-    python tests/test_convergence.py \
-        -n 32 \
-        --run \
-        --initialize \
-        --ncpu 2 \
-        --nparticles 1 \
-        --niter_todo 16 \
-        --precision single \
-        --wd "data/single"
-    python tests/test_convergence.py \
-        -n 32 \
-        --run \
-        --initialize \
-        --ncpu 2 \
-        --nparticles 1 \
-        --niter_todo 16 \
-        --precision double \
-        --wd "data/double"
-
diff --git a/tox_full.ini b/tox_full.ini
new file mode 100644
index 0000000000000000000000000000000000000000..d102b91ef0cb7b305d93d08d3dd899f80178a2f4
--- /dev/null
+++ b/tox_full.ini
@@ -0,0 +1,25 @@
+[tox]
+envlist = py34
+[testenv]
+sitepackages = True
+whitelist_externals =
+    echo
+    cp
+passenv = LD_LIBRARY_PATH
+changedir =
+    {envtmpdir}
+commands =
+    cp -r {toxinidir}/tests {envtmpdir}
+    python tests/test_io.py
+    python tests/test_interpolation.py
+    python tests/test_interpolation.py --precision double --wd "interp_double"
+    python tests/test_plain.py
+    python tests/test_plain.py --precision double --wd "data/double"
+    python tests/test_scaling.py
+    python tests/test_scaling.py --precision double --wd "data/double"
+    python tests/test_particles.py
+    python tests/test_particles.py --precision double --wd "data/double"
+    python tests/test_time_step.py
+    python tests/test_time_step.py --precision double --wd "data/double"
+    python tests/test_convergence.py
+    python tests/test_convergence.py --precision double --wd "data/double"
diff --git a/tox_interpolation.ini b/tox_interpolation.ini
deleted file mode 100644
index 71b94b2220f43496050b09855cf7e41b9e2da51e..0000000000000000000000000000000000000000
--- a/tox_interpolation.ini
+++ /dev/null
@@ -1,21 +0,0 @@
-[tox]
-envlist = py27
-[testenv]
-whitelist_externals =
-    echo
-    cp
-passenv = LD_LIBRARY_PATH
-changedir =
-    {envtmpdir}
-commands =
-    cp -r {toxinidir}/tests {envtmpdir}
-    python tests/test_interpolation.py \
-    -n 32 --ncpu 2 \
-    --initialize \
-    --nparticles 4225 \
-    --niter_todo 1 \
-    --niter_stat 1 \
-    --niter_part 1 \
-    --niter_out 1 \
-    --run
-
diff --git a/tox_io.ini b/tox_io.ini
deleted file mode 100644
index de8c2fc5d0bb5fa7498527ecb4b96b32ca000c69..0000000000000000000000000000000000000000
--- a/tox_io.ini
+++ /dev/null
@@ -1,14 +0,0 @@
-[tox]
-envlist = py27, py34
-[testenv]
-deps = matplotlib
-whitelist_externals =
-    echo
-    cp
-passenv = LD_LIBRARY_PATH
-changedir =
-    {envtmpdir}
-commands =
-    cp -r {toxinidir}/tests {envtmpdir}
-    python tests/test_io.py -n 32 --run --initialize --ncpu 2
-
diff --git a/tox_particles.ini b/tox_particles.ini
deleted file mode 100644
index e76f1b387753e12f9e6b6270f2959c1e1bca4ba5..0000000000000000000000000000000000000000
--- a/tox_particles.ini
+++ /dev/null
@@ -1,30 +0,0 @@
-[tox]
-envlist = py27
-[testenv]
-whitelist_externals =
-    echo
-    cp
-passenv = LD_LIBRARY_PATH
-changedir =
-    {envtmpdir}
-commands =
-    cp -r {toxinidir}/tests {envtmpdir}
-    python tests/test_particles.py \
-        -n 16 \
-        --run \
-        --initialize \
-        --ncpu 2 \
-        --nparticles 32 \
-        --niter_todo 16 \
-        --precision single \
-        --wd "data/single"
-    python tests/test_particles.py \
-        -n 16 \
-        --run \
-        --initialize \
-        --ncpu 2 \
-        --nparticles 32 \
-        --niter_todo 16 \
-        --precision double \
-        --wd "data/double"
-
diff --git a/tox_plain.ini b/tox_plain.ini
deleted file mode 100644
index 38f34b15fee4d479f34cbf805adebb61be8ec218..0000000000000000000000000000000000000000
--- a/tox_plain.ini
+++ /dev/null
@@ -1,30 +0,0 @@
-[tox]
-envlist = py34
-[testenv]
-sitepackages = True
-whitelist_externals =
-    echo
-    cp
-    g++
-passenv = LD_LIBRARY_PATH
-changedir =
-    {envtmpdir}
-commands =
-    cp -r {toxinidir}/tests {envtmpdir}
-    #python tests/test_plain.py -n 256 --run --initialize --ncpu 8 --niter_todo 8 --precision single --wd "data/single"
-    #python tests/test_plain.py \
-    #    -n 128 --run --initialize --ncpu 2 \
-    #    --nparticles 100 --niter_todo 24 \
-    #    --precision single --wd "data/single" #\
-    #    #--multiplejob
-    python tests/test_plain.py -n 32 \
-        --run --initialize --multiplejob \
-        --ncpu 2 --nparticles 16 \
-        --niter_todo 48 \
-        --precision single --wd "data/single"
-    python tests/test_plain.py -n 32 \
-        --run --initialize --multiplejob \
-        --ncpu 2 --nparticles 16 \
-        --niter_todo 48 \
-        --precision double --wd "data/double"
-
diff --git a/tox_time_step.ini b/tox_time_step.ini
deleted file mode 100644
index 87ec18d1af8b8290d18a3cd1e8c3e9b5d671bb6c..0000000000000000000000000000000000000000
--- a/tox_time_step.ini
+++ /dev/null
@@ -1,30 +0,0 @@
-[tox]
-envlist = py27
-[testenv]
-whitelist_externals =
-    echo
-    cp
-passenv = LD_LIBRARY_PATH
-changedir =
-    {envtmpdir}
-commands =
-    cp -r {toxinidir}/tests {envtmpdir}
-    python tests/test_time_step.py \
-        -n 32 \
-        --run \
-        --initialize \
-        --ncpu 2 \
-        --nparticles 32 \
-        --niter_todo 16 \
-        --precision single \
-        --wd "data/single"
-    python tests/test_time_step.py \
-        -n 32 \
-        --run \
-        --initialize \
-        --ncpu 2 \
-        --nparticles 32 \
-        --niter_todo 16 \
-        --precision double \
-        --wd "data/double"
-