diff --git a/AUTHORS b/AUTHORS
index 7cbdaffcf7813aae0488a4f284c789cf6f3e30d9..3bbcc3c251d52bfcf372e807e9e5c3d02ea30ca5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,3 +12,4 @@ Stefan Arridge		stefan.arridge@durham.ac.uk
 Josh Borrow             joshua.borrow@durham.ac.uk
 Loic Hausammann		loic.hausammann@epfl.ch
 Yves Revaz   		yves.revaz@epfl.ch
+Jacob Kegerreis         jacob.kegerreis@durham.ac.uk
diff --git a/configure.ac b/configure.ac
index b30e9c41d7323a84a1838d68bdf60d1fee6f00c7..714be8b97a4c8173d53131e25fe3212b8059b82c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -54,6 +54,19 @@ AX_COMPILER_VERSION
 #  Restrict support.
 AC_C_RESTRICT
 
+# logger
+AC_ARG_ENABLE([logger],
+	[AS_HELP_STRING([--enable-logger],
+		[enable the particle logger]
+	)],
+	[with_logger="${enableval}"],
+	[with_logger="no"]
+)
+
+if test "$with_logger" = "yes"; then
+   AC_DEFINE([WITH_LOGGER], 1, [logger enabled])
+fi
+
 # Interprocedural optimization support. Needs special handling for linking and
 # archiving as well as compilation with Intels, needs to be done before
 # libtool is configured (to use correct LD).
@@ -878,6 +891,43 @@ if test "$with_hdf5" = "yes"; then
 fi
 AM_CONDITIONAL([HAVEPARALLELHDF5],[test "$have_parallel_hdf5" = "yes"])
 
+# Check for grackle.
+have_grackle="no"
+AC_ARG_WITH([grackle],
+    [AS_HELP_STRING([--with-grackle=PATH],
+       [root directory where grackle is installed @<:@yes/no@:>@]
+    )],
+    [with_grackle="$withval"],
+    [with_grackle="no"]
+)
+if test "x$with_grackle" != "xno"; then
+   AC_PROG_FC
+   AC_FC_LIBRARY_LDFLAGS
+   if test "x$with_grackle" != "xyes" -a "x$with_grackle" != "x"; then
+      GRACKLE_LIBS="-L$with_grackle/lib -lgrackle"
+      GRACKLE_INCS="-I$with_grackle/include"
+   else
+      GRACKLE_LIBS="-lgrackle"
+      GRACKLE_INCS=""
+   fi
+
+   have_grackle="yes"
+
+   echo $GRACKLE_LIBS
+
+   AC_CHECK_LIB(
+      [grackle],
+      [initialize_chemistry_data],
+      [AC_DEFINE([HAVE_GRACKLE],1,[The GRACKLE library appears to be present.])
+        AC_DEFINE([CONFIG_BFLOAT_8],1,[Use doubles in grackle])
+      ],
+      [AC_MSG_ERROR(Cannot find grackle library!)],
+      [$GRACKLE_LIBS])
+fi
+AC_SUBST([GRACKLE_LIBS])
+AC_SUBST([GRACKLE_INCS])
+AM_CONDITIONAL([HAVEGRACKLE],[test -n "$GRACKLE_LIBS"])
+
 # Check for VELOCIraptor.
 have_velociraptor="no"
 AC_ARG_WITH([velociraptor],
@@ -910,6 +960,22 @@ fi
 AC_SUBST([VELOCIRAPTOR_LIBS])
 AM_CONDITIONAL([HAVEVELOCIRAPTOR],[test -n "$VELOCIRAPTOR_LIBS"])
 
+# Check for dummy VELOCIraptor.
+AC_ARG_ENABLE([dummy-velociraptor],
+    [AS_HELP_STRING([--enable-dummy-velociraptor],
+       [Enable dummy velociraptor compilation @<:@yes/no@:>@]
+    )],
+    [enable_dummy_velociraptor="$enableval"],
+    [enable_dummy_velociraptor="no"]
+)
+
+if test "$enable_dummy_velociraptor" = "yes"; then
+  have_velociraptor="yes"
+
+  AC_DEFINE(HAVE_VELOCIRAPTOR,1,[The VELOCIraptor library appears to be present.])
+  AC_DEFINE(HAVE_DUMMY_VELOCIRAPTOR,1,[The dummy VELOCIraptor library is present.])
+fi
+
 # Check for floating-point execeptions
 AC_CHECK_FUNC(feenableexcept, AC_DEFINE([HAVE_FE_ENABLE_EXCEPT],[1],
     [Defined if the floating-point exception can be enabled using non-standard GNU functions.]))
@@ -1305,43 +1371,6 @@ case "$with_riemann" in
       AC_MSG_ERROR([Unknown Riemann solver: $with_riemann])
    ;;
 esac
-
-# Check for grackle.
-have_grackle="no"
-AC_ARG_WITH([grackle],
-    [AS_HELP_STRING([--with-grackle=PATH],
-       [root directory where grackle is installed @<:@yes/no@:>@]
-    )],
-    [with_grackle="$withval"],
-    [with_grackle="no"]
-)
-if test "x$with_grackle" != "xno"; then
-   AC_PROG_FC
-   AC_FC_LIBRARY_LDFLAGS
-   if test "x$with_grackle" != "xyes" -a "x$with_grackle" != "x"; then
-      GRACKLE_LIBS="-L$with_grackle/lib -lgrackle"
-      GRACKLE_INCS="-I$with_grackle/include"
-   else
-      GRACKLE_LIBS="-lgrackle"
-      GRACKLE_INCS=""
-   fi
-
-   have_grackle="yes"
-
-   AC_CHECK_LIB(
-      [grackle],
-      [initialize_chemistry_data],
-      [AC_DEFINE([HAVE_GRACKLE],1,[The GRACKLE library appears to be present.])
-        AC_DEFINE([CONFIG_BFLOAT_8],1,[Use doubles in grackle])
-      ],
-      [AC_MSG_ERROR(Cannot find grackle library!)],
-      [$GRACKLE_LIBS $GRACKLE_INCS $FCLIBS]
-   )
-fi
-AC_SUBST([GRACKLE_LIBS])
-AC_SUBST([GRACKLE_INCS])
-AM_CONDITIONAL([HAVEGRACKLE],[test -n "$GRACKLE_LIBS"])
-
 #  Cooling function
 AC_ARG_WITH([cooling],
    [AS_HELP_STRING([--with-cooling=<function>],
@@ -1369,6 +1398,9 @@ case "$with_cooling" in
    const-lambda)
       AC_DEFINE([COOLING_CONST_LAMBDA], [1], [Const Lambda cooling function])
    ;;
+   compton)
+      AC_DEFINE([COOLING_COMPTON], [1], [Compton cooling off the CMB])
+   ;;
    grackle)
       AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
       AC_DEFINE([COOLING_GRACKLE_MODE], [0], [Grackle chemistry network, mode 0])
@@ -1486,7 +1518,7 @@ esac
 #  External potential
 AC_ARG_WITH([ext-potential],
    [AS_HELP_STRING([--with-ext-potential=<pot>],
-      [external potential @<:@none, point-mass, point-mass-ring, point-mass-softened, isothermal, softened-isothermal, disc-patch, sine-wave, default: none@:>@]
+      [external potential @<:@none, point-mass, point-mass-ring, point-mass-softened, isothermal, softened-isothermal, nfw, hernquist, disc-patch, sine-wave, default: none@:>@]
    )],
    [with_potential="$withval"],
    [with_potential="none"]
@@ -1501,6 +1533,12 @@ case "$with_potential" in
    isothermal)
       AC_DEFINE([EXTERNAL_POTENTIAL_ISOTHERMAL], [1], [Isothermal external potential])
    ;;
+   hernquist)
+      AC_DEFINE([EXTERNAL_POTENTIAL_HERNQUIST], [1], [Hernquist external potential])
+   ;;
+   nfw)
+      AC_DEFINE([EXTERNAL_POTENTIAL_NFW], [1], [Navarro-Frenk-White external potential])
+   ;;
    disc-patch)
       AC_DEFINE([EXTERNAL_POTENTIAL_DISC_PATCH], [1], [Disc-patch external potential])
    ;;
@@ -1556,8 +1594,9 @@ AC_CONFIG_FILES([tests/testFormat.sh], [chmod +x tests/testFormat.sh])
 # Save the compilation options
 AC_DEFINE_UNQUOTED([SWIFT_CONFIG_FLAGS],["$swift_config_flags"],[Flags passed to configure])
 
-# Make sure the latest git revision string gets included
-touch src/version.c
+# Make sure the latest git revision string gets included, when we are
+# working in a checked out repository.
+test -d ${srcdir}/.git && touch ${srcdir}/src/version.c
 
 #  Need to define this, instead of using fifth argument of AC_INIT, until
 #  2.64. Defer until now as this redefines PACKAGE_URL, which can emit a
@@ -1589,6 +1628,7 @@ AC_MSG_RESULT([
    CPU profiler         : $have_profiler
    Pthread barriers     : $have_pthread_barrier
    VELOCIraptor enabled : $have_velociraptor
+   Particle Logger      : $with_logger
 
    Hydro scheme       : $with_hydro
    Dimensionality     : $with_dimension
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index cba52250ccc37f50ed130c70d8a5039d8c786474..d2dd87257ea7da2b78bfe0503870112b830ee22c 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -761,11 +761,15 @@ WARN_LOGFILE           =
 
 INPUT                  =  @top_srcdir@ @top_srcdir@/src @top_srcdir@/tests @top_srcdir@/examples
 INPUT		       += @top_srcdir@/src/hydro/Minimal
+INPUT		       += @top_srcdir@/src/hydro/Gadget2
 INPUT		       += @top_srcdir@/src/gravity/Default
 INPUT		       += @top_srcdir@/src/stars/Default
 INPUT		       += @top_srcdir@/src/riemann
 INPUT		       += @top_srcdir@/src/potential/point_mass
 INPUT		       += @top_srcdir@/src/equation_of_state/ideal_gas
+INPUT		       += @top_srcdir@/src/cooling/const_du
+INPUT		       += @top_srcdir@/src/cooling/const_lambda
+INPUT		       += @top_srcdir@/src/cooling/Compton
 INPUT		       += @top_srcdir@/src/cooling/EAGLE
 INPUT		       += @top_srcdir@/src/chemistry/EAGLE
 
diff --git a/doc/RTD/source/CommandLineOptions/index.rst b/doc/RTD/source/CommandLineOptions/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a8ce7af082fd7b2bf3ec285b5bb0b2b5bb509401
--- /dev/null
+++ b/doc/RTD/source/CommandLineOptions/index.rst
@@ -0,0 +1,43 @@
+.. Command line options
+   Matthieu Schaller, 21st October 2018
+
+.. _cmdline-options:
+
+Command line options
+====================
+
+SWIFT requires a number of runtime options to run and get any sensible output.
+For instance, just running the ``swift`` binary will not use any SPH or gravity;
+the particles will just sit still!
+
+Below is a list of the command line options and when they should be used. The same list
+can be found by typing ``./swift -h``.
+
++ ``-a``: Pin runners using processor affinity.
++ ``-c``: Run with cosmological time integration.
++ ``-C``: Run with cooling.
++ ``-d``: Dry run. Read the parameter file, allocate memory but does not read
+  the particles from ICs and exit before the start of time integration. Allows
+  user to check validity of parameter and IC files as well as memory limits.
++ ``-D``: Always drift all particles even the ones far from active particles.
+  This emulates Gadget-[23] and GIZMO's default behaviours.
++ ``-e``: Enable floating-point exceptions (debugging mode).
++ ``-f``: {int} Overwrite the CPU frequency (Hz) to be used for time measurements.
++ ``-g``: Run with an external gravitational potential.
++ ``-G``: Run with self-gravity.
++ ``-M``: Reconstruct the multipoles every time-step.
++ ``-n``: {int} Execute a fixed number of time steps. When unset use the
+  time_end parameter to stop.
++ ``-o``: {str} Generate a default output parameter file.
++ ``-P``: {sec:par:val} Set parameter value and overwrites values read from the
+  parameters file. Can be used more than once.
++ ``-s``: Run with hydrodynamics.
++ ``-S``: Run with stars.
++ ``-t``: {int} The number of threads to use on each MPI rank. Defaults to 1 if
+  not specified.
++ ``-T``: Print timers every time-step.
++ ``-v``: [12] Increase the level of verbosity: 1, MPI-rank 0 writes, 2, All
+  MPI-ranks write.
++ ``-y``: {int} Time-step frequency at which task graphs are dumped.
++ ``-Y``: {int} Time-step frequency at which threadpool tasks are dumped.
++ ``-h``: Print a help message and exit.
diff --git a/doc/RTD/source/Cooling/index.rst b/doc/RTD/source/Cooling/index.rst
index 46a01b2a054629b7fc13f0ea190c2a5a0fdd6d9c..0ca3f62f3ca6fdff2abb95501681dc7bf4676fd2 100644
--- a/doc/RTD/source/Cooling/index.rst
+++ b/doc/RTD/source/Cooling/index.rst
@@ -45,6 +45,19 @@ to provide an HDF5 table computed by Cloudy.
 When starting a simulation without providing the different fractions, the code
 supposes an equilibrium and computes the fractions automatically.
 
+In order to compile SWIFT with Grackle, you need to provide the options ``with-grackle``
+and ``with-chemistry``.
+
+You will need a Grackle version later than 3.1. To compile it, run
+the following commands from the root directory of Grackle:
+``./configure; cd src/clib``.
+Update the variables ``LOCAL_HDF5_INSTALL`` and ``MACH_INSTALL_PREFIX`` in
+the file ``src/clib/Make.mach.linux-gnu``.
+Finish with ``make machine-linux-gnu; make && make install``.
+If you encounter any problem, you can look at the `Grackle documentation <https://grackle.readthedocs.io/en/latest/>`_
+
+You can now provide the path given for ``MACH_INSTALL_PREFIX`` to ``with-grackle``.
+
 Eagle
 ~~~~~
 
diff --git a/doc/RTD/source/ExternalPotentials/index.rst b/doc/RTD/source/ExternalPotentials/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e3138162465c200ec71d7d31b02c575e3e663416
--- /dev/null
+++ b/doc/RTD/source/ExternalPotentials/index.rst
@@ -0,0 +1,80 @@
+.. External potentials in SWIFT
+   Folkert Nobels, 25th October 2018
+   
+External Potentials 
+===================
+
+SWIFT can be run with an external potential on this page we will summarize the
+current potentials which can be run with SWIFT and how to implement your own 
+potential in SWIFT.
+
+Implemented External Potentials
+-------------------------------
+
+Currently there are several potentials implemented in SWIFT. On this page we 
+give a short overview of the potentials that are implemented in the code:
+
+1. No potential (none)
+2. Point mass potential (point-mass): classical point mass, can be placed at
+   a position with a mass.
+3. Plummer potential (point-mass-softened): in the code a softended point mass 
+   corresponds to a Plummer potential, can be placed at a position with a mass.
+4. Isothermal potential (isothermal): An isothermal potential which corresponds 
+   to a density profile which is :math:`\propto r^{-2}` and a potential which is 
+   logarithmic. This potential has as free parameters the rotation velocity 
+   and the position.
+5. Hernquist potential (hernquist): A potential that is given by the Hernquist 
+   potential: 
+   
+   :math:`\Phi(r) = - \frac{GM}{r+a}.`
+
+   The free paramters of Hernquist potential are mass, scale length,
+   and softening. The potential can be set at any position in the box.
+6. NFW potential (nfw): The most used potential to describe dark matter halos, the  
+   potential is given by:
+
+   :math:`\Phi(r) = - \frac{4\pi G \rho_0 R_s^3}{r} \ln \left( 1+ 
+   \frac{r}{R_s} \right).`
+
+   This potential has as free paramters the concentration of the DM halo, the
+   virial mass (:math:`M_{200}`) and the critical density.
+7. Sine wave (sine-wave)
+8. Point mass ring (point-mass-ring)
+9. Disc Patch (disc-patch)
+
+
+How to implement your own potential
+-----------------------------------
+
+The first step in implementing your own potential is making a directory of your
+potential in the ``src/potential`` folder and creating a file in the folder 
+called ``potential.h``.
+
+Configuring the potential 
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To get started you can copy a ``potential.h`` file from an already implemented 
+potential. In this potential the header guards (e.g. ``#IFDEF <>``) need to be 
+changed to the specific potential and the ``struct`` and 
+``potential_init_backend`` need to be  changed such that it uses your potential 
+and reads the correct potential from the parameter file during running the 
+program.
+
+Add the potential to the ``potential.h`` file in the ``src`` directory such that
+the program knows that it is possible to run with this potential.
+
+Furthermore during the configuration of the code it also needs to be clear for 
+the program that the code can be configured to run with the different 
+potentials. This means that the ``configure.ac`` file needs to be changed.
+This can be done to add an other case in the potential::
+
+  case "$with_potential" in
+     none)
+        AC_DEFINE([EXTERNAL_POTENTIAL_NONE], [1], [No external potential])
+     ;;
+     newpotential)
+        AC_DEFINE([EXTERNAL_POTENTIAL_NEWPOTENTIAL], [1], [New external potential])
+     ;;
+
+After this change it is possible to configure the code to use your new potential.
+
diff --git a/doc/RTD/source/GettingStarted/index.rst b/doc/RTD/source/GettingStarted/index.rst
index 36de8ea740490c16bc9d6b69d871290e80dc2091..2086bcfb4af0ac1b7bbc24c34caa85fa1ebec498 100644
--- a/doc/RTD/source/GettingStarted/index.rst
+++ b/doc/RTD/source/GettingStarted/index.rst
@@ -20,7 +20,6 @@ and keep on your desk.
    running_example
    runtime_options
    configuration_options
-   parameter_file
    what_about_mpi
    running_on_large_systems
    special_modes
diff --git a/doc/RTD/source/GettingStarted/running_example.rst b/doc/RTD/source/GettingStarted/running_example.rst
index 854e74cf830d58e51cf866d59a93ede6dceb57b6..7c9e333ca971ec73f3c2e25b56cc8e1381385567 100644
--- a/doc/RTD/source/GettingStarted/running_example.rst
+++ b/doc/RTD/source/GettingStarted/running_example.rst
@@ -19,14 +19,14 @@ as ``wget`` for grabbing the glass).
 
 
 This will run the 'SodShock' in 3D and produce a nice plot that shows you
-how the density has varied. Try running with GIZMO (this will take
+how the density has varied. Try running with GIZMO-MFV (this will take
 _significantly_ longer than with SPH) to see the difference. For that, you
 will need to reconfigure with the following options:
 
 .. code-block:: bash
    
    ./configure \
-   --with-hydro=gizmo \
+   --with-hydro=gizmo-mfv \
    --with-riemann-solver=hllc
 
 
diff --git a/doc/RTD/source/GettingStarted/runtime_options.rst b/doc/RTD/source/GettingStarted/runtime_options.rst
index b2ca10640d8830b9b5ecb8e117bf047af738889c..fdd2c1233cc09cc3a46c8eb2e38efb10729a2950 100644
--- a/doc/RTD/source/GettingStarted/runtime_options.rst
+++ b/doc/RTD/source/GettingStarted/runtime_options.rst
@@ -8,34 +8,5 @@ SWIFT requires a number of runtime options to run and get any sensible output.
 For instance, just running the ``swift`` binary will not use any SPH or gravity;
 the particles will just sit still!
 
-Below is a list of the runtime options and when they should be used. The same list
-can be found by typing ``./swift -h``.
+A list of available command line options can be found on the :ref:`cmdline-options` page.
 
-+ ``-a``: Pin runners using processor affinity.
-+ ``-c``: Run with cosmological time integration.
-+ ``-C``: Run with cooling.
-+ ``-d``: Dry run. Read the parameter file, allocate memory but does not read
-  the particles from ICs and exit before the start of time integration. Allows
-  user to check validity of parameter and IC files as well as memory limits.
-+ ``-D``: Always drift all particles even the ones far from active particles.
-  This emulates Gadget-[23] and GIZMO's default behaviours.
-+ ``-e``: Enable floating-point exceptions (debugging mode).
-+ ``-f``: {int} Overwrite the CPU frequency (Hz) to be used for time measurements.
-+ ``-g``: Run with an external gravitational potential.
-+ ``-G``: Run with self-gravity.
-+ ``-M``: Reconstruct the multipoles every time-step.
-+ ``-n``: {int} Execute a fixed number of time steps. When unset use the
-  time_end parameter to stop.
-+ ``-o``: {str} Generate a default output parameter file.
-+ ``-P``: {sec:par:val} Set parameter value and overwrites values read from the
-  parameters file. Can be used more than once.
-+ ``-s``: Run with hydrodynamics.
-+ ``-S``: Run with stars.
-+ ``-t``: {int} The number of threads to use on each MPI rank. Defaults to 1 if
-  not specified.
-+ ``-T``: Print timers every time-step.
-+ ``-v``: [12] Increase the level of verbosity: 1, MPI-rank 0 writes, 2, All
-  MPI-ranks write.
-+ ``-y``: {int} Time-step frequency at which task graphs are dumped.
-+ ``-Y``: {int} Time-step frequency at which threadpool tasks are dumped.
-+ ``-h``: Print a help message and exit.
diff --git a/doc/RTD/source/HydroSchemes/hopkins_sph.rst b/doc/RTD/source/HydroSchemes/hopkins_sph.rst
index bcc51e0ad96b18956f1c8e54f7bf2bf3b352c138..e4f1479230df96eabaa1fe16994960059858613b 100644
--- a/doc/RTD/source/HydroSchemes/hopkins_sph.rst
+++ b/doc/RTD/source/HydroSchemes/hopkins_sph.rst
@@ -28,3 +28,9 @@ scheme it includes a Monaghan AV scheme and a Balsara switch.
 .. code-block:: bash
    
    ./configure --with-hydro="pressure-energy"
+
+Both of the above schemes use a very simple, fixed artificial viscosity, only
+the ``SPH:viscosity_alpha`` parameter has any effect for this scheme. This will
+change the strength of the artificial viscosity throughout the simulation, and
+has a default of 0.8.
+
diff --git a/doc/RTD/source/HydroSchemes/minimal_sph.rst b/doc/RTD/source/HydroSchemes/minimal_sph.rst
index 1a16a23360aaba8b28920150af0d4f4b05c74c2f..bbcbe026b56381c007f58920f31115f9f9160d05 100644
--- a/doc/RTD/source/HydroSchemes/minimal_sph.rst
+++ b/doc/RTD/source/HydroSchemes/minimal_sph.rst
@@ -10,11 +10,17 @@ Minimal (Density-Energy) SPH
    :caption: Contents:
 
 This scheme is a textbook implementation of Density-Energy SPH, and can be used
-as a pedagogical example. It also implements a Monaghan AV scheme, like the
-GADGET-2 scheme. It uses very similar equations, but differs in implementation
-details; namely it tracks the internal energy \(u\) as the thermodynamic
-variable, rather than entropy \(A\). To use the minimal scheme, use
+as a pedagogical example. It also implements a Monaghan AV scheme with a
+Balsara switch, like the GADGET-2 scheme. It uses very similar equations, but
+differs in implementation details; namely it tracks the internal energy \(u\)
+as the thermodynamic variable, rather than entropy \(A\). To use the minimal
+scheme, use
 
 .. code-block:: bash
 
     ./configure --with-hydro="minimal"
+
+As it uses a very simple, fixed artificial viscosity, only the
+``SPH:viscosity_alpha`` parameter has any effect for this scheme. This will
+change the strength of the artificial viscosity throughout the simulation,
+and has a default of 0.8.
diff --git a/doc/RTD/source/HydroSchemes/traditional_sph.rst b/doc/RTD/source/HydroSchemes/traditional_sph.rst
index c69ea5f60644119b8590414ffe00a75246de49a6..455e8bebe516bd9be9f6df889f1ead2088ca94d2 100644
--- a/doc/RTD/source/HydroSchemes/traditional_sph.rst
+++ b/doc/RTD/source/HydroSchemes/traditional_sph.rst
@@ -15,3 +15,8 @@ a Monaghan artificial viscosity scheme and Balsara switch.
 To use this hydro scheme, you need no extra configuration options -- it is the
 default!
 
+As it uses a very simple, fixed artificial viscosity, only the
+``SPH:viscosity_alpha`` parameter has any effect for this scheme. This will
+change the strength of the artificial viscosity throughout the simulation,
+and has a default of 0.8.
+
diff --git a/doc/RTD/source/InitialConditions/index.rst b/doc/RTD/source/InitialConditions/index.rst
index 19ead066c4efb799e6587884a952baf488f794bf..00e7b98c5916a29b31d9396c317dfc2851d7389b 100644
--- a/doc/RTD/source/InitialConditions/index.rst
+++ b/doc/RTD/source/InitialConditions/index.rst
@@ -117,15 +117,6 @@ GADGET-2 based analysis programs:
 + ``Time``, time of the start of the simulation in internal units or expressed
   as a scale-factor for cosmological runs. SWIFT ignores this and reads it from
   the parameter file.
-  
-RuntimePars
-~~~~~~~~~~~
-
-In the ``/RuntimePars/``, the following attributes are required:
-
-+ ``PeriodicBoundariesOn``, a flag to tell the code whether or not you
-  have periodic boundaries switched on. Again, this is historical; it should be
-  set to 1 (default) if you have the code running in periodic mode, or 0 otherwise.
 
 
 Particle Data
@@ -145,7 +136,7 @@ individual particle type (e.g. ``/PartType0/``) that have the following *dataset
   velocities divided by ``sqrt(a)`` (see below for a fix).
 + ``ParticleIDs``, an array of length N that are unique identifying numbers for
   each particle. Note that these have to be unique to a particle, and cannot be
-  the same even between particle types. The **IDs must be >1**. 0 or negative
+  the same even between particle types. The **IDs must be >= 0**. Negative
   IDs will be rejected by the code.
 + ``Masses``, an array of length N that gives the masses of the particles.
 
@@ -176,7 +167,9 @@ as peculiar velocities divided by ``sqrt(a)``. This can be undone by swicthing
 on the parameter ``InitialConditions:cleanup_velocity_factors`` in the
 :ref:`Parameter_File_label`.
 
-     
+
+.. _ICs_units_label:
+
 Optional Components
 -------------------
 
@@ -214,8 +207,6 @@ You should have an HDF5 file with the following structure:
      Flag_Entropy_ICs=0
      NumPart_Total=[0, 1, 0, 0, 4, 5]
      NumPart_Total_HighWord=[0, 0, 0, 0, 0, 0]
-   RuntimePars/
-     PeriodicBoundariesOn=1
    Units/
      Unit current in cgs (U_I)=1.0
      Unit length in cgs (U_L)=1.0
diff --git a/doc/RTD/source/NewOption/index.rst b/doc/RTD/source/NewOption/index.rst
index a7445524017fefd99d76c80a4a1ecc646874bd7a..441cd860ed79dabad2005b39ae4549d1496ab98d 100644
--- a/doc/RTD/source/NewOption/index.rst
+++ b/doc/RTD/source/NewOption/index.rst
@@ -1,4 +1,4 @@
-.. Equation of State
+.. Adding new schemes
    Loic Hausammann, 7th April 2018
 
 .. _new_option:
diff --git a/doc/RTD/source/ParameterFiles/index.rst b/doc/RTD/source/ParameterFiles/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ae5451d71bf3e52e2b8c4bc5c202b930d665ce0a
--- /dev/null
+++ b/doc/RTD/source/ParameterFiles/index.rst
@@ -0,0 +1,378 @@
+.. Parameter Files
+   Matthieu Schaller, 21st October 2018
+
+Parameter Files
+===============
+
+File format and basic information
+---------------------------------
+
+The parameter file uses a format similar to the `YAML format
+<https://en.wikipedia.org/wiki/YAML>`_ but reduced to only the
+elements required for the SWIFT parameters. Options are given by a
+name followed by a column and the value of the parameter:
+
+.. code:: YAML
+
+   ICs:        santa_barbara.hdf5	  
+   dt_max:     1.5
+   shift:      [2., 4., 5.]
+
+Comments can be inserted anywhere and start with a hash:
+
+.. code:: YAML
+
+   # Descrption of the physics
+   viscosity_alpha:     2.0
+   dt_max:              1.5     # seconds
+
+A typical SWIFT parameter file is split into multiple sections that
+may or may not be present depending on the different configuration
+options. The sections start with a label and can contain any number of
+parameters:
+
+.. code:: YAML
+
+   Cosmology:    # Planck13
+     Omega_m:        0.307
+     Omega_lambda:   0.693
+     Omega_b:        0.0455
+     h:              0.6777
+     a_begin:        0.0078125     # z = 127
+
+The options can be integer values, floating point numbers, characters
+or strings. If SWIFT expects a number and string is given, an error
+will be raised. The code can also read an array of values:
+
+.. code:: YAML
+
+   shift:  [2., 4., 5.]
+	  
+Some options in the parameter file are optional and
+when not provided, SWIFT will run with the default value. However, if
+a compulsory parameter is missing an error will be raised at
+start-up.
+
+Finally, SWIFT outputs two YAML files at the start of a run. The first
+one ``used_parameters.yml`` contains all the parameters that were used
+for this run, **including all the optional parameters with their
+default values**. This file can be used to start an exact copy of the
+run. The second file, ``unused_parameters.yml`` contains all the
+values that were not read from the parameter file. This can be used to
+simplify the parameter file or check that nothing important was
+ignored (for instance because the code is not configured to use some
+options).
+
+The rest of this page describes all the SWIFT parameters, split by
+section. A list of all the possible parameters is kept in the file
+``examples/parameter_examples.yml``.
+
+Internal Unit System
+--------------------
+
+This section describes the units used internally by the code. This is
+the system of units in which all the equations are solved. All
+physical constants are converted to this system and if the ICs use a
+different system (see :ref:`ICs_units_label`) the particle quantities
+will be converted when read in.
+
+The system of units is described using the value of the 5 basic units
+of any system with respect to the CGS system. Instead of using a unit
+of time we use a unit of velocity as this is more intuitive. Users
+hence need to provide:
+
+* a unit of length: ``UnitLength_in_cgs``,
+* a unit of mass: ``UnitMass_in_cgs``,
+* a unit of velocity ``UnitVelocity_in_cgs``,
+* a unit of electric current ``UnitCurrent_in_cgs``,
+* a unit of temperature ``UnitTemp_in_cgs``.
+
+All these need to be expressed with respect to their cgs counter-part
+(i.e. :math:`cm`, :math:`g`, :math:`cm/s`, :math:`A` and :math:`K`). Recall
+that there are no h-factors in any of SWIFT's quantities; we, for instance,
+use :math:`cm` and not :math:`cm/h`.
+
+For instance to use the commonly adopted system of 10^10 Msun as a
+unit for mass, mega-parsec as a unit of length and km/s as a unit of
+speed, we would use:
+
+.. code:: YAML
+
+   # Common unit system for cosmo sims
+   InternalUnitSystem:
+     UnitMass_in_cgs:     1.98848e43    # 10^10 M_sun in grams
+     UnitLength_in_cgs:   3.08567758e24 # 1 Mpc in centimeters
+     UnitVelocity_in_cgs: 1e5           # 1 km/s in centimeters per second
+     UnitCurrent_in_cgs:  1             # 1 Ampere
+     UnitTemp_in_cgs:     1             # 1 Kelvin   
+	  
+Note that there are currently no variables in any of the SWIFT physics
+schemes that make use of the unit of electric current. There is also
+no incentive to use anything else than Kelvin but that makes the whole
+system consistent with any possible unit system.
+
+If one is interested in using the more humourous `FFF unit
+system <https://en.wikipedia.org/wiki/FFF_system>`_ one would use
+
+.. code:: YAML
+
+   # FFF unit system
+   InternalUnitSystem:
+     UnitMass_in_cgs:     40823.3133  # 1 Firkin (fir) in grams
+     UnitLength_in_cgs:   20116.8     # 1 Furlong (fur) in cm
+     UnitVelocity_in_cgs: 0.01663095  # 1 Furlong (fur) per Fortnight (ftn) in cm/s
+     UnitCurrent_in_cgs:  1           # 1 Ampere
+     UnitTemp_in_cgs:     1           # 1 Kelvin   
+
+The value of the physical constants in this system is left as an
+exercise for the reader [#f1]_.
+
+Cosmology
+---------
+
+When running a cosmological simulation, this section set the values of the
+cosmological model. The epanded :math:`\Lambda\rm{CDM}` parameters governing the
+background evolution of the Univese need to be specified here. These are:
+
+* The reduced Hubble constant: :math:`h`: ``h``,
+* The matter density parameter :math:`\Omega_m`: ``Omega_m``,
+* The cosmological constant density parameter :math:`\Omega_\Lambda`: ``Omega_lambda``,
+* The baryon density parameter :math:`\Omega_b`: ``Omega_b``,
+* The radiation density parameter :math:`\Omega_r`: ``Omega_r``.
+
+The last parameter can be omitted and will default to :math:`\Omega_r = 0`.
+
+This section als specifies the start and end of the simulation expressed in
+terms of scale-factors. The two parameters are:
+
+* Initial scale-factor: ``a_begin``,
+* Final scale-factor: ``a_end``.
+
+Two additional optional parameters can be used to change the equation of
+state of dark energy :math:`w(a)`. We use the evolution law :math:`w(a) =
+w_0 + w_a (1 - a)`. The two parameters in the YAML file are:
+
+* The :math:`z=0` dark energy equation of state parameter :math:`w_0`: ``w_0``
+* The dark energy equation of state evolutio parameter :math:`w_a`: ``w_a``
+
+If unspecified these parameters default to the default
+:math:`\Lambda\rm{CDM}` values of :math:`w_0 = -1` and :math:`w_a = 0`.
+
+For a Planck+13 cosmological model (ignoring radiation density as is
+commonly done) and running from :math:`z=127` to :math:`z=0`, one would hence
+use the following parameters:
+
+.. code:: YAML
+
+   Cosmology:
+     a_begin:        0.0078125     # z = 127
+     a_end:          1.0           # z = 0
+     h:              0.6777        
+     Omega_m:        0.307         
+     Omega_lambda:   0.693         
+     Omega_b:        0.0455        
+     Omega_r:        0.            # (Optional)
+     w_0:            -1.0          # (Optional)
+     w_a:            0.            # (Optional)
+
+When running a non-cosmological simulation (i.e. without the ``-c`` runtime
+flag) this section of the YAML file is entirely ignored.
+     
+Gravity
+-------
+
+The behaviour of the self-gravity solver can be modifed by the parameters
+provided in this section. The theory document puts these parameters into the
+context of the equations being solved. We give a brief overview here.
+
+* The Plummer-equivalent co-moving softening length used for all particles :math:`\epsilon_{com}`: ``comoving_softening``,
+* The Plummer-equivalent maximal physical softening length used for all particles :math:`\epsilon_{max}`: ``comoving_softening``, 
+
+At any redshift :math:`z`, the Plummer-equivalent softening length used by the
+code will be :math:`\epsilon=\min(\epsilon_{max},
+\frac{\epsilon_{com}}{z+1})`. This is expressed in internal units.
+
+* The opening angle (multipole acceptance criterion) used in the FMM :math:`\theta`: ``theta``,
+* The time-step size pre-factor :math:`\eta`: ``eta``,
+  
+The time-step of a given particle is given by :math:`\Delta t =
+\eta\sqrt{\frac{\epsilon}{|\overrightarrow{a}|}}`, where
+:math:`\overrightarrow{a}` is the particle's acceleration. Power et al. (2003) recommend using :math:`\eta=0.025`.
+The last tree-related parameter is
+
+* The tree rebuild frequency: ``rebuild_frequency``.
+
+Thqe tree rebuild frequency is an optional parameter defaulting to
+:math:`0.01`. It is used to trigger the re-construction of the tree every time a
+fraction of the particles have been integrated (kicked) forward in time.
+
+Simulations using periodic boundary conditions use additional parameters for the
+Particle-Mesh part of the calculation. The last three are optional:
+
+* The number cells along each axis of the mesh :math:`N`: ``mesh_side_length``,
+* The mesh smoothing scale in units of the mesh cell-size :math:`a_{\rm
+  smooth}`: ``a_smooth`` (default: ``1.25``),
+* The scale above which the short-range forces are assumed to be 0 (in units of
+  the mesh cell-size multiplied by :math:`a_{\rm smooth}`) :math:`r_{\rm
+  cut,max}`: ``r_cut_max`` (default: ``4.5``),
+* The scale bewlo which the short-range forces are assumed to be exactly Newtonian (in units of
+  the mesh cell-size multiplied by :math:`a_{\rm smooth}`) :math:`r_{\rm
+  cut,min}`: ``r_cut_min`` (default: ``0.1``),
+  
+For most runs, the default values can be used. Only the number of cells along
+each axis needs to be sepcified. The remaining three values are best described
+in the context of the full set of equations in the theory documents.
+  
+As a summary, here are the values used for the EAGLE :math:`100^3~{\rm Mpc}^3`
+simulation:
+
+.. code:: YAML
+	  
+   # Parameters for the self-gravity scheme for the EAGLE-100 box
+   Gravity:
+     eta:          0.025              
+     theta:        0.7                
+     comoving_softening:     0.0026994  # 0.7 proper kpc at z=2.8.
+     max_physical_softening: 0.0007     # 0.7 proper kpc
+     rebuild_frequency:      0.01       # Default optional value
+     mesh_side_length:       512       
+     a_smooth:     1.25                 # Default optional value
+     r_cut_max:    4.5                  # Default optional value
+     r_cut_min:    0.1                  # Default optional value
+
+
+      
+SPH
+---
+
+Time Integration
+----------------
+
+Physical Constants
+------------------
+
+For some idealised test it can be useful to overwrite the value of
+some physical constants; in particular the value of the gravitational
+constant. SWIFT offers an optional parameter to overwrite the value of
+:math:`G_N`. 
+
+.. code:: YAML
+
+   PhysicalConstants:
+     G:   1
+
+Note that this set :math:`G` to the specified value in the internal system
+of units. Setting a value of `1` when using the system of units (10^10 Msun,
+Mpc, km/s) will mean that :math:`G_N=1` in these units [#f2]_ instead of the
+normal value :math:`G_N=43.00927`.
+
+This option is only used for specific tests and debugging. This entire
+section of the YAML file can typically be left out. More constants may
+be handled in the same way in future versions.
+
+Snapshots
+---------
+
+Some additional specific options for the snapshot outputs are described in the
+following pages:
+
+.. toctree::
+   :maxdepth: 1
+
+   output_selection
+
+Statistics
+----------
+
+Restarts
+--------
+
+SWIFT can write check-pointing files and restart from them. The behaviour of
+this mechanism is driven by the options int he `Restarts` section of the YAML
+parameter file. All the parameters are optional but default to values that
+ensure a reasonable behaviour. 
+
+* Wether or not to enable the dump of restart files: ``enable`` (default:
+  ``1``).
+
+This parameter acts a master-switch for the check-pointing capabilities. All the
+other options require the ``enable`` parameter to be set to ``1``.
+  
+* Wether or not to save a copy of the previous set of check-pointing files:
+  ``save`` (default: ``1``),
+* Wether or not to dump a set of restart file on regular exit: ``onexit``
+  (default: ``0``),
+* The wall-clock time in hours between two sets of restart files:
+  ``delta_hours`` (default: ``6.0``).
+  
+Note that there is no buffer time added to the ``delta_hours`` value. If the
+system's batch queue run time limit is set to 6 hours, the user must specify a
+smaller value to allow for enough time to safely dump the check-point files.
+
+* The sub-directory in which to store the restart files: ``subdir`` (default:
+  ``restart``),
+* The basename of the restart files: ``basename`` (default: ``swift``)
+
+If the directory does not exist, SWIFT will create it.  When resuming a run,
+SWIFT, will look for files with the name provided in the sub-directory specified
+here. The files themselves are named ``basename_000001.rst`` where the basenme
+is replaced by the user-specified name and the 6-digits number corresponds to
+the MPI-rank. SWIFT writes one file per MPI rank. If the ``save`` option has
+been activated, the previous set of restart files will be named
+``basename_000000.rst.prev``.
+
+SWIFT can also be stopped by creating an empty file called ``stop`` in the
+directory where the code runs. This will make SWIFT dump a fresh set of restart
+file (irrespective of the specified ``delta_time`` between dumps) and exit
+cleanly. One parameter governs this behaviour:
+
+* Number of steps between two checks for the presence of a ``stop`` file:
+  ``stop_steps`` (default: ``100``).
+
+The default value is chosen such that SWIFT does not need to poll the
+file-system to often, which can take a significant amount of time on distributed
+systems. For runs where the small time-steps take a much larger amount of time,
+a smaller value is recommended to allow for a finer control over when the code
+can be stopped.
+
+Finally, SWIFT can automatically stop after a specified amount of wall-clock
+time. The code can also run a command when exiting in this fashion, which can be
+used, for instance, to interact with the batch queue system:
+
+* Maximal wall-clock run time in hours: ``max_run_time`` (default: ``24.0``),
+* Whether or not to run a command on exit: ``resubmit_on_exit`` (default:
+  ``0``),
+* The command to run on exit: ``resubmit_command`` (default: ``./resub.sh``).
+
+Note that no check is performed on the validity of the command to run. SWIFT
+simply calls ``system()`` with the user-specified command.
+
+To run SWIFT, dumping check-pointing files every 6 hours and running for 24
+hours after which a shell command will be run, one would use:
+
+.. code:: YAML
+	  
+  Restarts:
+    enable:             1          
+    save:               1          # Keep copies
+    onexit:             0          
+    subdir:             restart    # Sub-directory of the directory where SWIFT is run
+    basename:           swift      
+    delta_hours:        6.0        
+    stop_steps:         100        
+    max_run_time:       24.0       # In hours 
+    resubmit_on_exit:   1          
+    resubmit_command:   ./resub.sh 
+
+
+
+Scheduler
+---------
+
+Domain Decomposition
+--------------------
+
+.. [#f1] The thorough reader (or overly keen SWIFT tester) would find  that the speed of light is :math:`c=1.8026\times10^{12}\,\rm{fur}\,\rm{ftn}^{-1}`, Newton's contant becomes :math:`G_N=4.896735\times10^{-4}~\rm{fur}^3\,\rm{fir}^{-1}\,\rm{ftn}^{-2}` and Planck's constant turns into :math:`h=4.851453\times 10^{-34}~\rm{fur}^2\,\rm{fir}\,\rm{ftn}^{-1}`.
+
+
+.. [#f2] which would translate into a constant :math:`G_N=1.5517771\times10^{-9}~cm^{3}\,g^{-1}\,s^{-2}` if expressed in the CGS system.
diff --git a/doc/RTD/source/GettingStarted/parameter_file.rst b/doc/RTD/source/ParameterFiles/output_selection.rst
similarity index 69%
rename from doc/RTD/source/GettingStarted/parameter_file.rst
rename to doc/RTD/source/ParameterFiles/output_selection.rst
index 550040ed25ec307633d6fade81eced58ed65a254..ca905c5e613082cdf9de9db718bdd16c4a3c8951 100644
--- a/doc/RTD/source/GettingStarted/parameter_file.rst
+++ b/doc/RTD/source/ParameterFiles/output_selection.rst
@@ -1,23 +1,17 @@
 .. Parameter File
    Loic Hausammann, 1 june 2018
 
-.. _Parameter_File_label:
-
-Parameter File
-==============
-
-To run SWIFT, you will need to provide a ``yaml`` parameter file.  An example is
-given in ``examples/parameter_file.yml`` which should contain all possible
-parameters.  Each section in this file corresponds to a different option in
-SWIFT and are not always required depending on the configuration options and
-the run time parameters.
+.. _Output_list_label:
 
 Output List
 ~~~~~~~~~~~
 
-In the sections ``Snapshots`` and ``Statistics``, you can specify the options ``output_list_on`` and ``output_list``  which receive an int and a filename.
-The ``output_list_on`` enable or not the output list and ``output_list`` is the filename containing the output times.
-With the file header, you can choose between writing redshifts, scale factors or times.
+In the sections ``Snapshots`` and ``Statistics``, you can specify the
+options ``output_list_on`` and ``output_list`` which receive an int
+and a filename.  The ``output_list_on`` enable or not the output list
+and ``output_list`` is the filename containing the output times.  With
+the file header, you can choose between writing redshifts, scale
+factors or times.
 
 Example of file containing with times (in internal units)::
 
@@ -42,6 +36,8 @@ Example of file with redshift::
   10
   5
 
+.. _Output_selection_label:
+  
 Output Selection
 ~~~~~~~~~~~~~~~~
 
diff --git a/doc/RTD/source/VELOCIraptorInterface/index.rst b/doc/RTD/source/VELOCIraptorInterface/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..3ae17f04b9950d30d1484ac5117b65063b7739c6
--- /dev/null
+++ b/doc/RTD/source/VELOCIraptorInterface/index.rst
@@ -0,0 +1,24 @@
+.. VELOCIraptor Interface
+   Folkert Nobels, 8th October 2018
+
+VELOCIraptor Interface
+======================
+
+This section includes information on the VELOCIraptor interface implemented in
+SWIFT. There are mainly four subsection; the first section explains shortly 
+how VELOCIraptor works, the second subsection explains how to configure SWIFT
+with VELOCIraptor, the third subsection explains how to configure a standalone
+version of VELOCIraptor and the last subsection explains how the output format
+of VELOCIraptor works.
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   whatis
+   stfwithswift
+   stfalone
+   output
+
+
+
diff --git a/doc/RTD/source/VELOCIraptorInterface/output.rst b/doc/RTD/source/VELOCIraptorInterface/output.rst
new file mode 100644
index 0000000000000000000000000000000000000000..5a7073da17b733e9925ba885c67b4cf48ccd420f
--- /dev/null
+++ b/doc/RTD/source/VELOCIraptorInterface/output.rst
@@ -0,0 +1,294 @@
+.. VELOCIraptor output
+   Folkert Nobels 12th of October
+
+VELOCIraptor Output
+===================
+
+.. toctree::
+   :maxdepth: 2
+   :hidden:
+   :caption: Contents: 
+
+In general VELOCIraptor outputs six files per snapshot, of which 2 files are
+for unbound particles specifically.  In this part we will explain what is
+inside the different files.
+
+Catalog_groups file
+-------------------
+
+The first output file of VELOCIraptor is the ``.catalog_group`` file,
+this file contains all the information that is group specific, and does not go
+into depth of physical properties but only on numbers of particles and 
+group sizes, the interesting data in the ``.catalog_group`` files are: 
+
++ The ``group_size``: gives a list of all the halos and the number of particles
+  in the halo, this list is numbered from 0 until the number of groups minus
+  one. It is important that the groups are not ordered in any way [#order]_ 
++ The ``Num_of_groups`` or ``Total_num_of_groups``: gives the total number of
+  groups in the snapshot.
++ The ``Offset`` list: This list gives the offset off the particles. In the
+  output of VELOCIraptor there is no file which has an ID for every particle
+  and a corresponding group, rather the particles are ordered according to in
+  which group they are. So if we want to access the particles in group 0, we
+  need to look at the particles from ``Offset[0]`` until ``Offset[1]`` in the
+  ``.catalog_particles`` hdf5 file. In general this means that for group N we
+  need to look at particles ``Offset[N]`` until ``Offset[N+1]``. 
++ The ``Offset_unbound`` list: This list works exactly the same as the
+  ``Offset`` list only this list is for the gravitational unbound particles.
+
+Catalog_particles file
+----------------------
+
+The second file that is produced by VELOCIraptor is the ``.catalog_particles``
+file, this file contains mainly all the IDs of the particles and has two
+interesting parameters:
+
++ The ``Num_of_particles_in_groups`` and ``Num_of_particles_in_groups``
+  parameter: Gives the total number of particles in the file or the total 
+  number of particles that are in halos.
++ The ``Particle_IDs``: The list of particles as sorted by halo, in which halo
+  the individual particles are present can be found by using the
+  ``.catalog_group`` file and the corresponding ``Offset`` list. 
+
+Besides the ``.catalog_particles`` file, there is also a
+``.catalog_particles.unbound`` file, this file contains the same information
+but only for the unbound particles, a particle can only be present in one of
+these two lists. 
+
+Catalog_parttypes file
+----------------------
+
+The third file that is produced by VELOCIraptor is the ``.catalog_parttypes``
+file, this file contains the information what type of particle every particle
+is, it is ordered the same as the ``Particle_IDs`` in ``.catalog_particles``. 
+There are only two interesting parameters of the file which are:
+
++ The ``Num_of_particles_in_groups`` parameter: Gives the total number of
+  particles in the file which are in a halo.
++ The ``Particle_types`` list: Gives a list of particles types similar to the
+  snap shots (0 - gas, 1 - dm, 4 - stars).
+
+Besides the ``.catalog_parttypes`` file, there is also a
+``.catalog_parttypes.unbound`` file, this file contains this information for
+the unbound particles.
+
+Properties file
+---------------
+
+The Fourth file is the ``.properties`` file, this file contains many physical
+useful information of the corresponding halos. This can be divided in several
+useful groups of physical parameters, on this page we have divided the several
+variables which are present in the ``.properties`` file. This file has most 
+physical interesting parameters of the halos.
+
+Mass-Radius determination:
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``.properties`` file contains many ways to determine the size and mass 
+of the halos, in this subsection we will list several available variables in
+the output of VELOCIraptor and we list several mass and radius parameters in
+the output which are not classified as a mass-radius pair.
+
+Critical Density related:
+"""""""""""""""""""""""""
+
++ ``Mass_200crit``: The mass of a halo with an over density on average of
+  :math:`\Delta=200` based on the critical density of the Universe 
+  (:math:`M_{200}`).
++ ``R_200crit``: The :math:`R_{200}` radius of the halo based on the 
+  critical density of the Universe
+
+Mean Density related:
+"""""""""""""""""""""
+
++ ``Mass_200mean``: The mass of a halo with an over density on average of
+  :math:`\Delta=200` based on the mean density of the Universe 
+  (:math:`M_{200}`).
++ ``R_200mean``: The :math:`R_{200}` radius of the halo based on the 
+  mean density ofthe Universe.
+
+Virial properties:
+""""""""""""""""""
+
++ ``Mvir``: The virial mass of the halos.
++ ``Rvir``: The virial radius of the halo (:math:`R_{vir}`).
+
+Bryan and Norman 1998 properties:
+"""""""""""""""""""""""""""""""""
+
++ ``Mass_BN98``, The Bryan and Norman (1998) determination of the mass of the
+  halo [#BN98]_. 
++ ``R_BN98``, the Bryan and Norman (1998) corresponding radius[#BN98]_.
+
+Several Mass types:
+"""""""""""""""""""
+This is a list of masses which cannot be categorized as easy as the other 
+properties.
+
++ ``Mass_FOF``: The friends-of-friends mass of the halos.
++ ``M_gas``: The gas mass in the halo.
++ ``Mass_tot``: The total mass of the halo
++ ``M_gas_30kpc``: The gas mass within 30 kpc of the halo centre.
++ ``M_gas_500c``: The gas mass of the overdensity of 500 times the critical
+  density
++ ``M_gas_Rvmax``: The gas mass within the maximum rotation velocity.
+
+Several Radius types:
+"""""""""""""""""""""
+
++ ``R_HalfMass``: Radius of half the mass of the halo.
++ ``R_HalfMass_gas``: Radius of half the gas mass of the halo.
++ ``R_size``:
++ ``Rmax``: 
+
+Mass Structure of the Halos:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In this subsection we listed the properties of the halos that are determining 
+the mass structure of the halo, so the exact profile and the inertia tensor.
+
+NFW profile properties:
+"""""""""""""""""""""""
++ ``Xc``, ``Yc`` and ``Zc``: The x,y and z centre positions of the
+  halos.
+  
+  Centres are calculated using first all particles belonging to the
+  structure and then VELOCIraptor uses shrinking spheres to iterate to
+  a centre, stopping once the sphere contains <10% of all the
+  particles (this value can be changed to smaller amounts and there is
+  also a minimum particle number which can also be changed).
+  
++ ``Xc_gas``, ``Yc_gas``, ``Zc_gas``: The offset of the centre
+  positions of the halo based on the gas, to find the position of the
+  gas the offsets need to be added to ``Xc``, ``Yc`` and ``Zc``.
+
++ ``cNFW``: The concentration of the halo.
+
+  This is calculated using Vmax and Vvir, not using a fitted profile.
+  
++ ``VXc``, ``VYc`` and ``VZc`` are the velocities in the centre of the halo
+  [#check]_.
++ ``VXc_gas``, ``VYc_gas`` and ``VZc_gas`` are the velocities of the gas  in
+  the centre of the halo [#check]_.
+
+Intertia Tensor properties:
+"""""""""""""""""""""""""""
+
++ ``eig_ij``: Are the normalized eigenvectors of the inertia tensor.
++ The eigenvalue ratios: 
+
+  1. ``q`` is the semi-major over major; 
+  2. ``s`` is the minor over major.
+
++ ``eig_ij_gas``: Are the normalized eigenvectors of the inertia tensor for
+  only the gas particles.
++ The eigenvalue ratios for only the gas, similar to all particles:
+
+  1. ``q_gas`` is the semi-major over major for only gas; 
+  2. ``s_gas`` is the minor over major for only gas.
+
+Dynamical Structure of the Halos:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In this subsection we list several properties that determine the dynamical
+structure of the halo, like the angular momentum and the velocity dispersion
+tensor.
+
+Angular momentum and spin parameters:
+"""""""""""""""""""""""""""""""""""""
+
++ ``lambda_b`` is the bullock spin parameter, see the paper by Bullock et al.
+  (2001) [#Bullock]_. 
++ ``Lx``, ``Ly`` and ``Lz`` are the angular momentum of the halos, the 
+  calculation includes all the particle types.
++ ``Lx_gas``, ``Ly_gas`` and ``Lz_gas`` are the angular momentum for only 
+  the gas particles in the snapshot.
+
+Velocity Dispersion related:
+""""""""""""""""""""""""""""
+
++ The complete velocity dispersion tensor (:math:`\sigma_{ij}`) which has 
+  an array per component which gives the value for all the halos. In 
+  general these components are called ``veldisp_ij`` in which i and j are 
+  given by ``x``, ``y`` or ``z``. This means that there are nine 
+  components stored in the ``.properties`` file. This omits the fact 
+  that the dispersion tensor by nature is a symmetric tensor. All the 
+  components are given by: 
+  ``veldisp_xx``, ``veldisp_xy``, ``veldisp_xz``, ``veldisp_yx``, 
+  ``veldisp_yy``, ``veldisp_yz``, ``veldisp_zx``, ``veldisp_zy``, 
+  and ``veldisp_zz`` [#velodisp]_.
++ ``sigV``, the scalar velocity dispersion which corresponds with the 
+  trace of the velocity dispersion tensor 
+  (:math:`\sigma = \text{Tr}(\sigma_{ij})`).
+
+
+Energy properties of the halos:
+"""""""""""""""""""""""""""""""
+
++ ``Ekin``, the kinetic energy of the halo.
++ ``Epot``, the potential energy of the halo.
++ ``Krot``, the rotational energy of the halo.
++ ``Krot_gas``, the rotational energy of the gas in the halo.
+
+
+Halo and subhalo abstract variables:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In this subsection we list the ID convention for subhalos and halos and 
+some other abstract quantities of the halo which are not physical but 
+rather properties of the simulations.
+
+Structure types:
+""""""""""""""""
+
++ ``ID`` is the halo ID.
++ ``Structuretype`` is the parameter that indicates what kind of structure 
+  the current halo is. Halos have a structure type of ``10`` and subhalos
+  have a structure type of ``15``.
++ ``hostHaloID``, indicates the halo ID number of the host halo, in the case
+  that the halo has no parent (e.g. is the largest halo), the hostHaloID will
+  be ``-1``.
++ ``numSubStruct``, the number of substructures or subhalos in the halo.
+
+Particle types:
+"""""""""""""""
+
++ ``npart`` is the number of particles in the halo (all types of particles).
++ ``n_gas`` is the number of gas particles in the halo.
+
+Not specified parameters:
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In this section we list parameters which cannot specifically be classified 
+in a group.
+
+
+Most Bound Particle (MBP):
+""""""""""""""""""""""""""
+
++ ``ID_mbp``, the ID of the most bound particle in the halo.
++ ``Xcmbp``, ``Ycmbp`` and ``Zcmbp`` are the positions of the most bound 
+  halo particle [#check]_.
++ ``VXcmbp``, ``VYcmbp`` and ``VZcmbp`` are the velocities of the most bound
+  halo particle [#check]_.
+
+.. [#order] In most cases more massive groups appear earlier in the list, but 
+   this is not guaranteed for larger simulations. The order of the groups is 
+   more a matter of the way that VELOCIraptor searches instead of a physical 
+   reason.
+.. [#center] This is not the average positions of the halos particles, but
+   the halo position found by the VELOCIraptor algorithm. This includes a 
+   fit for all the parameters including the gas particles or other types of
+   particles.
+.. [#velodisp] In the velocity dispersion tensor ( :math:`\sigma_{ij}` )  
+   the following relations are satisfied between components:
+
+   + :math:`\sigma_{xy}=\sigma_{yx}`
+   + :math:`\sigma_{xz}=\sigma_{zx}`
+   + :math:`\sigma_{yz}=\sigma_{yz}`
+.. [#Bullock] The Bullock spin parameter is given by 
+   :math:`\lambda = \frac{J}{\sqrt{2}MVR}`, for more information see 
+   https://arxiv.org/abs/astro-ph/0011001. 
+.. [#BN98] The Bryan and Norman (1998) paper can be found here: 
+   https://arxiv.org/abs/astro-ph/9710107
+.. [#check] Needs to be checked.
diff --git a/doc/RTD/source/VELOCIraptorInterface/stfalone.rst b/doc/RTD/source/VELOCIraptorInterface/stfalone.rst
new file mode 100644
index 0000000000000000000000000000000000000000..113cff53a51e446d321f6a912222c565f2bdb38e
--- /dev/null
+++ b/doc/RTD/source/VELOCIraptorInterface/stfalone.rst
@@ -0,0 +1,92 @@
+.. VELOCIraptor stand alone 
+   Folkert Nobels 12th October 2018
+
+Stand alone VELOCIraptor configuration
+======================================
+
+
+.. toctree::    
+   :maxdepth: 2    
+   :hidden:    
+   :caption: Contents: 
+   
+Besides running VELOCIraptor on the fly when using SWIFT, it is also possible
+to run VELOCIraptor alone without using SWIFT. In this section we explain how 
+VELOCIraptor can be run stand alone without using SWIFT.
+
+Setting up VELOCIraptor
+-----------------------
+
+The first step is setting up VELOCIraptor, this requires us to download the 
+git repository as::
+  
+  git clone https://github.com/pelahi/VELOCIraptor-STF
+
+Similar to the SWIFT with VELOCIraptor configuration, we can use the 
+swift-interface branch to analyse individual snapshots. We can use this branch
+by doing::
+
+  cd VELOCIraptor-STF
+  git fetch
+  git checkout swift-interface
+
+Again we need to copy the default SWIFT config file to a other config file by
+doing::
+
+  cd stf
+  cp Makefile.config.SWIFT-template Makefile.config
+
+Similar to configuring VELOCIraptor with swift we need to change the first 20
+lines of ``Makefile.config`` to work with our compiler, but we also need to 
+change the fact that we do not use the swift-interface but the standalone 
+version of the code, so change ``SWIFTINTERFACE="on"`` to 
+``SWIFTINTERFACE="off"``.
+
+Compiling VELOCIraptor
+----------------------
+
+Compoling goes completely different as compared to the on the fly halo finder
+configuration with SWIFT. In this case we can compile the code as::
+
+  make 
+
+After this an additional folder is created in ``VELOCIraptor-stf/stf`` called
+``bin``, in which the binary files of ``stf-gas`` is present (assuming you 
+run a simulation with SPH [#nosph]_)
+
+Running VELOCIraptor on a Snapshot
+----------------------------------
+
+After the code is compile the next step is using VELOCIraptor on a single 
+snapshot of a simulation. The code has several options which can be used, which
+can be displayed by running a terminal command of an invalid letter like::
+
+  ./stf-gas -h
+
+which gives the information about the usage of the command::
+
+  USAGE:
+
+  -C <configuration file (overrides other options)> 
+  -I <input format [Gadget (Default) 1, HDF (if implemented)2, TIPSY 3, RAMSES 4, HDF 2, NCHILADA 5>
+  -i <input file> 
+  -s <number of files per output for gadget input 1 [default]>
+  -Z <number of threads used in parallel read (1)>
+  -o <output filename>
+   ===== EXTRA OPTIONS FOR GADGET INPUT ====== 
+  -g <number of extra sph/gas blocks for gadget>
+  -s <number of extra star blocks for gadget>
+  -b <number of extra bh blocks for gadget>
+   ===== EXTRA OPTIONS REQUIRED FOR RAMSES INPUT ====== 
+  -t <ramses snapnumber>
+
+After this we can run a VELOCIraptor on a snapshot as::
+  
+  ./stf-gas -i input -o output -C configfile.txt
+
+
+.. [#nosph] In the case that in the ``Makefile.config`` it is indicate that the 
+   simulation does only contain dark matter this will reflect back on the 
+   generated binary file. So ``stf-gas`` will change to ``stf`` in the case of 
+   a dark matter only simulation.
+
diff --git a/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst b/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ac8dcc5b0b3c4b6b77dbd5dc79be2613eae0edc6
--- /dev/null
+++ b/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst
@@ -0,0 +1,95 @@
+.. SWIFT with VELOCIraptor
+   Folkert Nobels 12th October 2018
+
+
+Configuring SWIFT with VELOCIraptor
+===================================
+
+.. toctree::    
+   :maxdepth: 2    
+   :hidden:    
+   :caption: Contents:
+
+In the following three paragraphs we will explain how to setup VELOCIraptor,
+how to compile it and how to compile SWIFT with VELOCIraptor. 
+
+
+Setting up VELOCIraptor
+-----------------------
+
+Before we can run SWIFT with VELOCIraptor we first need to download
+VELOCIraptor. This can be done by cloning the repository on GitHub_::
+
+  git clone https://github.com/pelahi/VELOCIraptor-STF
+
+Currently the best version that works with SWIFT is the swift-interface branch
+of VELOCIraptor, to get this branch use::
+
+  cd VELOCIraptor-STF 
+  git fetch 
+  git checkout swift-interface
+
+To get the default that works with SWIFT simply copy the SWIFT template file in
+the ``Makefile.config``::
+
+  cd stf 
+  cp Makefile.config.SWIFT-template Makefile.config
+
+Depending on your compiler you want to change the first 20 lines of your
+``Makefile.config`` to work with your compiler and whether you want to use MPI
+or not. 
+
+
+Compiling VELOCIraptor
+----------------------
+
+After we downloaded the files and made a configuration file we can compile
+VELOCIraptor as follows::
+
+  make lib 
+  make libstf
+
+After the compilation of your code, there is an additional folder created in
+the ``VELOCIraptor-stf/stf`` directory called ``lib`` this directory has the
+library of VELOCIraptor and is required to run SWIFT with
+VELOCIraptor. Note that VELOCIraptor needs a serial version of the
+HDF5 library, not a parallel build.
+
+Compiling SWIFT
+---------------
+The next part is compiling SWIFT with VELOCIraptor and assumes you already
+downloaded SWIFT from the GitLab_, this can be done by running::
+
+  ./autogen.sh 
+  ./configure --with-velociraptor=/path/to/VELOCIraptor-STF/stf/lib 
+  make 
+
+In which ``./autogen.sh`` only needs to be run once after the code is cloned
+from the GitLab_, and ``/path/to/`` is the path to the ``VELOCIraptor-STF``
+directory on your machine. In general ``./configure`` can be run with other
+options as desired. After this we can run SWIFT with VELOCIraptor, but for this
+we first need to add several lines to the yaml file of our simulation::
+
+    
+  #structure finding options
+  StructureFinding:
+  config_file_name:     stf_input_6dfof_dmonly_sub.cfg
+  basename:             ./stf
+  output_time_format:   1
+  scale_factor_first:   0.02
+  delta_time:           1.02
+
+In which we specify the ``.cfg`` file that is used by VELOCIraptor and the 
+other parameters which SWIFT needs to use. In the case of 
+the Small Cosmological Volume DMO example we can run a simulation with halo
+finder as::
+
+  cd examples/SmallCosmoVolume_DM 
+  ../swift -c -s -G -x -t 8 small_cosmo_volume_dm.yml
+
+In which there is an additional ``-x`` option which activates the VELOCIraptor
+interface.
+
+
+.. _GitHub: https://github.com/pelahi/VELOCIraptor-STF
+.. _GitLab: https://gitlab.cosma.dur.ac.uk/swift/swiftsim
diff --git a/doc/RTD/source/VELOCIraptorInterface/whatis.rst b/doc/RTD/source/VELOCIraptorInterface/whatis.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e7f067ec4723e41da0f3a95dddad8f55d9897e85
--- /dev/null
+++ b/doc/RTD/source/VELOCIraptorInterface/whatis.rst
@@ -0,0 +1,65 @@
+.. What is VELOCIraptor
+   Folkert Nobels 12th October 2018
+
+
+What is VELOCIraptor?
+=====================
+
+.. toctree::    
+   :maxdepth: 2    
+   :hidden:    
+   :caption: Contents: 
+
+In SWIFT it is possible to run a cosmological simulation and at the same time
+do on the fly halo finding at specific predefined intervals. For finding the 
+Halos SWIFT uses VELOCIraptor (Elahi, Thacker and Widrow; 2011) [#velo]_, this 
+is a C++ halo finder that can use MPI. It differs from other halo finder 
+algorithms in the sense that it uses the velocity distributions of the 
+particles in the simulations and the the positions of the particles to get
+a better estimate of which particles are part of a specific halo and 
+whether there are substructures in halos. 
+
+The Algorithm
+-------------
+
+The VELOCIraptor algorithm consist basically off the following steps [#ref]_:
+
+1. A kd-tree is constructed based on the maximization of the Shannon-entropy,
+   this means that every level in the kd-tree an equal number of particles 
+   are distributed between the 8 lower nodes. This is based on their position
+   and their corresponding density, this results in more equal density 
+   distributed nodes. This is also the implicit step in the algorithm that 
+   takes into account the absolute positions of the particles.
+2. The next part is calculating the the centre of mass velocity and the 
+   velocity distribution for every individual node in the kd-tree. 
+3. Than the algorithm estimates the background velocity density function for
+   every particle based on the cell of the particle and the six nearest
+   neighbour cells. This prevents the background velocity density function 
+   to be over sensitive for variations between different cells due to dominant
+   halo features in the velocity density function. 
+4. After this the algorithm searches for the nearest velocity neighbours 
+   (:math:`N_v`) from a set of nearest position neighbours (:math:`N_x>N_v`).
+   The position neighbours do not need to be in the cell of the particles, in
+   general the set of nearest position neighbours is substantially larger than
+   the nearest velocity neighbours, the default is set as :math:`N_x=32 N_v`.
+5. The individual local velocity density function is calculated for every 
+   particle.
+6. The fractional difference is calculated between the local velocity density 
+   function and the background velocity density function.
+7. Based on the calculated ratio outliers are picked and the outliers are  
+   grouped together in halos and subhalos.
+  
+
+
+.. Every halo finder has limitations, the limitations of VELOCIraptor are:
+
+.. 1. The algorithm is mostly sensitive to substructures that are on the tail
+   of the Gaussian velocity density function, this means that VELOCIraptor
+   is most sensitive for subhalos which are cold (slow ratating) but have 
+   a large bulk velocity
+
+
+.. _Velociraptor: http://adsabs.harvard.edu/abs/2011MNRAS.418..320E
+.. [#velo] For technical information regarding VELOCIraptor see: Velociraptor_
+.. [#ref] This part is based on the explanation given in the Elahi, Thacker and
+   Widrow (2011) paper (Velociraptor_)
diff --git a/doc/RTD/source/index.rst b/doc/RTD/source/index.rst
index 05c15c5a081cb03ae4e53c26bf6fe4e8dd78dfd5..d148398c1bd77eafbce5e0037457b34efddb4eca 100644
--- a/doc/RTD/source/index.rst
+++ b/doc/RTD/source/index.rst
@@ -15,9 +15,13 @@ difference is the parameter file that will need to be adapted for SWIFT.
    :maxdepth: 2
 
    GettingStarted/index
+   CommandLineOptions/index
+   ParameterFiles/index
    InitialConditions/index
    HydroSchemes/index
    Cooling/index
    EquationOfState/index
+   ExternalPotentials/index
    NewOption/index
    Task/index
+   VELOCIraptorInterface/index
diff --git a/examples/AgoraDisk/agora_disk.yml b/examples/AgoraDisk/agora_disk.yml
index 7368700d8a2a5ca8de7d677e1da78be51d669835..92f2532b3132c0f6314b7697f0b9b65f1afedb3b 100644
--- a/examples/AgoraDisk/agora_disk.yml
+++ b/examples/AgoraDisk/agora_disk.yml
@@ -39,20 +39,18 @@ Gravity:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
-  minimal_temperature:   10      # (internal units)
+  minimal_temperature:   10.      # Kelvin
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./agora_disk.hdf5     # The file to read
-  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
-  shift:    [674.1175, 674.1175, 674.1175]   # (Optional) A shift to apply to all particles read from the ICs (in internal units).
+  periodic:   0                     # Non-periodic BCs
+  cleanup_h_factors: 1              # Remove the h-factors inherited from Gadget
+  shift:    [674.1175, 674.1175, 674.1175]   # Centre the box
 
 # Dimensionless pre-factor for the time-step condition
 LambdaCooling:
-  lambda_cgs:                  1.0e-22    # Cooling rate (in cgs units)
-  minimum_temperature:         1.0e2      # Minimal temperature (Kelvin)
-  mean_molecular_weight:       0.59       # Mean molecular weight
-  hydrogen_mass_abundance:     0.75       # Hydrogen mass abundance (dimensionless)
+  lambda_nH2_cgs:              1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3])
   cooling_tstep_mult:          1.0        # Dimensionless pre-factor for the time-step condition
 
 # Cooling with Grackle 2.0
diff --git a/examples/AgoraDisk/getIC.sh b/examples/AgoraDisk/getIC.sh
old mode 100644
new mode 100755
index 620a751bedaf6c646119247270fad6dd3f740fde..c234b52b943ccb8d6dededed7d0f5070cd9fe5b2
--- a/examples/AgoraDisk/getIC.sh
+++ b/examples/AgoraDisk/getIC.sh
@@ -6,4 +6,4 @@ if [ "$#" -ne 1 ]; then
     exit
 fi
 
-wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/$1
+wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/$1.hdf5
diff --git a/examples/AgoraDisk/getSolution.sh b/examples/AgoraDisk/getSolution.sh
old mode 100644
new mode 100755
diff --git a/examples/AgoraDisk/run.sh b/examples/AgoraDisk/run.sh
old mode 100644
new mode 100755
diff --git a/examples/ConstantCosmoVolume/constant_volume.yml b/examples/ConstantCosmoVolume/constant_volume.yml
index db5b3de536b5b71229a207a3fdcfab8480718ef3..ebfcc4ffd72121571fa1a69f900985917b440c65 100644
--- a/examples/ConstantCosmoVolume/constant_volume.yml
+++ b/examples/ConstantCosmoVolume/constant_volume.yml
@@ -39,7 +39,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./constantBox.hdf5
-
+  periodic:   1
+  
 Scheduler:
   max_top_level_cells: 8
   cell_split_size:     50
diff --git a/examples/CoolingBox/coolingBox.yml b/examples/CoolingBox/coolingBox.yml
index 2bd2f19f6d78388ae638521f590255d410bc8697..1a06168cef739f33301a113cf1247b52cd3e2129 100644
--- a/examples/CoolingBox/coolingBox.yml
+++ b/examples/CoolingBox/coolingBox.yml
@@ -27,17 +27,16 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature: 100.       # Kelvin
   
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./coolingBox.hdf5     # The file to read
-
+  periodic:   1
+  
 # Dimensionless pre-factor for the time-step condition
 LambdaCooling:
-  lambda_cgs:                  1.0e-22    # Cooling rate (in cgs units)
-  minimum_temperature:         1.0e4      # Minimal temperature (Kelvin)
-  mean_molecular_weight:       0.59       # Mean molecular weight
-  hydrogen_mass_abundance:     0.75       # Hydrogen mass abundance (dimensionless)
+  lambda_nH2_cgs:              1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3])
   cooling_tstep_mult:          1.0        # Dimensionless pre-factor for the time-step condition
 
 # Cooling with Grackle 2.0
diff --git a/examples/CoolingHalo/cooling_halo.yml b/examples/CoolingHalo/cooling_halo.yml
index 68c3478b717261698ac175835fc246e134e3a6a7..3d6e44ae3efdb4ad0687f61d904d87d55bb2837b 100644
--- a/examples/CoolingHalo/cooling_halo.yml
+++ b/examples/CoolingHalo/cooling_halo.yml
@@ -27,11 +27,13 @@ Snapshots:
 SPH:
   resolution_eta:        1.2349   # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
-
+  minimal_temperature:   1e4      # Kelvin
+  
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  CoolingHalo.hdf5       # The file to read
- 
+  periodic:   1
+  
 # External potential parameters
 IsothermalPotential:
   vrot:            200.     # rotation speed of isothermal potential in internal units
@@ -40,8 +42,5 @@ IsothermalPotential:
 
 # Cooling parameters
 LambdaCooling:
-  lambda_cgs:                  1.0e-22    # Cooling rate (in cgs units)
-  minimum_temperature:         1.0e4  # Minimal temperature (Kelvin)
-  mean_molecular_weight:       0.59   # Mean molecular weight
-  hydrogen_mass_abundance:     0.75   # Hydrogen mass abundance (dimensionless)
+  lambda_nH2_cgs:              1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3])
   cooling_tstep_mult:          1.0    # Dimensionless pre-factor for the time-step condition
diff --git a/examples/CoolingHalo/makeIC.py b/examples/CoolingHalo/makeIC.py
index 3ec1be6f7b5e568ebe8e0fefe508ef8287edb29c..046e5d619f047f8c6d40eab5a5cfce2e3a02074d 100644
--- a/examples/CoolingHalo/makeIC.py
+++ b/examples/CoolingHalo/makeIC.py
@@ -91,10 +91,6 @@ grp.attrs["Unit current in cgs (U_I)"] = 1.
 grp.attrs["Unit temperature in cgs (U_T)"] = 1.
 
 
-# Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 # set seed for random number
 np.random.seed(1234)
 
diff --git a/examples/CoolingHalo/makeIC_random_box.py b/examples/CoolingHalo/makeIC_random_box.py
index 4295cb135233f2d5a59405b44e6d8e9c80a1f6c0..be8f2f172e5b7aef385f0974445e44068021c99d 100644
--- a/examples/CoolingHalo/makeIC_random_box.py
+++ b/examples/CoolingHalo/makeIC_random_box.py
@@ -102,10 +102,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 3
 
-# Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 # set seed for random number
 np.random.seed(1234)
 
diff --git a/examples/CoolingHaloWithSpin/cooling_halo.yml b/examples/CoolingHaloWithSpin/cooling_halo.yml
index f6e9fe3b124631fc2d5336db8a7ffb18f7b34a95..1b29e1376e47ad32beacaf9bfb5408b8ff4d3191 100644
--- a/examples/CoolingHaloWithSpin/cooling_halo.yml
+++ b/examples/CoolingHaloWithSpin/cooling_halo.yml
@@ -27,11 +27,13 @@ Snapshots:
 SPH:
   resolution_eta:        1.2349   # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:   1e4      # Kelvin
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  CoolingHalo.hdf5       # The file to read
- 
+  periodic:   1
+  
 # External potential parameters
 IsothermalPotential:
   vrot:            200.   # Rotation speed of isothermal potential in internal units
@@ -40,8 +42,5 @@ IsothermalPotential:
 
 # Cooling parameters
 LambdaCooling:
-  lambda_cgs:                  1.0e-22   # Cooling rate (in cgs units)
-  minimum_temperature:         1.0e4  # Minimal temperature (Kelvin)
-  mean_molecular_weight:       0.59   # Mean molecular weight
-  hydrogen_mass_abundance:     0.75   # Hydrogen mass abundance (dimensionless)
+  lambda_nH2_cgs:              1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3])
   cooling_tstep_mult:          0.1    # Dimensionless pre-factor for the time-step condition
diff --git a/examples/CoolingHaloWithSpin/makeIC.py b/examples/CoolingHaloWithSpin/makeIC.py
index 2cf3127c743f61756b3ff6c4a7738c83d185f9cd..9a839bfd01594fd1d1c899d43223d0ebce12a72f 100644
--- a/examples/CoolingHaloWithSpin/makeIC.py
+++ b/examples/CoolingHaloWithSpin/makeIC.py
@@ -92,11 +92,6 @@ grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velo
 grp.attrs["Unit current in cgs (U_I)"] = 1.
 grp.attrs["Unit temperature in cgs (U_T)"] = 1.
 
-
-# Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 # set seed for random number
 np.random.seed(1234)
 
diff --git a/examples/CoolingHaloWithSpin/makeIC_random_box.py b/examples/CoolingHaloWithSpin/makeIC_random_box.py
index 4295cb135233f2d5a59405b44e6d8e9c80a1f6c0..be8f2f172e5b7aef385f0974445e44068021c99d 100644
--- a/examples/CoolingHaloWithSpin/makeIC_random_box.py
+++ b/examples/CoolingHaloWithSpin/makeIC_random_box.py
@@ -102,10 +102,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 3
 
-# Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 # set seed for random number
 np.random.seed(1234)
 
diff --git a/examples/DiscPatch/GravityOnly/disc-patch.yml b/examples/DiscPatch/GravityOnly/disc-patch.yml
index 4ec061add978bec82c267660cc343cf0bfa8f4c6..bcc7d1a3decfb36201b60349eedb5d214e61f9a6 100644
--- a/examples/DiscPatch/GravityOnly/disc-patch.yml
+++ b/examples/DiscPatch/GravityOnly/disc-patch.yml
@@ -34,7 +34,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  Disc-Patch.hdf5       # The file to read
-
+  periodic:   1
+  
 # External potential parameters
 DiscPatchPotential:
   surface_density: 10.
diff --git a/examples/DiscPatch/GravityOnly/makeIC.py b/examples/DiscPatch/GravityOnly/makeIC.py
index 5f9650f44277cf858021c9b628d68134c47a19b7..3abf4f87fc6b6f78ed1814be08ca0d8e39359a26 100644
--- a/examples/DiscPatch/GravityOnly/makeIC.py
+++ b/examples/DiscPatch/GravityOnly/makeIC.py
@@ -111,10 +111,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 # set seed for random number
 numpy.random.seed(1234)
 
diff --git a/examples/DiscPatch/HydroStatic/disc-patch-icc.yml b/examples/DiscPatch/HydroStatic/disc-patch-icc.yml
index 983a7dcc103135ab4db61d6ea77701532226c101..aee54057cf2c5b9d178abac5599d9e4133652362 100644
--- a/examples/DiscPatch/HydroStatic/disc-patch-icc.yml
+++ b/examples/DiscPatch/HydroStatic/disc-patch-icc.yml
@@ -37,7 +37,8 @@ EoS:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  Disc-Patch.hdf5       # The file to read
-
+  periodic:   1
+  
 # External potential parameters
 DiscPatchPotential:
   surface_density: 10.
diff --git a/examples/DiscPatch/HydroStatic/disc-patch.yml b/examples/DiscPatch/HydroStatic/disc-patch.yml
index 422e1cf910202e8f6dc0a9395fc7e36ce80443ed..8651ac09dbc4c4a97f0915ce7df6c678837b2f45 100644
--- a/examples/DiscPatch/HydroStatic/disc-patch.yml
+++ b/examples/DiscPatch/HydroStatic/disc-patch.yml
@@ -34,7 +34,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  Disc-Patch-dynamic.hdf5       # The file to read
-
+  periodic:   1
+  
 # External potential parameters
 DiscPatchPotential:
   surface_density: 10.
diff --git a/examples/DiscPatch/HydroStatic/makeIC.py b/examples/DiscPatch/HydroStatic/makeIC.py
index 8b4c55560c34e7bdb538f2b4732369216f91a087..dd50a821a2eb376c0785afd849a3ea575e349703 100644
--- a/examples/DiscPatch/HydroStatic/makeIC.py
+++ b/examples/DiscPatch/HydroStatic/makeIC.py
@@ -182,10 +182,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 # write gas particles
 grp0   = file.create_group("/PartType0")
 
diff --git a/examples/DiscPatch/HydroStatic_1D/disc-patch-icc.yml b/examples/DiscPatch/HydroStatic_1D/disc-patch-icc.yml
index 450689034f4ae782cc74bf01dac93e723e5d2ce2..ea5d2e24eb93c64e21f37a8c137603b22885392c 100644
--- a/examples/DiscPatch/HydroStatic_1D/disc-patch-icc.yml
+++ b/examples/DiscPatch/HydroStatic_1D/disc-patch-icc.yml
@@ -34,7 +34,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  Disc-Patch.hdf5       # The file to read
-
+  periodic:   1
+  
 # External potential parameters
 DiscPatchPotential:
   surface_density: 10.
diff --git a/examples/DiscPatch/HydroStatic_1D/makeIC.py b/examples/DiscPatch/HydroStatic_1D/makeIC.py
index 983a550a3442c6470611792081a5884d38023a6a..b193c85e50d3526b8518cac06b9b00c3071c383a 100644
--- a/examples/DiscPatch/HydroStatic_1D/makeIC.py
+++ b/examples/DiscPatch/HydroStatic_1D/makeIC.py
@@ -168,10 +168,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 1
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 # write gas particles
 grp0   = file.create_group("/PartType0")
 
diff --git a/examples/DwarfGalaxy/README b/examples/DwarfGalaxy/README
new file mode 100644
index 0000000000000000000000000000000000000000..7a9167694a24c088997316180233b28b9126f298
--- /dev/null
+++ b/examples/DwarfGalaxy/README
@@ -0,0 +1,7 @@
+This example is a galaxy extracted from the example "ZoomIn". It allows
+to test SWIFT on a smaller problem. See the README in "ZoomIn" for more
+information.
+
+
+MD5 check-sum of the ICS: 
+ae2af84d88f30011b6a8af3f37d140cf  dwarf_galaxy.hdf5
\ No newline at end of file
diff --git a/examples/DwarfGalaxy/dwarf_galaxy.yml b/examples/DwarfGalaxy/dwarf_galaxy.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0d815a99c42249bcbbdaf21dbaa34a55f61731aa
--- /dev/null
+++ b/examples/DwarfGalaxy/dwarf_galaxy.yml
@@ -0,0 +1,72 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.98848e43    # 10^10 M_sun in grams
+  UnitLength_in_cgs:   3.08567758e21 # kpc in centimeters
+  UnitVelocity_in_cgs: 1e5           # km/s in centimeters per second
+  UnitCurrent_in_cgs:  1             # Amperes
+  UnitTemp_in_cgs:     1             # Kelvin
+
+# Structure finding options
+StructureFinding:
+  config_file_name:     stf_input.cfg # Name of the STF config file.
+  basename:             ./stf         # Common part of the name of output files.
+  output_time_format:   0             # Specifies the frequency format of structure finding. 0 for simulation steps (delta_step) and 1 for simulation time intervals (delta_time).
+  scale_factor_first:   0.92          # Scale-factor of the first snaphot (cosmological run)
+  time_first:           0.01        # Time of the first structure finding output (in internal units).
+  delta_step:           1000          # Time difference between consecutive structure finding outputs (in internal units) in simulation steps.
+  delta_time:           1.10          # Time difference between consecutive structure finding outputs (in internal units) in simulation time intervals.
+
+# Cosmological parameters
+Cosmology:
+  h:              0.673        # Reduced Hubble constant
+  a_begin:        0.9873046739     # Initial scale-factor of the simulation
+  a_end:          1.0           # Final scale factor of the simulation
+  Omega_m:        0.315         # Matter density parameter
+  Omega_lambda:   0.685         # Dark-energy density parameter
+  Omega_b:        0.0486        # Baryon density parameter
+  
+Scheduler:
+  max_top_level_cells:    8
+  cell_split_size:           400       # (Optional) Maximal number of particles per cell (this is the default value).
+  
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   1.  # The end time of the simulation (in internal units).
+  dt_min:     1e-10 # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            dwarf_galaxy # Common part of the name of output files
+  time_first:          0.  # Time of the first output (non-cosmological run) (in internal units)
+  delta_time:          0.02  # Time difference between consecutive outputs (in internal units)
+  compression:         1
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  scale_factor_first:  0.987345 # Scale-factor of the first stat dump (cosmological run)
+  time_first:          0.01 # Time of the first stat dump (non-cosmological run) (in internal units)
+  delta_time:          1.05 # Time between statistics output
+
+# Parameters for the self-gravity scheme
+Gravity:
+  eta:                    0.025    # Constant dimensionless multiplier for time integration.
+  theta:                  0.7      # Opening angle (Multipole acceptance criterion)
+  comoving_softening:     0.05 # Comoving softening length (in internal units).
+  max_physical_softening: 0.01    # Physical softening length (in internal units).
+  mesh_side_length:       16
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:   100      # (internal units)
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./dwarf_galaxy.hdf5     # The file to read
+  periodic:   1
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
diff --git a/examples/DwarfGalaxy/getIC.sh b/examples/DwarfGalaxy/getIC.sh
new file mode 100755
index 0000000000000000000000000000000000000000..92f4cd3939845d57a61683e95135163b8639371f
--- /dev/null
+++ b/examples/DwarfGalaxy/getIC.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget https://obswww.unige.ch/~lhausamm/swift/IC/DwarfGalaxy/dwarf_galaxy.hdf5
diff --git a/examples/DwarfGalaxy/run.sh b/examples/DwarfGalaxy/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4cc87880215a37c9eac59b19e584f4887cba2c38
--- /dev/null
+++ b/examples/DwarfGalaxy/run.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e dwarf_galaxy.hdf5 ]
+then
+    echo "Fetching initial conditions for the dwarf galaxy example..."
+    ./getIC.sh
+fi
+
+../swift -b -G -s -S -t 8 dwarf_galaxy.yml 2>&1 | tee output.log
+
diff --git a/examples/EAGLE_100/eagle_100.yml b/examples/EAGLE_100/eagle_100.yml
index dc2b814e0a3c116edd6c8dcac3c20b21a96e5927..3bcda091bdac5b740f3568de9c0814cc84c3b846 100644
--- a/examples/EAGLE_100/eagle_100.yml
+++ b/examples/EAGLE_100/eagle_100.yml
@@ -55,6 +55,7 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_100.hdf5   # The file to read
+  periodic:   1
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
 
diff --git a/examples/EAGLE_12/eagle_12.yml b/examples/EAGLE_12/eagle_12.yml
index 6d143c3f00dc63eb8c2198b1b3f88944e7575249..aa42d2d00db776e114a25dc52d4207b6dad8f4ff 100644
--- a/examples/EAGLE_12/eagle_12.yml
+++ b/examples/EAGLE_12/eagle_12.yml
@@ -63,6 +63,7 @@ FOF:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_12.hdf5    # The file to read
+  periodic:   1
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
 
diff --git a/examples/EAGLE_25/eagle_25.yml b/examples/EAGLE_25/eagle_25.yml
index 79a35421bc0dd082d3807234077bd76ba2d5e208..c5b2b9ec49135e818dd6bd97843ef7876554b41f 100644
--- a/examples/EAGLE_25/eagle_25.yml
+++ b/examples/EAGLE_25/eagle_25.yml
@@ -72,6 +72,7 @@ FOF:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_25.hdf5    # The file to read
+  periodic:   1
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
 
diff --git a/examples/EAGLE_50/eagle_50.yml b/examples/EAGLE_50/eagle_50.yml
index f4bca303fe8919dbc25f3f7e926e0ec0f26df860..51f607eadd60d10a585641ab54769aa012931e5f 100644
--- a/examples/EAGLE_50/eagle_50.yml
+++ b/examples/EAGLE_50/eagle_50.yml
@@ -65,5 +65,6 @@ FOF:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_50.hdf5     # The file to read
+  periodic:   1
   cleanup_h_factors: 1                # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1         # Remove the sqrt(a) factor in the velocities inherited from Gadget
diff --git a/examples/EAGLE_6/eagle_6.yml b/examples/EAGLE_6/eagle_6.yml
index 3b20475f7aeb08c5e7c47fd5fa7094b40d83ef39..c69e5a24d8d4812f84ba88c17ca5ed11bc6b9bb6 100644
--- a/examples/EAGLE_6/eagle_6.yml
+++ b/examples/EAGLE_6/eagle_6.yml
@@ -76,5 +76,6 @@ FOF:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_6.hdf5     # The file to read
+  periodic:   1
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
diff --git a/examples/EAGLE_DMO_100/eagle_100.yml b/examples/EAGLE_DMO_100/eagle_100.yml
index f04c32c8d08b5548c2c710cf8782b39a59c3821e..5a3066195647b79eeb6a6d67d037d15ce8370c39 100644
--- a/examples/EAGLE_DMO_100/eagle_100.yml
+++ b/examples/EAGLE_DMO_100/eagle_100.yml
@@ -49,6 +49,7 @@ Gravity:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  EAGLE_DMO_ICs_100.hdf5
+  periodic:   1
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
 
diff --git a/examples/EAGLE_DMO_12/eagle_12.yml b/examples/EAGLE_DMO_12/eagle_12.yml
index 2354216a5b0dcefe139d6e39699b4c67035a4173..0660d98e87adfae62a2d795efec7ad6509cc1354 100644
--- a/examples/EAGLE_DMO_12/eagle_12.yml
+++ b/examples/EAGLE_DMO_12/eagle_12.yml
@@ -51,6 +51,7 @@ Gravity:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  EAGLE_DMO_ICs_12.hdf5
+  periodic:   1
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
 
diff --git a/examples/EAGLE_DMO_25/eagle_25.yml b/examples/EAGLE_DMO_25/eagle_25.yml
index b02f9742a597687d2742b7c2d9eddf836258b06a..558c68ffaad204ebbe1d5781f945f0d95108d227 100644
--- a/examples/EAGLE_DMO_25/eagle_25.yml
+++ b/examples/EAGLE_DMO_25/eagle_25.yml
@@ -50,6 +50,7 @@ Gravity:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  EAGLE_DMO_ICs_25.hdf5
+  periodic:   1
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
 
diff --git a/examples/EAGLE_DMO_50/eagle_50.yml b/examples/EAGLE_DMO_50/eagle_50.yml
index 97299df063cd1f611f59a56ccd9b091b1217bef3..3cab2b1dc869b5187cf647caa7893281b783591a 100644
--- a/examples/EAGLE_DMO_50/eagle_50.yml
+++ b/examples/EAGLE_DMO_50/eagle_50.yml
@@ -49,6 +49,7 @@ Gravity:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  EAGLE_DMO_ICs_50.hdf5
+  periodic:   1
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
 
diff --git a/examples/EvrardCollapse_3D/evrard.yml b/examples/EvrardCollapse_3D/evrard.yml
index f9a4e69f72e6bb19b818cb985ef92122b1a10b2a..c14f9151b5a4ba6af60307a689d5b2530068deb3 100644
--- a/examples/EvrardCollapse_3D/evrard.yml
+++ b/examples/EvrardCollapse_3D/evrard.yml
@@ -39,6 +39,7 @@ Gravity:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./evrard.hdf5       # The file to read
-
+  periodic:   0
+  
 PhysicalConstants:
   G: 1.
diff --git a/examples/EvrardCollapse_3D/makeIC.py b/examples/EvrardCollapse_3D/makeIC.py
index f4d3c4c5bf7f91e5f79cfcd4e9ae23388932144e..29c4acd69ebf0638edf1273efc0f411766aebb6d 100644
--- a/examples/EvrardCollapse_3D/makeIC.py
+++ b/examples/EvrardCollapse_3D/makeIC.py
@@ -86,10 +86,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 0
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/ExternalPointMass/externalPointMass.yml b/examples/ExternalPointMass/externalPointMass.yml
index de05a9ff3c10afa7871ebeafbf4d8d272056d39f..c9b1ef34d618eddfc2ba410785deb4919ed1b835 100644
--- a/examples/ExternalPointMass/externalPointMass.yml
+++ b/examples/ExternalPointMass/externalPointMass.yml
@@ -31,11 +31,13 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  PointMass.hdf5        # The file to read
+  periodic:   0
   shift:      [50.,50.,50.]         # A shift to apply to all particles read from the ICs (in internal units).
 
 # External potential parameters
 PointMassPotential:
   position:        [50.,50.,50.]    # location of external point mass in internal units
+  useabspos:       1        # Position is absolute
   mass:            1e10     # mass of external point mass in internal units
   timestep_mult:   0.03     # controls time step
 
diff --git a/examples/ExternalPointMass/makeIC.py b/examples/ExternalPointMass/makeIC.py
index fdc5b1fd67ffcbd85beae3a9d6d1274d3d48c279..7a9e2e1fd555e4823957721e3c7bf53da9eff48d 100644
--- a/examples/ExternalPointMass/makeIC.py
+++ b/examples/ExternalPointMass/makeIC.py
@@ -79,9 +79,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
 
 #Units
 grp = file.create_group("/Units")
diff --git a/examples/Gradients/gradientsCartesian.yml b/examples/Gradients/gradientsCartesian.yml
index b2131bdd4d3a9242d30ff0f32b7bf3395cb433a8..0264e9ced8652f45feeba79573d3143e6b0086bb 100644
--- a/examples/Gradients/gradientsCartesian.yml
+++ b/examples/Gradients/gradientsCartesian.yml
@@ -31,4 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./Gradients_cartesian.hdf5       # The file to read
-
+  periodic:   1
diff --git a/examples/Gradients/gradientsRandom.yml b/examples/Gradients/gradientsRandom.yml
index 57ae849898bf8ccd63ccd7a5d685f9690403403d..1c6fcc1d077e0fd260b42e7de77490d58fb5aea9 100644
--- a/examples/Gradients/gradientsRandom.yml
+++ b/examples/Gradients/gradientsRandom.yml
@@ -31,4 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./Gradients_random.hdf5       # The file to read
-
+  periodic:   1
diff --git a/examples/Gradients/makeICs.py b/examples/Gradients/makeICs.py
index 38d035d2ad2dd3dd6daacfd6f58d824e9daf6742..be70a9e614e8bc32db0c0979c42ab892ef7d068f 100644
--- a/examples/Gradients/makeICs.py
+++ b/examples/Gradients/makeICs.py
@@ -26,7 +26,6 @@ import sys
 # reconstruction
 
 # Parameters
-periodic= 1      # 1 For periodic box
 gamma = 5./3.     # Gas adiabatic index
 gridtype = "cartesian"
 if len(sys.argv) > 1:
@@ -153,10 +152,6 @@ grp.attrs["NumFilesPerSnapshot"] = 1
 grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 #Particle group
 grp = file.create_group("/PartType0")
 ds = grp.create_dataset('Coordinates', (npart, 3), 'd')
diff --git a/examples/Gravity_glass/makeIC.py b/examples/Gravity_glass/makeIC.py
index 1a3fde9e2868c8881923fa61d1c308bca0f2f095..f573c79b19a5e3655d4f55f761ef20a6468342de 100644
--- a/examples/Gravity_glass/makeIC.py
+++ b/examples/Gravity_glass/makeIC.py
@@ -53,10 +53,6 @@ grp.attrs["MassTable"] = [0.0, mass, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/Gravity_glass/uniform_DM_box.yml b/examples/Gravity_glass/uniform_DM_box.yml
index 8f3ef6f025afb1a92320eeb702b50e8bf4befce6..00a5864cdb6ff0897501248437b3cc00be0f7acf 100644
--- a/examples/Gravity_glass/uniform_DM_box.yml
+++ b/examples/Gravity_glass/uniform_DM_box.yml
@@ -42,3 +42,4 @@ Statistics:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./uniform_DM_box.hdf5
+  periodic:   1
\ No newline at end of file
diff --git a/examples/GreshoVortex_2D/gresho.yml b/examples/GreshoVortex_2D/gresho.yml
index df941450196a7de6cd1471e1d258756ca8c36fb1..2006bb451179ce646ec2cc41cb3aa5603489dc29 100644
--- a/examples/GreshoVortex_2D/gresho.yml
+++ b/examples/GreshoVortex_2D/gresho.yml
@@ -34,3 +34,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./greshoVortex.hdf5     # The file to read
+  periodic:   1
\ No newline at end of file
diff --git a/examples/GreshoVortex_2D/makeIC.py b/examples/GreshoVortex_2D/makeIC.py
index 4f4ec3407b04971882fbf3d7d7479e74bf56c762..4fb382925e41a1d00463b369bc8d95c4bc6b0aa1 100644
--- a/examples/GreshoVortex_2D/makeIC.py
+++ b/examples/GreshoVortex_2D/makeIC.py
@@ -89,10 +89,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = fileOutput.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = fileOutput.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/GreshoVortex_3D/gresho.yml b/examples/GreshoVortex_3D/gresho.yml
index 113c03b9bd0e411bf04f29c70937ac7fab3708f3..a95a0eae3255b87337fc838f1eabe5469a724a09 100644
--- a/examples/GreshoVortex_3D/gresho.yml
+++ b/examples/GreshoVortex_3D/gresho.yml
@@ -35,3 +35,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./greshoVortex.hdf5     # The file to read
+  periodic:   1
\ No newline at end of file
diff --git a/examples/GreshoVortex_3D/makeIC.py b/examples/GreshoVortex_3D/makeIC.py
index cba2158016bc86f58b6e89f83cbfb473798e1cf7..03f99df1082928bd57779ff2c0e7e85f112b4f1f 100644
--- a/examples/GreshoVortex_3D/makeIC.py
+++ b/examples/GreshoVortex_3D/makeIC.py
@@ -90,10 +90,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = fileOutput.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = fileOutput.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/Hernquist_circularorbit/hernquistcirc.yml b/examples/Hernquist_circularorbit/hernquistcirc.yml
new file mode 100755
index 0000000000000000000000000000000000000000..5e81d180003283ecb74209b19e1ff3db8097b08f
--- /dev/null
+++ b/examples/Hernquist_circularorbit/hernquistcirc.yml
@@ -0,0 +1,38 @@
+# Define the system of units to use internally.
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.988e+33    # Grams
+  UnitLength_in_cgs:   3.086e+21 # Centimeters
+  UnitVelocity_in_cgs: 1e5       # Centimeters per second
+  UnitCurrent_in_cgs:  1         # Amperes
+  UnitTemp_in_cgs:     1         # Kelvin
+
+# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.)
+TimeIntegration:
+  time_begin:          0.      # The starting time of the simulation (in internal units).
+  time_end:            2.0     # The end time of the simulation (in internal units).
+  dt_min:              1e-10    # The minimal time-step size of the simulation (in internal units).
+  dt_max:              1e0    # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            output  # Common part of the name of output files
+  time_first:          0.      # Time of the first output (in internal units)
+  delta_time:          1e-3    # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e0    # Time between statistics output
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:          circularorbitshernquist.hdf5 # The file to read
+  periodic:           0
+
+# Hernquist potential parameters
+HernquistPotential:
+  useabspos:       0        # 0 -> positions based on centre, 1 -> absolute positions 
+  position:        [0.,0.,0.]    # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units)
+  mass:            2e12     # Mass of the Hernquist potential
+  scalelength:     10.0     # Scale length of the potential
+  timestep_mult:   0.005     # Dimensionless pre-factor for the time-step condition
+  epsilon:         0.1      # Softening size (internal units)
diff --git a/examples/Hernquist_circularorbit/makeIC.py b/examples/Hernquist_circularorbit/makeIC.py
new file mode 100755
index 0000000000000000000000000000000000000000..474450f0e23704bfc43730872a978107f28704e9
--- /dev/null
+++ b/examples/Hernquist_circularorbit/makeIC.py
@@ -0,0 +1,81 @@
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+from galpy.potential import NFWPotential
+from galpy.orbit import Orbit
+from galpy.util import bovy_conversion
+import numpy as np
+import matplotlib.pyplot as plt
+from astropy import units
+import h5py as h5
+
+C = 8.0
+M_200 = 2.0
+N_PARTICLES = 3
+print("Initial conditions written to 'test_nfw.hdf5'")
+
+pos = np.zeros((3, 3))
+pos[0, 2] = 50.0
+pos[1, 2] = 10.0
+pos[2, 2] = 2.0
+pos = pos + 500.0
+vel = np.zeros((3, 3))
+vel[0, 1] = 348.0
+vel[1, 1] = 466.9
+vel[2, 1] = 348.0
+ids = np.array([1.0, 2.0, 3.0])
+mass = np.array([1.0, 1.0, 1.0])
+
+# File
+file = h5.File("circularorbitshernquist.hdf5", "w")
+
+# Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 3.086e21
+grp.attrs["Unit mass in cgs (U_M)"] = 1.988e33
+grp.attrs["Unit time in cgs (U_t)"] = 3.086e16
+grp.attrs["Unit current in cgs (U_I)"] = 1.0
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.0
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = 1000.0
+grp.attrs["NumPart_Total"] = [0, N_PARTICLES, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [0, N_PARTICLES, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+# Particle group
+grp1 = file.create_group("/PartType1")
+ds = grp1.create_dataset("Velocities", (N_PARTICLES, 3), "f", data=vel)
+
+ds = grp1.create_dataset("Masses", (N_PARTICLES,), "f", data=mass)
+
+ds = grp1.create_dataset("ParticleIDs", (N_PARTICLES,), "L", data=ids)
+
+ds = grp1.create_dataset("Coordinates", (N_PARTICLES, 3), "d", data=pos)
+
+file.close()
diff --git a/examples/Hernquist_circularorbit/plotprog.py b/examples/Hernquist_circularorbit/plotprog.py
new file mode 100755
index 0000000000000000000000000000000000000000..849ae4e365e0712faac7436a9b01a4e51c9794f0
--- /dev/null
+++ b/examples/Hernquist_circularorbit/plotprog.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+import numpy as np
+import h5py
+import matplotlib.pyplot as plt
+from scipy.integrate import odeint
+
+t = np.linspace(0, 40, 1e5)
+y0 = [0, 10]
+a = 30.0
+G = 4.300927e-06
+M = 1e15
+GM = G * M
+
+
+lengthrun = 2001
+numbpar = 3
+
+radius = np.zeros((numbpar, lengthrun))
+xx = np.zeros((numbpar, lengthrun))
+yy = np.zeros((numbpar, lengthrun))
+zz = np.zeros((numbpar, lengthrun))
+time = np.zeros(lengthrun)
+for i in range(0, lengthrun):
+    Data = h5py.File("output_%04d.hdf5" % i, "r")
+    header = Data["Header"]
+    time[i] = header.attrs["Time"]
+    particles = Data["PartType1"]
+    positions = particles["Coordinates"]
+    xx[:, i] = positions[:, 0] - 500.0
+    yy[:, i] = positions[:, 1] - 500.0
+    zz[:, i] = positions[:, 2] - 500.0
+
+col = ["b", "r", "c", "y", "k"]
+print(np.shape(xx), np.shape(yy), np.shape(zz))
+
+for i in range(0, numbpar):
+    plt.plot(xx[i, :], yy[i, :], col[i])
+
+plt.ylabel("y (kpc)")
+plt.xlabel("x (kpc)")
+plt.savefig("xyplot.png")
+plt.close()
+
+
+for i in range(0, numbpar):
+    plt.plot(xx[i, :], zz[i, :], col[i])
+
+plt.ylabel("z (kpc)")
+plt.xlabel("x (kpc)")
+plt.savefig("xzplot.png")
+plt.close()
+
+for i in range(0, numbpar):
+    plt.plot(yy[i, :], zz[i, :], col[i])
+
+plt.ylabel("z (kpc)")
+plt.xlabel("y (kpc)")
+plt.savefig("yzplot.png")
+plt.close()
diff --git a/examples/Hernquist_circularorbit/run.sh b/examples/Hernquist_circularorbit/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a2fedd0914edece43995d20776e048fd85e33963
--- /dev/null
+++ b/examples/Hernquist_circularorbit/run.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+if [ ! -e circularorbitshernquist.hdf5 ]
+then 
+    echo "Generate initial conditions for circular orbits"
+    if command -v python3 &>/dev/null; then
+        python3 makeIC.py
+    else 
+        python makeIC.py
+    fi
+
+fi
+
+# self gravity G, external potential g, hydro s, threads t and high verbosity v
+../swift -g -t 6 hernquistcirc.yml 2>&1 | tee output.log
+
+
+echo "Save plots of the circular orbits"
+if command -v python3 &>/dev/null; then
+    python3 plotprog.py
+else 
+    python plotprog.py
+fi
diff --git a/examples/Hernquist_radialinfall/README b/examples/Hernquist_radialinfall/README
new file mode 100644
index 0000000000000000000000000000000000000000..be22a1a11a5b1e0538723781607aa374644a4e0f
--- /dev/null
+++ b/examples/Hernquist_radialinfall/README
@@ -0,0 +1,3 @@
+This example generates 5 particles at radii of 10, 20, 30, 40 and 50 kpc
+without velocitiy and follows the evolution of these particles in an Hernquist
+potential as they are free falling.
diff --git a/examples/Hernquist_radialinfall/hernquist.yml b/examples/Hernquist_radialinfall/hernquist.yml
new file mode 100644
index 0000000000000000000000000000000000000000..adea54ed9a33ee889b39bb519c8098917b33ef9f
--- /dev/null
+++ b/examples/Hernquist_radialinfall/hernquist.yml
@@ -0,0 +1,39 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.98848e33    # M_sun
+  UnitLength_in_cgs:   3.08567758e21 # kpc
+  UnitVelocity_in_cgs: 1e5           # km/s
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   40.    # The end time of the simulation (in internal units).
+  dt_min:     9e-10  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-3 # Time between statistics output
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            hernquist # Common part of the name of output files
+  time_first:          0.         # Time of the first output (in internal units)
+  delta_time:          0.02       # Time difference between consecutive outputs (in internal units)
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  Hernquist.hdf5       # The file to read
+  periodic:   1
+  shift:      [200.,200.,200.]      # Shift all particles to be in the potential
+ 
+# External potential parameters
+HernquistPotential:
+  useabspos:       0          # Whether to use absolute position (1) or relative potential to centre of box (0)
+  position:        [0.,0.,0.]
+  mass:            1e9
+  scalelength:     1.0
+  timestep_mult:   0.01      # controls time step
+  epsilon:         2.0         # No softening at the centre of the halo
diff --git a/examples/Hernquist_radialinfall/makeIC.py b/examples/Hernquist_radialinfall/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..567e15a95302bc8848c1d026b82dc5be54c7a0c6
--- /dev/null
+++ b/examples/Hernquist_radialinfall/makeIC.py
@@ -0,0 +1,167 @@
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import h5py
+import sys
+import numpy
+import math
+import random
+import numpy as np
+
+# Generates N particles in a spherical distribution centred on [0,0,0], to be moved in an isothermal potential
+# usage: python makeIC.py 1000 0 : generate 1000 particles on circular orbits
+#        python makeIC.py 1000 1 : generate 1000 particles with Lz/L uniform in [0,1]
+# all particles move in the xy plane, and start at y=0
+
+# physical constants in cgs
+NEWTON_GRAVITY_CGS = 6.67408e-8
+SOLAR_MASS_IN_CGS = 1.98848e33
+PARSEC_IN_CGS = 3.08567758e18
+YEAR_IN_CGS = 3.15569252e7
+
+# choice of units
+const_unit_length_in_cgs = 1000 * PARSEC_IN_CGS
+const_unit_mass_in_cgs = SOLAR_MASS_IN_CGS
+const_unit_velocity_in_cgs = 1e5
+
+
+# Properties of the Hernquist potential
+Mass = 1e15
+scaleLength = 30.0  # kpc
+
+
+# derived units
+const_unit_time_in_cgs = const_unit_length_in_cgs / const_unit_velocity_in_cgs
+const_G = (
+    NEWTON_GRAVITY_CGS
+    * const_unit_mass_in_cgs
+    * const_unit_time_in_cgs
+    * const_unit_time_in_cgs
+    / (const_unit_length_in_cgs * const_unit_length_in_cgs * const_unit_length_in_cgs)
+)
+print("G=", const_G)
+
+
+def hernquistcircvel(r, M, a):
+    """ Function that calculates the circular velocity in a 
+    Hernquist potential.
+    @param r: radius from centre of potential
+    @param M: mass of the Hernquist potential
+    @param a: Scale length of the potential
+    @return: circular velocity
+    """
+    return (const_G * M * r) ** 0.5 / (r + a)
+
+
+# Parameters
+periodic = 1  # 1 For periodic box
+boxSize = 400.0  #  [kpc]
+Radius = 100.0  # maximum radius of particles [kpc]
+G = const_G
+
+N = 5
+L = N ** (1.0 / 3.0)
+
+fileName = "Hernquist.hdf5"
+
+
+# ---------------------------------------------------
+numPart = N
+mass = 1
+
+# --------------------------------------------------
+
+# File
+file = h5py.File(fileName, "w")
+
+# Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs
+grp.attrs["Unit time in cgs (U_t)"] = (
+    const_unit_length_in_cgs / const_unit_velocity_in_cgs
+)
+grp.attrs["Unit current in cgs (U_I)"] = 1.0
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.0
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] = [0, numPart, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [0, numPart, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# set seed for random number
+numpy.random.seed(1234)
+
+# Particle group
+grp1 = file.create_group("/PartType1")
+# generate particle positions
+# radius = Radius * (numpy.random.rand(N))**(1./3.) + 10.
+radius = np.zeros(N)
+radius[0] = 10
+radius[1] = 20
+radius[2] = 30
+radius[3] = 40
+radius[4] = 50
+# this part is not even used:
+# ctheta = -1. + 2 * numpy.random.rand(N)
+# stheta = numpy.sqrt(1.-ctheta**2)
+# phi    =  2 * math.pi * numpy.random.rand(N)
+# end
+r = numpy.zeros((numPart, 3))
+r[:, 0] = radius
+
+# import matplotlib.pyplot as plt
+# plt.plot(r[:,0],'.')
+# plt.show()
+
+# print('Mass = ', Mass)
+# print('radius = ', radius)
+# print('scaleLength = ',scaleLength)
+#
+v = numpy.zeros((numPart, 3))
+# v[:,0] = hernquistcircvel(radius,Mass,scaleLength)
+omega = v[:, 0] / radius
+period = 2.0 * math.pi / omega
+print("period = minimum = ", min(period), " maximum = ", max(period))
+print("Circular velocity = minimum =", min(v[:, 0]), " maximum = ", max(v[:, 0]))
+
+omegav = omega
+
+v[:, 0] = -omegav * r[:, 1]
+v[:, 1] = omegav * r[:, 0]
+
+ds = grp1.create_dataset("Velocities", (numPart, 3), "f", data=v)
+
+m = numpy.full((numPart,), mass, dtype="f")
+ds = grp1.create_dataset("Masses", (numPart,), "f", data=m)
+
+ids = 1 + numpy.linspace(0, numPart, numPart, endpoint=False)
+ds = grp1.create_dataset("ParticleIDs", (numPart,), "L", data=ids)
+
+ds = grp1.create_dataset("Coordinates", (numPart, 3), "d", data=r)
+
+
+file.close()
diff --git a/examples/Hernquist_radialinfall/plotprog.py b/examples/Hernquist_radialinfall/plotprog.py
new file mode 100755
index 0000000000000000000000000000000000000000..d8de00a6b694bb33bf96ef7065c972aa6bb3f6cb
--- /dev/null
+++ b/examples/Hernquist_radialinfall/plotprog.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+import numpy as np
+import h5py
+import matplotlib.pyplot as plt
+from scipy.integrate import odeint
+
+
+lengthrun = 2001
+numbpar = 5
+
+radius = np.zeros((numbpar, lengthrun))
+time = np.zeros(lengthrun)
+for i in range(0, lengthrun):
+    Data = h5py.File("hernquist_%04d.hdf5" % i, "r")
+    header = Data["Header"]
+    time[i] = header.attrs["Time"]
+    particles = Data["PartType1"]
+    positions = particles["Coordinates"]
+    radius[:, i] = positions[:, 0] - 200.0
+
+col = ["b", "r", "c", "y", "k"]
+
+for i in range(0, numbpar):
+    plt.plot(time, radius[i, :], col[i])
+    plt.axhline(np.max(radius[i, :]), color=col[i], linestyle="--")
+    plt.axhline(-np.max(radius[i, :]), color=col[i], linestyle="--")
+
+
+plt.ylabel("Radial distance (kpc)")
+plt.xlabel("Simulation time (internal units)")
+plt.savefig("radial_infall.png")
+plt.close()
diff --git a/examples/Hernquist_radialinfall/run.sh b/examples/Hernquist_radialinfall/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b2bcd99f9e7b0e571cffcd18b0fc29e50cad8b06
--- /dev/null
+++ b/examples/Hernquist_radialinfall/run.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+if [ ! -e Hernquist.hdf5 ]
+then
+    echo "Generate initial conditions for radial orbits"
+    if command -v python3 &>/dev/null; then
+        python3 makeIC.py 
+    else 
+        python makeIC.py
+    fi
+fi
+
+rm -rf hernquist_*.hdf5
+../swift -g -t 1 hernquist.yml 2>&1 | tee output.log
+
+
+
+echo "Make plots of the radially free falling particles" 
+if command -v python3 &>/dev/null; then
+    python3 plotprog.py 
+else 
+    python plotprog.py
+fi
diff --git a/examples/HydrostaticHalo/hydrostatic.yml b/examples/HydrostaticHalo/hydrostatic.yml
index 0cc11d0d8708b518b8b0b3a8df1374b6a5ead7e2..874d6344cf5787bb310b6a1b730acb3455a8b6a6 100644
--- a/examples/HydrostaticHalo/hydrostatic.yml
+++ b/examples/HydrostaticHalo/hydrostatic.yml
@@ -31,7 +31,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  Hydrostatic.hdf5       # The file to read
- 
+  periodic:   1
+  
 # External potential parameters
 IsothermalPotential:
   vrot:            200.     # rotation speed of isothermal potential in internal units
diff --git a/examples/HydrostaticHalo/makeIC.py b/examples/HydrostaticHalo/makeIC.py
index d5081ac84473edc87857c6872278b4d0ca6389b1..b8a4036b77c430866f700047fd06bf2c8de490e7 100644
--- a/examples/HydrostaticHalo/makeIC.py
+++ b/examples/HydrostaticHalo/makeIC.py
@@ -91,10 +91,6 @@ grp.attrs["Unit current in cgs (U_I)"] = 1.
 grp.attrs["Unit temperature in cgs (U_T)"] = 1.
 
 
-# Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 # set seed for random number
 np.random.seed(1234)
 
diff --git a/examples/InteractingBlastWaves_1D/interactingBlastWaves.yml b/examples/InteractingBlastWaves_1D/interactingBlastWaves.yml
index e845599730828fd7b9880ae9aca11420ba50026c..c4960dfa2c07b6b08cd6559b1de49f27b518bf94 100644
--- a/examples/InteractingBlastWaves_1D/interactingBlastWaves.yml
+++ b/examples/InteractingBlastWaves_1D/interactingBlastWaves.yml
@@ -31,3 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./interactingBlastWaves.hdf5          # The file to read
+  periodic:   1
diff --git a/examples/InteractingBlastWaves_1D/makeIC.py b/examples/InteractingBlastWaves_1D/makeIC.py
index bed0e20c833ccbe54ed571b954cad03ab93f4c0c..3a47bf7c42e1359dc1a9aa151e360ad0f93d2d32 100644
--- a/examples/InteractingBlastWaves_1D/makeIC.py
+++ b/examples/InteractingBlastWaves_1D/makeIC.py
@@ -62,10 +62,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 1
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/IsothermalPotential/energy_plot.py b/examples/IsothermalPotential/energy_plot.py
index dab30715fbdaa0393f62c764ba552bbe4106325d..d157e4233cae2221f23d37f6bdf0c30a2486f972 100644
--- a/examples/IsothermalPotential/energy_plot.py
+++ b/examples/IsothermalPotential/energy_plot.py
@@ -86,7 +86,7 @@ for i in range(402):
 
     time_snap[i] = f["Header"].attrs["Time"]
     E_kin_snap[i] = np.sum(0.5 * mass * (vel_x[:]**2 + vel_y[:]**2 + vel_z[:]**2))
-    E_pot_snap[i] = np.sum(-mass * Vrot**2 *  log(r))
+    E_pot_snap[i] = np.sum(mass * Vrot**2 *  log(r))
     E_tot_snap[i] = E_kin_snap[i] + E_pot_snap[i]
     Lz_snap[i] = np.sum(Lz)
 
diff --git a/examples/IsothermalPotential/isothermal.yml b/examples/IsothermalPotential/isothermal.yml
index 5f626ff72e979ad0f3d404e01002be6b6018c758..4f8d98a1f7615659ddb3c922b149fc2db04415c6 100644
--- a/examples/IsothermalPotential/isothermal.yml
+++ b/examples/IsothermalPotential/isothermal.yml
@@ -26,10 +26,13 @@ Snapshots:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  Isothermal.hdf5       # The file to read
+  periodic:   1
   shift:      [200.,200.,200.]      # Shift all particles to be in the potential
  
 # External potential parameters
 IsothermalPotential:
+  useabspos:       0          # Whether to use absolute position (1) or relative potential to centre of box (0)
+  position:        [0.,0.,0.]
   vrot:            200.       # rotation speed of isothermal potential in internal units
   timestep_mult:   0.01       # controls time step
   epsilon:         0.         # No softening at the centre of the halo
diff --git a/examples/IsothermalPotential/makeIC.py b/examples/IsothermalPotential/makeIC.py
index eab16d21e6a4abd077dc0f4a015a4577427a3591..ebcbb6dda11f1a2d88dfcfb717578f114f3512e9 100644
--- a/examples/IsothermalPotential/makeIC.py
+++ b/examples/IsothermalPotential/makeIC.py
@@ -97,10 +97,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 # set seed for random number
 numpy.random.seed(1234)
 
diff --git a/examples/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml b/examples/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml
index 380dc2ab3a530e89b952aa41f425e50709d73ee9..e5a46cca1aa0c8972a5427126d2ce57a26d1b262 100644
--- a/examples/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml
+++ b/examples/KelvinHelmholtzGrowthRate_2D/kelvinHelmholtzGrowthRate.yml
@@ -31,3 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./kelvinHelmholtzGrowthRate.hdf5     # The file to read
+  periodic:   1
diff --git a/examples/KelvinHelmholtzGrowthRate_2D/makeIC.py b/examples/KelvinHelmholtzGrowthRate_2D/makeIC.py
index f21d0c0abf9b15f8253f627bcb1da43ae276fb35..25ef65fd758e0dd97d45732a2da6d2aa19f793bc 100644
--- a/examples/KelvinHelmholtzGrowthRate_2D/makeIC.py
+++ b/examples/KelvinHelmholtzGrowthRate_2D/makeIC.py
@@ -76,10 +76,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = fileOutput.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = fileOutput.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py b/examples/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py
index 5029165a6a328b6c706d37b632b14cbcd51501d0..55cd17823a1101164191c89810029370dee21e26 100644
--- a/examples/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py
+++ b/examples/KelvinHelmholtzGrowthRate_2D/makeIC_regular.py
@@ -82,10 +82,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = fileOutput.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = fileOutput.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml
index e39c01645b766ae585558452683dc8e1bdf425a8..f5f7157f7d3252e8fe256b7bfc4ba83cb09ef03e 100644
--- a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml
+++ b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml
@@ -32,3 +32,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./kelvinHelmholtzGrowthRate.hdf5     # The file to read
+  periodic:   1
diff --git a/examples/KelvinHelmholtzGrowthRate_3D/makeIC.py b/examples/KelvinHelmholtzGrowthRate_3D/makeIC.py
index a9bc20559b9fbb5da400ba5de2563cd715f473d5..d28f3617214193eca6159a7220263d36500dd1aa 100644
--- a/examples/KelvinHelmholtzGrowthRate_3D/makeIC.py
+++ b/examples/KelvinHelmholtzGrowthRate_3D/makeIC.py
@@ -76,10 +76,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = fileOutput.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = fileOutput.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py b/examples/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py
index aa7dd8f214f8ece1c1d142bf02bd653cd35f9973..51ab694f387d380c83a0b646696fd23111b3f98c 100644
--- a/examples/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py
+++ b/examples/KelvinHelmholtzGrowthRate_3D/makeIC_regular.py
@@ -84,10 +84,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = fileOutput.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = fileOutput.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
index ccc7526b391374a4da0883f6615a65c7b93a0948..6e4e2bd43cfa3def8386b85c84570e9b9a48fbcf 100644
--- a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
+++ b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
@@ -31,3 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./kelvinHelmholtz.hdf5     # The file to read
+  periodic:   1
diff --git a/examples/KelvinHelmholtz_2D/makeIC.py b/examples/KelvinHelmholtz_2D/makeIC.py
index 744b39de8260720521ae8e77ed5d0a12161f2b6a..919066955c519dbac4e78e8e2a0eece842c40ab3 100644
--- a/examples/KelvinHelmholtz_2D/makeIC.py
+++ b/examples/KelvinHelmholtz_2D/makeIC.py
@@ -122,10 +122,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = fileOutput.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = fileOutput.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/KeplerianRing/keplerian_ring.yml b/examples/KeplerianRing/keplerian_ring.yml
index cc5db2a06adbe9678207454c6504a6fa315675cf..2195acfb55121ff595c471ad146b40752d9aa84e 100644
--- a/examples/KeplerianRing/keplerian_ring.yml
+++ b/examples/KeplerianRing/keplerian_ring.yml
@@ -32,7 +32,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  initial_conditions.hdf5        # The file to read
-
+  periodic:   1
+  
 # External potential parameters
 PointMassPotential:
   position:        [5.,5.,5.]  # location of external point mass in internal units
diff --git a/examples/Makefile.am b/examples/Makefile.am
index dae300933aa1c8a479d4c44b604c5ad11b5a0217..91c8baeadeb2db2644b8b8bd2fc2e9d0c666f979 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -19,7 +19,7 @@
 MYFLAGS = 
 
 # Add the source directory and the non-standard paths to the included library headers to CFLAGS
-AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS)
+AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(GRACKLE_INCS)
 
 AM_LDFLAGS = $(HDF5_LDFLAGS)
 
@@ -97,7 +97,8 @@ EXTRA_DIST = CoolingBox/coolingBox.yml CoolingBox/energy_plot.py CoolingBox/make
 	     PerturbedBox_3D/makeIC.py PerturbedBox_3D/perturbedBox.yml PerturbedBox_3D/run.sh \
 	     PMillennium-384/p-mill-384.yml \
 	     PMillennium-768/p-mill-768.yml \
-	     SantaBarbara/README SantaBarbara/getIC.sh SantaBarbara/santa_barbara.yml \
+	     SantaBarbara/README SantaBarbara/getIC.sh SantaBarbara/santa_barbara.yml SantaBarbara/run.sh \
+	     SantaBarbara_low/README SantaBarbara_low/getIC.sh SantaBarbara_low/santa_barbara.yml SantaBarbara_low/run.sh \
 	     SedovBlast_1D/makeIC.py SedovBlast_1D/plotSolution.py SedovBlast_1D/run.sh SedovBlast_1D/sedov.yml \
 	     SedovBlast_2D/getGlass.sh SedovBlast_2D/makeIC.py SedovBlast_2D/plotSolution.py SedovBlast_2D/run.sh SedovBlast_2D/sedov.yml \
 	     SedovBlast_3D/getGlass.sh SedovBlast_3D/makeIC.py SedovBlast_3D/plotSolution.py SedovBlast_3D/run.sh SedovBlast_3D/sedov.yml \
diff --git a/examples/MultiTypes/makeIC.py b/examples/MultiTypes/makeIC.py
index 41a5ef5f2ffc4073ef8a4e93a130b43fcbe2c1f5..80d49c762b1fe13bbfafd05c6818d3f202e5b033 100644
--- a/examples/MultiTypes/makeIC.py
+++ b/examples/MultiTypes/makeIC.py
@@ -93,10 +93,6 @@ for n in range(num_files):
     grp.attrs["MassTable"] = [0.0, massDM, 0.0, 0.0, 0.0, 0.0]
     grp.attrs["Flag_Entropy_ICs"] = 0
     grp.attrs["Dimension"] = 3
-
-    #Runtime parameters
-    grp = file.create_group("/RuntimePars")
-    grp.attrs["PeriodicBoundariesOn"] = periodic
     
     #Units
     grp = file.create_group("/Units")
diff --git a/examples/MultiTypes/multiTypes.yml b/examples/MultiTypes/multiTypes.yml
index 04647f0f00e69f5baf2560aca0feeb14a26cc50a..121a15b0837df19e4d2e9e64a56107c24fbde066 100644
--- a/examples/MultiTypes/multiTypes.yml
+++ b/examples/MultiTypes/multiTypes.yml
@@ -31,6 +31,7 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./multiTypes.hdf5     # The file to read
+  periodic:   1
   replicate:  2                     # Replicate all particles twice along each axis
 
 # External potential parameters
diff --git a/examples/NFW_Halo/README b/examples/NFW_Halo/README
new file mode 100755
index 0000000000000000000000000000000000000000..059d35c9a94d7851233dd0fa423abca3a1d7cddf
--- /dev/null
+++ b/examples/NFW_Halo/README
@@ -0,0 +1,5 @@
+This just provides a test that the NFW potential is giving the correct orbit 
+for an elliptical orbit as calculated by Jo Bovy's galpy package. If 
+galpy is not installed on your system you can install it by using:
+pp install galpy --user
+
diff --git a/examples/NFW_Halo/makeIC.py b/examples/NFW_Halo/makeIC.py
new file mode 100755
index 0000000000000000000000000000000000000000..68d8108f84aa759fe16956226122d53765c5ed1d
--- /dev/null
+++ b/examples/NFW_Halo/makeIC.py
@@ -0,0 +1,75 @@
+################################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Ashley Kelly ()
+#                    Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+
+import numpy as np
+import matplotlib.pyplot as plt
+from astropy import units
+import h5py as h5
+
+C = 8.0
+M_200 = 2.0
+N_PARTICLES = 1
+
+
+print("\nInitial conditions written to 'test_nfw.hdf5'")
+
+pos = np.array([8.0, 0.0, 0.0]) + 500.0
+vel = np.array([0.0, 240.0, 5.0])
+ids = np.array([1.0])
+mass = np.array([1.0])
+
+# File
+file = h5.File("test_nfw.hdf5", "w")
+
+# Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 3.086e21
+grp.attrs["Unit mass in cgs (U_M)"] = 1.988e33
+grp.attrs["Unit time in cgs (U_t)"] = 3.086e16
+grp.attrs["Unit current in cgs (U_I)"] = 1.0
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.0
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = 1000.0
+grp.attrs["NumPart_Total"] = [0, N_PARTICLES, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [0, N_PARTICLES, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+# Particle group
+grp1 = file.create_group("/PartType1")
+ds = grp1.create_dataset("Velocities", (N_PARTICLES, 3), "f", data=vel)
+
+ds = grp1.create_dataset("Masses", (N_PARTICLES,), "f", data=mass)
+
+ds = grp1.create_dataset("ParticleIDs", (N_PARTICLES,), "L", data=ids)
+
+ds = grp1.create_dataset("Coordinates", (N_PARTICLES, 3), "d", data=pos)
+
+file.close()
diff --git a/examples/NFW_Halo/makePlots.py b/examples/NFW_Halo/makePlots.py
new file mode 100755
index 0000000000000000000000000000000000000000..5e6f24d7a72dafe47d26ccb1b2d33b136affad98
--- /dev/null
+++ b/examples/NFW_Halo/makePlots.py
@@ -0,0 +1,73 @@
+################################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Ashley Kelly ()
+#                    Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+from galpy.potential import NFWPotential
+from galpy.orbit import Orbit
+import numpy as np
+import matplotlib.pyplot as plt
+from astropy import units
+import h5py as h5
+
+C = 8.0
+M_200 = 2.0
+
+
+def read_data():
+    R = np.array([])
+    z = np.array([])
+    for frame in range(0, 599, 1):
+        try:
+            sim = h5.File("output_%04d.hdf5" % frame, "r")
+        except IOError:
+            break
+
+        boxSize = sim["/Header"].attrs["BoxSize"][0]
+        pos = sim["/PartType1/Coordinates"][:, :] - boxSize / 2.0
+        R = np.append(R, np.sqrt(pos[0, 0] ** 2 + pos[0, 1] ** 2))
+        z = np.append(z, pos[0, 2])
+    return (R, z)
+
+
+def galpy_nfw_orbit():
+    # Setting up the potential
+    nfw = NFWPotential(conc=C, mvir=M_200, H=70.0, wrtcrit=True, overdens=200)
+    nfw.turn_physical_on()
+    vxvv = [
+        8.0 * units.kpc,
+        0.0 * units.km / units.s,
+        240.0 * units.km / units.s,
+        0.0 * units.pc,
+        5.0 * units.km / units.s,
+    ]
+
+    # Calculating the orbit
+    ts = np.linspace(0.0, 0.58, 1000) * units.Gyr
+    o = Orbit(vxvv=vxvv)
+    o.integrate(ts, nfw, method="odeint")
+
+    return o
+
+
+o = galpy_nfw_orbit()
+(R, z) = read_data()
+
+o.plot()
+plt.scatter(R, z, s=1, color="black", marker="x")
+plt.savefig("comparison.png")
+plt.close()
diff --git a/examples/NFW_Halo/run.sh b/examples/NFW_Halo/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5501f09ef4076715ae9650daf2cded91dbc0be9e
--- /dev/null
+++ b/examples/NFW_Halo/run.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+if [ ! -e test_nfw.hdf5 ]
+then
+    echo "Generate initial conditions for NFW example"	
+    if command -v python3 &>/dev/null; then
+        python3 makeIC.py
+    else 
+        python makeIC.py
+    fi
+fi
+
+# self gravity G, external potential g, hydro s, threads t and high verbosity v
+../swift -g -t 6 test.yml 2>&1 | tee output.log
+
+if command -v python3 &>/dev/null; then
+    python3 makePlots.py
+else 
+    python makePlots.py
+fi
diff --git a/examples/NFW_Halo/test.yml b/examples/NFW_Halo/test.yml
new file mode 100755
index 0000000000000000000000000000000000000000..73831af30769942bd7aa1c89bd7464025d2ddc85
--- /dev/null
+++ b/examples/NFW_Halo/test.yml
@@ -0,0 +1,41 @@
+# Define the system of units to use internally.
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.988e+33 # Solar mass
+  UnitLength_in_cgs:   3.086e+21 # kpc
+  UnitVelocity_in_cgs: 1e5       # km / s
+  UnitCurrent_in_cgs:  1         # Amperes
+  UnitTemp_in_cgs:     1         # Kelvin
+
+# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.)
+TimeIntegration:
+  time_begin:          0.      # The starting time of the simulation (in internal units).
+  time_end:            0.6     # The end time of the simulation (in internal units).
+  dt_min:              1e-8    # The minimal time-step size of the simulation (in internal units).
+  dt_max:              1e-1    # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            output  # Common part of the name of output files
+  time_first:          0.      # Time of the first output (in internal units)
+  delta_time:          1e-3    # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-3    # Time between statistics output
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:          test_nfw.hdf5 # The file to read
+  shift_x:            0.          # (Optional) A shift to apply to all particles read from the ICs (in internal units).
+  shift_y:            0.
+  shift_z:            0.
+  periodic:           0
+
+# Isothermal potential parameters
+NFWPotential:
+  useabspos:          0
+  position:           [0.0,0.0,0.0]      # Location of centre of potential with respect to centre of the box (internal units)
+  concentration:      8.
+  M_200:              2.0e+12  # Virial mass (internal units)
+  critical_density:   140      # Critical density (internal units)
+  timestep_mult:      0.01     # Dimensionless pre-factor for the time-step condition
diff --git a/examples/Noh_1D/makeIC.py b/examples/Noh_1D/makeIC.py
index 176f3517455db7a8b0994ac7d1e65fb9cb7419d4..9d9a5e5b62edeedd8f5b2732c240b9ea2878c92d 100644
--- a/examples/Noh_1D/makeIC.py
+++ b/examples/Noh_1D/makeIC.py
@@ -66,10 +66,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 1
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/Noh_1D/noh.yml b/examples/Noh_1D/noh.yml
index 1d126f19babd0c9fe28afff907b3fe8259467a24..58e13ddda8939c8fc5fa4360a498a87f1c5b189a 100644
--- a/examples/Noh_1D/noh.yml
+++ b/examples/Noh_1D/noh.yml
@@ -31,4 +31,6 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./noh.hdf5          # The file to read
+  periodic:   1
 
+  
\ No newline at end of file
diff --git a/examples/Noh_2D/makeIC.py b/examples/Noh_2D/makeIC.py
index f7239fa3cd188637df929f86451d20a9978bd1f5..83bb1ac6773074d0c10d3eb425b34c082a971fd8 100644
--- a/examples/Noh_2D/makeIC.py
+++ b/examples/Noh_2D/makeIC.py
@@ -73,10 +73,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/Noh_2D/noh.yml b/examples/Noh_2D/noh.yml
index 1d126f19babd0c9fe28afff907b3fe8259467a24..eaf991631854e9a9781f0fcee50d996f8af949cd 100644
--- a/examples/Noh_2D/noh.yml
+++ b/examples/Noh_2D/noh.yml
@@ -31,4 +31,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./noh.hdf5          # The file to read
+  periodic:   1
 
diff --git a/examples/Noh_3D/makeIC.py b/examples/Noh_3D/makeIC.py
index 0c25a5c8b3e967185cf16bae4b1f21c215266def..2d560a1e869c6c12e557c82402d6e8629ecf661c 100644
--- a/examples/Noh_3D/makeIC.py
+++ b/examples/Noh_3D/makeIC.py
@@ -75,10 +75,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/Noh_3D/noh.yml b/examples/Noh_3D/noh.yml
index cc15af7ec190cd2c10cdff3a3ccb3f0beaf7e177..e005d394a6d3645ca33950af625b0267a62ca7d7 100644
--- a/examples/Noh_3D/noh.yml
+++ b/examples/Noh_3D/noh.yml
@@ -32,4 +32,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./noh.hdf5          # The file to read
+  periodic:   1
 
diff --git a/examples/PMillennium-384/p-mill-384.yml b/examples/PMillennium-384/p-mill-384.yml
index b7fc6f762205a0a493b13392ff3001c653445d8d..4aede77c0c1a8a6818c95c318364150ede919a01 100644
--- a/examples/PMillennium-384/p-mill-384.yml
+++ b/examples/PMillennium-384/p-mill-384.yml
@@ -46,5 +46,6 @@ Gravity:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:                   ics.hdf5
+  periodic:                    1
   cleanup_h_factors:           1    
   cleanup_velocity_factors:    1  
diff --git a/examples/PMillennium-768/p-mill-768.yml b/examples/PMillennium-768/p-mill-768.yml
index 8b93a7752d09545c08b98af87db1b1d46eb15189..a70c9c70831af9c237a466165b25b6300df69336 100644
--- a/examples/PMillennium-768/p-mill-768.yml
+++ b/examples/PMillennium-768/p-mill-768.yml
@@ -46,5 +46,6 @@ Gravity:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:                   ics.hdf5
+  periodic:                    1
   cleanup_h_factors:           1    
   cleanup_velocity_factors:    1  
diff --git a/examples/PerturbedBox_2D/makeIC.py b/examples/PerturbedBox_2D/makeIC.py
index 87a41517772570870e04c79d3694c115a909e214..7f52525bdf508603a23f93c0fc7d8cda7f8f13cb 100644
--- a/examples/PerturbedBox_2D/makeIC.py
+++ b/examples/PerturbedBox_2D/makeIC.py
@@ -86,10 +86,6 @@ grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["NumPart_Total"] = numPart
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/PerturbedBox_2D/perturbedPlane.yml b/examples/PerturbedBox_2D/perturbedPlane.yml
index a0c6b6d9dbc7a677002dbce5abc6e5d268b56e97..4d03b30398bec34414636803caf6bf3bdc99251d 100644
--- a/examples/PerturbedBox_2D/perturbedPlane.yml
+++ b/examples/PerturbedBox_2D/perturbedPlane.yml
@@ -31,3 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./perturbedPlane.hdf5     # The file to read
+  periodic:   1
diff --git a/examples/PerturbedBox_3D/makeIC.py b/examples/PerturbedBox_3D/makeIC.py
index 1b0fc284e4c40b51fca45f117b92175a0ea45f31..f2d8357f2f96a4aa6efaa14822c442a884415b56 100644
--- a/examples/PerturbedBox_3D/makeIC.py
+++ b/examples/PerturbedBox_3D/makeIC.py
@@ -88,10 +88,6 @@ grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["NumPart_Total"] = numPart
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/PerturbedBox_3D/perturbedBox.yml b/examples/PerturbedBox_3D/perturbedBox.yml
index 3148510979d0e349c0d6242bf11e1a0db94f9e1f..6010cf457b2b67c0fce0332a0216aa9359673e3b 100644
--- a/examples/PerturbedBox_3D/perturbedBox.yml
+++ b/examples/PerturbedBox_3D/perturbedBox.yml
@@ -31,3 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./perturbedBox.hdf5     # The file to read
+  periodic:   1
diff --git a/examples/SantaBarbara/santa_barbara.yml b/examples/SantaBarbara/santa_barbara.yml
index b01321c521ec28b78c97cd082f0fd520d7024c3d..65e6c239e152d46647a6b0e40b2f100a9d7a3d32 100644
--- a/examples/SantaBarbara/santa_barbara.yml
+++ b/examples/SantaBarbara/santa_barbara.yml
@@ -53,6 +53,7 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./SantaBarbara.hdf5
+  periodic:   1
   cleanup_h_factors: 1              # ICs were generated for Gadget, we need to get rid of h-factors
   cleanup_velocity_factors: 1       # ICs were generated for Gadget, we need to get rid of sqrt(a) factors in the velocity
   generate_gas_in_ics: 1            # Generate gas particles from the DM-only ICs
diff --git a/examples/SantaBarbara_low/README b/examples/SantaBarbara_low/README
new file mode 100644
index 0000000000000000000000000000000000000000..f86f1a4a4e1d16c3f4011c9e3ed8f35f643bd47e
--- /dev/null
+++ b/examples/SantaBarbara_low/README
@@ -0,0 +1,21 @@
+Initital conditions for the Santa-Barbara cluster comparison project.
+These have been regenerated from the orinigal Frenk et al. 1999 paper.
+
+The cosmology is Omega_m = 1, Omega_b = 0.1, h = 0.5 and sigma_8 = 0.9.
+
+The ICs are 128^3 particles in a 64^3 Mpc^3 volume. This is about 10x
+higher resolution than in the original paper. The ICs have been
+created for Gadget and the positions and box size are hence expressed
+in h-full units (e.g. box size of 32 / h Mpc). Similarly, the peculiar
+velocitites contain an extra sqrt(a) factor. 
+
+We will use SWIFT to cancel the h- and a-factors from the ICs. Gas
+particles will be generated at startup.
+
+MD5 check-sum of the ICS:
+1a1600b41002789b6057b1fa6333f3f0  SantaBarbara_128.hdf5
+
+You can use the script run_velociraptor.sh to also run a basic 3D FoF
+with VELOCIraptor on your output data. You will need to set the
+VELOCIRAPTOR_PATH environment variable to tell us where the stf-gas
+binary lives.
diff --git a/examples/SantaBarbara_low/getIC.sh b/examples/SantaBarbara_low/getIC.sh
new file mode 100755
index 0000000000000000000000000000000000000000..759cef50dcfc346b389b1400054fe38358793fdd
--- /dev/null
+++ b/examples/SantaBarbara_low/getIC.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/SantaBarbara_128.hdf5
diff --git a/examples/SantaBarbara_low/run.sh b/examples/SantaBarbara_low/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..044f9f4ef340ab95ffe643104294552469a676ce
--- /dev/null
+++ b/examples/SantaBarbara_low/run.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+# Run SWIFT
+../swift -c -s -G -t 28 santa_barbara.yml
+
diff --git a/examples/SantaBarbara_low/santa_barbara.yml b/examples/SantaBarbara_low/santa_barbara.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0e3c66b1a1c1e04bf6fad30e806327b83f03737e
--- /dev/null
+++ b/examples/SantaBarbara_low/santa_barbara.yml
@@ -0,0 +1,60 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.98848e43    # 10^10 M_sun in grams
+  UnitLength_in_cgs:   3.08567758e24 # Mpc in centimeters
+  UnitVelocity_in_cgs: 1e5           # 1 km/s 
+  UnitCurrent_in_cgs:  1             # Amperes
+  UnitTemp_in_cgs:     1             # Kelvin
+
+# Cosmological parameters
+Cosmology:
+  h:              0.5        
+  a_begin:        0.047619048        # z_ini = 20
+  a_end:          1.0                # z_end = 0
+  Omega_m:        1.0        
+  Omega_lambda:   0.0        
+  Omega_b:        0.1        
+  
+# Parameters governing the time integration
+TimeIntegration:
+  dt_max:     0.01
+  dt_min:     1e-10
+
+Scheduler:
+  max_top_level_cells: 16
+  cell_split_size:     100
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            santabarbara_low
+  scale_factor_first:  0.05
+  delta_time:          1.02
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:           1.02
+  scale_factor_first:   0.05
+
+# Parameters for the self-gravity scheme
+Gravity:
+  eta:                    0.025  
+  theta:                  0.5
+  comoving_softening:     0.02    # 20 kpc = 1/25 mean inter-particle separation
+  max_physical_softening: 0.00526 # 20 ckpc = 5.26 pkpc at z=2.8 (EAGLE-like evolution of softening).
+  mesh_side_length:       64
+
+# Parameters of the hydro scheme
+SPH:
+  resolution_eta:      1.2348   # "48 Ngb" with the cubic spline kernel
+  CFL_condition:       0.1
+  initial_temperature: 1200.    # (1 + z_ini)^2 * 2.72K
+  minimal_temperature: 100.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./SantaBarbara_128.hdf5
+  periodic:   1
+  cleanup_h_factors: 1              # ICs were generated for Gadget, we need to get rid of h-factors
+  cleanup_velocity_factors: 1       # ICs were generated for Gadget, we need to get rid of sqrt(a) factors in the velocity
+  generate_gas_in_ics: 1            # Generate gas particles from the DM-only ICs
+  cleanup_smoothing_lengths: 1      # Since we generate gas, make use of the (expensive) cleaning-up procedure.
\ No newline at end of file
diff --git a/examples/SedovBlast_1D/makeIC.py b/examples/SedovBlast_1D/makeIC.py
index 7177f3a7670aa054e3d7341a11a7359b3d855837..28b9c4bfd69395b94628bda3cfc3e59166460c79 100644
--- a/examples/SedovBlast_1D/makeIC.py
+++ b/examples/SedovBlast_1D/makeIC.py
@@ -72,10 +72,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 1
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SedovBlast_1D/sedov.yml b/examples/SedovBlast_1D/sedov.yml
index 5ef105b06c23ba577129f29a817c058457e7387f..b4912a95e797440dc6eb0c9f48806a5954adbc41 100644
--- a/examples/SedovBlast_1D/sedov.yml
+++ b/examples/SedovBlast_1D/sedov.yml
@@ -31,4 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./sedov.hdf5          # The file to read
-
+  periodic:   1
diff --git a/examples/SedovBlast_2D/makeIC.py b/examples/SedovBlast_2D/makeIC.py
index 0e83c7b19b9ac9bd69e20950a64e8a49dd8d0df9..cd1e433c104fd013a71c5a501c166194a7f3f50f 100644
--- a/examples/SedovBlast_2D/makeIC.py
+++ b/examples/SedovBlast_2D/makeIC.py
@@ -72,10 +72,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SedovBlast_2D/sedov.yml b/examples/SedovBlast_2D/sedov.yml
index 098ca7a0d6264f016727709723aafdfb1224d460..84177ece31ef98ec55c41513276c9c0158e69bcf 100644
--- a/examples/SedovBlast_2D/sedov.yml
+++ b/examples/SedovBlast_2D/sedov.yml
@@ -31,4 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./sedov.hdf5          # The file to read
-
+  periodic:   1
diff --git a/examples/SedovBlast_3D/makeIC.py b/examples/SedovBlast_3D/makeIC.py
index e1b743c6cdcd8dcc2f8da14d1d5589fb9ed111f0..30e0e31927db6343e58549bc9c7754bc274f51ce 100644
--- a/examples/SedovBlast_3D/makeIC.py
+++ b/examples/SedovBlast_3D/makeIC.py
@@ -72,10 +72,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SedovBlast_3D/sedov.yml b/examples/SedovBlast_3D/sedov.yml
index 75849e33c0c644a18cd7357f901699d0d682c160..6cf5b02427b8004787b646e6bcdd4bacaa25bc06 100644
--- a/examples/SedovBlast_3D/sedov.yml
+++ b/examples/SedovBlast_3D/sedov.yml
@@ -32,5 +32,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:                    ./sedov.hdf5          
+  periodic:                     1
   smoothing_length_scaling:     3.33
- 
diff --git a/examples/SineWavePotential_1D/makeIC.py b/examples/SineWavePotential_1D/makeIC.py
index afbf1bc0fa47a27677cb9c5645d439432bd9fd9a..39a78393650c7a8c0c01814fa10f514cc277e685 100644
--- a/examples/SineWavePotential_1D/makeIC.py
+++ b/examples/SineWavePotential_1D/makeIC.py
@@ -74,10 +74,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 1
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SineWavePotential_1D/sineWavePotential.yml b/examples/SineWavePotential_1D/sineWavePotential.yml
index e6285785099f10902ea60b21334a0ad26c0593de..a21a0b5936ab0a62a7b1f29c56145bed79ba73c4 100644
--- a/examples/SineWavePotential_1D/sineWavePotential.yml
+++ b/examples/SineWavePotential_1D/sineWavePotential.yml
@@ -31,7 +31,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  sineWavePotential.hdf5       # The file to read
- 
+  periodic:   1
+  
 # External potential parameters
 SineWavePotential:
   amplitude: 10.
diff --git a/examples/SineWavePotential_2D/makeIC.py b/examples/SineWavePotential_2D/makeIC.py
index 62ae89f8f52bff9c0db37cd537f286ab817da3fe..057760502e561b5ec5d98e716b79119e3637ef57 100644
--- a/examples/SineWavePotential_2D/makeIC.py
+++ b/examples/SineWavePotential_2D/makeIC.py
@@ -70,10 +70,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SineWavePotential_2D/sineWavePotential.yml b/examples/SineWavePotential_2D/sineWavePotential.yml
index 9107652f65c343d68fc92e699d45710265d65308..63d575e7e2486cf4428bb8b11e1ba16da6e08d99 100644
--- a/examples/SineWavePotential_2D/sineWavePotential.yml
+++ b/examples/SineWavePotential_2D/sineWavePotential.yml
@@ -31,7 +31,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  sineWavePotential.hdf5       # The file to read
- 
+  periodic:   1
+  
 # External potential parameters
 SineWavePotential:
   amplitude: 10.
diff --git a/examples/SineWavePotential_3D/makeIC.py b/examples/SineWavePotential_3D/makeIC.py
index 4833ec1b055e27b63751136f0491e972fb9e492a..a4f39238ba40bf6769e0fb44fe8da706730fe45b 100644
--- a/examples/SineWavePotential_3D/makeIC.py
+++ b/examples/SineWavePotential_3D/makeIC.py
@@ -81,10 +81,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SineWavePotential_3D/sineWavePotential.yml b/examples/SineWavePotential_3D/sineWavePotential.yml
index 8a49d8bc40eb662d62b2b6550b70fe380a7564f5..5b91feae0ecf8ad2f4f257374900a01f031acff1 100644
--- a/examples/SineWavePotential_3D/sineWavePotential.yml
+++ b/examples/SineWavePotential_3D/sineWavePotential.yml
@@ -31,7 +31,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  sineWavePotential.hdf5       # The file to read
- 
+  periodic:   1
+  
 # External potential parameters
 SineWavePotential:
   amplitude: 10.
diff --git a/examples/SmallCosmoVolume/small_cosmo_volume.yml b/examples/SmallCosmoVolume/small_cosmo_volume.yml
index 353ab469c8375e87652738daea12a4a0144bfcd8..a6ce1f28198a99422ea1c80178fc8000b66d777e 100644
--- a/examples/SmallCosmoVolume/small_cosmo_volume.yml
+++ b/examples/SmallCosmoVolume/small_cosmo_volume.yml
@@ -52,7 +52,8 @@ Scheduler:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  small_cosmo_volume.hdf5
+  periodic:                    1
   cleanup_h_factors:           1    
   cleanup_velocity_factors:    1  
-  generate_gas_in_ics: 1            # Generate gas particles from the DM-only ICs
-  cleanup_smoothing_lengths: 1      # Since we generate gas, make use of the (expensive) cleaning-up procedure.
+  generate_gas_in_ics:         1      # Generate gas particles from the DM-only ICs
+  cleanup_smoothing_lengths:   1      # Since we generate gas, make use of the (expensive) cleaning-up procedure.
diff --git a/examples/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml b/examples/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml
index ae4cb62c8da9e1b56ca56f3d14bdbf120ef80c6a..910137edc442c994a9f31a8c62e16818ca4ae97d 100644
--- a/examples/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml
+++ b/examples/SmallCosmoVolume_DM/small_cosmo_volume_dm.yml
@@ -53,5 +53,6 @@ Scheduler:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  small_cosmo_volume.hdf5
+  periodic:                    1
   cleanup_h_factors:           1    
   cleanup_velocity_factors:    1  
diff --git a/examples/SmallCosmoVolume_cooling/README b/examples/SmallCosmoVolume_cooling/README
new file mode 100644
index 0000000000000000000000000000000000000000..2ee364ca2f00a4280a492a11520bf7b9a1ac1f7d
--- /dev/null
+++ b/examples/SmallCosmoVolume_cooling/README
@@ -0,0 +1,19 @@
+Small LCDM cosmological simulation generated by C. Power. Cosmology
+is WMAP9 and the box is 100Mpc/h in size with 64^3 particles.
+We use a softening length of 1/25th of the mean inter-particle separation.
+
+The ICs have been generated to run with Gadget-2 so we need to switch
+on the options to cancel the h-factors and a-factors at reading time.
+We generate gas from the ICs using SWIFT's internal mechanism and set the
+temperature to the expected gas temperature at this redshift.
+
+This example runs with cooling switch on. Depending on the cooling
+model chosen at the time SWIFT was configured, the answer will be
+different. Interesting cases to compare to the no-cooling case are
+a constant cooling rate or Compton cooling.
+
+The 'plotTempEvolution.py' plots the temperature evolution of the gas
+in the simulated volume.
+
+MD5 checksum of the ICs:
+08736c3101fd738e22f5159f78e6022b  small_cosmo_volume.hdf5
diff --git a/examples/SmallCosmoVolume_cooling/getIC.sh b/examples/SmallCosmoVolume_cooling/getIC.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3b8136cc5aca00a25792655c6c505cfeeb0f2bc9
--- /dev/null
+++ b/examples/SmallCosmoVolume_cooling/getIC.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/small_cosmo_volume.hdf5
+
diff --git a/examples/SmallCosmoVolume_cooling/plotTempEvolution.py b/examples/SmallCosmoVolume_cooling/plotTempEvolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..d3e4e9047ed7f2cc57ef581526488e64ccc26092
--- /dev/null
+++ b/examples/SmallCosmoVolume_cooling/plotTempEvolution.py
@@ -0,0 +1,192 @@
+################################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+
+# Computes the temperature evolution of the gas in a cosmological box
+
+# Physical constants needed for internal energy to temperature conversion
+k_in_J_K = 1.38064852e-23
+mH_in_kg = 1.6737236e-27
+
+# Number of snapshots generated
+n_snapshots = 200
+
+import matplotlib
+matplotlib.use("Agg")
+from pylab import *
+import h5py
+import os.path
+
+# Plot parameters
+params = {'axes.labelsize': 10,
+'axes.titlesize': 10,
+'font.size': 9,
+'legend.fontsize': 9,
+'xtick.labelsize': 10,
+'ytick.labelsize': 10,
+'text.usetex': True,
+ 'figure.figsize' : (3.15,3.15),
+'figure.subplot.left'    : 0.14,
+'figure.subplot.right'   : 0.99,
+'figure.subplot.bottom'  : 0.12,
+'figure.subplot.top'     : 0.99,
+'figure.subplot.wspace'  : 0.15,
+'figure.subplot.hspace'  : 0.12,
+'lines.markersize' : 6,
+'lines.linewidth' : 2.,
+'text.latex.unicode': True
+}
+rcParams.update(params)
+rc('font',**{'family':'sans-serif','sans-serif':['Times']})
+
+# Read the simulation data
+sim = h5py.File("snap_0000.hdf5", "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][0]
+scheme = sim["/HydroScheme"].attrs["Scheme"][0]
+kernel = sim["/HydroScheme"].attrs["Kernel function"][0]
+neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0]
+eta = sim["/HydroScheme"].attrs["Kernel eta"][0]
+alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0]
+H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0]
+H_transition_temp = sim["/HydroScheme"].attrs["Hydrogen ionization transition temperature"][0]
+T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
+T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
+git = sim["Code"].attrs["Git Revision"]
+cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"]
+
+if cooling_model == "Constant Lambda":
+    Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0]
+    
+# Cosmological parameters
+H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
+gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0]
+
+unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"]
+unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"]
+unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"]
+
+unit_length_in_si = 0.01 * unit_length_in_cgs
+unit_mass_in_si = 0.001 * unit_mass_in_cgs
+unit_time_in_si = unit_time_in_cgs
+
+# Primoridal mean molecular weight as a function of temperature
+def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp):
+    if T > T_trans:
+        return 4. / (8. - 5. * (1. - H_frac))
+    else:
+        return 4. / (1. + 3. * H_frac)
+    
+# Temperature of some primoridal gas with a given internal energy
+def T(u, H_frac=H_mass_fraction, T_trans=H_transition_temp):
+    T_over_mu = (gas_gamma - 1.) * u * mH_in_kg / k_in_J_K
+    ret = np.ones(np.size(u)) * T_trans
+
+    # Enough energy to be ionized?
+    mask_ionized = (T_over_mu > (T_trans+1) / mu(T_trans+1, H_frac, T_trans))
+    if np.sum(mask_ionized)  > 0:
+        ret[mask_ionized] = T_over_mu[mask_ionized] * mu(T_trans*10, H_frac, T_trans)
+
+    # Neutral gas?
+    mask_neutral = (T_over_mu < (T_trans-1) / mu((T_trans-1), H_frac, T_trans))
+    if np.sum(mask_neutral)  > 0:
+        ret[mask_neutral] = T_over_mu[mask_neutral] * mu(0, H_frac, T_trans)
+        
+    return ret
+
+
+z = np.zeros(n_snapshots)
+a = np.zeros(n_snapshots)
+T_mean = np.zeros(n_snapshots)
+T_std = np.zeros(n_snapshots)
+T_log_mean = np.zeros(n_snapshots)
+T_log_std = np.zeros(n_snapshots)
+T_median = np.zeros(n_snapshots)
+T_min = np.zeros(n_snapshots)
+T_max = np.zeros(n_snapshots)
+
+# Loop over all the snapshots
+for i in range(n_snapshots):
+    sim = h5py.File("snap_%04d.hdf5"%i, "r")
+
+    z[i] = sim["/Cosmology"].attrs["Redshift"][0]
+    a[i] = sim["/Cosmology"].attrs["Scale-factor"][0]
+
+    u = sim["/PartType0/InternalEnergy"][:]
+
+    # Compute the temperature
+    u *= (unit_length_in_si**2 / unit_time_in_si**2)
+    u /= a[i]**(3 * (gas_gamma - 1.))
+    Temp = T(u)
+
+    # Gather statistics
+    T_median[i] = np.median(Temp)
+    T_mean[i] = Temp.mean()
+    T_std[i] = Temp.std()
+    T_log_mean[i] = np.log10(Temp).mean()
+    T_log_std[i] = np.log10(Temp).std()
+    T_min[i] = Temp.min()
+    T_max[i] = Temp.max()
+
+# CMB evolution
+a_evol = np.logspace(-3, 0, 60)
+T_cmb = (1. / a_evol)**2 * 2.72
+
+# Plot the interesting quantities
+figure()
+subplot(111, xscale="log", yscale="log")
+
+fill_between(a, T_mean-T_std, T_mean+T_std, color='C0', alpha=0.1)
+plot(a, T_max, ls='-.', color='C0', lw=1., label="${\\rm max}~T$")
+plot(a, T_min, ls=':', color='C0', lw=1., label="${\\rm min}~T$")
+plot(a, T_mean, color='C0', label="${\\rm mean}~T$", lw=1.5)
+fill_between(a, 10**(T_log_mean-T_log_std), 10**(T_log_mean+T_log_std), color='C1', alpha=0.1)
+plot(a, 10**T_log_mean, color='C1', label="${\\rm mean}~{\\rm log} T$", lw=1.5)
+plot(a, T_median, color='C2', label="${\\rm median}~T$", lw=1.5)
+
+legend(loc="upper left", frameon=False, handlelength=1.5)
+
+# Cooling model
+if cooling_model == "Constant Lambda":
+    text(1e-2, 6e4, "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$"%(Lambda/10.**(int(log10(Lambda))), log10(Lambda)), fontsize=7)
+else:
+    text(1e-2, 6e4, "No cooling")
+    
+# Expected lines
+plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], 'k--', lw=0.5, alpha=0.7)
+text(2.5e-2, H_transition_temp*1.07, "$T_{\\rm HII\\rightarrow HI}$", va="bottom", alpha=0.7, fontsize=8)
+plot([1e-10, 1e10], [T_minimal, T_minimal], 'k--', lw=0.5, alpha=0.7)
+text(1e-2, T_minimal*0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8)
+plot(a_evol, T_cmb, 'k--', lw=0.5, alpha=0.7)
+text(a_evol[20], T_cmb[20]*0.55, "$(1+z)^2\\times T_{\\rm CMB,0}$", rotation=-34, alpha=0.7, fontsize=8, va="top", bbox=dict(facecolor='w', edgecolor='none', pad=1.0, alpha=0.9))
+
+
+redshift_ticks = np.array([0., 1., 2., 5., 10., 20., 50., 100.])
+redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"]
+a_ticks = 1. / (redshift_ticks + 1.)
+
+xticks(a_ticks, redshift_labels)
+minorticks_off()
+
+xlabel("${\\rm Redshift}~z$", labelpad=0)
+ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
+xlim(9e-3, 1.1)
+ylim(20, 2.5e7)
+
+savefig("Temperature_evolution.png", dpi=200)
+
diff --git a/examples/SmallCosmoVolume_cooling/run.sh b/examples/SmallCosmoVolume_cooling/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..454ed279503db17ea2deddb2ae982206238ec23f
--- /dev/null
+++ b/examples/SmallCosmoVolume_cooling/run.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e small_cosmo_volume.hdf5 ]
+then
+    echo "Fetching initial conditions for the small cosmological volume example..."
+    ./getIC.sh
+fi
+
+# Run SWIFT
+../swift -c -s -G -C -t 8 small_cosmo_volume.yml 2>&1 | tee output.log
+
+# Plot the temperature evolution
+python plotTempEvolution.py
diff --git a/examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml b/examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b091207fa98d8b6604124fb9d99c80d65e97dfb2
--- /dev/null
+++ b/examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml
@@ -0,0 +1,63 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.98848e43    # 10^10 M_sun
+  UnitLength_in_cgs:   3.08567758e24 # 1 Mpc
+  UnitVelocity_in_cgs: 1e5           # 1 km/s
+  UnitCurrent_in_cgs:  1             # Amperes
+  UnitTemp_in_cgs:     1             # Kelvin
+
+Cosmology:                      # WMAP9 cosmology
+  Omega_m:        0.276
+  Omega_lambda:   0.724
+  Omega_b:        0.0455
+  h:              0.703
+  a_begin:        0.019607843	# z_ini = 50.
+  a_end:          1.0		# z_end = 0.
+
+# Parameters governing the time integration
+TimeIntegration:
+  dt_min:     1e-6 
+  dt_max:     1e-2 
+
+# Parameters for the self-gravity scheme
+Gravity:
+  eta:          0.025         
+  theta:        0.3           
+  comoving_softening:     0.0889     # 1/25th of the mean inter-particle separation: 88.9 kpc
+  max_physical_softening: 0.0889     # 1/25th of the mean inter-particle separation: 88.9 kpc
+  mesh_side_length:       64
+
+# Parameters of the hydro scheme
+SPH:
+  resolution_eta:      1.2348   # "48 Ngb" with the cubic spline kernel
+  CFL_condition:       0.1
+  initial_temperature: 7075.    # (1 + z_ini)^2 * 2.72K
+  minimal_temperature: 100.
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            snap
+  delta_time:          1.02
+  scale_factor_first:  0.02
+  
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1.02
+  scale_factor_first:  0.02
+  
+Scheduler:
+  max_top_level_cells: 8
+  cell_split_size:     50
+  
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  small_cosmo_volume.hdf5
+  periodic:                    1
+  cleanup_h_factors:           1    
+  cleanup_velocity_factors:    1  
+  generate_gas_in_ics:         1    # Generate gas particles from the DM-only ICs
+  cleanup_smoothing_lengths:   1    # Since we generate gas, make use of the (expensive) cleaning-up procedure.
+
+# Constant lambda cooling function
+LambdaCooling:
+  lambda_nH2_cgs:              1e-26 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3])
diff --git a/examples/SmoothedMetallicity/makeIC.py b/examples/SmoothedMetallicity/makeIC.py
index 86679d5efe897b9dfae7db94b36d74bb047661e6..542b4c5911c942015d16595f42e73ca8978d20da 100644
--- a/examples/SmoothedMetallicity/makeIC.py
+++ b/examples/SmoothedMetallicity/makeIC.py
@@ -84,10 +84,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-# Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 # Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SmoothedMetallicity/smoothed_metallicity.yml b/examples/SmoothedMetallicity/smoothed_metallicity.yml
index 2e37695392b12c545bbbdbe7fd94748d5b3b9ff8..f6841c6bd0744b4bbeacbe136a126b4ed5631f6f 100644
--- a/examples/SmoothedMetallicity/smoothed_metallicity.yml
+++ b/examples/SmoothedMetallicity/smoothed_metallicity.yml
@@ -31,4 +31,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./smoothed_metallicity.hdf5          # The file to read
+  periodic:   1
 
diff --git a/examples/SodShockSpherical_2D/makeIC.py b/examples/SodShockSpherical_2D/makeIC.py
index ac9f6e193769d7466f5b8e41a408da2350777be6..bc2c7ed1dcae5adfbfdcaf01c6b5a36bf5669e9e 100644
--- a/examples/SodShockSpherical_2D/makeIC.py
+++ b/examples/SodShockSpherical_2D/makeIC.py
@@ -100,10 +100,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SodShockSpherical_2D/sodShock.yml b/examples/SodShockSpherical_2D/sodShock.yml
index a26ab95b21c782ce83310038432ac08df0e024c3..4ef13c26ccf55163f9276b6e095c351efd9ecb35 100644
--- a/examples/SodShockSpherical_2D/sodShock.yml
+++ b/examples/SodShockSpherical_2D/sodShock.yml
@@ -31,4 +31,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./sodShock.hdf5       # The file to read
+  periodic:   1
 
diff --git a/examples/SodShockSpherical_3D/makeIC.py b/examples/SodShockSpherical_3D/makeIC.py
index be8f9b61a1beef00f49786860ce94287b30e2ab3..3884fc29280209d465b721230ae19b474a42f6a0 100644
--- a/examples/SodShockSpherical_3D/makeIC.py
+++ b/examples/SodShockSpherical_3D/makeIC.py
@@ -102,10 +102,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SodShockSpherical_3D/sodShock.yml b/examples/SodShockSpherical_3D/sodShock.yml
index 3fc4a1fb2b8cc5f6a603abf4c87ac99c7647b9bd..16d3bd313cf8a365fb82d3142ba1ac4fd065d193 100644
--- a/examples/SodShockSpherical_3D/sodShock.yml
+++ b/examples/SodShockSpherical_3D/sodShock.yml
@@ -32,4 +32,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./sodShock.hdf5       # The file to read
+  periodic:   1
 
diff --git a/examples/SodShock_1D/makeIC.py b/examples/SodShock_1D/makeIC.py
index a5c7f03b24d10e81057dbe25855f33f795218f19..d26bbbb4dbf71c1d6a63ad3c7900edfabe0fb9ec 100644
--- a/examples/SodShock_1D/makeIC.py
+++ b/examples/SodShock_1D/makeIC.py
@@ -92,10 +92,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 1
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SodShock_1D/sodShock.yml b/examples/SodShock_1D/sodShock.yml
index e827edadb9c287975d83214249d4fdd7734a5f6c..69554b4db733166fc5dbb6d198966fd8f9b8d49c 100644
--- a/examples/SodShock_1D/sodShock.yml
+++ b/examples/SodShock_1D/sodShock.yml
@@ -31,4 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./sodShock.hdf5       # The file to read
-
+  periodic:   1
diff --git a/examples/SodShock_2D/makeIC.py b/examples/SodShock_2D/makeIC.py
index 850ca24f54c39990a9b0c54c0d2f361a2aa01e95..2d3bd75fcc41e0fee6dd7cfde62873980bbc7143 100644
--- a/examples/SodShock_2D/makeIC.py
+++ b/examples/SodShock_2D/makeIC.py
@@ -98,10 +98,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SodShock_2D/sodShock.yml b/examples/SodShock_2D/sodShock.yml
index 51a188b6d4537d490cb837a03dab15f74c3b083c..b831dd78278fea619d75e2db8806cf00d8faf575 100644
--- a/examples/SodShock_2D/sodShock.yml
+++ b/examples/SodShock_2D/sodShock.yml
@@ -31,4 +31,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./sodShock.hdf5       # The file to read
+  periodic:   1
 
diff --git a/examples/SodShock_3D/makeIC.py b/examples/SodShock_3D/makeIC.py
index c71c07c6c97bb715c580f747cf8d39ddf08445c3..69f1bc506680d3f2f149c0fd7b75b069f9b00b64 100644
--- a/examples/SodShock_3D/makeIC.py
+++ b/examples/SodShock_3D/makeIC.py
@@ -98,10 +98,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SodShock_3D/sodShock.yml b/examples/SodShock_3D/sodShock.yml
index 6042c8090d00fef5467a7fed3d6f5a104c626f43..b2d783cd74d66a8eaa3cbbf4b08fc686b0298244 100644
--- a/examples/SodShock_3D/sodShock.yml
+++ b/examples/SodShock_3D/sodShock.yml
@@ -32,4 +32,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./sodShock.hdf5       # The file to read
+  periodic:   1
 
diff --git a/examples/SquareTest_2D/makeIC.py b/examples/SquareTest_2D/makeIC.py
index 186e653124a6ff62a964c37cf0fb2220f1152a0e..12a394873edf42f7ecfdf07c9795b62e3ad89745 100644
--- a/examples/SquareTest_2D/makeIC.py
+++ b/examples/SquareTest_2D/makeIC.py
@@ -96,10 +96,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = fileOutput.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = fileOutput.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/SquareTest_2D/square.yml b/examples/SquareTest_2D/square.yml
index b700c441a619ef8faac52656909567c944e344c3..54e0effa676cd5b1233ae7c38aded18d089f0ef2 100644
--- a/examples/SquareTest_2D/square.yml
+++ b/examples/SquareTest_2D/square.yml
@@ -31,3 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./square.hdf5     # The file to read
+  periodic:   1
diff --git a/examples/UniformBox_2D/makeIC.py b/examples/UniformBox_2D/makeIC.py
index 642896c6ec406a5a75127e024d19775ea4a8e09b..36bb1ba6118a31db3251a1cd7f332f01b2ba3df1 100644
--- a/examples/UniformBox_2D/makeIC.py
+++ b/examples/UniformBox_2D/makeIC.py
@@ -85,10 +85,6 @@ grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["NumPart_Total"] = numPart
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/UniformBox_2D/uniformPlane.yml b/examples/UniformBox_2D/uniformPlane.yml
index 58fe0d50557db0c0624fe89cbde888d2c92775e5..77f53d59c497b10b1c95ce5dcb763fa8bffcd5ca 100644
--- a/examples/UniformBox_2D/uniformPlane.yml
+++ b/examples/UniformBox_2D/uniformPlane.yml
@@ -31,3 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./uniformPlane.hdf5     # The file to read
+  periodic:   1
diff --git a/examples/UniformBox_3D/makeIC.py b/examples/UniformBox_3D/makeIC.py
index 01e37c67b6e2eec2984d62f4ffd503b23b5bd9ec..8311aae951f921b4c7f759ba09cc8fe73cf4a9f1 100644
--- a/examples/UniformBox_3D/makeIC.py
+++ b/examples/UniformBox_3D/makeIC.py
@@ -57,10 +57,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/UniformBox_3D/uniformBox.yml b/examples/UniformBox_3D/uniformBox.yml
index 17dd5632edd345802402cb9c6d1dcf184e909806..202ff8298fe763a8c194ab4570b1252fe352dccc 100644
--- a/examples/UniformBox_3D/uniformBox.yml
+++ b/examples/UniformBox_3D/uniformBox.yml
@@ -31,3 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./uniformBox.hdf5     # The file to read
+  periodic:   1
diff --git a/examples/VacuumSpherical_2D/makeIC.py b/examples/VacuumSpherical_2D/makeIC.py
index 498f1b5bc5277188d8ff8d34a5ec24cd314332d4..05f0d8414cfa88755ecceb2be757e24ca3cefdde 100644
--- a/examples/VacuumSpherical_2D/makeIC.py
+++ b/examples/VacuumSpherical_2D/makeIC.py
@@ -77,10 +77,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/VacuumSpherical_2D/vacuum.yml b/examples/VacuumSpherical_2D/vacuum.yml
index 881b155b62c7f1f2af12a1d013ff5c05f1c16a88..1d5642d5c1b645808229c5c6b99fb6d319351880 100644
--- a/examples/VacuumSpherical_2D/vacuum.yml
+++ b/examples/VacuumSpherical_2D/vacuum.yml
@@ -31,4 +31,4 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./vacuum.hdf5       # The file to read
-
+  periodic:   1
diff --git a/examples/VacuumSpherical_3D/makeIC.py b/examples/VacuumSpherical_3D/makeIC.py
index d67a30707a904268a09641210a6a3bfcbf305dad..dd4ddd7e8a8d6335e4d3d3b383c54bf301a06f1d 100644
--- a/examples/VacuumSpherical_3D/makeIC.py
+++ b/examples/VacuumSpherical_3D/makeIC.py
@@ -80,10 +80,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/VacuumSpherical_3D/vacuum.yml b/examples/VacuumSpherical_3D/vacuum.yml
index 8792f029d97f413882ae0ea6c8603d64efaddbfa..851abf74441a48a58eac551bd0526f1d4b6e4ce0 100644
--- a/examples/VacuumSpherical_3D/vacuum.yml
+++ b/examples/VacuumSpherical_3D/vacuum.yml
@@ -32,4 +32,6 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./vacuum.hdf5       # The file to read
+  periodic:   1
 
+  
\ No newline at end of file
diff --git a/examples/Vacuum_1D/makeIC.py b/examples/Vacuum_1D/makeIC.py
index 067304ec951182da862cf2812cdc68a51a56d23b..5b057b340cbfa9718fb230ab1af839bc63678032 100644
--- a/examples/Vacuum_1D/makeIC.py
+++ b/examples/Vacuum_1D/makeIC.py
@@ -63,10 +63,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 1
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/Vacuum_1D/vacuum.yml b/examples/Vacuum_1D/vacuum.yml
index 5ef5ce3da68febb086a14ad1a2207711f680d9ff..0be6427e50e1f674f7f59d4b865f2c4f9605a378 100644
--- a/examples/Vacuum_1D/vacuum.yml
+++ b/examples/Vacuum_1D/vacuum.yml
@@ -31,4 +31,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./vacuum.hdf5       # The file to read
+  periodic:   1
 
diff --git a/examples/Vacuum_2D/makeIC.py b/examples/Vacuum_2D/makeIC.py
index ef267c092cafdb95457d5adad1e6858df0e14bd3..4d9181b83c0e383d0e3fb0dc6ca79dbda6f88891 100644
--- a/examples/Vacuum_2D/makeIC.py
+++ b/examples/Vacuum_2D/makeIC.py
@@ -71,10 +71,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 2
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/Vacuum_2D/vacuum.yml b/examples/Vacuum_2D/vacuum.yml
index 5ef5ce3da68febb086a14ad1a2207711f680d9ff..0be6427e50e1f674f7f59d4b865f2c4f9605a378 100644
--- a/examples/Vacuum_2D/vacuum.yml
+++ b/examples/Vacuum_2D/vacuum.yml
@@ -31,4 +31,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./vacuum.hdf5       # The file to read
+  periodic:   1
 
diff --git a/examples/Vacuum_3D/makeIC.py b/examples/Vacuum_3D/makeIC.py
index 09c3cb4d6f5525d54fab59643ab4a7d0540a2a92..cee2d28d5190305a3536315001453e7595b7c7f2 100644
--- a/examples/Vacuum_3D/makeIC.py
+++ b/examples/Vacuum_3D/makeIC.py
@@ -73,10 +73,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 1.
diff --git a/examples/Vacuum_3D/vacuum.yml b/examples/Vacuum_3D/vacuum.yml
index cf44d2441f5009d2fc75084a2c872e3618e40912..49bd9747d677bfdf64009bd1e02a86bc52a8db9c 100644
--- a/examples/Vacuum_3D/vacuum.yml
+++ b/examples/Vacuum_3D/vacuum.yml
@@ -32,4 +32,5 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./vacuum.hdf5       # The file to read
+  periodic:   1
 
diff --git a/examples/ZeldovichPancake_3D/makeIC.py b/examples/ZeldovichPancake_3D/makeIC.py
index 79ed7e71e924941102049b8457fe070ebd08f5c2..efce60f128cacd04e153912d97e0d94b4ab15785 100644
--- a/examples/ZeldovichPancake_3D/makeIC.py
+++ b/examples/ZeldovichPancake_3D/makeIC.py
@@ -123,10 +123,6 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 grp.attrs["Dimension"] = 3
 
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
 #Units
 grp = file.create_group("/Units")
 grp.attrs["Unit length in cgs (U_L)"] = 100. * unit_l_in_si
diff --git a/examples/ZeldovichPancake_3D/zeldovichPancake.yml b/examples/ZeldovichPancake_3D/zeldovichPancake.yml
index 4d83d805cfebb837f37058167a4e3c974a936317..a1d2342b56d6816c3cfbe7da70220ab244104fbd 100644
--- a/examples/ZeldovichPancake_3D/zeldovichPancake.yml
+++ b/examples/ZeldovichPancake_3D/zeldovichPancake.yml
@@ -39,7 +39,8 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./zeldovichPancake.hdf5       # The file to read
-
+  periodic:   1
+  
 Scheduler:
   max_top_level_cells: 8
   cell_split_size:     50
diff --git a/examples/ZoomIn/README b/examples/ZoomIn/README
new file mode 100644
index 0000000000000000000000000000000000000000..cffc275f2ae1046156d392f8725a7b542c80471a
--- /dev/null
+++ b/examples/ZoomIn/README
@@ -0,0 +1,16 @@
+Initial conditions for a zoom in cosmological simulation of dwarf
+galaxies. These have been generated by MUSIC and ran up to z=0 with
+GEAR (see Revaz and Jablonka 2018 for more details on the simulation).
+
+The cosmology is taken from Planck 2015.
+
+The initial conditions have been cleaned to contain only the required
+fields. The ICs have been created for Gadget and the positions and box
+size are hence expressed in h-full units (e.g. box size of 32 / h Mpc).
+Similarly, the peculiar velocitites contain an extra sqrt(a) factor. 
+
+We will use SWIFT to cancel the h- and a-factors from the ICs. Gas
+particles will be generated at startup.
+
+MD5 check-sum of the ICS: 
+9aafe154438478ed435e88664c1c5dba zoom_in.hdf5
diff --git a/examples/ZoomIn/getIC.sh b/examples/ZoomIn/getIC.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6cdfaec981af515249578faa72798c53448e7ecb
--- /dev/null
+++ b/examples/ZoomIn/getIC.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget https://obswww.unige.ch/~lhausamm/swift/IC/ZoomIn/zoom_in.hdf5
diff --git a/examples/ZoomIn/run.sh b/examples/ZoomIn/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..99eda1cfc779c1958d19d0c7ae234b6c211f8127
--- /dev/null
+++ b/examples/ZoomIn/run.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e zoom_in.hdf5 ]
+then
+    echo "Fetching initial conditions for the zoom in example..."
+    ./getIC.sh
+fi
+
+../swift -b -c -G -s -S -t 8 zoom_in.yml 2>&1 | tee output.log
+
diff --git a/examples/ZoomIn/zoom_in.yml b/examples/ZoomIn/zoom_in.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8e5763c4af700b7fd95beb6188ed886198b559b3
--- /dev/null
+++ b/examples/ZoomIn/zoom_in.yml
@@ -0,0 +1,62 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.98848e43    # 10^10 M_sun in grams
+  UnitLength_in_cgs:   3.08567758e21 # kpc in centimeters
+  UnitVelocity_in_cgs: 1e5           # km/s in centimeters per second
+  UnitCurrent_in_cgs:  1             # Amperes
+  UnitTemp_in_cgs:     1             # Kelvin
+
+# Cosmological parameters
+Cosmology:
+  h:              0.673        # Reduced Hubble constant
+  a_begin:        0.9873046739     # Initial scale-factor of the simulation
+  a_end:          1.0           # Final scale factor of the simulation
+  Omega_m:        0.315         # Matter density parameter
+  Omega_lambda:   0.685         # Dark-energy density parameter
+  Omega_b:        0.0486        # Baryon density parameter
+  
+Scheduler:
+  max_top_level_cells:    8
+  
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   1e-2  # The end time of the simulation (in internal units).
+  dt_min:     1e-10 # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            zoom_in # Common part of the name of output files
+  scale_factor_first:  0.987345  # Scale-factor of the first snaphot (cosmological run)
+  time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
+  delta_time:          1.01  # Time difference between consecutive outputs (in internal units)
+  compression:         1
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  scale_factor_first:  0.987345 # Scale-factor of the first stat dump (cosmological run)
+  time_first:          0.01 # Time of the first stat dump (non-cosmological run) (in internal units)
+  delta_time:          1.05 # Time between statistics output
+
+# Parameters for the self-gravity scheme
+Gravity:
+  eta:                    0.025    # Constant dimensionless multiplier for time integration.
+  theta:                  0.7      # Opening angle (Multipole acceptance criterion)
+  comoving_softening:     0.05 # Comoving softening length (in internal units).
+  max_physical_softening: 0.01    # Physical softening length (in internal units).
+  mesh_side_length:       16
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:   100      # (internal units)
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./zoom_in.hdf5     # The file to read
+  periodic:   1
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
diff --git a/examples/analye_runtime.py b/examples/analyse_runtime.py
similarity index 93%
rename from examples/analye_runtime.py
rename to examples/analyse_runtime.py
index 9adbe9c5cf722e2953c35b995bb9f9470ad052fb..9093dfc2346e7ee5f5707a7085a786672caa2104 100644
--- a/examples/analye_runtime.py
+++ b/examples/analyse_runtime.py
@@ -52,8 +52,9 @@ labels = ['Gpart assignment', 'Mesh comunication', 'Forward Fourier transform',
           'Making gravity tasks', 'Splitting tasks', 'Counting and linking tasks', 'Setting super-pointers', 'Linking gravity tasks',
           'Creating send tasks', 'Exchanging cell tags', 'Creating recv tasks', 'Setting unlocks', 'Ranking the tasks', 'scheduler_reweight:', 
           'space_rebuild:', 'engine_drift_all:', 'engine_unskip:', 'engine_collect_end_of_step:', 'engine_launch:', 'writing particle properties', 
-          'engine_repartition:', 'engine_exchange_cells:', 'Dumping restart files', 'engine_print_stats:', 'engine_marktasks:']
-is_rebuild = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1]
+          'engine_repartition:', 'engine_exchange_cells:', 'Dumping restart files', 'engine_print_stats:', 'engine_marktasks:', 
+          'Reading initial conditions', 'engine_print_task_counts:', 'engine_drift_top_multipoles:', 'Communicating rebuild flag']
+is_rebuild = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0]
 times = np.zeros(len(labels))
 counts = np.zeros(len(labels))
 
@@ -81,7 +82,7 @@ for i in range(num_files):
             # Extract the different blocks
             if re.search("%s took"%labels[i], line):
                 counts[i] += 1.
-                times[i] += float(re.findall(r'[+-]?([0-9]*[.])?[0-9]+', line)[-1])
+                times[i] += float(re.findall(r'[+-]?((\d+\.?\d*)|(\.\d+))', line)[-1][0])
 
         # Find the last line with meaningful output (avoid crash report, batch system stuf....)
         if re.findall(r'\[[0-9]{4}\][ ]\[*', line) or re.findall(r'^\[[0-9]*[.][0-9]+\][ ]', line):
diff --git a/examples/analyse_tasks.py b/examples/analyse_tasks.py
index 78d03ae5436a1fc56abcd6f6b99a4920011fa6db..48a00a63ea5da5cb80ebd6f10187cc613c1a5ed5 100755
--- a/examples/analyse_tasks.py
+++ b/examples/analyse_tasks.py
@@ -54,7 +54,7 @@ infile = args.input
 TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair",
              "init_grav", "init_grav_out", "ghost_in", "ghost", "ghost_out", "extra_ghost", "drift_part", "drift_gpart",
              "end_force", "kick1", "kick2", "timestep", "send", "recv", "grav_long_range", "grav_mm", "grav_down_in", 
-             "grav_down", "grav_mesh", "cooling", "sourceterms",
+             "grav_down", "grav_mesh", "cooling", "star_formation", "sourceterms",
              "stars_ghost_in", "stars_ghost",   "stars_ghost_out",
              "count"]
 
diff --git a/examples/getCoolingTable.sh b/examples/getCoolingTable.sh
old mode 100644
new mode 100755
diff --git a/examples/main.c b/examples/main.c
index 768640afe54f82cdd8208efeb1d13690e8e2c5e3..ca3c041702db6592a5d13e8d26ce7570ec9243a9 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -605,9 +605,23 @@ int main(int argc, char *argv[]) {
 
   /* How often to check for the stop file and dump restarts and exit the
    * application. */
-  int restart_stop_steps =
+  const int restart_stop_steps =
       parser_get_opt_param_int(params, "Restarts:stop_steps", 100);
 
+  /* Get the maximal wall-clock time of this run */
+  const float restart_max_hours_runtime =
+      parser_get_opt_param_float(params, "Restarts:max_run_time", FLT_MAX);
+
+  /* Do we want to resubmit when we hit the limit? */
+  const int resubmit_after_max_hours =
+      parser_get_opt_param_int(params, "Restarts:resubmit_on_exit", 0);
+
+  /* What command should we run to resubmit at the end? */
+  char resubmit_command[PARSER_MAX_LINE_SIZE];
+  if (resubmit_after_max_hours)
+    parser_get_param_string(params, "Restarts:resubmit_command",
+                            resubmit_command);
+
   /* If restarting, look for the restart files. */
   if (restart) {
 
@@ -693,6 +707,28 @@ int main(int argc, char *argv[]) {
       phys_const_print(&prog_const);
     }
 
+    /* Read particles and space information from ICs */
+    char ICfileName[200] = "";
+    parser_get_param_string(params, "InitialConditions:file_name", ICfileName);
+    const int periodic =
+        parser_get_param_int(params, "InitialConditions:periodic");
+    const int replicate =
+        parser_get_opt_param_int(params, "InitialConditions:replicate", 1);
+    clean_smoothing_length_values = parser_get_opt_param_int(
+        params, "InitialConditions:cleanup_smoothing_lengths", 0);
+    const int cleanup_h = parser_get_opt_param_int(
+        params, "InitialConditions:cleanup_h_factors", 0);
+    const int cleanup_sqrt_a = parser_get_opt_param_int(
+        params, "InitialConditions:cleanup_velocity_factors", 0);
+    const int generate_gas_in_ics = parser_get_opt_param_int(
+        params, "InitialConditions:generate_gas_in_ics", 0);
+
+    /* Some checks that we are not doing something stupid */
+    if (generate_gas_in_ics && flag_entropy_ICs)
+      error("Can't generate gas if the entropy flag is set in the ICs.");
+    if (generate_gas_in_ics && !with_cosmology)
+      error("Can't generate gas if the run is not cosmological.");
+
     /* Initialise the cosmology */
     if (with_cosmology)
       cosmology_init(params, &us, &prog_const, &cosmo);
@@ -721,27 +757,12 @@ int main(int argc, char *argv[]) {
 
     /* Initialise the gravity properties */
     if (with_self_gravity)
-      gravity_props_init(&gravity_properties, params, &cosmo, with_cosmology);
+      gravity_props_init(&gravity_properties, params, &cosmo, with_cosmology,
+                         periodic);
     else
       bzero(&gravity_properties, sizeof(struct gravity_props));
 
-    /* Read particles and space information from (GADGET) ICs */
-    char ICfileName[200] = "";
-    parser_get_param_string(params, "InitialConditions:file_name", ICfileName);
-    const int replicate =
-        parser_get_opt_param_int(params, "InitialConditions:replicate", 1);
-    clean_smoothing_length_values = parser_get_opt_param_int(
-        params, "InitialConditions:cleanup_smoothing_lengths", 0);
-    const int cleanup_h = parser_get_opt_param_int(
-        params, "InitialConditions:cleanup_h_factors", 0);
-    const int cleanup_sqrt_a = parser_get_opt_param_int(
-        params, "InitialConditions:cleanup_velocity_factors", 0);
-    const int generate_gas_in_ics = parser_get_opt_param_int(
-        params, "InitialConditions:generate_gas_in_ics", 0);
-    if (generate_gas_in_ics && flag_entropy_ICs)
-      error("Can't generate gas if the entropy flag is set in the ICs.");
-    if (generate_gas_in_ics && !with_cosmology)
-      error("Can't generate gas if the run is not cosmological.");
+    /* Be verbose about what happens next */
     if (myrank == 0) message("Reading ICs from file '%s'", ICfileName);
     if (myrank == 0 && cleanup_h)
       message("Cleaning up h-factors (h=%f)", cosmo.h);
@@ -752,20 +773,19 @@ int main(int argc, char *argv[]) {
     /* Get ready to read particles of all kinds */
     size_t Ngas = 0, Ngpart = 0, Nspart = 0;
     double dim[3] = {0., 0., 0.};
-    int periodic = 0;
     if (myrank == 0) clocks_gettime(&tic);
 #if defined(HAVE_HDF5)
 #if defined(WITH_MPI)
 #if defined(HAVE_PARALLEL_HDF5)
     read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas,
-                     &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro,
+                     &Ngpart, &Nspart, &flag_entropy_ICs, with_hydro,
                      (with_external_gravity || with_self_gravity), with_stars,
                      cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank,
                      nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads,
                      dry_run);
 #else
     read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas,
-                   &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro,
+                   &Ngpart, &Nspart, &flag_entropy_ICs, with_hydro,
                    (with_external_gravity || with_self_gravity), with_stars,
                    cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank,
                    nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads,
@@ -773,7 +793,7 @@ int main(int argc, char *argv[]) {
 #endif
 #else
     read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas,
-                   &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro,
+                   &Ngpart, &Nspart, &flag_entropy_ICs, with_hydro,
                    (with_external_gravity || with_self_gravity), with_stars,
                    cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads,
                    dry_run);
@@ -931,6 +951,9 @@ int main(int argc, char *argv[]) {
       engine_policies |= engine_policy_structure_finding;
     if (with_fof) engine_policies |= engine_policy_fof;
 
+    // MATTHIEU: Temporary star formation law
+    // engine_policies |= engine_policy_star_formation;
+
     /* Initialize the engine with the space and policies. */
     if (myrank == 0) clocks_gettime(&tic);
     engine_init(&e, &s, params, N_total[0], N_total[1], N_total[2],
@@ -1001,6 +1024,10 @@ int main(int argc, char *argv[]) {
     engine_init_particles(&e, flag_entropy_ICs, clean_smoothing_length_values, 1);
 
     /* Write the state of the system before starting time integration. */
+#ifdef WITH_LOGGER
+    logger_log_all(e.logger, &e);
+    engine_dump_index(&e);
+#endif
     engine_dump_snapshot(&e);
     engine_print_stats(&e);
   
@@ -1049,7 +1076,7 @@ int main(int argc, char *argv[]) {
 
   /* Main simulation loop */
   /* ==================== */
-  int force_stop = 0;
+  int force_stop = 0, resubmit = 0;
   for (int j = 0; !engine_is_done(&e) && e.step - 1 != nsteps && !force_stop;
        j++) {
 
@@ -1070,6 +1097,13 @@ int main(int argc, char *argv[]) {
         message("Forcing application exit, dumping restart files...");
     }
 
+    /* Did we exceed the maximal runtime? */
+    if (clocks_get_hours_since_start() > restart_max_hours_runtime) {
+      force_stop = 1;
+      message("Runtime limit reached, dumping restart files...");
+      if (resubmit_after_max_hours) resubmit = 1;
+    }
+
     /* Also if using nsteps to exit, will not have saved any restarts on exit,
      * make sure we do that (useful in testing only). */
     if (force_stop || (e.restart_onexit && e.step - 1 == nsteps))
@@ -1081,8 +1115,8 @@ int main(int argc, char *argv[]) {
 #ifdef WITH_MPI
 
       /* Make sure output file is empty, only on one rank. */
-      char dumpfile[30];
-      snprintf(dumpfile, 30, "thread_info_MPI-step%d.dat", j + 1);
+      char dumpfile[35];
+      snprintf(dumpfile, sizeof(dumpfile), "thread_info_MPI-step%d.dat", j + 1);
       FILE *file_thread;
       if (myrank == 0) {
         file_thread = fopen(dumpfile, "w");
@@ -1108,21 +1142,24 @@ int main(int argc, char *argv[]) {
           int count = 0;
           for (int l = 0; l < e.sched.nr_tasks; l++) {
             if (!e.sched.tasks[l].implicit && e.sched.tasks[l].toc != 0) {
-              fprintf(
-                  file_thread,
-                  " %03i %i %i %i %i %lli %lli %i %i %i %i %lli %i\n", myrank,
-                  e.sched.tasks[l].rid, e.sched.tasks[l].type,
-                  e.sched.tasks[l].subtype, (e.sched.tasks[l].cj == NULL),
-                  e.sched.tasks[l].tic, e.sched.tasks[l].toc,
-                  (e.sched.tasks[l].ci != NULL) ? e.sched.tasks[l].ci->count
-                                                : 0,
-                  (e.sched.tasks[l].cj != NULL) ? e.sched.tasks[l].cj->count
-                                                : 0,
-                  (e.sched.tasks[l].ci != NULL) ? e.sched.tasks[l].ci->gcount
-                                                : 0,
-                  (e.sched.tasks[l].cj != NULL) ? e.sched.tasks[l].cj->gcount
-                                                : 0,
-                  e.sched.tasks[l].flags, e.sched.tasks[l].sid);
+              fprintf(file_thread,
+                      " %03i %i %i %i %i %lli %lli %i %i %i %i %lli %i\n",
+                      myrank, e.sched.tasks[l].rid, e.sched.tasks[l].type,
+                      e.sched.tasks[l].subtype, (e.sched.tasks[l].cj == NULL),
+                      e.sched.tasks[l].tic, e.sched.tasks[l].toc,
+                      (e.sched.tasks[l].ci != NULL)
+                          ? e.sched.tasks[l].ci->hydro.count
+                          : 0,
+                      (e.sched.tasks[l].cj != NULL)
+                          ? e.sched.tasks[l].cj->hydro.count
+                          : 0,
+                      (e.sched.tasks[l].ci != NULL)
+                          ? e.sched.tasks[l].ci->grav.count
+                          : 0,
+                      (e.sched.tasks[l].cj != NULL)
+                          ? e.sched.tasks[l].cj->grav.count
+                          : 0,
+                      e.sched.tasks[l].flags, e.sched.tasks[l].sid);
             }
             fflush(stdout);
             count++;
@@ -1135,8 +1172,8 @@ int main(int argc, char *argv[]) {
       }
 
 #else
-      char dumpfile[30];
-      snprintf(dumpfile, 30, "thread_info-step%d.dat", j + 1);
+      char dumpfile[32];
+      snprintf(dumpfile, sizeof(dumpfile), "thread_info-step%d.dat", j + 1);
       FILE *file_thread;
       file_thread = fopen(dumpfile, "w");
       /* Add some information to help with the plots */
@@ -1150,10 +1187,14 @@ int main(int argc, char *argv[]) {
               e.sched.tasks[l].rid, e.sched.tasks[l].type,
               e.sched.tasks[l].subtype, (e.sched.tasks[l].cj == NULL),
               e.sched.tasks[l].tic, e.sched.tasks[l].toc,
-              (e.sched.tasks[l].ci == NULL) ? 0 : e.sched.tasks[l].ci->count,
-              (e.sched.tasks[l].cj == NULL) ? 0 : e.sched.tasks[l].cj->count,
-              (e.sched.tasks[l].ci == NULL) ? 0 : e.sched.tasks[l].ci->gcount,
-              (e.sched.tasks[l].cj == NULL) ? 0 : e.sched.tasks[l].cj->gcount,
+              (e.sched.tasks[l].ci == NULL) ? 0
+                                            : e.sched.tasks[l].ci->hydro.count,
+              (e.sched.tasks[l].cj == NULL) ? 0
+                                            : e.sched.tasks[l].cj->hydro.count,
+              (e.sched.tasks[l].ci == NULL) ? 0
+                                            : e.sched.tasks[l].ci->grav.count,
+              (e.sched.tasks[l].cj == NULL) ? 0
+                                            : e.sched.tasks[l].cj->grav.count,
               e.sched.tasks[l].sid);
         }
       }
@@ -1212,17 +1253,24 @@ int main(int argc, char *argv[]) {
   }
 
   /* Write final output. */
-  engine_drift_all(&e);
-  engine_print_stats(&e);
-  engine_dump_snapshot(&e);
+  if (!force_stop) {
+    engine_drift_all(&e);
+    engine_print_stats(&e);
+#ifdef WITH_LOGGER
+    logger_log_all(e.logger, &e);
+    engine_dump_index(&e);
+#endif
+    // write a final snapshot with logger, in order to facilitate a restart
+    engine_dump_snapshot(&e);
 
 #ifdef HAVE_VELOCIRAPTOR
-  /* Call VELOCIraptor at the end of the run to find groups. */
-  if (e.policy & engine_policy_structure_finding) {
-    velociraptor_init(&e);
-    velociraptor_invoke(&e);
-  }
+    /* Call VELOCIraptor at the end of the run to find groups. */
+    if (e.policy & engine_policy_structure_finding) {
+      velociraptor_init(&e);
+      velociraptor_invoke(&e);
+    }
 #endif
+  }
 
 #ifdef WITH_MPI
   if ((res = MPI_Finalize()) != MPI_SUCCESS)
@@ -1233,10 +1281,19 @@ int main(int argc, char *argv[]) {
    * stop file if normal exit happened first. */
   if (myrank == 0) force_stop = restart_stop_now(restart_dir, 1);
 
+  /* Did we want to run a re-submission command just before dying? */
+  if (myrank == 0 && resubmit) {
+    message("Running the resubmission command:");
+    restart_resubmit(resubmit_command);
+    fflush(stdout);
+    fflush(stderr);
+    message("resubmission command completed.");
+  }
+
   /* Clean everything */
   if (with_verbose_timers) timers_close_file();
-  if (with_cosmology) cosmology_clean(&cosmo);
-  if (with_self_gravity) pm_mesh_clean(&mesh);
+  if (with_cosmology) cosmology_clean(e.cosmology);
+  if (with_self_gravity) pm_mesh_clean(e.mesh);
   engine_clean(&e);
   free(params);
 
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index 3f83a8f25b312cb167922f7e094c616f16211bf9..63a61af3e714d5dd6b19312e9a233f51deda0011 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -34,6 +34,10 @@ SPH:
   minimal_temperature:   0        # (Optional) Minimal temperature (in internal units) allowed for the gas particles. Value is ignored if set to 0.
   H_mass_fraction:       0.755    # (Optional) Hydrogen mass fraction used for initial conversion from temp to internal energy. Default value is derived from the physical constants.
   H_ionization_temperature: 1e4   # (Optional) Temperature of the transition from neutral to ionized Hydrogen for primoridal gas.
+  viscosity_alpha:       0.8      # (Optional) Override for the initial value of the artificial viscosity. In schemes that have a fixed AV, this remains as alpha throughout the run.
+  viscosity_alpha_max:   2.0      # (Optional) Maximal value for the artificial viscosity in schemes that allow alpha to vary
+  viscosity_alpha_min:   0.1      # (Optional) Minimal value for the artificial viscosity in schemes that allow alpha to vary
+  viscosity_length:      0.1      # (Optional) Decay length for the artificial viscosity in schemes that allow alpha to vary
 
 # Parameters for the self-gravity scheme
 Gravity:
@@ -87,6 +91,13 @@ Snapshots:
   output_list_on:      0  # (Optional) Enable the output list
   output_list:         snaplist.txt # (Optional) File containing the output times (see documentation in "Parameter File" section)
 
+# Parameters governing the logger snapshot system
+Logger:
+  delta_step:           10     # Update the particle log every this many updates
+  initial_buffer_size:  1      # buffer size in GB
+  buffer_scale:		10     # (Optional) When buffer size is too small, update it with required memory times buffer_scale
+  basename:             index  # Common part of the filenames
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:           1e-2     # Time between statistics output
@@ -100,6 +111,7 @@ Statistics:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  SedovBlast/sedov.hdf5 # The file to read
+  periodic:                    1    # Are we running with periodic ICs?
   generate_gas_in_ics:         0    # (Optional) Generate gas particles from the DM-only ICs (e.g. from panphasia).
   cleanup_h_factors:           0    # (Optional) Clean up the h-factors used in the ICs (e.g. in Gadget files).
   cleanup_velocity_factors:    0    # (Optional) Clean up the scale-factors used in the definition of the velocity variable in the ICs (e.g. in Gadget files).
@@ -110,13 +122,16 @@ InitialConditions:
 
 # Parameters controlling restarts
 Restarts:
-  enable:      1        # (Optional) whether to enable dumping restarts at fixed intervals.
-  save:        1        # (Optional) whether to save copies of the previous set of restart files (named .prev)
-  onexit:      0        # (Optional) whether to dump restarts on exit (*needs enable*)
-  subdir:      restart  # (Optional) name of subdirectory for restart files.
-  basename:    swift    # (Optional) prefix used in naming restart files.
-  delta_hours: 6.0      # (Optional) decimal hours between dumps of restart files.
-  stop_steps:  100      # (Optional) how many steps to process before checking if the <subdir>/stop file exists. When present the application will attempt to exit early, dumping restart files first.
+  enable:             1          # (Optional) whether to enable dumping restarts at fixed intervals.
+  save:               1          # (Optional) whether to save copies of the previous set of restart files (named .prev)
+  onexit:             0          # (Optional) whether to dump restarts on exit (*needs enable*)
+  subdir:             restart    # (Optional) name of subdirectory for restart files.
+  basename:           swift      # (Optional) prefix used in naming restart files.
+  delta_hours:        6.0        # (Optional) decimal hours between dumps of restart files.
+  stop_steps:         100        # (Optional) how many steps to process before checking if the <subdir>/stop file exists. When present the application will attempt to exit early, dumping restart files first.
+  max_run_time:       24.0       # (optional) Maximal wall-clock time in hours. The application will exit when this limit is reached.
+  resubmit_on_exit:   0          # (Optional) whether to run a command when exiting after the time limit has been reached.
+  resubmit_command:   ./resub.sh # (Optional) Command to run when time limit is reached. Compulsory if resubmit_on_exit is switched on. Note potentially unsafe.
 
 # Parameters governing domain decomposition
 DomainDecomposition:
@@ -133,9 +148,9 @@ DomainDecomposition:
                             # new decomposition, or number of steps (>1) between decompositions
   minfrac:          0.9     # (Optional) Fractional of all particles that should be updated in previous step when
                             # using CPU time trigger
-  usemetis          0       # Use serial METIS when ParMETIS is also available.
-  adaptive          1       # Use adaptive repartition when ParMETIS is available, otherwise simple refinement.
-  itr               100     # When adaptive defines the ratio of inter node communication time to data redistribution time, in the range 0.00001 to 10000000.0.
+  usemetis:         0       # Use serial METIS when ParMETIS is also available.
+  adaptive:         1       # Use adaptive repartition when ParMETIS is available, otherwise simple refinement.
+  itr:              100     # When adaptive defines the ratio of inter node communication time to data redistribution time, in the range 0.00001 to 10000000.0.
                             # Lower values give less data movement during redistributions, at the cost of global balance which may require more communication.
 
 # Parameters related to the equation of state ------------------------------------------
@@ -160,6 +175,7 @@ EoS:
 
 # Point mass external potentials
 PointMassPotential:
+  useabspos:       0        # 0 -> positions based on centre, 1 -> absolute positions 
   position:        [50.,50.0,50.]      # location of external point mass (internal units)
   mass:            1e10                # mass of external point mass (internal units)
   timestep_mult:   0.03                # Dimensionless pre-factor for the time-step condition
@@ -167,10 +183,29 @@ PointMassPotential:
 
 # Isothermal potential parameters
 IsothermalPotential:
+  useabspos:       0        # 0 -> positions based on centre, 1 -> absolute positions 
   position:        [100.,100.,100.]    # Location of centre of isothermal potential with respect to centre of the box (internal units)
   vrot:            200.     # Rotation speed of isothermal potential (internal units)
   timestep_mult:   0.03     # Dimensionless pre-factor for the time-step condition
   epsilon:         0.1      # Softening size (internal units)
+  
+# Hernquist potential parameters
+HernquistPotential:
+  useabspos:       0        # 0 -> positions based on centre, 1 -> absolute positions 
+  position:        [100.,100.,100.]    # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units)
+  mass:            1e10     # Mass of the Hernquist potential
+  scalelength:     10.0     # Scale length of the potential
+  timestep_mult:   0.01     # Dimensionless pre-factor for the time-step condition, basically determines the fraction of the orbital time we use to do the time integration
+  epsilon:         0.1      # Softening size (internal units)
+ 
+# Isothermal potential parameters
+NFWPotential:
+  useabspos:          0
+  position:           [0.0,0.0,0.0]      # Location of centre of isothermal potential with respect to centre of the box (internal units) if useabspos=0 otherwise with respect to the 0,0,0, coordinates.
+  concentration:      8.       # Concentration of the halo
+  M_200:              2.0e+12  # Mass of the halo (M_200 in internal units)
+  critical_density:   127.4    # Critical density (internal units).
+  timestep_mult:      0.01     # Dimensionless pre-factor for the time-step condition, basically determines fraction of orbital time we need to do an integration step
 
 # Disk-patch potential parameters
 DiscPatchPotential:
@@ -198,11 +233,8 @@ ConstCooling:
 
 # Constant lambda cooling function
 LambdaCooling:
-  lambda:                      2.0   # Cooling rate (in cgs units)
-  minimum_temperature:         1.0e4 # Minimal temperature (Kelvin)
-  mean_molecular_weight:       0.59  # Mean molecular weight
-  hydrogen_mass_abundance:     0.75  # Hydrogen mass abundance (dimensionless)
-  cooling_tstep_mult:          1.0   # Dimensionless pre-factor for the time-step condition
+  lambda_nH2_cgs:              1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3])
+  cooling_tstep_mult:          1.0   # (Optional) Dimensionless pre-factor for the time-step condition.
 
 # Cooling with Grackle 3.0
 GrackleCooling:
diff --git a/examples/plot_task_level.py b/examples/plot_task_level.py
new file mode 100644
index 0000000000000000000000000000000000000000..2d51f4829692bc71826ec6c4b93c025f7106828f
--- /dev/null
+++ b/examples/plot_task_level.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+"""
+Usage:
+  ./plot_task_level.py task_level.txt
+
+Description:
+  Plot the number of tasks for each depth level and each type of task.
+"""
+
+
+import pandas as pd
+import matplotlib.pyplot as plt
+import sys
+
+# get filename
+filename = sys.argv[-1]
+
+# Column names
+names = ["type", "subtype", "depth", "count"]
+
+# read file
+data = pd.read_csv(filename, sep=' ', comment="#", names=names)
+
+# generate color map
+cmap = plt.get_cmap("hsv")
+N = data["depth"].max() + 5
+
+# plot data
+for i in range(data["depth"].max()):
+    ind = data["depth"] == i
+    label = "depth = %i" % i
+    c = cmap(i / N)
+    plt.plot(data["type"][ind] + "_" + data["subtype"][ind],
+             data["count"][ind], ".", label=label,
+             color=c)
+
+# modify figure parameters and show it
+plt.gca().set_yscale("log")
+plt.xticks(rotation=45)
+plt.ylabel("Number of Tasks")
+plt.gcf().subplots_adjust(bottom=0.15)
+plt.legend()
+plt.show()
diff --git a/examples/plot_tasks.py b/examples/plot_tasks.py
index a8d8c49a7ca8676278ac1ab50c7b6cdf3755445c..76128ac66af21cd5f6d9a7ab0546ed813bde4536 100755
--- a/examples/plot_tasks.py
+++ b/examples/plot_tasks.py
@@ -112,7 +112,7 @@ pl.rcParams.update(PLOT_PARAMS)
 TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair",
              "init_grav", "init_grav_out", "ghost_in", "ghost", "ghost_out", "extra_ghost", "drift_part", "drift_gpart",
              "end_force", "kick1", "kick2", "timestep", "send", "recv", "grav_long_range", "grav_mm", "grav_down_in", 
-             "grav_down", "grav_mesh", "cooling", "sourceterms",
+             "grav_down", "grav_mesh", "cooling", "star_formation", "sourceterms",
              "stars_ghost_in", "stars_ghost",   "stars_ghost_out",
              "count"]
 
@@ -157,11 +157,11 @@ for task in SUBTYPES:
 
 #  For fiddling with colours...
 if args.verbose:
-    print "#Selected colours:"
+    print("#Selected colours:")
     for task in sorted(TASKCOLOURS.keys()):
-        print "# " + task + ": " + TASKCOLOURS[task]
+        print("# " + task + ": " + TASKCOLOURS[task])
     for task in sorted(SUBCOLOURS.keys()):
-        print "# " + task + ": " + SUBCOLOURS[task]
+        print("# " + task + ": " + SUBCOLOURS[task])
 
 #  Read input.
 data = pl.loadtxt( infile )
@@ -169,11 +169,11 @@ data = pl.loadtxt( infile )
 #  Do we have an MPI file?
 full_step = data[0,:]
 if full_step.size == 13:
-    print "# MPI mode"
+    print("# MPI mode")
     mpimode = True
     if ranks == None:
         ranks = range(int(max(data[:,0])) + 1)
-    print "# Number of ranks:", len(ranks)
+    print("# Number of ranks:", len(ranks))
     rankcol = 0
     threadscol = 1
     taskcol = 2
@@ -181,7 +181,7 @@ if full_step.size == 13:
     ticcol = 5
     toccol = 6
 else:
-    print "# non MPI mode"
+    print("# non MPI mode")
     ranks = [0]
     mpimode = False
     rankcol = -1
@@ -194,10 +194,10 @@ else:
 #  Get CPU_CLOCK to convert ticks into milliseconds.
 CPU_CLOCK = float(full_step[-1]) / 1000.0
 if args.verbose:
-    print "# CPU frequency:", CPU_CLOCK * 1000.0
+    print("# CPU frequency:", CPU_CLOCK * 1000.0)
 
 nthread = int(max(data[:,threadscol])) + 1
-print "# Number of threads:", nthread
+print("# Number of threads:", nthread)
 
 # Avoid start and end times of zero.
 sdata = data[data[:,ticcol] != 0]
@@ -224,28 +224,31 @@ if delta_t == 0:
         dt = toc_step - tic_step
         if dt > delta_t:
             delta_t = dt
-    print "# Data range: ", delta_t / CPU_CLOCK, "ms"
+    print("# Data range: ", delta_t / CPU_CLOCK, "ms")
 
 # Once more doing the real gather and plots this time.
 for rank in ranks:
-    print "# Processing rank: ", rank
+    print("# Processing rank: ", rank)
     if mpimode:
         data = sdata[sdata[:,rankcol] == rank]
         full_step = data[0,:]
     tic_step = int(full_step[ticcol])
     toc_step = int(full_step[toccol])
-    print "# Min tic = ", tic_step
+    print("# Min tic = ", tic_step)
     data = data[1:,:]
     typesseen = []
     nethread = 0
 
     #  Dummy image for ranks that have no tasks.
     if data.size == 0:
-        print "# Rank ", rank, " has no tasks"
+        print("# Rank ", rank, " has no tasks")
         fig = pl.figure()
         ax = fig.add_subplot(1,1,1)
         ax.set_xlim(-delta_t * 0.01 / CPU_CLOCK, delta_t * 1.01 / CPU_CLOCK)
-        ax.set_ylim(0, nthread*expand)
+        if nthread == 0:
+            ax.set_ylim(0, expand)
+        else:
+            ax.set_ylim(0, nthread*expand)
         if mintic < 0:
             start_t = tic_step
         else:
@@ -305,7 +308,7 @@ for rank in ranks:
         fig = pl.figure()
         ax = fig.add_subplot(1,1,1)
         ax.set_xlim(-delta_t * 0.01 / CPU_CLOCK, delta_t * 1.01 / CPU_CLOCK)
-        ax.set_ylim(0, nethread)
+        ax.set_ylim(0.5, nethread+1.0)
         for i in range(nethread):
 
             #  Collect ranges and colours into arrays.
@@ -327,19 +330,16 @@ for rank in ranks:
                     typesseen.append(qtask)
 
             #  Now plot.
-            ax.broken_barh(tictocs, [i+0.05,0.90], facecolors = colours, linewidth=0)
+            ax.broken_barh(tictocs, [i+0.55,0.9], facecolors = colours, linewidth=0)
 
 
     #  Legend and room for it.
-    nrow = len(typesseen) / 5
-    ax.fill_between([0, 0], nethread+0.5, nethread + nrow + 0.5, facecolor="white")
-    ax.set_ylim(0, nethread + 0.5)
+    nrow = len(typesseen) / 8
+    ax.fill_between([0, 0], nethread, nethread + nrow, facecolor="white")
     if data.size > 0 and not args.nolegend:
-        ax.fill_between([0, 0], nethread+0.5, nethread + nrow + 0.5, facecolor="white")
-        ax.set_ylim(0, nethread + 0.5)
-        ax.legend(loc=1, shadow=True, bbox_to_anchor=(0., 1.05 ,1., 0.2), mode="expand", ncol=5)
-        box = ax.get_position()
-        ax.set_position([box.x0, box.y0, box.width, box.height*0.8])
+        ax.fill_between([0, 0], nethread, nethread + nrow, facecolor="white")
+        ax.legend(loc="lower left", shadow=True,
+                  bbox_to_anchor=(0., 1.0, 1., 0.2), mode="expand", ncol=8)
 
     # Start and end of time-step
     if mintic < 0:
@@ -366,7 +366,7 @@ for rank in ranks:
         outpng = outbase + str(rank) + ".png"
     else:
         outpng = outbase + ".png"
-    pl.savefig(outpng)
-    print "Graphics done, output written to", outpng
+    pl.savefig(outpng, bbox_inches="tight")
+    print("Graphics done, output written to", outpng)
 
 sys.exit(0)
diff --git a/examples/process_plot_tasks_MPI b/examples/process_plot_tasks_MPI
index 22c9a106f52ca28244f9fef60839b1125474f14c..736aad05b98aea619f79e2b2114815c8e0fbaa1c 100755
--- a/examples/process_plot_tasks_MPI
+++ b/examples/process_plot_tasks_MPI
@@ -87,10 +87,22 @@ echo $list | xargs -n 3 | while read f s g; do
 <ul style="list-style-type:none">
 <li>
 EOF
+
+    cat <<EOF3 > step${s}r.html
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<body>
+EOF3
+
     for i in $(seq 0 $nrank); do
-        cat <<EOF2 >> index.html
-<a href="step${s}r${i}.html"><img src="step${s}r${i}.png" width=400px/></a>
-EOF2
+
+        cat <<EOF >> index.html
+<a href="step${s}r.html"><img src="step${s}r${i}.png" width=400px/></a>
+EOF
+        cat <<EOF3 >> step${s}r.html
+<a href="step${s}r${i}.html"><img src="step${s}r${i}.png"/></a>
+EOF3
+
     cat <<EOF2 > step${s}r${i}.html
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html>
@@ -104,7 +116,14 @@ cat <<EOF2 >> step${s}r${i}.html
 </body>
 </html>
 EOF2
+
     done
+
+cat <<EOF3 >> step${s}r.html
+</body>
+</html>
+EOF3
+
 cat <<EOF >> index.html
 </li>
 </ul>
diff --git a/m4/ax_gcc_archflag.m4 b/m4/ax_gcc_archflag.m4
index b91c9e8f4003ce7ee70a3f587b89df754f7302d5..ef8e7c199da1622354a029ec142386b7f1f9e442 100644
--- a/m4/ax_gcc_archflag.m4
+++ b/m4/ax_gcc_archflag.m4
@@ -65,7 +65,7 @@
 #   modified version of the Autoconf Macro, you may extend this special
 #   exception to the GPL to apply to your modified version as well.
 
-#serial 21 (modified for SWIFT)
+#serial 22 (modified for SWIFT)
 
 AC_DEFUN([AX_GCC_ARCHFLAG],
 [AC_REQUIRE([AC_PROG_CC])
@@ -109,7 +109,7 @@ case $host_cpu in
 	    *3?6[[ae]]?:*:*:*) ax_gcc_arch="ivybridge core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;;
 	    *3?6[[cf]]?:*:*:*|*4?6[[56]]?:*:*:*) ax_gcc_arch="haswell core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;;
 	    *3?6d?:*:*:*|*4?6[[7f]]?:*:*:*|*5?66?:*:*:*) ax_gcc_arch="broadwell core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;;
-	    *4?6[[de]]?:*:*:*) ax_gcc_arch="skylake haswell sandybridge core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;;	
+	    *4?6[[de]]?:*:*:*|*5?6[[de]]?:*:*:*) ax_gcc_arch="skylake haswell sandybridge core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;;	
 	    *5?6[[56]]?:*:*:*) ax_gcc_arch="skylake-avx512 skylake haswell sandybridge core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;;	
             *8?6[[de]]?:*:*:*|*9?6[[de]]?:*:*:*) ax_gcc_arch="kabylake skylake broadwell haswell sandybridge core-avx2 core-avx-i corei7-avx corei7 core2 pentium-m pentium3 pentiumpro" ;;
 	    *1?6c?:*:*:*|*2?6[[67]]?:*:*:*|*3?6[[56]]?:*:*:*) ax_gcc_arch="bonnell atom core2 pentium-m pentium3 pentiumpro" ;;
@@ -201,6 +201,10 @@ case $host_cpu in
        *POWER4*|*power4*|*gq*) ax_gcc_arch="power4 970";;
        *POWER5*|*power5*|*gr*|*gs*) ax_gcc_arch="power5 power4 970";;
        603ev|8240) ax_gcc_arch="$cputype 603e 603";;
+       *POWER7*) ax_gcc_arch="power7";;
+       *POWER8*) ax_gcc_arch="power8";;
+       *POWER9*) ax_gcc_arch="power9";;
+       *POWER10*) ax_gcc_arch="power10";;
        *) ax_gcc_arch=$cputype ;;
      esac
      ax_gcc_arch="$ax_gcc_arch powerpc"
diff --git a/src/Makefile.am b/src/Makefile.am
index 5f486c93122cf593312119041c99d476b0dc3629..839ad42a724195896da0a5fea8d05141d6656f59 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,7 +16,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # Add the non-standard paths to the included library headers
-AM_CFLAGS = $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS)
+AM_CFLAGS = $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(GRACKLE_INCS)
 
 # Assign a "safe" version number
 AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0
@@ -25,7 +25,7 @@ AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0
 GIT_CMD = @GIT_CMD@
 
 # Additional dependencies for shared libraries.
-EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIB) $(GSL_LIBS)
+EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS)
 
 # MPI libraries.
 MPI_LIBS = $(PARMETIS_LIBS) $(METIS_LIBS) $(MPI_THREAD_LIBS)
@@ -48,11 +48,12 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \
     dump.h logger.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h \
     gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h \
     chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h space_getsid.h utilities.h \
-    mesh_gravity.h cbrt.h velociraptor_interface.h swift_velociraptor_part.h outputlist.h fof.h
+    mesh_gravity.h cbrt.h velociraptor_interface.h swift_velociraptor_part.h outputlist.h fof.h \
+    logger_io.h
 
 # Common source files
-AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \
-    serial_io.c timers.c debug.c scheduler.c proxy.c parallel_io.c \
+AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c engine_maketasks.c \
+    engine_marktasks.c serial_io.c timers.c debug.c scheduler.c proxy.c parallel_io.c \
     units.c common_io.c single_io.c multipole.c version.c map.c \
     kernel_hydro.c tools.c part.c partition.c clocks.c parser.c \
     physical_constants.c potential.c hydro_properties.c \
@@ -61,13 +62,14 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \
     part_type.c xmf.c gravity_properties.c gravity.c \
     collectgroup.c hydro_space.c equation_of_state.c \
     chemistry.c cosmology.c restart.c mesh_gravity.c velociraptor_interface.c \
-    outputlist.c fof.c
+    outputlist.c velociraptor_dummy.c logger_io.c fof.c
 
 # Include files for distribution, not installation.
 nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \
 		 gravity_iact.h kernel_long_gravity.h vector.h cache.h runner_doiact.h runner_doiact_vec.h runner_doiact_grav.h  \
                  runner_doiact_nosort.h runner_doiact_stars.h units.h intrinsics.h minmax.h kick.h timestep.h drift.h \
 		 adiabatic_index.h io_properties.h dimension.h part_type.h periodic.h memswap.h dump.h logger.h sign.h \
+		 logger_io.h \
 		 gravity.h gravity_io.h gravity_cache.h \
 		 gravity/Default/gravity.h gravity/Default/gravity_iact.h gravity/Default/gravity_io.h \
 		 gravity/Default/gravity_debug.h gravity/Default/gravity_part.h  \
@@ -133,6 +135,8 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h
                  potential/sine_wave/potential.h \
 		 cooling/none/cooling.h cooling/none/cooling_struct.h \
                  cooling/none/cooling_io.h \
+		 cooling/Compton/cooling.h cooling/Compton/cooling_struct.h \
+                 cooling/Compton/cooling_io.h \
 	         cooling/const_du/cooling.h cooling/const_du/cooling_struct.h \
                  cooling/const_du/cooling_io.h \
                  cooling/const_lambda/cooling.h cooling/const_lambda/cooling_struct.h \
diff --git a/src/active.h b/src/active.h
index 5b8c2684822ebbc1d1c86b6ebb0d7d6a869959a4..a3d2d5ad90c93b06bbd4853a2633d087304ee570 100644
--- a/src/active.h
+++ b/src/active.h
@@ -39,15 +39,16 @@ __attribute__((always_inline)) INLINE static int cell_are_part_drifted(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_old_part > e->ti_current)
+  if (c->hydro.ti_old_part > e->ti_current)
     error(
-        "Cell has been drifted too far forward in time! c->ti_old=%lld (t=%e) "
+        "Cell has been drifted too far forward in time! c->ti_old_part=%lld "
+        "(t=%e) "
         "and e->ti_current=%lld (t=%e, a=%e)",
-        c->ti_old_part, c->ti_old_part * e->time_base, e->ti_current,
-        e->ti_current * e->time_base, e->cosmology->a);
+        c->hydro.ti_old_part, c->hydro.ti_old_part * e->time_base,
+        e->ti_current, e->ti_current * e->time_base, e->cosmology->a);
 #endif
 
-  return (c->ti_old_part == e->ti_current);
+  return (c->hydro.ti_old_part == e->ti_current);
 }
 
 /**
@@ -62,15 +63,15 @@ __attribute__((always_inline)) INLINE static int cell_are_gpart_drifted(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_old_gpart > e->ti_current)
+  if (c->grav.ti_old_part > e->ti_current)
     error(
         "Cell has been drifted too far forward in time! c->ti_old=%lld (t=%e) "
         "and e->ti_current=%lld (t=%e)",
-        c->ti_old_gpart, c->ti_old_gpart * e->time_base, e->ti_current,
+        c->grav.ti_old_part, c->grav.ti_old_part * e->time_base, e->ti_current,
         e->ti_current * e->time_base);
 #endif
 
-  return (c->ti_old_gpart == e->ti_current);
+  return (c->grav.ti_old_part == e->ti_current);
 }
 
 /* Are cells / particles active for regular tasks ? */
@@ -86,15 +87,15 @@ __attribute__((always_inline)) INLINE static int cell_is_active_hydro(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_hydro_end_min < e->ti_current)
+  if (c->hydro.ti_end_min < e->ti_current)
     error(
         "cell in an impossible time-zone! c->ti_end_min=%lld (t=%e) and "
         "e->ti_current=%lld (t=%e, a=%e)",
-        c->ti_hydro_end_min, c->ti_hydro_end_min * e->time_base, e->ti_current,
+        c->hydro.ti_end_min, c->hydro.ti_end_min * e->time_base, e->ti_current,
         e->ti_current * e->time_base, e->cosmology->a);
 #endif
 
-  return (c->ti_hydro_end_min == e->ti_current);
+  return (c->hydro.ti_end_min == e->ti_current);
 }
 
 /**
@@ -108,14 +109,14 @@ __attribute__((always_inline)) INLINE static int cell_is_all_active_hydro(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_hydro_end_max < e->ti_current)
+  if (c->hydro.ti_end_max < e->ti_current)
     error(
         "cell in an impossible time-zone! c->ti_end_max=%lld "
         "e->ti_current=%lld",
-        c->ti_hydro_end_max, e->ti_current);
+        c->hydro.ti_end_max, e->ti_current);
 #endif
 
-  return (c->ti_hydro_end_max == e->ti_current);
+  return (c->hydro.ti_end_max == e->ti_current);
 }
 
 /**
@@ -129,15 +130,15 @@ __attribute__((always_inline)) INLINE static int cell_is_active_gravity(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_gravity_end_min < e->ti_current)
+  if (c->grav.ti_end_min < e->ti_current)
     error(
         "cell in an impossible time-zone! c->ti_end_min=%lld (t=%e) and "
         "e->ti_current=%lld (t=%e, a=%e)",
-        c->ti_gravity_end_min, c->ti_gravity_end_min * e->time_base,
-        e->ti_current, e->ti_current * e->time_base, e->cosmology->a);
+        c->grav.ti_end_min, c->grav.ti_end_min * e->time_base, e->ti_current,
+        e->ti_current * e->time_base, e->cosmology->a);
 #endif
 
-  return (c->ti_gravity_end_min == e->ti_current);
+  return (c->grav.ti_end_min == e->ti_current);
 }
 
 /**
@@ -150,7 +151,7 @@ __attribute__((always_inline)) INLINE static int cell_is_active_gravity(
 __attribute__((always_inline)) INLINE static int cell_is_active_gravity_mm(
     const struct cell *c, const struct engine *e) {
 
-  return (c->ti_gravity_end_min == e->ti_current);
+  return (c->grav.ti_end_min == e->ti_current);
 }
 
 /**
@@ -164,14 +165,14 @@ __attribute__((always_inline)) INLINE static int cell_is_all_active_gravity(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_gravity_end_max < e->ti_current)
+  if (c->grav.ti_end_max < e->ti_current)
     error(
         "cell in an impossible time-zone! c->ti_end_max=%lld "
         "e->ti_current=%lld",
-        c->ti_gravity_end_max, e->ti_current);
+        c->grav.ti_end_max, e->ti_current);
 #endif
 
-  return (c->ti_gravity_end_max == e->ti_current);
+  return (c->grav.ti_end_max == e->ti_current);
 }
 
 /**
@@ -277,6 +278,42 @@ __attribute__((always_inline)) INLINE static int spart_is_active(
   return (spart_bin <= max_active_bin);
 }
 
+/**
+ * @brief Has this particle been inhibited?
+ *
+ * @param p The #part.
+ * @param e The #engine containing information about the current time.
+ * @return 1 if the #part is inhibited, 0 otherwise.
+ */
+__attribute__((always_inline)) INLINE static int part_is_inhibited(
+    const struct part *p, const struct engine *e) {
+  return p->time_bin == time_bin_inhibited;
+}
+
+/**
+ * @brief Has this gravity particle been inhibited?
+ *
+ * @param gp The #gpart.
+ * @param e The #engine containing information about the current time.
+ * @return 1 if the #part is inhibited, 0 otherwise.
+ */
+__attribute__((always_inline)) INLINE static int gpart_is_inhibited(
+    const struct gpart *gp, const struct engine *e) {
+  return gp->time_bin == time_bin_inhibited;
+}
+
+/**
+ * @brief Has this star particle been inhibited?
+ *
+ * @param sp The #spart.
+ * @param e The #engine containing information about the current time.
+ * @return 1 if the #part is inhibited, 0 otherwise.
+ */
+__attribute__((always_inline)) INLINE static int spart_is_inhibited(
+    const struct spart *sp, const struct engine *e) {
+  return sp->time_bin == time_bin_inhibited;
+}
+
 /* Are cells / particles active for kick1 tasks ? */
 
 /**
@@ -290,15 +327,15 @@ __attribute__((always_inline)) INLINE static int cell_is_starting_hydro(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_hydro_beg_max > e->ti_current)
+  if (c->hydro.ti_beg_max > e->ti_current)
     error(
         "cell in an impossible time-zone! c->ti_beg_max=%lld (t=%e) and "
         "e->ti_current=%lld (t=%e, a=%e)",
-        c->ti_hydro_beg_max, c->ti_hydro_beg_max * e->time_base, e->ti_current,
+        c->hydro.ti_beg_max, c->hydro.ti_beg_max * e->time_base, e->ti_current,
         e->ti_current * e->time_base, e->cosmology->a);
 #endif
 
-  return (c->ti_hydro_beg_max == e->ti_current);
+  return (c->hydro.ti_beg_max == e->ti_current);
 }
 
 /**
@@ -312,15 +349,15 @@ __attribute__((always_inline)) INLINE static int cell_is_starting_gravity(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_gravity_beg_max > e->ti_current)
+  if (c->grav.ti_beg_max > e->ti_current)
     error(
         "cell in an impossible time-zone! c->ti_beg_max=%lld (t=%e) and "
         "e->ti_current=%lld (t=%e, a=%e)",
-        c->ti_gravity_beg_max, c->ti_gravity_beg_max * e->time_base,
-        e->ti_current, e->ti_current * e->time_base, e->cosmology->a);
+        c->grav.ti_beg_max, c->grav.ti_beg_max * e->time_base, e->ti_current,
+        e->ti_current * e->time_base, e->cosmology->a);
 #endif
 
-  return (c->ti_gravity_beg_max == e->ti_current);
+  return (c->grav.ti_beg_max == e->ti_current);
 }
 
 /**
@@ -406,4 +443,5 @@ __attribute__((always_inline)) INLINE static int spart_is_starting(
 
   return (spart_bin <= max_active_bin);
 }
+
 #endif /* SWIFT_ACTIVE_H */
diff --git a/src/cache.h b/src/cache.h
index c41e11c34246ef0de93bb1ae7500277aab555b9e..5dd8164b1dc80795a8593cc2af42c2c9e7e68885 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -197,12 +197,12 @@ __attribute__((always_inline)) INLINE void cache_read_particles(
   swift_declare_aligned_ptr(float, vy, ci_cache->vy, SWIFT_CACHE_ALIGNMENT);
   swift_declare_aligned_ptr(float, vz, ci_cache->vz, SWIFT_CACHE_ALIGNMENT);
 
-  const struct part *restrict parts = ci->parts;
+  const struct part *restrict parts = ci->hydro.parts;
   const double loc[3] = {ci->loc[0], ci->loc[1], ci->loc[2]};
 
   /* Shift the particles positions to a local frame so single precision can be
    * used instead of double precision. */
-  for (int i = 0; i < ci->count; i++) {
+  for (int i = 0; i < ci->hydro.count; i++) {
     x[i] = (float)(parts[i].x[0] - loc[0]);
     y[i] = (float)(parts[i].x[1] - loc[1]);
     z[i] = (float)(parts[i].x[2] - loc[2]);
@@ -248,7 +248,7 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset(
   swift_declare_aligned_ptr(float, vy, ci_cache->vy, SWIFT_CACHE_ALIGNMENT);
   swift_declare_aligned_ptr(float, vz, ci_cache->vz, SWIFT_CACHE_ALIGNMENT);
 
-  const struct part *restrict parts = ci->parts;
+  const struct part *restrict parts = ci->hydro.parts;
 
   /* The cell is on the right so read the particles
    * into the cache from the start of the cell. */
@@ -258,7 +258,7 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset(
       const int pad = VEC_SIZE - rem;
 
       /* Increase last_pi if there are particles in the cell left to read. */
-      if (*last_pi + pad < ci->count) *last_pi += pad;
+      if (*last_pi + pad < ci->hydro.count) *last_pi += pad;
     }
 
     /* Shift the particles positions to a local frame so single precision can be
@@ -278,11 +278,11 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset(
     /* Pad cache with fake particles that exist outside the cell so will not
      * interact. We use values of the same magnitude (but negative!) as the real
      * particles to avoid overflow problems. */
-    const double max_dx = ci->dx_max_part;
+    const double max_dx = ci->hydro.dx_max_part;
     const float pos_padded[3] = {-(2. * ci->width[0] + max_dx),
                                  -(2. * ci->width[1] + max_dx),
                                  -(2. * ci->width[2] + max_dx)};
-    const float h_padded = ci->parts[0].h;
+    const float h_padded = ci->hydro.parts[0].h;
 
     for (int i = *last_pi; i < *last_pi + VEC_SIZE; i++) {
       x[i] = pos_padded[0];
@@ -299,7 +299,7 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset(
   /* The cell is on the left so read the particles
    * into the cache from the end of the cell. */
   else {
-    const int rem = (ci->count - *first_pi) % VEC_SIZE;
+    const int rem = (ci->hydro.count - *first_pi) % VEC_SIZE;
     if (rem != 0) {
       const int pad = VEC_SIZE - rem;
 
@@ -307,7 +307,7 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset(
       if (*first_pi - pad >= 0) *first_pi -= pad;
     }
 
-    const int ci_cache_count = ci->count - *first_pi;
+    const int ci_cache_count = ci->hydro.count - *first_pi;
 
     /* Shift the particles positions to a local frame so single precision can be
      * used instead of double precision. */
@@ -326,14 +326,14 @@ __attribute__((always_inline)) INLINE void cache_read_particles_subset(
     /* Pad cache with fake particles that exist outside the cell so will not
      * interact. We use values of the same magnitude (but negative!) as the real
      * particles to avoid overflow problems. */
-    const double max_dx = ci->dx_max_part;
+    const double max_dx = ci->hydro.dx_max_part;
     const float pos_padded[3] = {-(2. * ci->width[0] + max_dx),
                                  -(2. * ci->width[1] + max_dx),
                                  -(2. * ci->width[2] + max_dx)};
-    const float h_padded = ci->parts[0].h;
+    const float h_padded = ci->hydro.parts[0].h;
 
-    for (int i = ci->count - *first_pi; i < ci->count - *first_pi + VEC_SIZE;
-         i++) {
+    for (int i = ci->hydro.count - *first_pi;
+         i < ci->hydro.count - *first_pi + VEC_SIZE; i++) {
       x[i] = pos_padded[0];
       y[i] = pos_padded[1];
       z[i] = pos_padded[2];
@@ -382,12 +382,12 @@ __attribute__((always_inline)) INLINE void cache_read_force_particles(
   swift_declare_aligned_ptr(float, soundspeed, ci_cache->soundspeed,
                             SWIFT_CACHE_ALIGNMENT);
 
-  const struct part *restrict parts = ci->parts;
+  const struct part *restrict parts = ci->hydro.parts;
   const double loc[3] = {ci->loc[0], ci->loc[1], ci->loc[2]};
 
   /* Shift the particles positions to a local frame so single precision can be
    * used instead of double precision. */
-  for (int i = 0; i < ci->count; i++) {
+  for (int i = 0; i < ci->hydro.count; i++) {
     x[i] = (float)(parts[i].x[0] - loc[0]);
     y[i] = (float)(parts[i].x[1] - loc[1]);
     z[i] = (float)(parts[i].x[2] - loc[2]);
@@ -433,7 +433,7 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted(
    * cache. */
 
   /* Is the number of particles to read a multiple of the vector size? */
-  int rem = (ci->count - *first_pi) % VEC_SIZE;
+  int rem = (ci->hydro.count - *first_pi) % VEC_SIZE;
   if (rem != 0) {
     int pad = VEC_SIZE - rem;
 
@@ -446,14 +446,14 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted(
     int pad = VEC_SIZE - rem;
 
     /* Increase last_pj if there are particles in the cell left to read. */
-    if (*last_pj + pad < cj->count) *last_pj += pad;
+    if (*last_pj + pad < cj->hydro.count) *last_pj += pad;
   }
 
   /* Get some local pointers */
   const int first_pi_align = *first_pi;
   const int last_pj_align = *last_pj;
-  const struct part *restrict parts_i = ci->parts;
-  const struct part *restrict parts_j = cj->parts;
+  const struct part *restrict parts_i = ci->hydro.parts;
+  const struct part *restrict parts_j = cj->hydro.parts;
 
   /* Shift particles to the local frame and account for boundary conditions.*/
   const double total_ci_shift[3] = {
@@ -471,7 +471,7 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted(
   swift_declare_aligned_ptr(float, vy, ci_cache->vy, SWIFT_CACHE_ALIGNMENT);
   swift_declare_aligned_ptr(float, vz, ci_cache->vz, SWIFT_CACHE_ALIGNMENT);
 
-  int ci_cache_count = ci->count - first_pi_align;
+  int ci_cache_count = ci->hydro.count - first_pi_align;
 
   /* Shift the particles positions to a local frame (ci frame) so single
    * precision can be used instead of double precision.  */
@@ -491,11 +491,14 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted(
 
 #ifdef SWIFT_DEBUG_CHECKS
   const float shift_threshold_x =
-      2. * ci->width[0] + 2. * max(ci->dx_max_part, cj->dx_max_part);
+      2. * ci->width[0] +
+      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_y =
-      2. * ci->width[1] + 2. * max(ci->dx_max_part, cj->dx_max_part);
+      2. * ci->width[1] +
+      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_z =
-      2. * ci->width[2] + 2. * max(ci->dx_max_part, cj->dx_max_part);
+      2. * ci->width[2] +
+      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
 
   /* Make sure that particle positions have been shifted correctly. */
   for (int i = 0; i < ci_cache_count; i++) {
@@ -529,14 +532,14 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted(
   /* Pad cache with fake particles that exist outside the cell so will not
    * interact. We use values of the same magnitude (but negative!) as the real
    * particles to avoid overflow problems. */
-  const double max_dx = max(ci->dx_max_part, cj->dx_max_part);
+  const double max_dx = max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float pos_padded[3] = {-(2. * ci->width[0] + max_dx),
                                -(2. * ci->width[1] + max_dx),
                                -(2. * ci->width[2] + max_dx)};
-  const float h_padded = ci->parts[0].h;
+  const float h_padded = ci->hydro.parts[0].h;
 
-  for (int i = ci->count - first_pi_align;
-       i < ci->count - first_pi_align + VEC_SIZE; i++) {
+  for (int i = ci->hydro.count - first_pi_align;
+       i < ci->hydro.count - first_pi_align + VEC_SIZE; i++) {
     x[i] = pos_padded[0];
     y[i] = pos_padded[1];
     z[i] = pos_padded[2];
@@ -609,7 +612,7 @@ __attribute__((always_inline)) INLINE void cache_read_two_partial_cells_sorted(
   const float pos_padded_j[3] = {-(2. * cj->width[0] + max_dx),
                                  -(2. * cj->width[1] + max_dx),
                                  -(2. * cj->width[2] + max_dx)};
-  const float h_padded_j = cj->parts[0].h;
+  const float h_padded_j = cj->hydro.parts[0].h;
 
   for (int i = last_pj_align + 1; i < last_pj_align + 1 + VEC_SIZE; i++) {
     xj[i] = pos_padded_j[0];
@@ -650,7 +653,7 @@ cache_read_two_partial_cells_sorted_force(
    * cache. */
 
   /* Is the number of particles to read a multiple of the vector size? */
-  int rem = (ci->count - *first_pi) % VEC_SIZE;
+  int rem = (ci->hydro.count - *first_pi) % VEC_SIZE;
   if (rem != 0) {
     int pad = VEC_SIZE - rem;
 
@@ -663,14 +666,14 @@ cache_read_two_partial_cells_sorted_force(
     int pad = VEC_SIZE - rem;
 
     /* Increase last_pj if there are particles in the cell left to read. */
-    if (*last_pj + pad < cj->count) *last_pj += pad;
+    if (*last_pj + pad < cj->hydro.count) *last_pj += pad;
   }
 
   /* Get some local pointers */
   const int first_pi_align = *first_pi;
   const int last_pj_align = *last_pj;
-  const struct part *restrict parts_i = ci->parts;
-  const struct part *restrict parts_j = cj->parts;
+  const struct part *restrict parts_i = ci->hydro.parts;
+  const struct part *restrict parts_j = cj->hydro.parts;
 
   /* Shift particles to the local frame and account for boundary conditions.*/
   const double total_ci_shift[3] = {
@@ -697,7 +700,7 @@ cache_read_two_partial_cells_sorted_force(
   swift_declare_aligned_ptr(float, soundspeed, ci_cache->soundspeed,
                             SWIFT_CACHE_ALIGNMENT);
 
-  int ci_cache_count = ci->count - first_pi_align;
+  int ci_cache_count = ci->hydro.count - first_pi_align;
   /* Shift the particles positions to a local frame (ci frame) so single
    * precision can be  used instead of double precision.  */
   for (int i = 0; i < ci_cache_count; i++) {
@@ -723,14 +726,14 @@ cache_read_two_partial_cells_sorted_force(
   /* Pad cache with fake particles that exist outside the cell so will not
    * interact. We use values of the same magnitude (but negative!) as the real
    * particles to avoid overflow problems. */
-  const double max_dx = max(ci->dx_max_part, cj->dx_max_part);
+  const double max_dx = max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float pos_padded[3] = {-(2. * ci->width[0] + max_dx),
                                -(2. * ci->width[1] + max_dx),
                                -(2. * ci->width[2] + max_dx)};
-  const float h_padded = ci->parts[0].h;
+  const float h_padded = ci->hydro.parts[0].h;
 
-  for (int i = ci->count - first_pi_align;
-       i < ci->count - first_pi_align + VEC_SIZE; i++) {
+  for (int i = ci->hydro.count - first_pi_align;
+       i < ci->hydro.count - first_pi_align + VEC_SIZE; i++) {
     x[i] = pos_padded[0];
     y[i] = pos_padded[1];
     z[i] = pos_padded[2];
@@ -791,7 +794,7 @@ cache_read_two_partial_cells_sorted_force(
   const float pos_padded_j[3] = {-(2. * cj->width[0] + max_dx),
                                  -(2. * cj->width[1] + max_dx),
                                  -(2. * cj->width[2] + max_dx)};
-  const float h_padded_j = cj->parts[0].h;
+  const float h_padded_j = cj->hydro.parts[0].h;
 
   for (int i = last_pj_align + 1; i < last_pj_align + 1 + VEC_SIZE; i++) {
     xj[i] = pos_padded_j[0];
@@ -831,6 +834,7 @@ static INLINE void cache_clean(struct cache *c) {
     free(c->balsara);
     free(c->soundspeed);
   }
+  c->count = 0;
 }
 
 #endif /* WITH_VECTORIZATION */
diff --git a/src/cell.c b/src/cell.c
index 8ecaf74711434dbbfa2b0c39b8a90bdf4ccc3155..37b7be42f3aa04d79496d2913d30654af93ea45e 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -97,7 +97,7 @@ int cell_getsize(struct cell *c) {
  */
 int cell_link_parts(struct cell *c, struct part *parts) {
 
-  c->parts = parts;
+  c->hydro.parts = parts;
 
   /* Fill the progeny recursively, depth-first. */
   if (c->split) {
@@ -109,7 +109,7 @@ int cell_link_parts(struct cell *c, struct part *parts) {
   }
 
   /* Return the total number of linked particles. */
-  return c->count;
+  return c->hydro.count;
 }
 
 /**
@@ -122,7 +122,7 @@ int cell_link_parts(struct cell *c, struct part *parts) {
  */
 int cell_link_gparts(struct cell *c, struct gpart *gparts) {
 
-  c->gparts = gparts;
+  c->grav.parts = gparts;
 
   /* Fill the progeny recursively, depth-first. */
   if (c->split) {
@@ -134,7 +134,7 @@ int cell_link_gparts(struct cell *c, struct gpart *gparts) {
   }
 
   /* Return the total number of linked particles. */
-  return c->gcount;
+  return c->grav.count;
 }
 
 /**
@@ -147,7 +147,7 @@ int cell_link_gparts(struct cell *c, struct gpart *gparts) {
  */
 int cell_link_sparts(struct cell *c, struct spart *sparts) {
 
-  c->sparts = sparts;
+  c->stars.parts = sparts;
 
   /* Fill the progeny recursively, depth-first. */
   if (c->split) {
@@ -159,7 +159,7 @@ int cell_link_sparts(struct cell *c, struct spart *sparts) {
   }
 
   /* Return the total number of linked particles. */
-  return c->scount;
+  return c->stars.count;
 }
 
 /**
@@ -179,31 +179,31 @@ int cell_pack(struct cell *restrict c, struct pcell *restrict pc,
 #ifdef WITH_MPI
 
   /* Start by packing the data of the current cell. */
-  pc->h_max = c->h_max;
-  pc->ti_hydro_end_min = c->ti_hydro_end_min;
-  pc->ti_hydro_end_max = c->ti_hydro_end_max;
-  pc->ti_gravity_end_min = c->ti_gravity_end_min;
-  pc->ti_gravity_end_max = c->ti_gravity_end_max;
-  pc->ti_old_part = c->ti_old_part;
-  pc->ti_old_gpart = c->ti_old_gpart;
-  pc->ti_old_multipole = c->ti_old_multipole;
-  pc->count = c->count;
-  pc->gcount = c->gcount;
-  pc->scount = c->scount;
+  pc->hydro.h_max = c->hydro.h_max;
+  pc->hydro.ti_end_min = c->hydro.ti_end_min;
+  pc->hydro.ti_end_max = c->hydro.ti_end_max;
+  pc->grav.ti_end_min = c->grav.ti_end_min;
+  pc->grav.ti_end_max = c->grav.ti_end_max;
+  pc->hydro.ti_old_part = c->hydro.ti_old_part;
+  pc->grav.ti_old_part = c->grav.ti_old_part;
+  pc->grav.ti_old_multipole = c->grav.ti_old_multipole;
+  pc->hydro.count = c->hydro.count;
+  pc->grav.count = c->grav.count;
+  pc->stars.count = c->stars.count;
 
   /* Copy the Multipole related information */
   if (with_gravity) {
-    const struct gravity_tensors *mp = c->multipole;
+    const struct gravity_tensors *mp = c->grav.multipole;
 
-    pc->m_pole = mp->m_pole;
-    pc->CoM[0] = mp->CoM[0];
-    pc->CoM[1] = mp->CoM[1];
-    pc->CoM[2] = mp->CoM[2];
-    pc->CoM_rebuild[0] = mp->CoM_rebuild[0];
-    pc->CoM_rebuild[1] = mp->CoM_rebuild[1];
-    pc->CoM_rebuild[2] = mp->CoM_rebuild[2];
-    pc->r_max = mp->r_max;
-    pc->r_max_rebuild = mp->r_max_rebuild;
+    pc->grav.m_pole = mp->m_pole;
+    pc->grav.CoM[0] = mp->CoM[0];
+    pc->grav.CoM[1] = mp->CoM[1];
+    pc->grav.CoM[2] = mp->CoM[2];
+    pc->grav.CoM_rebuild[0] = mp->CoM_rebuild[0];
+    pc->grav.CoM_rebuild[1] = mp->CoM_rebuild[1];
+    pc->grav.CoM_rebuild[2] = mp->CoM_rebuild[2];
+    pc->grav.r_max = mp->r_max;
+    pc->grav.r_max_rebuild = mp->r_max_rebuild;
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -221,7 +221,7 @@ int cell_pack(struct cell *restrict c, struct pcell *restrict pc,
     }
 
   /* Return the number of packed cells used. */
-  c->pcell_size = count;
+  c->mpi.pcell_size = count;
   return count;
 
 #else
@@ -243,7 +243,7 @@ int cell_pack_tags(const struct cell *c, int *tags) {
 #ifdef WITH_MPI
 
   /* Start by packing the data of the current cell. */
-  tags[0] = c->tag;
+  tags[0] = c->mpi.tag;
 
   /* Fill in the progeny, depth-first recursion. */
   int count = 1;
@@ -252,7 +252,7 @@ int cell_pack_tags(const struct cell *c, int *tags) {
       count += cell_pack_tags(c->progeny[k], &tags[count]);
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->pcell_size != count) error("Inconsistent tag and pcell count!");
+  if (c->mpi.pcell_size != count) error("Inconsistent tag and pcell count!");
 #endif  // SWIFT_DEBUG_CHECKS
 
   /* Return the number of packed tags used. */
@@ -281,17 +281,17 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c,
 #ifdef WITH_MPI
 
   /* Unpack the current pcell. */
-  c->h_max = pc->h_max;
-  c->ti_hydro_end_min = pc->ti_hydro_end_min;
-  c->ti_hydro_end_max = pc->ti_hydro_end_max;
-  c->ti_gravity_end_min = pc->ti_gravity_end_min;
-  c->ti_gravity_end_max = pc->ti_gravity_end_max;
-  c->ti_old_part = pc->ti_old_part;
-  c->ti_old_gpart = pc->ti_old_gpart;
-  c->ti_old_multipole = pc->ti_old_multipole;
-  c->count = pc->count;
-  c->gcount = pc->gcount;
-  c->scount = pc->scount;
+  c->hydro.h_max = pc->hydro.h_max;
+  c->hydro.ti_end_min = pc->hydro.ti_end_min;
+  c->hydro.ti_end_max = pc->hydro.ti_end_max;
+  c->grav.ti_end_min = pc->grav.ti_end_min;
+  c->grav.ti_end_max = pc->grav.ti_end_max;
+  c->hydro.ti_old_part = pc->hydro.ti_old_part;
+  c->grav.ti_old_part = pc->grav.ti_old_part;
+  c->grav.ti_old_multipole = pc->grav.ti_old_multipole;
+  c->hydro.count = pc->hydro.count;
+  c->grav.count = pc->grav.count;
+  c->stars.count = pc->stars.count;
 #ifdef SWIFT_DEBUG_CHECKS
   c->cellID = pc->cellID;
 #endif
@@ -299,30 +299,31 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c,
   /* Copy the Multipole related information */
   if (with_gravity) {
 
-    struct gravity_tensors *mp = c->multipole;
+    struct gravity_tensors *mp = c->grav.multipole;
 
-    mp->m_pole = pc->m_pole;
-    mp->CoM[0] = pc->CoM[0];
-    mp->CoM[1] = pc->CoM[1];
-    mp->CoM[2] = pc->CoM[2];
-    mp->CoM_rebuild[0] = pc->CoM_rebuild[0];
-    mp->CoM_rebuild[1] = pc->CoM_rebuild[1];
-    mp->CoM_rebuild[2] = pc->CoM_rebuild[2];
-    mp->r_max = pc->r_max;
-    mp->r_max_rebuild = pc->r_max_rebuild;
+    mp->m_pole = pc->grav.m_pole;
+    mp->CoM[0] = pc->grav.CoM[0];
+    mp->CoM[1] = pc->grav.CoM[1];
+    mp->CoM[2] = pc->grav.CoM[2];
+    mp->CoM_rebuild[0] = pc->grav.CoM_rebuild[0];
+    mp->CoM_rebuild[1] = pc->grav.CoM_rebuild[1];
+    mp->CoM_rebuild[2] = pc->grav.CoM_rebuild[2];
+    mp->r_max = pc->grav.r_max;
+    mp->r_max_rebuild = pc->grav.r_max_rebuild;
   }
 
   /* Number of new cells created. */
   int count = 1;
 
   /* Fill the progeny recursively, depth-first. */
+  c->split = 0;
   for (int k = 0; k < 8; k++)
     if (pc->progeny[k] >= 0) {
       struct cell *temp;
       space_getcells(s, 1, &temp);
-      temp->count = 0;
-      temp->gcount = 0;
-      temp->scount = 0;
+      temp->hydro.count = 0;
+      temp->grav.count = 0;
+      temp->stars.count = 0;
       temp->loc[0] = c->loc[0];
       temp->loc[1] = c->loc[1];
       temp->loc[2] = c->loc[2];
@@ -335,8 +336,9 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c,
       if (k & 1) temp->loc[2] += temp->width[2];
       temp->depth = c->depth + 1;
       temp->split = 0;
-      temp->dx_max_part = 0.f;
-      temp->dx_max_sort = 0.f;
+      temp->hydro.dx_max_part = 0.f;
+      temp->hydro.dx_max_sort = 0.f;
+      temp->stars.dx_max_part = 0.f;
       temp->nodeID = c->nodeID;
       temp->parent = c;
       c->progeny[k] = temp;
@@ -345,7 +347,7 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c,
     }
 
   /* Return the total number of unpacked cells. */
-  c->pcell_size = count;
+  c->mpi.pcell_size = count;
   return count;
 
 #else
@@ -367,7 +369,7 @@ int cell_unpack_tags(const int *tags, struct cell *restrict c) {
 #ifdef WITH_MPI
 
   /* Unpack the current pcell. */
-  c->tag = tags[0];
+  c->mpi.tag = tags[0];
 
   /* Number of new cells created. */
   int count = 1;
@@ -379,7 +381,7 @@ int cell_unpack_tags(const int *tags, struct cell *restrict c) {
     }
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->pcell_size != count) error("Inconsistent tag and pcell count!");
+  if (c->mpi.pcell_size != count) error("Inconsistent tag and pcell count!");
 #endif  // SWIFT_DEBUG_CHECKS
 
   /* Return the total number of unpacked tags. */
@@ -405,11 +407,12 @@ int cell_pack_end_step(struct cell *restrict c,
 #ifdef WITH_MPI
 
   /* Pack this cell's data. */
-  pcells[0].ti_hydro_end_min = c->ti_hydro_end_min;
-  pcells[0].ti_hydro_end_max = c->ti_hydro_end_max;
-  pcells[0].ti_gravity_end_min = c->ti_gravity_end_min;
-  pcells[0].ti_gravity_end_max = c->ti_gravity_end_max;
-  pcells[0].dx_max_part = c->dx_max_part;
+  pcells[0].hydro.ti_end_min = c->hydro.ti_end_min;
+  pcells[0].hydro.ti_end_max = c->hydro.ti_end_max;
+  pcells[0].grav.ti_end_min = c->grav.ti_end_min;
+  pcells[0].grav.ti_end_max = c->grav.ti_end_max;
+  pcells[0].hydro.dx_max_part = c->hydro.dx_max_part;
+  pcells[0].stars.dx_max_part = c->stars.dx_max_part;
 
   /* Fill in the progeny, depth-first recursion. */
   int count = 1;
@@ -441,11 +444,12 @@ int cell_unpack_end_step(struct cell *restrict c,
 #ifdef WITH_MPI
 
   /* Unpack this cell's data. */
-  c->ti_hydro_end_min = pcells[0].ti_hydro_end_min;
-  c->ti_hydro_end_max = pcells[0].ti_hydro_end_max;
-  c->ti_gravity_end_min = pcells[0].ti_gravity_end_min;
-  c->ti_gravity_end_max = pcells[0].ti_gravity_end_max;
-  c->dx_max_part = pcells[0].dx_max_part;
+  c->hydro.ti_end_min = pcells[0].hydro.ti_end_min;
+  c->hydro.ti_end_max = pcells[0].hydro.ti_end_max;
+  c->grav.ti_end_min = pcells[0].grav.ti_end_min;
+  c->grav.ti_end_max = pcells[0].grav.ti_end_max;
+  c->hydro.dx_max_part = pcells[0].hydro.dx_max_part;
+  c->stars.dx_max_part = pcells[0].stars.dx_max_part;
 
   /* Fill in the progeny, depth-first recursion. */
   int count = 1;
@@ -478,7 +482,7 @@ int cell_pack_multipoles(struct cell *restrict c,
 #ifdef WITH_MPI
 
   /* Pack this cell's data. */
-  pcells[0] = *c->multipole;
+  pcells[0] = *c->grav.multipole;
 
   /* Fill in the progeny, depth-first recursion. */
   int count = 1;
@@ -510,7 +514,7 @@ int cell_unpack_multipoles(struct cell *restrict c,
 #ifdef WITH_MPI
 
   /* Unpack this cell's data. */
-  *c->multipole = pcells[0];
+  *c->grav.multipole = pcells[0];
 
   /* Fill in the progeny, depth-first recursion. */
   int count = 1;
@@ -539,16 +543,16 @@ int cell_locktree(struct cell *c) {
   TIMER_TIC
 
   /* First of all, try to lock this cell. */
-  if (c->hold || lock_trylock(&c->lock) != 0) {
+  if (c->hydro.hold || lock_trylock(&c->hydro.lock) != 0) {
     TIMER_TOC(timer_locktree);
     return 1;
   }
 
   /* Did somebody hold this cell in the meantime? */
-  if (c->hold) {
+  if (c->hydro.hold) {
 
     /* Unlock this cell. */
-    if (lock_unlock(&c->lock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&c->hydro.lock) != 0) error("Failed to unlock cell.");
 
     /* Admit defeat. */
     TIMER_TOC(timer_locktree);
@@ -560,13 +564,13 @@ int cell_locktree(struct cell *c) {
   for (finger = c->parent; finger != NULL; finger = finger->parent) {
 
     /* Lock this cell. */
-    if (lock_trylock(&finger->lock) != 0) break;
+    if (lock_trylock(&finger->hydro.lock) != 0) break;
 
     /* Increment the hold. */
-    atomic_inc(&finger->hold);
+    atomic_inc(&finger->hydro.hold);
 
     /* Unlock the cell. */
-    if (lock_unlock(&finger->lock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&finger->hydro.lock) != 0) error("Failed to unlock cell.");
   }
 
   /* If we reached the top of the tree, we're done. */
@@ -581,10 +585,10 @@ int cell_locktree(struct cell *c) {
     /* Undo the holds up to finger. */
     for (struct cell *finger2 = c->parent; finger2 != finger;
          finger2 = finger2->parent)
-      atomic_dec(&finger2->hold);
+      atomic_dec(&finger2->hydro.hold);
 
     /* Unlock this cell. */
-    if (lock_unlock(&c->lock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&c->hydro.lock) != 0) error("Failed to unlock cell.");
 
     /* Admit defeat. */
     TIMER_TOC(timer_locktree);
@@ -603,16 +607,16 @@ int cell_glocktree(struct cell *c) {
   TIMER_TIC
 
   /* First of all, try to lock this cell. */
-  if (c->ghold || lock_trylock(&c->glock) != 0) {
+  if (c->grav.phold || lock_trylock(&c->grav.plock) != 0) {
     TIMER_TOC(timer_locktree);
     return 1;
   }
 
   /* Did somebody hold this cell in the meantime? */
-  if (c->ghold) {
+  if (c->grav.phold) {
 
     /* Unlock this cell. */
-    if (lock_unlock(&c->glock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&c->grav.plock) != 0) error("Failed to unlock cell.");
 
     /* Admit defeat. */
     TIMER_TOC(timer_locktree);
@@ -624,13 +628,13 @@ int cell_glocktree(struct cell *c) {
   for (finger = c->parent; finger != NULL; finger = finger->parent) {
 
     /* Lock this cell. */
-    if (lock_trylock(&finger->glock) != 0) break;
+    if (lock_trylock(&finger->grav.plock) != 0) break;
 
     /* Increment the hold. */
-    atomic_inc(&finger->ghold);
+    atomic_inc(&finger->grav.phold);
 
     /* Unlock the cell. */
-    if (lock_unlock(&finger->glock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&finger->grav.plock) != 0) error("Failed to unlock cell.");
   }
 
   /* If we reached the top of the tree, we're done. */
@@ -645,10 +649,10 @@ int cell_glocktree(struct cell *c) {
     /* Undo the holds up to finger. */
     for (struct cell *finger2 = c->parent; finger2 != finger;
          finger2 = finger2->parent)
-      atomic_dec(&finger2->ghold);
+      atomic_dec(&finger2->grav.phold);
 
     /* Unlock this cell. */
-    if (lock_unlock(&c->glock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&c->grav.plock) != 0) error("Failed to unlock cell.");
 
     /* Admit defeat. */
     TIMER_TOC(timer_locktree);
@@ -667,16 +671,16 @@ int cell_mlocktree(struct cell *c) {
   TIMER_TIC
 
   /* First of all, try to lock this cell. */
-  if (c->mhold || lock_trylock(&c->mlock) != 0) {
+  if (c->grav.mhold || lock_trylock(&c->grav.mlock) != 0) {
     TIMER_TOC(timer_locktree);
     return 1;
   }
 
   /* Did somebody hold this cell in the meantime? */
-  if (c->mhold) {
+  if (c->grav.mhold) {
 
     /* Unlock this cell. */
-    if (lock_unlock(&c->mlock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&c->grav.mlock) != 0) error("Failed to unlock cell.");
 
     /* Admit defeat. */
     TIMER_TOC(timer_locktree);
@@ -688,13 +692,13 @@ int cell_mlocktree(struct cell *c) {
   for (finger = c->parent; finger != NULL; finger = finger->parent) {
 
     /* Lock this cell. */
-    if (lock_trylock(&finger->mlock) != 0) break;
+    if (lock_trylock(&finger->grav.mlock) != 0) break;
 
     /* Increment the hold. */
-    atomic_inc(&finger->mhold);
+    atomic_inc(&finger->grav.mhold);
 
     /* Unlock the cell. */
-    if (lock_unlock(&finger->mlock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&finger->grav.mlock) != 0) error("Failed to unlock cell.");
   }
 
   /* If we reached the top of the tree, we're done. */
@@ -709,10 +713,10 @@ int cell_mlocktree(struct cell *c) {
     /* Undo the holds up to finger. */
     for (struct cell *finger2 = c->parent; finger2 != finger;
          finger2 = finger2->parent)
-      atomic_dec(&finger2->mhold);
+      atomic_dec(&finger2->grav.mhold);
 
     /* Unlock this cell. */
-    if (lock_unlock(&c->mlock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&c->grav.mlock) != 0) error("Failed to unlock cell.");
 
     /* Admit defeat. */
     TIMER_TOC(timer_locktree);
@@ -731,16 +735,16 @@ int cell_slocktree(struct cell *c) {
   TIMER_TIC
 
   /* First of all, try to lock this cell. */
-  if (c->shold || lock_trylock(&c->slock) != 0) {
+  if (c->stars.hold || lock_trylock(&c->stars.lock) != 0) {
     TIMER_TOC(timer_locktree);
     return 1;
   }
 
   /* Did somebody hold this cell in the meantime? */
-  if (c->shold) {
+  if (c->stars.hold) {
 
     /* Unlock this cell. */
-    if (lock_unlock(&c->slock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&c->stars.lock) != 0) error("Failed to unlock cell.");
 
     /* Admit defeat. */
     TIMER_TOC(timer_locktree);
@@ -752,13 +756,13 @@ int cell_slocktree(struct cell *c) {
   for (finger = c->parent; finger != NULL; finger = finger->parent) {
 
     /* Lock this cell. */
-    if (lock_trylock(&finger->slock) != 0) break;
+    if (lock_trylock(&finger->stars.lock) != 0) break;
 
     /* Increment the hold. */
-    atomic_inc(&finger->shold);
+    atomic_inc(&finger->stars.hold);
 
     /* Unlock the cell. */
-    if (lock_unlock(&finger->slock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&finger->stars.lock) != 0) error("Failed to unlock cell.");
   }
 
   /* If we reached the top of the tree, we're done. */
@@ -773,10 +777,10 @@ int cell_slocktree(struct cell *c) {
     /* Undo the holds up to finger. */
     for (struct cell *finger2 = c->parent; finger2 != finger;
          finger2 = finger2->parent)
-      atomic_dec(&finger2->shold);
+      atomic_dec(&finger2->stars.hold);
 
     /* Unlock this cell. */
-    if (lock_unlock(&c->slock) != 0) error("Failed to unlock cell.");
+    if (lock_unlock(&c->stars.lock) != 0) error("Failed to unlock cell.");
 
     /* Admit defeat. */
     TIMER_TOC(timer_locktree);
@@ -794,11 +798,11 @@ void cell_unlocktree(struct cell *c) {
   TIMER_TIC
 
   /* First of all, try to unlock this cell. */
-  if (lock_unlock(&c->lock) != 0) error("Failed to unlock cell.");
+  if (lock_unlock(&c->hydro.lock) != 0) error("Failed to unlock cell.");
 
   /* Climb up the tree and unhold the parents. */
   for (struct cell *finger = c->parent; finger != NULL; finger = finger->parent)
-    atomic_dec(&finger->hold);
+    atomic_dec(&finger->hydro.hold);
 
   TIMER_TOC(timer_locktree);
 }
@@ -813,11 +817,11 @@ void cell_gunlocktree(struct cell *c) {
   TIMER_TIC
 
   /* First of all, try to unlock this cell. */
-  if (lock_unlock(&c->glock) != 0) error("Failed to unlock cell.");
+  if (lock_unlock(&c->grav.plock) != 0) error("Failed to unlock cell.");
 
   /* Climb up the tree and unhold the parents. */
   for (struct cell *finger = c->parent; finger != NULL; finger = finger->parent)
-    atomic_dec(&finger->ghold);
+    atomic_dec(&finger->grav.phold);
 
   TIMER_TOC(timer_locktree);
 }
@@ -832,11 +836,11 @@ void cell_munlocktree(struct cell *c) {
   TIMER_TIC
 
   /* First of all, try to unlock this cell. */
-  if (lock_unlock(&c->mlock) != 0) error("Failed to unlock cell.");
+  if (lock_unlock(&c->grav.mlock) != 0) error("Failed to unlock cell.");
 
   /* Climb up the tree and unhold the parents. */
   for (struct cell *finger = c->parent; finger != NULL; finger = finger->parent)
-    atomic_dec(&finger->mhold);
+    atomic_dec(&finger->grav.mhold);
 
   TIMER_TOC(timer_locktree);
 }
@@ -851,11 +855,11 @@ void cell_sunlocktree(struct cell *c) {
   TIMER_TIC
 
   /* First of all, try to unlock this cell. */
-  if (lock_unlock(&c->slock) != 0) error("Failed to unlock cell.");
+  if (lock_unlock(&c->stars.lock) != 0) error("Failed to unlock cell.");
 
   /* Climb up the tree and unhold the parents. */
   for (struct cell *finger = c->parent; finger != NULL; finger = finger->parent)
-    atomic_dec(&finger->shold);
+    atomic_dec(&finger->stars.hold);
 
   TIMER_TOC(timer_locktree);
 }
@@ -865,25 +869,26 @@ void cell_sunlocktree(struct cell *c) {
  *
  * @param c The #cell array to be sorted.
  * @param parts_offset Offset of the cell parts array relative to the
- *        space's parts array, i.e. c->parts - s->parts.
+ *        space's parts array, i.e. c->hydro.parts - s->parts.
  * @param sparts_offset Offset of the cell sparts array relative to the
- *        space's sparts array, i.e. c->sparts - s->sparts.
- * @param buff A buffer with at least max(c->count, c->gcount) entries,
- *        used for sorting indices.
- * @param sbuff A buffer with at least max(c->scount, c->gcount) entries,
- *        used for sorting indices for the sparts.
- * @param gbuff A buffer with at least max(c->count, c->gcount) entries,
- *        used for sorting indices for the gparts.
+ *        space's sparts array, i.e. c->stars.parts - s->stars.parts.
+ * @param buff A buffer with at least max(c->hydro.count, c->grav.count)
+ * entries, used for sorting indices.
+ * @param sbuff A buffer with at least max(c->stars.count, c->grav.count)
+ * entries, used for sorting indices for the sparts.
+ * @param gbuff A buffer with at least max(c->hydro.count, c->grav.count)
+ * entries, used for sorting indices for the gparts.
  */
 void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset,
                 struct cell_buff *buff, struct cell_buff *sbuff,
                 struct cell_buff *gbuff) {
 
-  const int count = c->count, gcount = c->gcount, scount = c->scount;
-  struct part *parts = c->parts;
-  struct xpart *xparts = c->xparts;
-  struct gpart *gparts = c->gparts;
-  struct spart *sparts = c->sparts;
+  const int count = c->hydro.count, gcount = c->grav.count,
+            scount = c->stars.count;
+  struct part *parts = c->hydro.parts;
+  struct xpart *xparts = c->hydro.xparts;
+  struct gpart *gparts = c->grav.parts;
+  struct spart *sparts = c->stars.parts;
   const double pivot[3] = {c->loc[0] + c->width[0] / 2,
                            c->loc[1] + c->width[1] / 2,
                            c->loc[2] + c->width[2] / 2};
@@ -958,9 +963,9 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset,
 
   /* Store the counts and offsets. */
   for (int k = 0; k < 8; k++) {
-    c->progeny[k]->count = bucket_count[k];
-    c->progeny[k]->parts = &c->parts[bucket_offset[k]];
-    c->progeny[k]->xparts = &c->xparts[bucket_offset[k]];
+    c->progeny[k]->hydro.count = bucket_count[k];
+    c->progeny[k]->hydro.parts = &c->hydro.parts[bucket_offset[k]];
+    c->progeny[k]->hydro.xparts = &c->hydro.xparts[bucket_offset[k]];
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -974,54 +979,55 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset,
 
   /* Verify that _all_ the parts have been assigned to a cell. */
   for (int k = 1; k < 8; k++)
-    if (&c->progeny[k - 1]->parts[c->progeny[k - 1]->count] !=
-        c->progeny[k]->parts)
+    if (&c->progeny[k - 1]->hydro.parts[c->progeny[k - 1]->hydro.count] !=
+        c->progeny[k]->hydro.parts)
       error("Particle sorting failed (internal consistency).");
-  if (c->progeny[0]->parts != c->parts)
+  if (c->progeny[0]->hydro.parts != c->hydro.parts)
     error("Particle sorting failed (left edge).");
-  if (&c->progeny[7]->parts[c->progeny[7]->count] != &c->parts[count])
+  if (&c->progeny[7]->hydro.parts[c->progeny[7]->hydro.count] !=
+      &c->hydro.parts[count])
     error("Particle sorting failed (right edge).");
 
   /* Verify a few sub-cells. */
-  for (int k = 0; k < c->progeny[0]->count; k++)
-    if (c->progeny[0]->parts[k].x[0] >= pivot[0] ||
-        c->progeny[0]->parts[k].x[1] >= pivot[1] ||
-        c->progeny[0]->parts[k].x[2] >= pivot[2])
+  for (int k = 0; k < c->progeny[0]->hydro.count; k++)
+    if (c->progeny[0]->hydro.parts[k].x[0] >= pivot[0] ||
+        c->progeny[0]->hydro.parts[k].x[1] >= pivot[1] ||
+        c->progeny[0]->hydro.parts[k].x[2] >= pivot[2])
       error("Sorting failed (progeny=0).");
-  for (int k = 0; k < c->progeny[1]->count; k++)
-    if (c->progeny[1]->parts[k].x[0] >= pivot[0] ||
-        c->progeny[1]->parts[k].x[1] >= pivot[1] ||
-        c->progeny[1]->parts[k].x[2] < pivot[2])
+  for (int k = 0; k < c->progeny[1]->hydro.count; k++)
+    if (c->progeny[1]->hydro.parts[k].x[0] >= pivot[0] ||
+        c->progeny[1]->hydro.parts[k].x[1] >= pivot[1] ||
+        c->progeny[1]->hydro.parts[k].x[2] < pivot[2])
       error("Sorting failed (progeny=1).");
-  for (int k = 0; k < c->progeny[2]->count; k++)
-    if (c->progeny[2]->parts[k].x[0] >= pivot[0] ||
-        c->progeny[2]->parts[k].x[1] < pivot[1] ||
-        c->progeny[2]->parts[k].x[2] >= pivot[2])
+  for (int k = 0; k < c->progeny[2]->hydro.count; k++)
+    if (c->progeny[2]->hydro.parts[k].x[0] >= pivot[0] ||
+        c->progeny[2]->hydro.parts[k].x[1] < pivot[1] ||
+        c->progeny[2]->hydro.parts[k].x[2] >= pivot[2])
       error("Sorting failed (progeny=2).");
-  for (int k = 0; k < c->progeny[3]->count; k++)
-    if (c->progeny[3]->parts[k].x[0] >= pivot[0] ||
-        c->progeny[3]->parts[k].x[1] < pivot[1] ||
-        c->progeny[3]->parts[k].x[2] < pivot[2])
+  for (int k = 0; k < c->progeny[3]->hydro.count; k++)
+    if (c->progeny[3]->hydro.parts[k].x[0] >= pivot[0] ||
+        c->progeny[3]->hydro.parts[k].x[1] < pivot[1] ||
+        c->progeny[3]->hydro.parts[k].x[2] < pivot[2])
       error("Sorting failed (progeny=3).");
-  for (int k = 0; k < c->progeny[4]->count; k++)
-    if (c->progeny[4]->parts[k].x[0] < pivot[0] ||
-        c->progeny[4]->parts[k].x[1] >= pivot[1] ||
-        c->progeny[4]->parts[k].x[2] >= pivot[2])
+  for (int k = 0; k < c->progeny[4]->hydro.count; k++)
+    if (c->progeny[4]->hydro.parts[k].x[0] < pivot[0] ||
+        c->progeny[4]->hydro.parts[k].x[1] >= pivot[1] ||
+        c->progeny[4]->hydro.parts[k].x[2] >= pivot[2])
       error("Sorting failed (progeny=4).");
-  for (int k = 0; k < c->progeny[5]->count; k++)
-    if (c->progeny[5]->parts[k].x[0] < pivot[0] ||
-        c->progeny[5]->parts[k].x[1] >= pivot[1] ||
-        c->progeny[5]->parts[k].x[2] < pivot[2])
+  for (int k = 0; k < c->progeny[5]->hydro.count; k++)
+    if (c->progeny[5]->hydro.parts[k].x[0] < pivot[0] ||
+        c->progeny[5]->hydro.parts[k].x[1] >= pivot[1] ||
+        c->progeny[5]->hydro.parts[k].x[2] < pivot[2])
       error("Sorting failed (progeny=5).");
-  for (int k = 0; k < c->progeny[6]->count; k++)
-    if (c->progeny[6]->parts[k].x[0] < pivot[0] ||
-        c->progeny[6]->parts[k].x[1] < pivot[1] ||
-        c->progeny[6]->parts[k].x[2] >= pivot[2])
+  for (int k = 0; k < c->progeny[6]->hydro.count; k++)
+    if (c->progeny[6]->hydro.parts[k].x[0] < pivot[0] ||
+        c->progeny[6]->hydro.parts[k].x[1] < pivot[1] ||
+        c->progeny[6]->hydro.parts[k].x[2] >= pivot[2])
       error("Sorting failed (progeny=6).");
-  for (int k = 0; k < c->progeny[7]->count; k++)
-    if (c->progeny[7]->parts[k].x[0] < pivot[0] ||
-        c->progeny[7]->parts[k].x[1] < pivot[1] ||
-        c->progeny[7]->parts[k].x[2] < pivot[2])
+  for (int k = 0; k < c->progeny[7]->hydro.count; k++)
+    if (c->progeny[7]->hydro.parts[k].x[0] < pivot[0] ||
+        c->progeny[7]->hydro.parts[k].x[1] < pivot[1] ||
+        c->progeny[7]->hydro.parts[k].x[2] < pivot[2])
       error("Sorting failed (progeny=7).");
 #endif
 
@@ -1074,8 +1080,8 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset,
 
   /* Store the counts and offsets. */
   for (int k = 0; k < 8; k++) {
-    c->progeny[k]->scount = bucket_count[k];
-    c->progeny[k]->sparts = &c->sparts[bucket_offset[k]];
+    c->progeny[k]->stars.count = bucket_count[k];
+    c->progeny[k]->stars.parts = &c->stars.parts[bucket_offset[k]];
   }
 
   /* Finally, do the same song and dance for the gparts. */
@@ -1136,8 +1142,8 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset,
 
   /* Store the counts and offsets. */
   for (int k = 0; k < 8; k++) {
-    c->progeny[k]->gcount = bucket_count[k];
-    c->progeny[k]->gparts = &c->gparts[bucket_offset[k]];
+    c->progeny[k]->grav.count = bucket_count[k];
+    c->progeny[k]->grav.parts = &c->grav.parts[bucket_offset[k]];
   }
 }
 
@@ -1153,9 +1159,12 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset, ptrdiff_t sparts_offset,
  */
 void cell_sanitize(struct cell *c, int treated) {
 
-  const int count = c->count;
-  struct part *parts = c->parts;
+  const int count = c->hydro.count;
+  const int scount = c->stars.count;
+  struct part *parts = c->hydro.parts;
+  struct spart *sparts = c->stars.parts;
   float h_max = 0.f;
+  float stars_h_max = 0.f;
 
   /* Treat cells will <1000 particles */
   if (count < 1000 && !treated) {
@@ -1168,6 +1177,10 @@ void cell_sanitize(struct cell *c, int treated) {
       if (parts[i].h == 0.f || parts[i].h > upper_h_max)
         parts[i].h = upper_h_max;
     }
+    for (int i = 0; i < scount; ++i) {
+      if (sparts[i].h == 0.f || sparts[i].h > upper_h_max)
+        sparts[i].h = upper_h_max;
+    }
   }
 
   /* Recurse and gather the new h_max values */
@@ -1180,17 +1193,21 @@ void cell_sanitize(struct cell *c, int treated) {
         cell_sanitize(c->progeny[k], (count < 1000));
 
         /* And collect */
-        h_max = max(h_max, c->progeny[k]->h_max);
+        h_max = max(h_max, c->progeny[k]->hydro.h_max);
+        stars_h_max = max(stars_h_max, c->progeny[k]->stars.h_max);
       }
     }
   } else {
 
     /* Get the new value of h_max */
     for (int i = 0; i < count; ++i) h_max = max(h_max, parts[i].h);
+    for (int i = 0; i < scount; ++i)
+      stars_h_max = max(stars_h_max, sparts[i].h);
   }
 
   /* Record the change */
-  c->h_max = h_max;
+  c->hydro.h_max = h_max;
+  c->stars.h_max = stars_h_max;
 }
 
 /**
@@ -1200,11 +1217,11 @@ void cell_sanitize(struct cell *c, int treated) {
  * @param data Unused parameter
  */
 void cell_clean_links(struct cell *c, void *data) {
-  c->density = NULL;
-  c->gradient = NULL;
-  c->force = NULL;
-  c->grav = NULL;
-  c->grav_mm = NULL;
+  c->hydro.density = NULL;
+  c->hydro.gradient = NULL;
+  c->hydro.force = NULL;
+  c->grav.grav = NULL;
+  c->grav.mm = NULL;
 }
 
 /**
@@ -1225,14 +1242,18 @@ void cell_check_part_drift_point(struct cell *c, void *data) {
   /* Only check local cells */
   if (c->nodeID != engine_rank) return;
 
-  if (c->ti_old_part != ti_drift)
-    error("Cell in an incorrect time-zone! c->ti_old_part=%lld ti_drift=%lld",
-          c->ti_old_part, ti_drift);
+  /* Only check cells with content */
+  if (c->hydro.count == 0) return;
+
+  if (c->hydro.ti_old_part != ti_drift)
+    error("Cell in an incorrect time-zone! c->hydro.ti_old=%lld ti_drift=%lld",
+          c->hydro.ti_old_part, ti_drift);
 
-  for (int i = 0; i < c->count; ++i)
-    if (c->parts[i].ti_drift != ti_drift)
+  for (int i = 0; i < c->hydro.count; ++i)
+    if (c->hydro.parts[i].ti_drift != ti_drift &&
+        c->hydro.parts[i].time_bin != time_bin_inhibited)
       error("part in an incorrect time-zone! p->ti_drift=%lld ti_drift=%lld",
-            c->parts[i].ti_drift, ti_drift);
+            c->hydro.parts[i].ti_drift, ti_drift);
 #else
   error("Calling debugging code without debugging flag activated.");
 #endif
@@ -1256,19 +1277,26 @@ void cell_check_gpart_drift_point(struct cell *c, void *data) {
   /* Only check local cells */
   if (c->nodeID != engine_rank) return;
 
-  if (c->ti_old_gpart != ti_drift)
-    error("Cell in an incorrect time-zone! c->ti_old_gpart=%lld ti_drift=%lld",
-          c->ti_old_gpart, ti_drift);
+  /* Only check cells with content */
+  if (c->grav.count == 0) return;
+
+  if (c->grav.ti_old_part != ti_drift)
+    error(
+        "Cell in an incorrect time-zone! c->grav.ti_old_part=%lld "
+        "ti_drift=%lld",
+        c->grav.ti_old_part, ti_drift);
 
-  for (int i = 0; i < c->gcount; ++i)
-    if (c->gparts[i].ti_drift != ti_drift)
+  for (int i = 0; i < c->grav.count; ++i)
+    if (c->grav.parts[i].ti_drift != ti_drift &&
+        c->grav.parts[i].time_bin != time_bin_inhibited)
       error("g-part in an incorrect time-zone! gp->ti_drift=%lld ti_drift=%lld",
-            c->gparts[i].ti_drift, ti_drift);
+            c->grav.parts[i].ti_drift, ti_drift);
 
-  for (int i = 0; i < c->scount; ++i)
-    if (c->sparts[i].ti_drift != ti_drift)
+  for (int i = 0; i < c->stars.count; ++i)
+    if (c->stars.parts[i].ti_drift != ti_drift &&
+        c->stars.parts[i].time_bin != time_bin_inhibited)
       error("s-part in an incorrect time-zone! sp->ti_drift=%lld ti_drift=%lld",
-            c->sparts[i].ti_drift, ti_drift);
+            c->stars.parts[i].ti_drift, ti_drift);
 #else
   error("Calling debugging code without debugging flag activated.");
 #endif
@@ -1288,11 +1316,18 @@ void cell_check_multipole_drift_point(struct cell *c, void *data) {
 
   const integertime_t ti_drift = *(integertime_t *)data;
 
-  if (c->ti_old_multipole != ti_drift)
+  /* Only check local cells */
+  if (c->nodeID != engine_rank) return;
+
+  /* Only check cells with content */
+  if (c->grav.count == 0) return;
+
+  if (c->grav.ti_old_multipole != ti_drift)
     error(
-        "Cell multipole in an incorrect time-zone! c->ti_old_multipole=%lld "
-        "ti_drift=%lld (depth=%d)",
-        c->ti_old_multipole, ti_drift, c->depth);
+        "Cell multipole in an incorrect time-zone! "
+        "c->grav.ti_old_multipole=%lld "
+        "ti_drift=%lld (depth=%d, node=%d)",
+        c->grav.ti_old_multipole, ti_drift, c->depth, c->nodeID);
 
 #else
   error("Calling debugging code without debugging flag activated.");
@@ -1325,7 +1360,7 @@ void cell_reset_task_counters(struct cell *c) {
 void cell_make_multipoles(struct cell *c, integertime_t ti_current) {
 
   /* Reset everything */
-  gravity_reset(c->multipole);
+  gravity_reset(c->grav.multipole);
 
   if (c->split) {
 
@@ -1341,7 +1376,7 @@ void cell_make_multipoles(struct cell *c, integertime_t ti_current) {
 
     for (int k = 0; k < 8; ++k) {
       if (c->progeny[k] != NULL) {
-        const struct gravity_tensors *m = c->progeny[k]->multipole;
+        const struct gravity_tensors *m = c->progeny[k]->grav.multipole;
         CoM[0] += m->CoM[0] * m->m_pole.M_000;
         CoM[1] += m->CoM[1] * m->m_pole.M_000;
         CoM[2] += m->CoM[2] * m->m_pole.M_000;
@@ -1350,9 +1385,9 @@ void cell_make_multipoles(struct cell *c, integertime_t ti_current) {
     }
 
     const double mass_inv = 1. / mass;
-    c->multipole->CoM[0] = CoM[0] * mass_inv;
-    c->multipole->CoM[1] = CoM[1] * mass_inv;
-    c->multipole->CoM[2] = CoM[2] * mass_inv;
+    c->grav.multipole->CoM[0] = CoM[0] * mass_inv;
+    c->grav.multipole->CoM[1] = CoM[1] * mass_inv;
+    c->grav.multipole->CoM[2] = CoM[2] * mass_inv;
 
     /* Now shift progeny multipoles and add them up */
     struct multipole temp;
@@ -1360,64 +1395,70 @@ void cell_make_multipoles(struct cell *c, integertime_t ti_current) {
     for (int k = 0; k < 8; ++k) {
       if (c->progeny[k] != NULL) {
         const struct cell *cp = c->progeny[k];
-        const struct multipole *m = &cp->multipole->m_pole;
+        const struct multipole *m = &cp->grav.multipole->m_pole;
 
         /* Contribution to multipole */
-        gravity_M2M(&temp, m, c->multipole->CoM, cp->multipole->CoM);
-        gravity_multipole_add(&c->multipole->m_pole, &temp);
+        gravity_M2M(&temp, m, c->grav.multipole->CoM, cp->grav.multipole->CoM);
+        gravity_multipole_add(&c->grav.multipole->m_pole, &temp);
 
         /* Upper limit of max CoM<->gpart distance */
-        const double dx = c->multipole->CoM[0] - cp->multipole->CoM[0];
-        const double dy = c->multipole->CoM[1] - cp->multipole->CoM[1];
-        const double dz = c->multipole->CoM[2] - cp->multipole->CoM[2];
+        const double dx =
+            c->grav.multipole->CoM[0] - cp->grav.multipole->CoM[0];
+        const double dy =
+            c->grav.multipole->CoM[1] - cp->grav.multipole->CoM[1];
+        const double dz =
+            c->grav.multipole->CoM[2] - cp->grav.multipole->CoM[2];
         const double r2 = dx * dx + dy * dy + dz * dz;
-        r_max = max(r_max, cp->multipole->r_max + sqrt(r2));
+        r_max = max(r_max, cp->grav.multipole->r_max + sqrt(r2));
       }
     }
     /* Alternative upper limit of max CoM<->gpart distance */
-    const double dx = c->multipole->CoM[0] > c->loc[0] + c->width[0] * 0.5
-                          ? c->multipole->CoM[0] - c->loc[0]
-                          : c->loc[0] + c->width[0] - c->multipole->CoM[0];
-    const double dy = c->multipole->CoM[1] > c->loc[1] + c->width[1] * 0.5
-                          ? c->multipole->CoM[1] - c->loc[1]
-                          : c->loc[1] + c->width[1] - c->multipole->CoM[1];
-    const double dz = c->multipole->CoM[2] > c->loc[2] + c->width[2] * 0.5
-                          ? c->multipole->CoM[2] - c->loc[2]
-                          : c->loc[2] + c->width[2] - c->multipole->CoM[2];
+    const double dx = c->grav.multipole->CoM[0] > c->loc[0] + c->width[0] * 0.5
+                          ? c->grav.multipole->CoM[0] - c->loc[0]
+                          : c->loc[0] + c->width[0] - c->grav.multipole->CoM[0];
+    const double dy = c->grav.multipole->CoM[1] > c->loc[1] + c->width[1] * 0.5
+                          ? c->grav.multipole->CoM[1] - c->loc[1]
+                          : c->loc[1] + c->width[1] - c->grav.multipole->CoM[1];
+    const double dz = c->grav.multipole->CoM[2] > c->loc[2] + c->width[2] * 0.5
+                          ? c->grav.multipole->CoM[2] - c->loc[2]
+                          : c->loc[2] + c->width[2] - c->grav.multipole->CoM[2];
 
     /* Take minimum of both limits */
-    c->multipole->r_max = min(r_max, sqrt(dx * dx + dy * dy + dz * dz));
+    c->grav.multipole->r_max = min(r_max, sqrt(dx * dx + dy * dy + dz * dz));
 
   } else {
 
-    if (c->gcount > 0) {
-      gravity_P2M(c->multipole, c->gparts, c->gcount);
-      const double dx = c->multipole->CoM[0] > c->loc[0] + c->width[0] * 0.5
-                            ? c->multipole->CoM[0] - c->loc[0]
-                            : c->loc[0] + c->width[0] - c->multipole->CoM[0];
-      const double dy = c->multipole->CoM[1] > c->loc[1] + c->width[1] * 0.5
-                            ? c->multipole->CoM[1] - c->loc[1]
-                            : c->loc[1] + c->width[1] - c->multipole->CoM[1];
-      const double dz = c->multipole->CoM[2] > c->loc[2] + c->width[2] * 0.5
-                            ? c->multipole->CoM[2] - c->loc[2]
-                            : c->loc[2] + c->width[2] - c->multipole->CoM[2];
-      c->multipole->r_max = sqrt(dx * dx + dy * dy + dz * dz);
+    if (c->grav.count > 0) {
+      gravity_P2M(c->grav.multipole, c->grav.parts, c->grav.count);
+      const double dx =
+          c->grav.multipole->CoM[0] > c->loc[0] + c->width[0] * 0.5
+              ? c->grav.multipole->CoM[0] - c->loc[0]
+              : c->loc[0] + c->width[0] - c->grav.multipole->CoM[0];
+      const double dy =
+          c->grav.multipole->CoM[1] > c->loc[1] + c->width[1] * 0.5
+              ? c->grav.multipole->CoM[1] - c->loc[1]
+              : c->loc[1] + c->width[1] - c->grav.multipole->CoM[1];
+      const double dz =
+          c->grav.multipole->CoM[2] > c->loc[2] + c->width[2] * 0.5
+              ? c->grav.multipole->CoM[2] - c->loc[2]
+              : c->loc[2] + c->width[2] - c->grav.multipole->CoM[2];
+      c->grav.multipole->r_max = sqrt(dx * dx + dy * dy + dz * dz);
     } else {
-      gravity_multipole_init(&c->multipole->m_pole);
-      c->multipole->CoM[0] = c->loc[0] + c->width[0] * 0.5;
-      c->multipole->CoM[1] = c->loc[1] + c->width[1] * 0.5;
-      c->multipole->CoM[2] = c->loc[2] + c->width[2] * 0.5;
-      c->multipole->r_max = 0.;
+      gravity_multipole_init(&c->grav.multipole->m_pole);
+      c->grav.multipole->CoM[0] = c->loc[0] + c->width[0] * 0.5;
+      c->grav.multipole->CoM[1] = c->loc[1] + c->width[1] * 0.5;
+      c->grav.multipole->CoM[2] = c->loc[2] + c->width[2] * 0.5;
+      c->grav.multipole->r_max = 0.;
     }
   }
 
   /* Also update the values at rebuild time */
-  c->multipole->r_max_rebuild = c->multipole->r_max;
-  c->multipole->CoM_rebuild[0] = c->multipole->CoM[0];
-  c->multipole->CoM_rebuild[1] = c->multipole->CoM[1];
-  c->multipole->CoM_rebuild[2] = c->multipole->CoM[2];
+  c->grav.multipole->r_max_rebuild = c->grav.multipole->r_max;
+  c->grav.multipole->CoM_rebuild[0] = c->grav.multipole->CoM[0];
+  c->grav.multipole->CoM_rebuild[1] = c->grav.multipole->CoM[1];
+  c->grav.multipole->CoM_rebuild[2] = c->grav.multipole->CoM[2];
 
-  c->ti_old_multipole = ti_current;
+  c->grav.ti_old_multipole = ti_current;
 }
 
 /**
@@ -1443,17 +1484,17 @@ void cell_check_foreign_multipole(const struct cell *c) {
       if (cp != NULL) {
 
         /* Check the mass */
-        M_000 += cp->multipole->m_pole.M_000;
+        M_000 += cp->grav.multipole->m_pole.M_000;
 
         /* Check the number of particles */
-        num_gpart += cp->multipole->m_pole.num_gpart;
+        num_gpart += cp->grav.multipole->m_pole.num_gpart;
 
         /* Now recurse */
         cell_check_foreign_multipole(cp);
       }
     }
 
-    if (num_gpart != c->multipole->m_pole.num_gpart)
+    if (num_gpart != c->grav.multipole->m_pole.num_gpart)
       error("Sum of particles in progenies does not match");
   }
 
@@ -1479,29 +1520,29 @@ void cell_check_multipole(struct cell *c) {
     for (int k = 0; k < 8; k++)
       if (c->progeny[k] != NULL) cell_check_multipole(c->progeny[k]);
 
-  if (c->gcount > 0) {
+  if (c->grav.count > 0) {
 
     /* Brute-force calculation */
-    gravity_P2M(&ma, c->gparts, c->gcount);
+    gravity_P2M(&ma, c->grav.parts, c->grav.count);
 
     /* Now  compare the multipole expansion */
-    if (!gravity_multipole_equal(&ma, c->multipole, tolerance)) {
+    if (!gravity_multipole_equal(&ma, c->grav.multipole, tolerance)) {
       message("Multipoles are not equal at depth=%d! tol=%f", c->depth,
               tolerance);
       message("Correct answer:");
       gravity_multipole_print(&ma.m_pole);
       message("Recursive multipole:");
-      gravity_multipole_print(&c->multipole->m_pole);
+      gravity_multipole_print(&c->grav.multipole->m_pole);
       error("Aborting");
     }
 
     /* Check that the upper limit of r_max is good enough */
-    if (!(1.1 * c->multipole->r_max >= ma.r_max)) {
+    if (!(1.1 * c->grav.multipole->r_max >= ma.r_max)) {
       error("Upper-limit r_max=%e too small. Should be >=%e.",
-            c->multipole->r_max, ma.r_max);
-    } else if (c->multipole->r_max * c->multipole->r_max >
+            c->grav.multipole->r_max, ma.r_max);
+    } else if (c->grav.multipole->r_max * c->grav.multipole->r_max >
                3. * c->width[0] * c->width[0]) {
-      error("r_max=%e larger than cell diagonal %e.", c->multipole->r_max,
+      error("r_max=%e larger than cell diagonal %e.", c->grav.multipole->r_max,
             sqrt(3. * c->width[0] * c->width[0]));
     }
   }
@@ -1518,9 +1559,9 @@ void cell_check_multipole(struct cell *c) {
 void cell_clean(struct cell *c) {
 
   for (int i = 0; i < 13; i++)
-    if (c->sort[i] != NULL) {
-      free(c->sort[i]);
-      c->sort[i] = NULL;
+    if (c->hydro.sort[i] != NULL) {
+      free(c->hydro.sort[i]);
+      c->hydro.sort[i] = NULL;
     }
 
   /* Recurse */
@@ -1532,10 +1573,10 @@ void cell_clean(struct cell *c) {
  * @brief Clear the drift flags on the given cell.
  */
 void cell_clear_drift_flags(struct cell *c, void *data) {
-  c->do_drift = 0;
-  c->do_sub_drift = 0;
-  c->do_grav_drift = 0;
-  c->do_grav_sub_drift = 0;
+  c->hydro.do_drift = 0;
+  c->hydro.do_sub_drift = 0;
+  c->grav.do_drift = 0;
+  c->grav.do_sub_drift = 0;
 }
 
 /**
@@ -1544,29 +1585,30 @@ void cell_clear_drift_flags(struct cell *c, void *data) {
 void cell_activate_drift_part(struct cell *c, struct scheduler *s) {
 
   /* If this cell is already marked for drift, quit early. */
-  if (c->do_drift) return;
+  if (c->hydro.do_drift) return;
 
   /* Mark this cell for drifting. */
-  c->do_drift = 1;
+  c->hydro.do_drift = 1;
 
   /* Set the do_sub_drifts all the way up and activate the super drift
      if this has not yet been done. */
-  if (c == c->super_hydro) {
+  if (c == c->hydro.super) {
 #ifdef SWIFT_DEBUG_CHECKS
-    if (c->drift_part == NULL)
-      error("Trying to activate un-existing c->drift_part");
+    if (c->hydro.drift == NULL)
+      error("Trying to activate un-existing c->hydro.drift");
 #endif
-    scheduler_activate(s, c->drift_part);
+    scheduler_activate(s, c->hydro.drift);
   } else {
     for (struct cell *parent = c->parent;
-         parent != NULL && !parent->do_sub_drift; parent = parent->parent) {
-      parent->do_sub_drift = 1;
-      if (parent == c->super_hydro) {
+         parent != NULL && !parent->hydro.do_sub_drift;
+         parent = parent->parent) {
+      parent->hydro.do_sub_drift = 1;
+      if (parent == c->hydro.super) {
 #ifdef SWIFT_DEBUG_CHECKS
-        if (parent->drift_part == NULL)
-          error("Trying to activate un-existing parent->drift_part");
+        if (parent->hydro.drift == NULL)
+          error("Trying to activate un-existing parent->hydro.drift");
 #endif
-        scheduler_activate(s, parent->drift_part);
+        scheduler_activate(s, parent->hydro.drift);
         break;
       }
     }
@@ -1579,58 +1621,67 @@ void cell_activate_drift_part(struct cell *c, struct scheduler *s) {
 void cell_activate_drift_gpart(struct cell *c, struct scheduler *s) {
 
   /* If this cell is already marked for drift, quit early. */
-  if (c->do_grav_drift) return;
+  if (c->grav.do_drift) return;
 
   /* Mark this cell for drifting. */
-  c->do_grav_drift = 1;
+  c->grav.do_drift = 1;
 
   /* Set the do_grav_sub_drifts all the way up and activate the super drift
      if this has not yet been done. */
-  if (c == c->super_gravity) {
+  if (c == c->grav.super) {
 #ifdef SWIFT_DEBUG_CHECKS
-    if (c->drift_gpart == NULL)
-      error("Trying to activate un-existing c->drift_gpart");
+    if (c->grav.drift == NULL)
+      error("Trying to activate un-existing c->grav.drift");
 #endif
-    scheduler_activate(s, c->drift_gpart);
+    scheduler_activate(s, c->grav.drift);
   } else {
     for (struct cell *parent = c->parent;
-         parent != NULL && !parent->do_grav_sub_drift;
+         parent != NULL && !parent->grav.do_sub_drift;
          parent = parent->parent) {
-      parent->do_grav_sub_drift = 1;
-      if (parent == c->super_gravity) {
+      parent->grav.do_sub_drift = 1;
+      if (parent == c->grav.super) {
 #ifdef SWIFT_DEBUG_CHECKS
-        if (parent->drift_gpart == NULL)
-          error("Trying to activate un-existing parent->drift_gpart");
+        if (parent->grav.drift == NULL)
+          error("Trying to activate un-existing parent->grav.drift");
 #endif
-        scheduler_activate(s, parent->drift_gpart);
+        scheduler_activate(s, parent->grav.drift);
         break;
       }
     }
   }
 }
 
+/**
+ * @brief Activate the #spart drifts on the given cell.
+ */
+void cell_activate_drift_spart(struct cell *c, struct scheduler *s) {
+  cell_activate_drift_gpart(c, s);
+}
+
 /**
  * @brief Activate the sorts up a cell hierarchy.
  */
 void cell_activate_sorts_up(struct cell *c, struct scheduler *s) {
 
-  if (c == c->super_hydro) {
+  if (c == c->hydro.super) {
 #ifdef SWIFT_DEBUG_CHECKS
-    if (c->sorts == NULL) error("Trying to activate un-existing c->sorts");
+    if (c->hydro.sorts == NULL)
+      error("Trying to activate un-existing c->hydro.sorts");
 #endif
-    scheduler_activate(s, c->sorts);
+    scheduler_activate(s, c->hydro.sorts);
     if (c->nodeID == engine_rank) cell_activate_drift_part(c, s);
   } else {
 
     for (struct cell *parent = c->parent;
-         parent != NULL && !parent->do_sub_sort; parent = parent->parent) {
-      parent->do_sub_sort = 1;
-      if (parent == c->super_hydro) {
+         parent != NULL && !parent->hydro.do_sub_sort;
+         parent = parent->parent) {
+      parent->hydro.do_sub_sort = 1;
+      if (parent == c->hydro.super) {
 #ifdef SWIFT_DEBUG_CHECKS
-        if (parent->sorts == NULL)
-          error("Trying to activate un-existing parents->sorts");
+        if (parent->hydro.sorts == NULL)
+          error("Trying to activate un-existing parents->hydro.sorts");
 #endif
-        scheduler_activate(s, parent->sorts);
+        scheduler_activate(s, parent->hydro.sorts);
         if (parent->nodeID == engine_rank) cell_activate_drift_part(parent, s);
         break;
       }
@@ -1644,21 +1695,21 @@ void cell_activate_sorts_up(struct cell *c, struct scheduler *s) {
 void cell_activate_sorts(struct cell *c, int sid, struct scheduler *s) {
 
   /* Do we need to re-sort? */
-  if (c->dx_max_sort > space_maxreldx * c->dmin) {
+  if (c->hydro.dx_max_sort > space_maxreldx * c->dmin) {
 
     /* Climb up the tree to active the sorts in that direction */
     for (struct cell *finger = c; finger != NULL; finger = finger->parent) {
-      if (finger->requires_sorts) {
-        atomic_or(&finger->do_sort, finger->requires_sorts);
+      if (finger->hydro.requires_sorts) {
+        atomic_or(&finger->hydro.do_sort, finger->hydro.requires_sorts);
         cell_activate_sorts_up(finger, s);
       }
-      finger->sorted = 0;
+      finger->hydro.sorted = 0;
     }
   }
 
   /* Has this cell been sorted at all for the given sid? */
-  if (!(c->sorted & (1 << sid)) || c->nodeID != engine_rank) {
-    atomic_or(&c->do_sort, (1 << sid));
+  if (!(c->hydro.sorted & (1 << sid)) || c->nodeID != engine_rank) {
+    atomic_or(&c->hydro.do_sort, (1 << sid));
     cell_activate_sorts_up(c, s);
   }
 }
@@ -1676,18 +1727,19 @@ void cell_activate_subcell_hydro_tasks(struct cell *ci, struct cell *cj,
   const struct engine *e = s->space->e;
 
   /* Store the current dx_max and h_max values. */
-  ci->dx_max_old = ci->dx_max_part;
-  ci->h_max_old = ci->h_max;
+  ci->hydro.dx_max_part_old = ci->hydro.dx_max_part;
+  ci->hydro.h_max_old = ci->hydro.h_max;
+
   if (cj != NULL) {
-    cj->dx_max_old = cj->dx_max_part;
-    cj->h_max_old = cj->h_max;
+    cj->hydro.dx_max_part_old = cj->hydro.dx_max_part;
+    cj->hydro.h_max_old = cj->hydro.h_max;
   }
 
   /* Self interaction? */
   if (cj == NULL) {
 
     /* Do anything? */
-    if (ci->count == 0 || !cell_is_active_hydro(ci, e)) return;
+    if (ci->hydro.count == 0 || !cell_is_active_hydro(ci, e)) return;
 
     /* Recurse? */
     if (cell_can_recurse_in_self_hydro_task(ci)) {
@@ -1714,7 +1766,7 @@ void cell_activate_subcell_hydro_tasks(struct cell *ci, struct cell *cj,
 
     /* Should we even bother? */
     if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
-    if (ci->count == 0 || cj->count == 0) return;
+    if (ci->hydro.count == 0 || cj->hydro.count == 0) return;
 
     /* Get the orientation of the pair. */
     double shift[3];
@@ -2002,10 +2054,10 @@ void cell_activate_subcell_hydro_tasks(struct cell *ci, struct cell *cj,
     else if (cell_is_active_hydro(ci, e) || cell_is_active_hydro(cj, e)) {
 
       /* We are going to interact this pair, so store some values. */
-      atomic_or(&ci->requires_sorts, 1 << sid);
-      atomic_or(&cj->requires_sorts, 1 << sid);
-      ci->dx_max_sort_old = ci->dx_max_sort;
-      cj->dx_max_sort_old = cj->dx_max_sort;
+      atomic_or(&ci->hydro.requires_sorts, 1 << sid);
+      atomic_or(&cj->hydro.requires_sorts, 1 << sid);
+      ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+      cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
 
       /* Activate the drifts if the cells are local. */
       if (ci->nodeID == engine_rank) cell_activate_drift_part(ci, s);
@@ -2028,7 +2080,353 @@ void cell_activate_subcell_hydro_tasks(struct cell *ci, struct cell *cj,
  */
 void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj,
                                        struct scheduler *s) {
-  // LOIC: to implement
+  const struct engine *e = s->space->e;
+
+  /* Store the current dx_max and h_max values. */
+  ci->stars.dx_max_part_old = ci->stars.dx_max_part;
+  ci->stars.h_max_old = ci->stars.h_max;
+
+  if (cj != NULL) {
+    cj->stars.dx_max_part_old = cj->stars.dx_max_part;
+    cj->stars.h_max_old = cj->stars.h_max;
+  }
+
+  /* Self interaction? */
+  if (cj == NULL) {
+
+    /* Do anything? */
+    if (ci->stars.count == 0 || !cell_is_active_stars(ci, e)) return;
+
+    /* Recurse? */
+    if (cell_can_recurse_in_self_stars_task(ci)) {
+
+      /* Loop over all progenies and pairs of progenies */
+      for (int j = 0; j < 8; j++) {
+        if (ci->progeny[j] != NULL) {
+          cell_activate_subcell_stars_tasks(ci->progeny[j], NULL, s);
+          for (int k = j + 1; k < 8; k++)
+            if (ci->progeny[k] != NULL)
+              cell_activate_subcell_stars_tasks(ci->progeny[j], ci->progeny[k],
+                                                s);
+        }
+      }
+    } else {
+
+      /* We have reached the bottom of the tree: activate drift */
+      cell_activate_drift_spart(ci, s);
+      cell_activate_drift_part(ci, s);
+    }
+  }
+
+  /* Otherwise, pair interation */
+  else {
+
+    /* Should we even bother? */
+    if (!cell_is_active_stars(ci, e) && !cell_is_active_stars(cj, e)) return;
+    if (ci->stars.count == 0 || cj->stars.count == 0) return;
+
+    /* Get the orientation of the pair. */
+    double shift[3];
+    int sid = space_getsid(s->space, &ci, &cj, shift);
+
+    /* recurse? */
+    if (cell_can_recurse_in_pair_stars_task(ci) &&
+        cell_can_recurse_in_pair_stars_task(cj)) {
+
+      /* Different types of flags. */
+      switch (sid) {
+
+        /* Regular sub-cell interactions of a single cell. */
+        case 0: /* (  1 ,  1 ,  1 ) */
+          if (ci->progeny[7] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[0],
+                                              s);
+          break;
+
+        case 1: /* (  1 ,  1 ,  0 ) */
+          if (ci->progeny[6] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[0],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[1],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[0],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[1],
+                                              s);
+          break;
+
+        case 2: /* (  1 ,  1 , -1 ) */
+          if (ci->progeny[6] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[1],
+                                              s);
+          break;
+
+        case 3: /* (  1 ,  0 ,  1 ) */
+          if (ci->progeny[5] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[0],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[2],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[0],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[2],
+                                              s);
+          break;
+
+        case 4: /* (  1 ,  0 ,  0 ) */
+          if (ci->progeny[4] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[4], cj->progeny[0],
+                                              s);
+          if (ci->progeny[4] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[4], cj->progeny[1],
+                                              s);
+          if (ci->progeny[4] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[4], cj->progeny[2],
+                                              s);
+          if (ci->progeny[4] != NULL && cj->progeny[3] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[4], cj->progeny[3],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[0],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[1],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[2],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[3] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[3],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[0],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[1],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[2],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[3] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[3],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[0],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[1],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[2],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[3] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[3],
+                                              s);
+          break;
+
+        case 5: /* (  1 ,  0 , -1 ) */
+          if (ci->progeny[4] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[4], cj->progeny[1],
+                                              s);
+          if (ci->progeny[4] != NULL && cj->progeny[3] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[4], cj->progeny[3],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[1],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[3] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[3],
+                                              s);
+          break;
+
+        case 6: /* (  1 , -1 ,  1 ) */
+          if (ci->progeny[5] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[2],
+                                              s);
+          break;
+
+        case 7: /* (  1 , -1 ,  0 ) */
+          if (ci->progeny[4] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[4], cj->progeny[2],
+                                              s);
+          if (ci->progeny[4] != NULL && cj->progeny[3] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[4], cj->progeny[3],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[2],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[3] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[3],
+                                              s);
+          break;
+
+        case 8: /* (  1 , -1 , -1 ) */
+          if (ci->progeny[4] != NULL && cj->progeny[3] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[4], cj->progeny[3],
+                                              s);
+          break;
+
+        case 9: /* (  0 ,  1 ,  1 ) */
+          if (ci->progeny[3] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[0],
+                                              s);
+          if (ci->progeny[3] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[4],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[0],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[4],
+                                              s);
+          break;
+
+        case 10: /* (  0 ,  1 ,  0 ) */
+          if (ci->progeny[2] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[2], cj->progeny[0],
+                                              s);
+          if (ci->progeny[2] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[2], cj->progeny[1],
+                                              s);
+          if (ci->progeny[2] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[2], cj->progeny[4],
+                                              s);
+          if (ci->progeny[2] != NULL && cj->progeny[5] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[2], cj->progeny[5],
+                                              s);
+          if (ci->progeny[3] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[0],
+                                              s);
+          if (ci->progeny[3] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[1],
+                                              s);
+          if (ci->progeny[3] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[4],
+                                              s);
+          if (ci->progeny[3] != NULL && cj->progeny[5] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[5],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[0],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[1],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[4],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[5] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[5],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[0],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[1],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[4],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[5] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[5],
+                                              s);
+          break;
+
+        case 11: /* (  0 ,  1 , -1 ) */
+          if (ci->progeny[2] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[2], cj->progeny[1],
+                                              s);
+          if (ci->progeny[2] != NULL && cj->progeny[5] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[2], cj->progeny[5],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[1] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[1],
+                                              s);
+          if (ci->progeny[6] != NULL && cj->progeny[5] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[6], cj->progeny[5],
+                                              s);
+          break;
+
+        case 12: /* (  0 ,  0 ,  1 ) */
+          if (ci->progeny[1] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[1], cj->progeny[0],
+                                              s);
+          if (ci->progeny[1] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[1], cj->progeny[2],
+                                              s);
+          if (ci->progeny[1] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[1], cj->progeny[4],
+                                              s);
+          if (ci->progeny[1] != NULL && cj->progeny[6] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[1], cj->progeny[6],
+                                              s);
+          if (ci->progeny[3] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[0],
+                                              s);
+          if (ci->progeny[3] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[2],
+                                              s);
+          if (ci->progeny[3] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[4],
+                                              s);
+          if (ci->progeny[3] != NULL && cj->progeny[6] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[3], cj->progeny[6],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[0],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[2],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[4],
+                                              s);
+          if (ci->progeny[5] != NULL && cj->progeny[6] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[5], cj->progeny[6],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[0] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[0],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[2] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[2],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[4] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[4],
+                                              s);
+          if (ci->progeny[7] != NULL && cj->progeny[6] != NULL)
+            cell_activate_subcell_stars_tasks(ci->progeny[7], cj->progeny[6],
+                                              s);
+          break;
+      }
+
+    }
+
+    /* Otherwise, activate the sorts and drifts. */
+    else if (cell_is_active_stars(ci, e) || cell_is_active_stars(cj, e)) {
+
+      /* We are going to interact this pair, so store some values. */
+      atomic_or(&ci->hydro.requires_sorts, 1 << sid);
+      atomic_or(&cj->hydro.requires_sorts, 1 << sid);
+      ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+      cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
+
+      /* Activate the drifts if the cells are local. */
+      if (ci->nodeID == engine_rank) cell_activate_drift_part(ci, s);
+      if (ci->nodeID == engine_rank) cell_activate_drift_spart(ci, s);
+      if (cj->nodeID == engine_rank) cell_activate_drift_part(cj, s);
+      if (cj->nodeID == engine_rank) cell_activate_drift_spart(cj, s);
+
+      /* Do we need to sort the cells? */
+      cell_activate_sorts(ci, sid, s);
+      cell_activate_sorts(cj, sid, s);
+    }
+  } /* Otherwise, pair interation */
 }
 
 /**
@@ -2049,7 +2447,7 @@ void cell_activate_subcell_grav_tasks(struct cell *ci, struct cell *cj,
   if (cj == NULL) {
 
     /* Do anything? */
-    if (ci->gcount == 0 || !cell_is_active_gravity(ci, e)) return;
+    if (ci->grav.count == 0 || !cell_is_active_gravity(ci, e)) return;
 
     /* Recurse? */
     if (ci->split) {
@@ -2077,17 +2475,17 @@ void cell_activate_subcell_grav_tasks(struct cell *ci, struct cell *cj,
     /* Anything to do here? */
     if (!cell_is_active_gravity(ci, e) && !cell_is_active_gravity(cj, e))
       return;
-    if (ci->gcount == 0 || cj->gcount == 0) return;
+    if (ci->grav.count == 0 || cj->grav.count == 0) return;
 
     /* Atomically drift the multipole in ci */
-    lock_lock(&ci->mlock);
-    if (ci->ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e);
-    if (lock_unlock(&ci->mlock) != 0) error("Impossible to unlock m-pole");
+    lock_lock(&ci->grav.mlock);
+    if (ci->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e);
+    if (lock_unlock(&ci->grav.mlock) != 0) error("Impossible to unlock m-pole");
 
     /* Atomically drift the multipole in cj */
-    lock_lock(&cj->mlock);
-    if (cj->ti_old_multipole < e->ti_current) cell_drift_multipole(cj, e);
-    if (lock_unlock(&cj->mlock) != 0) error("Impossible to unlock m-pole");
+    lock_lock(&cj->grav.mlock);
+    if (cj->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(cj, e);
+    if (lock_unlock(&cj->grav.mlock) != 0) error("Impossible to unlock m-pole");
 
     /* Can we use multipoles ? */
     if (cell_can_use_pair_mm(ci, cj, e, sp)) {
@@ -2108,8 +2506,8 @@ void cell_activate_subcell_grav_tasks(struct cell *ci, struct cell *cj,
     else {
 
       /* Recover the multipole information */
-      const struct gravity_tensors *const multi_i = ci->multipole;
-      const struct gravity_tensors *const multi_j = cj->multipole;
+      const struct gravity_tensors *const multi_i = ci->grav.multipole;
+      const struct gravity_tensors *const multi_j = cj->grav.multipole;
       const double ri_max = multi_i->r_max;
       const double rj_max = multi_j->r_max;
 
@@ -2207,7 +2605,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
   int rebuild = 0;
 
   /* Un-skip the density tasks involved with this cell. */
-  for (struct link *l = c->density; l != NULL; l = l->next) {
+  for (struct link *l = c->hydro.density; l != NULL; l = l->next) {
     struct task *t = l->t;
     struct cell *ci = t->ci;
     struct cell *cj = t->cj;
@@ -2234,10 +2632,10 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
       /* Set the correct sorting flags and activate hydro drifts */
       else if (t->type == task_type_pair) {
         /* Store some values. */
-        atomic_or(&ci->requires_sorts, 1 << t->flags);
-        atomic_or(&cj->requires_sorts, 1 << t->flags);
-        ci->dx_max_sort_old = ci->dx_max_sort;
-        cj->dx_max_sort_old = cj->dx_max_sort;
+        atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+        atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+        ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+        cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
 
         /* Activate the drift tasks. */
         if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
@@ -2266,23 +2664,23 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
 
         /* If the local cell is active, receive data from the foreign cell. */
         if (cj_active) {
-          scheduler_activate(s, ci->recv_xv);
+          scheduler_activate(s, ci->mpi.hydro.recv_xv);
           if (ci_active) {
-            scheduler_activate(s, ci->recv_rho);
+            scheduler_activate(s, ci->mpi.hydro.recv_rho);
 
 #ifdef EXTRA_HYDRO_LOOP
-            scheduler_activate(s, ci->recv_gradient);
+            scheduler_activate(s, ci->mpi.hydro.recv_gradient);
 #endif
           }
         }
 
         /* If the foreign cell is active, we want its ti_end values. */
-        if (ci_active) scheduler_activate(s, ci->recv_ti);
+        if (ci_active) scheduler_activate(s, ci->mpi.recv_ti);
 
         /* Is the foreign cell active and will need stuff from us? */
         if (ci_active) {
 
-          scheduler_activate_send(s, cj->send_xv, ci_nodeID);
+          scheduler_activate_send(s, cj->mpi.hydro.send_xv, ci_nodeID);
 
           /* Drift the cell which will be sent; note that not all sent
              particles will be drifted, only those that are needed. */
@@ -2290,38 +2688,38 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
 
           /* If the local cell is also active, more stuff will be needed. */
           if (cj_active) {
-            scheduler_activate_send(s, cj->send_rho, ci_nodeID);
+            scheduler_activate_send(s, cj->mpi.hydro.send_rho, ci_nodeID);
 
 #ifdef EXTRA_HYDRO_LOOP
-            scheduler_activate_send(s, cj->send_gradient, ci_nodeID);
+            scheduler_activate_send(s, cj->mpi.hydro.send_gradient, ci_nodeID);
 #endif
           }
         }
 
         /* If the local cell is active, send its ti_end values. */
-        if (cj_active) scheduler_activate_send(s, cj->send_ti, ci_nodeID);
+        if (cj_active) scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID);
 
       } else if (cj_nodeID != nodeID) {
 
         /* If the local cell is active, receive data from the foreign cell. */
         if (ci_active) {
-          scheduler_activate(s, cj->recv_xv);
+          scheduler_activate(s, cj->mpi.hydro.recv_xv);
           if (cj_active) {
-            scheduler_activate(s, cj->recv_rho);
+            scheduler_activate(s, cj->mpi.hydro.recv_rho);
 
 #ifdef EXTRA_HYDRO_LOOP
-            scheduler_activate(s, cj->recv_gradient);
+            scheduler_activate(s, cj->mpi.hydro.recv_gradient);
 #endif
           }
         }
 
         /* If the foreign cell is active, we want its ti_end values. */
-        if (cj_active) scheduler_activate(s, cj->recv_ti);
+        if (cj_active) scheduler_activate(s, cj->mpi.recv_ti);
 
         /* Is the foreign cell active and will need stuff from us? */
         if (cj_active) {
 
-          scheduler_activate_send(s, ci->send_xv, cj_nodeID);
+          scheduler_activate_send(s, ci->mpi.hydro.send_xv, cj_nodeID);
 
           /* Drift the cell which will be sent; note that not all sent
              particles will be drifted, only those that are needed. */
@@ -2330,16 +2728,16 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
           /* If the local cell is also active, more stuff will be needed. */
           if (ci_active) {
 
-            scheduler_activate_send(s, ci->send_rho, cj_nodeID);
+            scheduler_activate_send(s, ci->mpi.hydro.send_rho, cj_nodeID);
 
 #ifdef EXTRA_HYDRO_LOOP
-            scheduler_activate_send(s, ci->send_gradient, cj_nodeID);
+            scheduler_activate_send(s, ci->mpi.hydro.send_gradient, cj_nodeID);
 #endif
           }
         }
 
         /* If the local cell is active, send its ti_end values. */
-        if (ci_active) scheduler_activate_send(s, ci->send_ti, cj_nodeID);
+        if (ci_active) scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID);
       }
 #endif
     }
@@ -2348,21 +2746,25 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
   /* Unskip all the other task types. */
   if (c->nodeID == nodeID && cell_is_active_hydro(c, e)) {
 
-    for (struct link *l = c->gradient; l != NULL; l = l->next)
+    for (struct link *l = c->hydro.gradient; l != NULL; l = l->next)
       scheduler_activate(s, l->t);
-    for (struct link *l = c->force; l != NULL; l = l->next)
+    for (struct link *l = c->hydro.force; l != NULL; l = l->next)
       scheduler_activate(s, l->t);
 
-    if (c->extra_ghost != NULL) scheduler_activate(s, c->extra_ghost);
-    if (c->ghost_in != NULL) scheduler_activate(s, c->ghost_in);
-    if (c->ghost_out != NULL) scheduler_activate(s, c->ghost_out);
-    if (c->ghost != NULL) scheduler_activate(s, c->ghost);
+    if (c->hydro.extra_ghost != NULL)
+      scheduler_activate(s, c->hydro.extra_ghost);
+    if (c->hydro.ghost_in != NULL) scheduler_activate(s, c->hydro.ghost_in);
+    if (c->hydro.ghost_out != NULL) scheduler_activate(s, c->hydro.ghost_out);
+    if (c->hydro.ghost != NULL) scheduler_activate(s, c->hydro.ghost);
     if (c->kick1 != NULL) scheduler_activate(s, c->kick1);
     if (c->kick2 != NULL) scheduler_activate(s, c->kick2);
     if (c->timestep != NULL) scheduler_activate(s, c->timestep);
     if (c->end_force != NULL) scheduler_activate(s, c->end_force);
-    if (c->cooling != NULL) scheduler_activate(s, c->cooling);
+    if (c->hydro.cooling != NULL) scheduler_activate(s, c->hydro.cooling);
+    if (c->hydro.star_formation != NULL)
+      scheduler_activate(s, c->hydro.star_formation);
     if (c->sourceterms != NULL) scheduler_activate(s, c->sourceterms);
+    if (c->logger != NULL) scheduler_activate(s, c->logger);
   }
 
   return rebuild;
@@ -2384,7 +2786,7 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) {
   int rebuild = 0;
 
   /* Un-skip the gravity tasks involved with this cell. */
-  for (struct link *l = c->grav; l != NULL; l = l->next) {
+  for (struct link *l = c->grav.grav; l != NULL; l = l->next) {
     struct task *t = l->t;
     struct cell *ci = t->ci;
     struct cell *cj = t->cj;
@@ -2426,15 +2828,15 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) {
       if (ci_nodeID != nodeID) {
 
         /* If the local cell is active, receive data from the foreign cell. */
-        if (cj_active) scheduler_activate(s, ci->recv_grav);
+        if (cj_active) scheduler_activate(s, ci->mpi.grav.recv);
 
         /* If the foreign cell is active, we want its ti_end values. */
-        if (ci_active) scheduler_activate(s, ci->recv_ti);
+        if (ci_active) scheduler_activate(s, ci->mpi.recv_ti);
 
         /* Is the foreign cell active and will need stuff from us? */
         if (ci_active) {
 
-          scheduler_activate_send(s, cj->send_grav, ci_nodeID);
+          scheduler_activate_send(s, cj->mpi.grav.send, ci_nodeID);
 
           /* Drift the cell which will be sent at the level at which it is
              sent, i.e. drift the cell specified in the send task (l->t)
@@ -2443,20 +2845,20 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) {
         }
 
         /* If the local cell is active, send its ti_end values. */
-        if (cj_active) scheduler_activate_send(s, cj->send_ti, ci_nodeID);
+        if (cj_active) scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID);
 
       } else if (cj_nodeID != nodeID) {
 
         /* If the local cell is active, receive data from the foreign cell. */
-        if (ci_active) scheduler_activate(s, cj->recv_grav);
+        if (ci_active) scheduler_activate(s, cj->mpi.grav.recv);
 
         /* If the foreign cell is active, we want its ti_end values. */
-        if (cj_active) scheduler_activate(s, cj->recv_ti);
+        if (cj_active) scheduler_activate(s, cj->mpi.recv_ti);
 
         /* Is the foreign cell active and will need stuff from us? */
         if (cj_active) {
 
-          scheduler_activate_send(s, ci->send_grav, cj_nodeID);
+          scheduler_activate_send(s, ci->mpi.grav.send, cj_nodeID);
 
           /* Drift the cell which will be sent at the level at which it is
              sent, i.e. drift the cell specified in the send task (l->t)
@@ -2465,13 +2867,13 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) {
         }
 
         /* If the local cell is active, send its ti_end values. */
-        if (ci_active) scheduler_activate_send(s, ci->send_ti, cj_nodeID);
+        if (ci_active) scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID);
       }
 #endif
     }
   }
 
-  for (struct link *l = c->grav_mm; l != NULL; l = l->next) {
+  for (struct link *l = c->grav.mm; l != NULL; l = l->next) {
 
     struct task *t = l->t;
     struct cell *ci = t->ci;
@@ -2501,18 +2903,22 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) {
   /* Unskip all the other task types. */
   if (c->nodeID == nodeID && cell_is_active_gravity(c, e)) {
 
-    if (c->init_grav != NULL) scheduler_activate(s, c->init_grav);
-    if (c->init_grav_out != NULL) scheduler_activate(s, c->init_grav_out);
+    if (c->grav.init != NULL) scheduler_activate(s, c->grav.init);
+    if (c->grav.init_out != NULL) scheduler_activate(s, c->grav.init_out);
     if (c->kick1 != NULL) scheduler_activate(s, c->kick1);
     if (c->kick2 != NULL) scheduler_activate(s, c->kick2);
     if (c->timestep != NULL) scheduler_activate(s, c->timestep);
     if (c->end_force != NULL) scheduler_activate(s, c->end_force);
-    if ((e->policy & engine_policy_cooling) && c->cooling != NULL)
-      scheduler_activate(s, c->cooling);
-    if (c->grav_down != NULL) scheduler_activate(s, c->grav_down);
-    if (c->grav_down_in != NULL) scheduler_activate(s, c->grav_down_in);
-    if (c->grav_mesh != NULL) scheduler_activate(s, c->grav_mesh);
-    if (c->grav_long_range != NULL) scheduler_activate(s, c->grav_long_range);
+    if ((e->policy & engine_policy_cooling) && c->hydro.cooling != NULL)
+      scheduler_activate(s, c->hydro.cooling);
+    if ((e->policy & engine_policy_star_formation) &&
+        c->hydro.star_formation != NULL)
+      scheduler_activate(s, c->hydro.star_formation);
+    if (c->grav.down != NULL) scheduler_activate(s, c->grav.down);
+    if (c->grav.down_in != NULL) scheduler_activate(s, c->grav.down_in);
+    if (c->grav.mesh != NULL) scheduler_activate(s, c->grav.mesh);
+    if (c->grav.long_range != NULL) scheduler_activate(s, c->grav.long_range);
+    if (c->logger != NULL) scheduler_activate(s, c->logger);
   }
 
   return rebuild;
@@ -2533,7 +2939,7 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
   int rebuild = 0;
 
   /* Un-skip the density tasks involved with this cell. */
-  for (struct link *l = c->stars_density; l != NULL; l = l->next) {
+  for (struct link *l = c->stars.density; l != NULL; l = l->next) {
     struct task *t = l->t;
     struct cell *ci = t->ci;
     struct cell *cj = t->cj;
@@ -2554,10 +2960,10 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
       /* Set the correct sorting flags and activate hydro drifts */
       else if (t->type == task_type_pair) {
         /* Store some values. */
-        atomic_or(&ci->requires_sorts, 1 << t->flags);
-        atomic_or(&cj->requires_sorts, 1 << t->flags);
-        ci->dx_max_sort_old = ci->dx_max_sort;
-        cj->dx_max_sort_old = cj->dx_max_sort;
+        atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+        atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+        ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+        cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
 
         /* Activate the drift tasks. */
         if (ci->nodeID == nodeID) cell_activate_drift_part(ci, s);
@@ -2588,20 +2994,20 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
       /*   /\* If the local cell is active, receive data from the foreign cell.
        * *\/ */
       /*   if (cj_active) { */
-      /*     scheduler_activate(s, ci->recv_xv); */
+      /*     scheduler_activate(s, ci->hydro.recv_xv); */
       /*     if (ci_active) { */
-      /*       scheduler_activate(s, ci->recv_rho); */
+      /*       scheduler_activate(s, ci->hydro.recv_rho); */
 
       /*     } */
       /*   } */
 
       /*   /\* If the foreign cell is active, we want its ti_end values. *\/ */
-      /*   if (ci_active) scheduler_activate(s, ci->recv_ti); */
+      /*   if (ci_active) scheduler_activate(s, ci->mpi.recv_ti); */
 
       /*   /\* Is the foreign cell active and will need stuff from us? *\/ */
       /*   if (ci_active) { */
 
-      /*     scheduler_activate_send(s, cj->send_xv, ci->nodeID); */
+      /*     scheduler_activate_send(s, cj->hydro.send_xv, ci->nodeID); */
 
       /*     /\* Drift the cell which will be sent; note that not all sent */
       /*        particles will be drifted, only those that are needed. *\/ */
@@ -2610,13 +3016,14 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
       /*     /\* If the local cell is also active, more stuff will be needed.
        * *\/ */
       /*     if (cj_active) { */
-      /*       scheduler_activate_send(s, cj->send_rho, ci->nodeID); */
+      /*       scheduler_activate_send(s, cj->hydro.send_rho, ci->nodeID); */
 
       /*     } */
       /*   } */
 
       /*   /\* If the local cell is active, send its ti_end values. *\/ */
-      /*   if (cj_active) scheduler_activate_send(s, cj->send_ti, ci->nodeID);
+      /*   if (cj_active) scheduler_activate_send(s, cj->mpi.send_ti,
+       * ci->nodeID);
        */
 
       /* } else if (cj->nodeID != nodeID) { */
@@ -2624,20 +3031,20 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
       /*   /\* If the local cell is active, receive data from the foreign cell.
        * *\/ */
       /*   if (ci_active) { */
-      /*     scheduler_activate(s, cj->recv_xv); */
+      /*     scheduler_activate(s, cj->hydro.recv_xv); */
       /*     if (cj_active) { */
-      /*       scheduler_activate(s, cj->recv_rho); */
+      /*       scheduler_activate(s, cj->hydro.recv_rho); */
 
       /*     } */
       /*   } */
 
       /*   /\* If the foreign cell is active, we want its ti_end values. *\/ */
-      /*   if (cj_active) scheduler_activate(s, cj->recv_ti); */
+      /*   if (cj_active) scheduler_activate(s, cj->mpi.recv_ti); */
 
       /*   /\* Is the foreign cell active and will need stuff from us? *\/ */
       /*   if (cj_active) { */
 
-      /*     scheduler_activate_send(s, ci->send_xv, cj->nodeID); */
+      /*     scheduler_activate_send(s, ci->hydro.send_xv, cj->nodeID); */
 
       /*     /\* Drift the cell which will be sent; note that not all sent */
       /*        particles will be drifted, only those that are needed. *\/ */
@@ -2647,13 +3054,14 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
        * *\/ */
       /*     if (ci_active) { */
 
-      /*       scheduler_activate_send(s, ci->send_rho, cj->nodeID); */
+      /*       scheduler_activate_send(s, ci->hydro.send_rho, cj->nodeID); */
 
       /*     } */
       /*   } */
 
       /*   /\* If the local cell is active, send its ti_end values. *\/ */
-      /*   if (ci_active) scheduler_activate_send(s, ci->send_ti, cj->nodeID);
+      /*   if (ci_active) scheduler_activate_send(s, ci->mpi.send_ti,
+       * cj->nodeID);
        */
       /* } */
 #endif
@@ -2663,9 +3071,10 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
   /* Unskip all the other task types. */
   if (c->nodeID == nodeID && cell_is_active_stars(c, e)) {
 
-    if (c->stars_ghost_in != NULL) scheduler_activate(s, c->stars_ghost_in);
-    if (c->stars_ghost_out != NULL) scheduler_activate(s, c->stars_ghost_out);
-    if (c->stars_ghost != NULL) scheduler_activate(s, c->stars_ghost);
+    if (c->stars.ghost_in != NULL) scheduler_activate(s, c->stars.ghost_in);
+    if (c->stars.ghost_out != NULL) scheduler_activate(s, c->stars.ghost_out);
+    if (c->stars.ghost != NULL) scheduler_activate(s, c->stars.ghost);
+    if (c->logger != NULL) scheduler_activate(s, c->logger);
   }
 
   return rebuild;
@@ -2680,7 +3089,7 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
 void cell_set_super(struct cell *c, struct cell *super) {
 
   /* Are we in a cell with some kind of self/pair task ? */
-  if (super == NULL && (c->nr_tasks > 0 || c->nr_mm_tasks > 0)) super = c;
+  if (super == NULL && (c->nr_tasks > 0 || c->grav.nr_mm_tasks > 0)) super = c;
 
   /* Set the super-cell */
   c->super = super;
@@ -2701,10 +3110,10 @@ void cell_set_super(struct cell *c, struct cell *super) {
 void cell_set_super_hydro(struct cell *c, struct cell *super_hydro) {
 
   /* Are we in a cell with some kind of self/pair task ? */
-  if (super_hydro == NULL && c->density != NULL) super_hydro = c;
+  if (super_hydro == NULL && c->hydro.density != NULL) super_hydro = c;
 
   /* Set the super-cell */
-  c->super_hydro = super_hydro;
+  c->hydro.super = super_hydro;
 
   /* Recurse */
   if (c->split)
@@ -2723,11 +3132,11 @@ void cell_set_super_hydro(struct cell *c, struct cell *super_hydro) {
 void cell_set_super_gravity(struct cell *c, struct cell *super_gravity) {
 
   /* Are we in a cell with some kind of self/pair task ? */
-  if (super_gravity == NULL && (c->grav != NULL || c->grav_mm != NULL))
+  if (super_gravity == NULL && (c->grav.grav != NULL || c->grav.mm != NULL))
     super_gravity = c;
 
   /* Set the super-cell */
-  c->super_gravity = super_gravity;
+  c->grav.super = super_gravity;
 
   /* Recurse */
   if (c->split)
@@ -2752,7 +3161,7 @@ void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data) {
 
     /* All top-level cells get an MPI tag. */
 #ifdef WITH_MPI
-    if (c->tag < 0 && c->sendto) cell_tag(c);
+    if (c->mpi.tag < 0 && c->mpi.sendto) cell_tag(c);
 #endif
 
     /* Super-pointer for hydro */
@@ -2779,7 +3188,7 @@ void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data) {
 int cell_has_tasks(struct cell *c) {
 
 #ifdef WITH_MPI
-  if (c->timestep != NULL || c->recv_ti != NULL) return 1;
+  if (c->timestep != NULL || c->mpi.recv_ti != NULL) return 1;
 #else
   if (c->timestep != NULL) return 1;
 #endif
@@ -2804,17 +3213,17 @@ int cell_has_tasks(struct cell *c) {
 void cell_drift_part(struct cell *c, const struct engine *e, int force) {
 
   const float hydro_h_max = e->hydro_properties->h_max;
-  const integertime_t ti_old_part = c->ti_old_part;
+  const integertime_t ti_old_part = c->hydro.ti_old_part;
   const integertime_t ti_current = e->ti_current;
-  struct part *const parts = c->parts;
-  struct xpart *const xparts = c->xparts;
+  struct part *const parts = c->hydro.parts;
+  struct xpart *const xparts = c->hydro.xparts;
 
   float dx_max = 0.f, dx2_max = 0.f;
   float dx_max_sort = 0.0f, dx2_max_sort = 0.f;
   float cell_h_max = 0.f;
 
   /* Drift irrespective of cell flags? */
-  force |= c->do_drift;
+  force |= c->hydro.do_drift;
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that we only drift local cells. */
@@ -2825,7 +3234,7 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) {
 #endif
 
   /* Are we not in a leaf ? */
-  if (c->split && (force || c->do_sub_drift)) {
+  if (c->split && (force || c->hydro.do_sub_drift)) {
 
     /* Loop over the progeny and collect their data. */
     for (int k = 0; k < 8; k++) {
@@ -2836,19 +3245,19 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) {
         cell_drift_part(cp, e, force);
 
         /* Update */
-        dx_max = max(dx_max, cp->dx_max_part);
-        dx_max_sort = max(dx_max_sort, cp->dx_max_sort);
-        cell_h_max = max(cell_h_max, cp->h_max);
+        dx_max = max(dx_max, cp->hydro.dx_max_part);
+        dx_max_sort = max(dx_max_sort, cp->hydro.dx_max_sort);
+        cell_h_max = max(cell_h_max, cp->hydro.h_max);
       }
     }
 
     /* Store the values */
-    c->h_max = cell_h_max;
-    c->dx_max_part = dx_max;
-    c->dx_max_sort = dx_max_sort;
+    c->hydro.h_max = cell_h_max;
+    c->hydro.dx_max_part = dx_max;
+    c->hydro.dx_max_sort = dx_max_sort;
 
     /* Update the time of the last drift */
-    c->ti_old_part = ti_current;
+    c->hydro.ti_old_part = ti_current;
 
   } else if (!c->split && force && ti_current > ti_old_part) {
 
@@ -2871,13 +3280,16 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) {
     }
 
     /* Loop over all the gas particles in the cell */
-    const size_t nr_parts = c->count;
+    const size_t nr_parts = c->hydro.count;
     for (size_t k = 0; k < nr_parts; k++) {
 
       /* Get a handle on the part. */
       struct part *const p = &parts[k];
       struct xpart *const xp = &xparts[k];
 
+      /* Ignore inhibited particles */
+      if (part_is_inhibited(p, e)) continue;
+
       /* Drift... */
       drift_part(p, xp, dt_drift, dt_kick_hydro, dt_kick_grav, dt_therm,
                  ti_old_part, ti_current);
@@ -2949,17 +3361,17 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) {
     dx_max_sort = sqrtf(dx2_max_sort);
 
     /* Store the values */
-    c->h_max = cell_h_max;
-    c->dx_max_part = dx_max;
-    c->dx_max_sort = dx_max_sort;
+    c->hydro.h_max = cell_h_max;
+    c->hydro.dx_max_part = dx_max;
+    c->hydro.dx_max_sort = dx_max_sort;
 
     /* Update the time of the last drift */
-    c->ti_old_part = ti_current;
+    c->hydro.ti_old_part = ti_current;
   }
 
   /* Clear the drift flags. */
-  c->do_drift = 0;
-  c->do_sub_drift = 0;
+  c->hydro.do_drift = 0;
+  c->hydro.do_sub_drift = 0;
 }
 
 /**
@@ -2971,13 +3383,17 @@ void cell_drift_part(struct cell *c, const struct engine *e, int force) {
  */
 void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
 
-  const integertime_t ti_old_gpart = c->ti_old_gpart;
+  const float stars_h_max = e->stars_properties->h_max;
+  const integertime_t ti_old_gpart = c->grav.ti_old_part;
   const integertime_t ti_current = e->ti_current;
-  struct gpart *const gparts = c->gparts;
-  struct spart *const sparts = c->sparts;
+  struct gpart *const gparts = c->grav.parts;
+  struct spart *const sparts = c->stars.parts;
+
+  float dx_max = 0.f, dx2_max = 0.f;
+  float cell_h_max = 0.f;
 
   /* Drift irrespective of cell flags? */
-  force |= c->do_grav_drift;
+  force |= c->grav.do_drift;
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that we only drift local cells. */
@@ -2988,7 +3404,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
 #endif
 
   /* Are we not in a leaf ? */
-  if (c->split && (force || c->do_grav_sub_drift)) {
+  if (c->split && (force || c->grav.do_sub_drift)) {
 
     /* Loop over the progeny and collect their data. */
     for (int k = 0; k < 8; k++) {
@@ -2997,11 +3413,19 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
 
         /* Recurse */
         cell_drift_gpart(cp, e, force);
+
+        /* Update */
+        dx_max = max(dx_max, cp->stars.dx_max_part);
+        cell_h_max = max(cell_h_max, cp->stars.h_max);
       }
     }
 
+    /* Store the values */
+    c->stars.h_max = cell_h_max;
+    c->stars.dx_max_part = dx_max;
+
     /* Update the time of the last drift */
-    c->ti_old_gpart = ti_current;
+    c->grav.ti_old_part = ti_current;
 
   } else if (!c->split && force && ti_current > ti_old_gpart) {
 
@@ -3014,12 +3438,15 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
       dt_drift = (ti_current - ti_old_gpart) * e->time_base;
 
     /* Loop over all the g-particles in the cell */
-    const size_t nr_gparts = c->gcount;
+    const size_t nr_gparts = c->grav.count;
     for (size_t k = 0; k < nr_gparts; k++) {
 
       /* Get a handle on the gpart. */
       struct gpart *const gp = &gparts[k];
 
+      /* Ignore inhibited particles */
+      if (gpart_is_inhibited(gp, e)) continue;
+
       /* Drift... */
       drift_gpart(gp, dt_drift, ti_old_gpart, ti_current);
 
@@ -3059,12 +3486,15 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
     }
 
     /* Loop over all the star particles in the cell */
-    const size_t nr_sparts = c->scount;
+    const size_t nr_sparts = c->stars.count;
     for (size_t k = 0; k < nr_sparts; k++) {
 
       /* Get a handle on the spart. */
       struct spart *const sp = &sparts[k];
 
+      /* Ignore inhibited particles */
+      if (spart_is_inhibited(sp, e)) continue;
+
       /* Drift... */
       drift_spart(sp, dt_drift, ti_old_gpart, ti_current);
 
@@ -3077,16 +3507,34 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
       }
 #endif
 
-      /* Note: no need to compute dx_max as all spart have a gpart */
-    }
+      /* Limit h to within the allowed range */
+      sp->h = min(sp->h, stars_h_max);
+
+      /* Compute (square of) motion since last cell construction */
+      const float dx2 = sp->x_diff[0] * sp->x_diff[0] +
+                        sp->x_diff[1] * sp->x_diff[1] +
+                        sp->x_diff[2] * sp->x_diff[2];
+      dx2_max = max(dx2_max, dx2);
+
+      /* Maximal smoothing length */
+      cell_h_max = max(cell_h_max, sp->h);
+
+    } /* Note: no need to compute dx_max as all spart have a gpart */
+
+    /* Now, get the maximal particle motion from its square */
+    dx_max = sqrtf(dx2_max);
+
+    /* Store the values */
+    c->stars.h_max = cell_h_max;
+    c->stars.dx_max_part = dx_max;
 
     /* Update the time of the last drift */
-    c->ti_old_gpart = ti_current;
+    c->grav.ti_old_part = ti_current;
   }
 
   /* Clear the drift flags. */
-  c->do_grav_drift = 0;
-  c->do_grav_sub_drift = 0;
+  c->grav.do_drift = 0;
+  c->grav.do_sub_drift = 0;
 }
 
 /**
@@ -3097,7 +3545,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
  */
 void cell_drift_all_multipoles(struct cell *c, const struct engine *e) {
 
-  const integertime_t ti_old_multipole = c->ti_old_multipole;
+  const integertime_t ti_old_multipole = c->grav.ti_old_multipole;
   const integertime_t ti_current = e->ti_current;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -3114,7 +3562,7 @@ void cell_drift_all_multipoles(struct cell *c, const struct engine *e) {
     dt_drift = (ti_current - ti_old_multipole) * e->time_base;
 
   /* Drift the multipole */
-  if (ti_current > ti_old_multipole) gravity_drift(c->multipole, dt_drift);
+  if (ti_current > ti_old_multipole) gravity_drift(c->grav.multipole, dt_drift);
 
   /* Are we not in a leaf ? */
   if (c->split) {
@@ -3125,7 +3573,7 @@ void cell_drift_all_multipoles(struct cell *c, const struct engine *e) {
   }
 
   /* Update the time of the last drift */
-  c->ti_old_multipole = ti_current;
+  c->grav.ti_old_multipole = ti_current;
 }
 
 /**
@@ -3139,7 +3587,7 @@ void cell_drift_all_multipoles(struct cell *c, const struct engine *e) {
  */
 void cell_drift_multipole(struct cell *c, const struct engine *e) {
 
-  const integertime_t ti_old_multipole = c->ti_old_multipole;
+  const integertime_t ti_old_multipole = c->grav.ti_old_multipole;
   const integertime_t ti_current = e->ti_current;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -3155,10 +3603,10 @@ void cell_drift_multipole(struct cell *c, const struct engine *e) {
   else
     dt_drift = (ti_current - ti_old_multipole) * e->time_base;
 
-  if (ti_current > ti_old_multipole) gravity_drift(c->multipole, dt_drift);
+  if (ti_current > ti_old_multipole) gravity_drift(c->grav.multipole, dt_drift);
 
   /* Update the time of the last drift */
-  c->ti_old_multipole = ti_current;
+  c->grav.ti_old_multipole = ti_current;
 }
 
 /**
@@ -3167,7 +3615,7 @@ void cell_drift_multipole(struct cell *c, const struct engine *e) {
 void cell_check_timesteps(struct cell *c) {
 #ifdef SWIFT_DEBUG_CHECKS
 
-  if (c->ti_hydro_end_min == 0 && c->ti_gravity_end_min == 0 && c->nr_tasks > 0)
+  if (c->hydro.ti_end_min == 0 && c->grav.ti_end_min == 0 && c->nr_tasks > 0)
     error("Cell without assigned time-step");
 
   if (c->split) {
@@ -3176,8 +3624,8 @@ void cell_check_timesteps(struct cell *c) {
   } else {
 
     if (c->nodeID == engine_rank)
-      for (int i = 0; i < c->count; ++i)
-        if (c->parts[i].time_bin == 0)
+      for (int i = 0; i < c->hydro.count; ++i)
+        if (c->hydro.parts[i].time_bin == 0)
           error("Particle without assigned time-bin");
   }
 #else
@@ -3185,6 +3633,165 @@ void cell_check_timesteps(struct cell *c) {
 #endif
 }
 
+/**
+ * @brief "Remove" a gas particle from the calculation.
+ *
+ * The particle is inhibited and will officially be removed at the next rebuild.
+ *
+ * @param e The #engine running on this node.
+ * @param c The #cell from which to remove the particle.
+ * @param p The #part to remove.
+ * @param xp The extended data of the particle to remove.
+ */
+void cell_remove_part(const struct engine *e, struct cell *c, struct part *p,
+                      struct xpart *xp) {
+
+  /* Quick cross-check */
+  if (c->nodeID != e->nodeID)
+    error("Can't remove a particle in a foreign cell.");
+
+  /* Mark the particle as inhibited */
+  p->time_bin = time_bin_inhibited;
+
+  /* Mark the gpart as inhibited and stand-alone */
+  if (p->gpart) {
+    p->gpart->time_bin = time_bin_inhibited;
+    p->gpart->id_or_neg_offset = p->id;
+    p->gpart->type = swift_type_dark_matter;
+  }
+
+  /* Un-link the part */
+  p->gpart = NULL;
+}
+
+/**
+ * @brief "Remove" a gravity particle from the calculation.
+ *
+ * The particle is inhibited and will officially be removed at the next rebuild.
+ *
+ * @param e The #engine running on this node.
+ * @param c The #cell from which to remove the particle.
+ * @param gp The #gpart to remove.
+ */
+void cell_remove_gpart(const struct engine *e, struct cell *c,
+                       struct gpart *gp) {
+
+  /* Quick cross-check */
+  if (c->nodeID != e->nodeID)
+    error("Can't remove a particle in a foreign cell.");
+
+  if (gp->type != swift_type_dark_matter)
+    error("Trying to remove a non-dark matter gpart.");
+
+  /* Mark the particle as inhibited */
+  gp->time_bin = time_bin_inhibited;
+}
+
+/**
+ * @brief "Remove" a star particle from the calculation.
+ *
+ * The particle is inhibited and will officially be removed at the next rebuild.
+ *
+ * @param e The #engine running on this node.
+ * @param c The #cell from which to remove the particle.
+ * @param sp The #spart to remove.
+ */
+void cell_remove_spart(const struct engine *e, struct cell *c,
+                       struct spart *sp) {
+
+  /* Quick cross-check */
+  if (c->nodeID != e->nodeID)
+    error("Can't remove a particle in a foreign cell.");
+
+  /* Mark the particle as inhibited and stand-alone */
+  sp->time_bin = time_bin_inhibited;
+  if (sp->gpart) {
+    sp->gpart->time_bin = time_bin_inhibited;
+    sp->gpart->id_or_neg_offset = sp->id;
+    sp->gpart->type = swift_type_dark_matter;
+  }
+
+  /* Un-link the spart */
+  sp->gpart = NULL;
+}
+
+/**
+ * @brief "Remove" a gas particle from the calculation and convert its gpart
+ * friend to a dark matter particle.
+ *
+ * The particle is inhibited and will officially be removed at the next rebuild.
+ *
+ * @param e The #engine running on this node.
+ * @param c The #cell from which to remove the particle.
+ * @param p The #part to remove.
+ * @param xp The extended data of the particle to remove.
+ */
+void cell_convert_part_to_gpart(const struct engine *e, struct cell *c,
+                                struct part *p, struct xpart *xp) {
+
+  /* Quick cross-checks */
+  if (c->nodeID != e->nodeID)
+    error("Can't remove a particle in a foreign cell.");
+
+  if (p->gpart == NULL)
+    error("Trying to convert part without gpart friend to dark matter!");
+
+  /* Get a handle */
+  struct gpart *gp = p->gpart;
+
+  /* Mark the particle as inhibited */
+  p->time_bin = time_bin_inhibited;
+
+  /* Un-link the part */
+  p->gpart = NULL;
+
+  /* Mark the gpart as dark matter */
+  gp->type = swift_type_dark_matter;
+  gp->id_or_neg_offset = p->id;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  gp->ti_kick = p->ti_kick;
+#endif
+}
+
+/**
+ * @brief "Remove" a spart particle from the calculation and convert its gpart
+ * friend to a dark matter particle.
+ *
+ * The particle is inhibited and will officially be removed at the next rebuild.
+ *
+ * @param e The #engine running on this node.
+ * @param c The #cell from which to remove the particle.
+ * @param sp The #spart to remove.
+ */
+void cell_convert_spart_to_gpart(const struct engine *e, struct cell *c,
+                                 struct spart *sp) {
+
+  /* Quick cross-check */
+  if (c->nodeID != e->nodeID)
+    error("Can't remove a particle in a foreign cell.");
+
+  if (sp->gpart == NULL)
+    error("Trying to convert spart without gpart friend to dark matter!");
+
+  /* Get a handle */
+  struct gpart *gp = sp->gpart;
+
+  /* Mark the particle as inhibited */
+  sp->time_bin = time_bin_inhibited;
+
+  /* Un-link the spart */
+  sp->gpart = NULL;
+
+  /* Mark the gpart as dark matter */
+  gp->type = swift_type_dark_matter;
+  gp->id_or_neg_offset = sp->id;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  gp->ti_kick = sp->ti_kick;
+#endif
+}
+
 /**
  * @brief Can we use the MM interactions fo a given pair of cells?
  *
@@ -3201,8 +3808,8 @@ int cell_can_use_pair_mm(const struct cell *ci, const struct cell *cj,
   const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
 
   /* Recover the multipole information */
-  const struct gravity_tensors *const multi_i = ci->multipole;
-  const struct gravity_tensors *const multi_j = cj->multipole;
+  const struct gravity_tensors *const multi_i = ci->grav.multipole;
+  const struct gravity_tensors *const multi_j = cj->grav.multipole;
 
   /* Get the distance between the CoMs */
   double dx = multi_i->CoM[0] - multi_j->CoM[0];
@@ -3240,8 +3847,8 @@ int cell_can_use_pair_mm_rebuild(const struct cell *ci, const struct cell *cj,
   const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
 
   /* Recover the multipole information */
-  const struct gravity_tensors *const multi_i = ci->multipole;
-  const struct gravity_tensors *const multi_j = cj->multipole;
+  const struct gravity_tensors *const multi_i = ci->grav.multipole;
+  const struct gravity_tensors *const multi_j = cj->grav.multipole;
 
 #ifdef SWIFT_DEBUG_CHECKS
 
diff --git a/src/cell.h b/src/cell.h
index 373a10b04a4a0e28a8735ea8089e86e022e725d2..89f7c954c29f23845cb9c876b93c6bb3d469fbe5 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -77,62 +77,80 @@ struct link {
  */
 struct pcell {
 
-  /*! This cell's gravity-related tensors */
-  struct multipole m_pole;
+  /*! Hydro variables */
+  struct {
 
-  /*! Centre of mass. */
-  double CoM[3];
+    /*! Maximal smoothing length. */
+    double h_max;
 
-  /*! Centre of mass at rebuild time. */
-  double CoM_rebuild[3];
+    /*! Minimal integer end-of-timestep in this cell for hydro tasks */
+    integertime_t ti_end_min;
 
-  /*! Upper limit of the CoM<->gpart distance. */
-  double r_max;
+    /*! Maximal integer end-of-timestep in this cell for hydro tasks */
+    integertime_t ti_end_max;
 
-  /*! Upper limit of the CoM<->gpart distance at last rebuild. */
-  double r_max_rebuild;
+    /*! Maximal integer beginning-of-timestep in this cell for hydro tasks */
+    integertime_t ti_beg_max;
 
-  /*! Relative indices of the cell's progeny. */
-  int progeny[8];
+    /*! Integer time of the last drift of the #part in this cell */
+    integertime_t ti_old_part;
+
+    /*! Number of #part in this cell. */
+    int count;
+
+  } hydro;
+
+  /*! Gravity variables */
+  struct {
+
+    /*! This cell's gravity-related tensors */
+    struct multipole m_pole;
+
+    /*! Centre of mass. */
+    double CoM[3];
+
+    /*! Centre of mass at rebuild time. */
+    double CoM_rebuild[3];
 
-  /*! Maximal smoothing length. */
-  double h_max;
+    /*! Upper limit of the CoM<->gpart distance. */
+    double r_max;
 
-  /*! Minimal integer end-of-timestep in this cell for hydro tasks */
-  integertime_t ti_hydro_end_min;
+    /*! Upper limit of the CoM<->gpart distance at last rebuild. */
+    double r_max_rebuild;
 
-  /*! Maximal integer end-of-timestep in this cell for hydro tasks */
-  integertime_t ti_hydro_end_max;
+    /*! Minimal integer end-of-timestep in this cell for gravity tasks */
+    integertime_t ti_end_min;
 
-  /*! Maximal integer beginning-of-timestep in this cell for hydro tasks */
-  integertime_t ti_hydro_beg_max;
+    /*! Maximal integer end-of-timestep in this cell for gravity tasks */
+    integertime_t ti_end_max;
 
-  /*! Minimal integer end-of-timestep in this cell for gravity tasks */
-  integertime_t ti_gravity_end_min;
+    /*! Maximal integer beginning-of-timestep in this cell for gravity tasks */
+    integertime_t ti_beg_max;
 
-  /*! Maximal integer end-of-timestep in this cell for gravity tasks */
-  integertime_t ti_gravity_end_max;
+    /*! Integer time of the last drift of the #gpart in this cell */
+    integertime_t ti_old_part;
 
-  /*! Maximal integer beginning-of-timestep in this cell for gravity tasks */
-  integertime_t ti_gravity_beg_max;
+    /*! Integer time of the last drift of the #multipole in this cell */
+    integertime_t ti_old_multipole;
 
-  /*! Integer time of the last drift of the #part in this cell */
-  integertime_t ti_old_part;
+    /*! Number of #gpart in this cell. */
+    int count;
 
-  /*! Integer time of the last drift of the #gpart in this cell */
-  integertime_t ti_old_gpart;
+  } grav;
 
-  /*! Integer time of the last drift of the #multipole in this cell */
-  integertime_t ti_old_multipole;
+  /*! Stars variables */
+  struct {
 
-  /*! Number of #part in this cell. */
-  int count;
+    /*! Number of #spart in this cell. */
+    int count;
 
-  /*! Number of #gpart in this cell. */
-  int gcount;
+    /*! Maximal smoothing length. */
+    double h_max;
 
-  /*! Number of #spart in this cell. */
-  int scount;
+  } stars;
+
+  /*! Relative indices of the cell's progeny. */
+  int progeny[8];
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Cell ID (for debugging) */
@@ -146,20 +164,38 @@ struct pcell {
  */
 struct pcell_step {
 
-  /*! Minimal integer end-of-timestep in this cell (hydro) */
-  integertime_t ti_hydro_end_min;
+  /*! Hydro variables */
+  struct {
+
+    /*! Minimal integer end-of-timestep in this cell (hydro) */
+    integertime_t ti_end_min;
+
+    /*! Minimal integer end-of-timestep in this cell (hydro) */
+    integertime_t ti_end_max;
+
+    /*! Maximal distance any #part has travelled since last rebuild */
+    float dx_max_part;
+
+  } hydro;
+
+  /*! Grav variables */
+  struct {
 
-  /*! Minimal integer end-of-timestep in this cell (hydro) */
-  integertime_t ti_hydro_end_max;
+    /*! Minimal integer end-of-timestep in this cell (gravity) */
+    integertime_t ti_end_min;
 
-  /*! Minimal integer end-of-timestep in this cell (gravity) */
-  integertime_t ti_gravity_end_min;
+    /*! Minimal integer end-of-timestep in this cell (gravity) */
+    integertime_t ti_end_max;
 
-  /*! Minimal integer end-of-timestep in this cell (gravity) */
-  integertime_t ti_gravity_end_max;
+  } grav;
 
-  /*! Maximal distance any #part has travelled since last rebuild */
-  float dx_max_part;
+  /*! Stars variables */
+  struct {
+
+    /*! Maximal distance any #part has travelled since last rebuild */
+    float dx_max_part;
+
+  } stars;
 };
 
 /**
@@ -175,30 +211,9 @@ struct cell {
   /*! The cell dimensions. */
   double width[3];
 
-  /*! Max smoothing length in this cell. */
-  double h_max;
-
-  /*! This cell's multipole. */
-  struct gravity_tensors *multipole;
-
   /*! Linking pointer for "memory management". */
   struct cell *next;
 
-  /*! Pointer to the #part data. */
-  struct part *parts;
-
-  /*! Pointer to the #xpart data. */
-  struct xpart *xparts;
-
-  /*! Pointer to the #gpart data. */
-  struct gpart *gparts;
-
-  /*! Pointer to the #spart data. */
-  struct spart *sparts;
-
-  /*! Pointer for the sorted indices. */
-  struct entry *sort[13];
-
   /*! Pointers to the next level of cells. */
   struct cell *progeny[8];
 
@@ -208,253 +223,348 @@ struct cell {
   /*! Super cell, i.e. the highest-level parent cell with *any* task */
   struct cell *super;
 
-  /*! Super cell, i.e. the highest-level parent cell that has a hydro pair/self
-   * tasks */
-  struct cell *super_hydro;
+  /*! Hydro variables */
+  struct {
 
-  /*! Super cell, i.e. the highest-level parent cell that has a grav pair/self
-   * tasks */
-  struct cell *super_gravity;
+    /*! Pointer to the #part data. */
+    struct part *parts;
 
-  /*! Linked list of the tasks computing this cell's hydro density. */
-  struct link *density;
+    /*! Pointer to the #xpart data. */
+    struct xpart *xparts;
 
-  /* Linked list of the tasks computing this cell's hydro gradients. */
-  struct link *gradient;
+    /*! Pointer for the sorted indices. */
+    struct entry *sort[13];
 
-  /*! Linked list of the tasks computing this cell's hydro forces. */
-  struct link *force;
+    /*! Super cell, i.e. the highest-level parent cell that has a hydro
+     * pair/self tasks */
+    struct cell *super;
 
-  /*! Linked list of the tasks computing this cell's gravity forces. */
-  struct link *grav;
+    /*! Last (integer) time the cell's part were drifted forward in time. */
+    integertime_t ti_old_part;
 
-  /*! Linked list of the tasks computing this cell's gravity M-M forces. */
-  struct link *grav_mm;
+    /*! Maximum part movement in this cell since last construction. */
+    float dx_max_part;
 
-  /*! The task computing this cell's sorts. */
-  struct task *sorts;
+    /*! Maximum particle movement in this cell since the last sort. */
+    float dx_max_sort;
 
-  /*! The multipole initialistation task */
-  struct task *init_grav;
+    /*! Max smoothing length in this cell. */
+    double h_max;
 
-  /*! Implicit task for the gravity initialisation */
-  struct task *init_grav_out;
+    /*! Minimum end of (integer) time step in this cell for hydro tasks. */
+    integertime_t ti_end_min;
 
-  /*! Dependency implicit task for the ghost  (in->ghost->out)*/
-  struct task *ghost_in;
+    /*! Maximum end of (integer) time step in this cell for hydro tasks. */
+    integertime_t ti_end_max;
 
-  /*! Dependency implicit task for the ghost  (in->ghost->out)*/
-  struct task *ghost_out;
+    /*! Maximum beginning of (integer) time step in this cell for hydro tasks.
+     */
+    integertime_t ti_beg_max;
 
-  /*! The ghost task itself */
-  struct task *ghost;
+    /*! Nr of #part in this cell. */
+    int count;
 
-  /*! The extra ghost task for complex hydro schemes */
-  struct task *extra_ghost;
+    /*! Spin lock for various uses (#part case). */
+    swift_lock_type lock;
 
-  /*! The drift task for parts */
-  struct task *drift_part;
+    /*! Number of #part updated in this cell. */
+    int updated;
 
-  /*! The drift task for gparts */
-  struct task *drift_gpart;
+    /*! Number of #part inhibited in this cell. */
+    int inhibited;
 
-  /*! The first kick task */
-  struct task *kick1;
+    /*! Is the #part data of this cell being used in a sub-cell? */
+    int hold;
 
-  /*! The second kick task */
-  struct task *kick2;
+    /*! Values of h_max before the drifts, used for sub-cell tasks. */
+    float h_max_old;
 
-  /*! The task to end the force calculation */
-  struct task *end_force;
+    /*! Values of dx_max before the drifts, used for sub-cell tasks. */
+    float dx_max_part_old;
 
-  /*! The task to compute time-steps */
-  struct task *timestep;
+    /*! Values of dx_max_sort before the drifts, used for sub-cell tasks. */
+    float dx_max_sort_old;
 
-  /*! Task computing long range non-periodic gravity interactions */
-  struct task *grav_long_range;
+    /*! Bit mask of sort directions that will be needed in the next timestep. */
+    unsigned int requires_sorts;
 
-  /*! Implicit task for the down propagation */
-  struct task *grav_down_in;
+    /*! Bit mask of sorts that need to be computed for this cell. */
+    unsigned int do_sort;
 
-  /*! Task propagating the mesh forces to the particles */
-  struct task *grav_mesh;
+    /*! Does this cell need to be drifted (hydro)? */
+    char do_drift;
 
-  /*! Task propagating the multipole to the particles */
-  struct task *grav_down;
+    /*! Do any of this cell's sub-cells need to be drifted (hydro)? */
+    char do_sub_drift;
 
-  /*! Dependency implicit task for the star ghost  (in->ghost->out)*/
-  struct task *stars_ghost_in;
+    /*! Do any of this cell's sub-cells need to be sorted? */
+    char do_sub_sort;
 
-  /*! Dependency implicit task for the star ghost  (in->ghost->out)*/
-  struct task *stars_ghost_out;
+    /*! Bit-mask indicating the sorted directions */
+    unsigned int sorted;
 
-  /*! The star ghost task itself */
-  struct task *stars_ghost;
+    /*! The task computing this cell's sorts. */
+    struct task *sorts;
 
-  /*! Linked list of the tasks computing this cell's star density. */
-  struct link *stars_density;
+    /*! The drift task for parts */
+    struct task *drift;
 
-  /*! Task for cooling */
-  struct task *cooling;
+    /*! Linked list of the tasks computing this cell's hydro density. */
+    struct link *density;
 
-  /*! Task for source terms */
-  struct task *sourceterms;
+    /* Linked list of the tasks computing this cell's hydro gradients. */
+    struct link *gradient;
 
-#ifdef WITH_MPI
+    /*! Linked list of the tasks computing this cell's hydro forces. */
+    struct link *force;
 
-  /* Task receiving hydro data (positions). */
-  struct task *recv_xv;
+    /*! Dependency implicit task for the ghost  (in->ghost->out)*/
+    struct task *ghost_in;
 
-  /* Task receiving hydro data (density). */
-  struct task *recv_rho;
+    /*! Dependency implicit task for the ghost  (in->ghost->out)*/
+    struct task *ghost_out;
 
-  /* Task receiving hydro data (gradient). */
-  struct task *recv_gradient;
+    /*! The ghost task itself */
+    struct task *ghost;
 
-  /* Task receiving gpart data. */
-  struct task *recv_grav;
+    /*! The extra ghost task for complex hydro schemes */
+    struct task *extra_ghost;
 
-  /* Task receiving data (time-step). */
-  struct task *recv_ti;
+    /*! Task for cooling */
+    struct task *cooling;
 
-  /* Linked list for sending hydro data (positions). */
-  struct link *send_xv;
+    /*! Task for star formation */
+    struct task *star_formation;
 
-  /* Linked list for sending hydro data (density). */
-  struct link *send_rho;
+#ifdef SWIFT_DEBUG_CHECKS
 
-  /* Linked list for sending hydro data (gradient). */
-  struct link *send_gradient;
+    /*! Last (integer) time the cell's sort arrays were updated. */
+    integertime_t ti_sort;
 
-  /* Linked list for sending gpart data. */
-  struct link *send_grav;
+#endif
 
-  /* Linked list for sending data (time-step). */
-  struct link *send_ti;
+  } hydro;
 
-  /*! Bit mask of the proxies this cell is registered with. */
-  unsigned long long int sendto;
+  /*! Grav variables */
+  struct {
 
-  /*! Pointer to this cell's packed representation. */
-  struct pcell *pcell;
+    /*! Pointer to the #gpart data. */
+    struct gpart *parts;
 
-  /*! Size of the packed representation */
-  int pcell_size;
+    /*! This cell's multipole. */
+    struct gravity_tensors *multipole;
 
-  /*! MPI tag associated with this cell */
-  int tag;
+    /*! Super cell, i.e. the highest-level parent cell that has a grav pair/self
+     * tasks */
+    struct cell *super;
 
-#endif
+    /*! Minimum end of (integer) time step in this cell for gravity tasks. */
+    integertime_t ti_end_min;
 
-  /*! Minimum end of (integer) time step in this cell for hydro tasks. */
-  integertime_t ti_hydro_end_min;
+    /*! Maximum end of (integer) time step in this cell for gravity tasks. */
+    integertime_t ti_end_max;
 
-  /*! Maximum end of (integer) time step in this cell for hydro tasks. */
-  integertime_t ti_hydro_end_max;
+    /*! Maximum beginning of (integer) time step in this cell for gravity tasks.
+     */
+    integertime_t ti_beg_max;
 
-  /*! Maximum beginning of (integer) time step in this cell for hydro tasks. */
-  integertime_t ti_hydro_beg_max;
+    /*! Last (integer) time the cell's gpart were drifted forward in time. */
+    integertime_t ti_old_part;
 
-  /*! Minimum end of (integer) time step in this cell for gravity tasks. */
-  integertime_t ti_gravity_end_min;
+    /*! Last (integer) time the cell's multipole was drifted forward in time. */
+    integertime_t ti_old_multipole;
 
-  /*! Maximum end of (integer) time step in this cell for gravity tasks. */
-  integertime_t ti_gravity_end_max;
+    /*! Nr of #gpart in this cell. */
+    int count;
 
-  /*! Maximum beginning of (integer) time step in this cell for gravity tasks.
-   */
-  integertime_t ti_gravity_beg_max;
+    /*! Spin lock for various uses (#gpart case). */
+    swift_lock_type plock;
 
-  /*! Last (integer) time the cell's part were drifted forward in time. */
-  integertime_t ti_old_part;
+    /*! Spin lock for various uses (#multipole case). */
+    swift_lock_type mlock;
 
-  /*! Last (integer) time the cell's gpart were drifted forward in time. */
-  integertime_t ti_old_gpart;
+    /*! Number of #gpart updated in this cell. */
+    int updated;
 
-  /*! Last (integer) time the cell's multipole was drifted forward in time. */
-  integertime_t ti_old_multipole;
+    /*! Number of #gpart inhibited in this cell. */
+    int inhibited;
 
-  /*! Minimum dimension, i.e. smallest edge of this cell (min(width)). */
-  float dmin;
+    /*! Is the #gpart data of this cell being used in a sub-cell? */
+    int phold;
 
-  /*! Maximum particle movement in this cell since the last sort. */
-  float dx_max_sort;
+    /*! Is the #multipole data of this cell being used in a sub-cell? */
+    int mhold;
 
-  /*! Maximum part movement in this cell since last construction. */
-  float dx_max_part;
+    /*! Does this cell need to be drifted (gravity)? */
+    char do_drift;
 
-  /*! Nr of #part in this cell. */
-  int count;
+    /*! Do any of this cell's sub-cells need to be drifted (gravity)? */
+    char do_sub_drift;
 
-  /*! Nr of #gpart in this cell. */
-  int gcount;
+    /*! The drift task for gparts */
+    struct task *drift;
 
-  /*! Nr of #spart in this cell. */
-  int scount;
+    /*! Linked list of the tasks computing this cell's gravity forces. */
+    struct link *grav;
 
-  /*! Bit-mask indicating the sorted directions */
-  unsigned int sorted;
+    /*! Linked list of the tasks computing this cell's gravity M-M forces. */
+    struct link *mm;
 
-  /*! Spin lock for various uses (#part case). */
-  swift_lock_type lock;
+    /*! The multipole initialistation task */
+    struct task *init;
 
-  /*! Spin lock for various uses (#gpart case). */
-  swift_lock_type glock;
+    /*! Implicit task for the gravity initialisation */
+    struct task *init_out;
 
-  /*! Spin lock for various uses (#multipole case). */
-  swift_lock_type mlock;
+    /*! Task computing long range non-periodic gravity interactions */
+    struct task *long_range;
 
-  /*! Spin lock for various uses (#spart case). */
-  swift_lock_type slock;
+    /*! Implicit task for the down propagation */
+    struct task *down_in;
 
-  /*! ID of the previous owner, e.g. runner. */
-  int owner;
+    /*! Task propagating the mesh forces to the particles */
+    struct task *mesh;
 
-  /*! Number of #part updated in this cell. */
-  int updated;
+    /*! Task propagating the multipole to the particles */
+    struct task *down;
 
-  /*! Number of #gpart updated in this cell. */
-  int g_updated;
+    /*! Number of M-M tasks that are associated with this cell. */
+    short int nr_mm_tasks;
 
-  /*! Number of #spart updated in this cell. */
-  int s_updated;
+  } grav;
 
-  /*! ID of the node this cell lives on. */
-  int nodeID;
+  /*! Stars variables */
+  struct {
+
+    /*! Pointer to the #spart data. */
+    struct spart *parts;
+
+    /*! Nr of #spart in this cell. */
+    int count;
+
+    /*! Max smoothing length in this cell. */
+    double h_max;
+
+    /*! Values of h_max before the drifts, used for sub-cell tasks. */
+    float h_max_old;
+
+    /*! Maximum part movement in this cell since last construction. */
+    float dx_max_part;
+
+    /*! Values of dx_max before the drifts, used for sub-cell tasks. */
+    float dx_max_part_old;
+
+    /*! Dependency implicit task for the star ghost  (in->ghost->out)*/
+    struct task *ghost_in;
+
+    /*! Dependency implicit task for the star ghost  (in->ghost->out)*/
+    struct task *ghost_out;
+
+    /*! The star ghost task itself */
+    struct task *ghost;
+
+    /*! Linked list of the tasks computing this cell's star density. */
+    struct link *density;
+
+    /*! Number of #spart updated in this cell. */
+    int updated;
+
+    /*! Number of #spart inhibited in this cell. */
+    int inhibited;
+
+    /*! Is the #spart data of this cell being used in a sub-cell? */
+    int hold;
+
+    /*! Spin lock for various uses (#spart case). */
+    swift_lock_type lock;
+
+  } stars;
+
+#ifdef WITH_MPI
+  /*! MPI variables */
+  struct {
 
-  /*! Is the #part data of this cell being used in a sub-cell? */
-  int hold;
+    struct {
+      /* Task receiving hydro data (positions). */
+      struct task *recv_xv;
 
-  /*! Is the #gpart data of this cell being used in a sub-cell? */
-  int ghold;
+      /* Task receiving hydro data (density). */
+      struct task *recv_rho;
 
-  /*! Is the #multipole data of this cell being used in a sub-cell? */
-  int mhold;
+      /* Task receiving hydro data (gradient). */
+      struct task *recv_gradient;
 
-  /*! Is the #spart data of this cell being used in a sub-cell? */
-  int shold;
+      /* Linked list for sending hydro data (positions). */
+      struct link *send_xv;
 
-  /*! Values of dx_max before the drifts, used for sub-cell tasks. */
-  float dx_max_old;
+      /* Linked list for sending hydro data (density). */
+      struct link *send_rho;
 
-  /*! Values of h_max before the drifts, used for sub-cell tasks. */
-  float h_max_old;
+      /* Linked list for sending hydro data (gradient). */
+      struct link *send_gradient;
 
-  /*! Values of dx_max_sort before the drifts, used for sub-cell tasks. */
-  float dx_max_sort_old;
+    } hydro;
 
-  /*! Bit mask of sort directions that will be needed in the next timestep. */
-  unsigned int requires_sorts;
+    struct {
 
-  /*! Bit mask of sorts that need to be computed for this cell. */
-  unsigned int do_sort;
+      /* Task receiving gpart data. */
+      struct task *recv;
+
+      /* Linked list for sending gpart data. */
+      struct link *send;
+    } grav;
+
+    /* Task receiving data (time-step). */
+    struct task *recv_ti;
+
+    /* Linked list for sending data (time-step). */
+    struct link *send_ti;
+
+    /*! Bit mask of the proxies this cell is registered with. */
+    unsigned long long int sendto;
+
+    /*! Pointer to this cell's packed representation. */
+    struct pcell *pcell;
+
+    /*! Size of the packed representation */
+    int pcell_size;
+
+    /*! MPI tag associated with this cell */
+    int tag;
+
+  } mpi;
+#endif
+
+  /*! The task to end the force calculation */
+  struct task *end_force;
+
+  /*! The first kick task */
+  struct task *kick1;
+
+  /*! The second kick task */
+  struct task *kick2;
+
+  /*! The task to compute time-steps */
+  struct task *timestep;
+
+  /*! Task for source terms */
+  struct task *sourceterms;
+
+  /*! The logger task */
+  struct task *logger;
+
+  /*! Minimum dimension, i.e. smallest edge of this cell (min(width)). */
+  float dmin;
+
+  /*! ID of the previous owner, e.g. runner. */
+  int owner;
+
+  /*! ID of the node this cell lives on. */
+  int nodeID;
 
   /*! Number of tasks that are associated with this cell. */
   short int nr_tasks;
 
-  /*! Number of M-M tasks that are associated with this cell. */
-  short int nr_mm_tasks;
-
   /*! The depth of this cell in the tree. */
   char depth;
 
@@ -464,28 +574,10 @@ struct cell {
   /*! The maximal depth of this cell and its progenies */
   char maxdepth;
 
-  /*! Does this cell need to be drifted (hydro)? */
-  char do_drift;
-
-  /*! Do any of this cell's sub-cells need to be drifted (hydro)? */
-  char do_sub_drift;
-
-  /*! Does this cell need to be drifted (gravity)? */
-  char do_grav_drift;
-
-  /*! Do any of this cell's sub-cells need to be drifted (gravity)? */
-  char do_grav_sub_drift;
-
-  /*! Do any of this cell's sub-cells need to be sorted? */
-  char do_sub_sort;
-
 #ifdef SWIFT_DEBUG_CHECKS
   /* Cell ID (for debugging) */
   int cellID;
 
-  /*! Last (integer) time the cell's sort arrays were updated. */
-  integertime_t ti_sort;
-
   /*! The list of tasks that have been executed on this cell */
   char tasks_executed[64];
 
@@ -558,6 +650,16 @@ void cell_activate_sorts(struct cell *c, int sid, struct scheduler *s);
 void cell_clear_drift_flags(struct cell *c, void *data);
 void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data);
 int cell_has_tasks(struct cell *c);
+void cell_remove_part(const struct engine *e, struct cell *c, struct part *p,
+                      struct xpart *xp);
+void cell_remove_gpart(const struct engine *e, struct cell *c,
+                       struct gpart *gp);
+void cell_remove_spart(const struct engine *e, struct cell *c,
+                       struct spart *sp);
+void cell_convert_part_to_gpart(const struct engine *e, struct cell *c,
+                                struct part *p, struct xpart *xp);
+void cell_convert_spart_to_gpart(const struct engine *e, struct cell *c,
+                                 struct spart *sp);
 int cell_can_use_pair_mm(const struct cell *ci, const struct cell *cj,
                          const struct engine *e, const struct space *s);
 int cell_can_use_pair_mm_rebuild(const struct cell *ci, const struct cell *cj,
@@ -578,8 +680,8 @@ cell_can_recurse_in_pair_hydro_task(const struct cell *c) {
   /* If so, is the cut-off radius plus the max distance the parts have moved */
   /* smaller than the sub-cell sizes ? */
   /* Note: We use the _old values as these might have been updated by a drift */
-  return c->split &&
-         ((kernel_gamma * c->h_max_old + c->dx_max_old) < 0.5f * c->dmin);
+  return c->split && ((kernel_gamma * c->hydro.h_max_old +
+                       c->hydro.dx_max_part_old) < 0.5f * c->dmin);
 }
 
 /**
@@ -592,7 +694,7 @@ __attribute__((always_inline)) INLINE static int
 cell_can_recurse_in_self_hydro_task(const struct cell *c) {
 
   /* Is the cell split and not smaller than the smoothing length? */
-  return c->split && (kernel_gamma * c->h_max_old < 0.5f * c->dmin);
+  return c->split && (kernel_gamma * c->hydro.h_max_old < 0.5f * c->dmin);
 }
 
 /**
@@ -604,8 +706,12 @@ cell_can_recurse_in_self_hydro_task(const struct cell *c) {
 __attribute__((always_inline)) INLINE static int
 cell_can_recurse_in_pair_stars_task(const struct cell *c) {
 
-  // LOIC: To implement
-  return 0;
+  /* Is the cell split ? */
+  /* If so, is the cut-off radius plus the max distance the parts have moved */
+  /* smaller than the sub-cell sizes ? */
+  /* Note: We use the _old values as these might have been updated by a drift */
+  return c->split && ((kernel_gamma * c->stars.h_max_old +
+                       c->stars.dx_max_part_old) < 0.5f * c->dmin);
 }
 
 /**
@@ -617,8 +723,8 @@ cell_can_recurse_in_pair_stars_task(const struct cell *c) {
 __attribute__((always_inline)) INLINE static int
 cell_can_recurse_in_self_stars_task(const struct cell *c) {
 
-  // LOIC: To implement
-  return 0;
+  /* Is the cell split and not smaller than the smoothing length? */
+  return c->split && (kernel_gamma * c->stars.h_max_old < 0.5f * c->dmin);
 }
 
 /**
@@ -635,7 +741,8 @@ __attribute__((always_inline)) INLINE static int cell_can_split_pair_hydro_task(
   /* the sub-cell sizes ? */
   /* Note that since tasks are create after a rebuild no need to take */
   /* into account any part motion (i.e. dx_max == 0 here) */
-  return c->split && (space_stretch * kernel_gamma * c->h_max < 0.5f * c->dmin);
+  return c->split &&
+         (space_stretch * kernel_gamma * c->hydro.h_max < 0.5f * c->dmin);
 }
 
 /**
@@ -652,7 +759,8 @@ __attribute__((always_inline)) INLINE static int cell_can_split_self_hydro_task(
   /* the sub-cell sizes ? */
   /* Note: No need for more checks here as all the sub-pairs and sub-self */
   /* tasks will be created. So no need to check for h_max */
-  return c->split && (space_stretch * kernel_gamma * c->h_max < 0.5f * c->dmin);
+  return c->split &&
+         (space_stretch * kernel_gamma * c->hydro.h_max < 0.5f * c->dmin);
 }
 
 /**
@@ -664,8 +772,13 @@ __attribute__((always_inline)) INLINE static int cell_can_split_self_hydro_task(
 __attribute__((always_inline)) INLINE static int cell_can_split_pair_stars_task(
     const struct cell *c) {
 
-  // LOIC: To implement
-  return 0;
+  /* Is the cell split ? */
+  /* If so, is the cut-off radius with some leeway smaller than */
+  /* the sub-cell sizes ? */
+  /* Note that since tasks are create after a rebuild no need to take */
+  /* into account any part motion (i.e. dx_max == 0 here) */
+  return c->split &&
+         (space_stretch * kernel_gamma * c->stars.h_max < 0.5f * c->dmin);
 }
 
 /**
@@ -677,8 +790,13 @@ __attribute__((always_inline)) INLINE static int cell_can_split_pair_stars_task(
 __attribute__((always_inline)) INLINE static int cell_can_split_self_stars_task(
     const struct cell *c) {
 
-  // LOIC: To implement
-  return 0;
+  /* Is the cell split ? */
+  /* If so, is the cut-off radius with some leeway smaller than */
+  /* the sub-cell sizes ? */
+  /* Note: No need for more checks here as all the sub-pairs and sub-self */
+  /* tasks will be created. So no need to check for h_max */
+  return c->split &&
+         (space_stretch * kernel_gamma * c->stars.h_max < 0.5f * c->dmin);
 }
 
 /**
@@ -720,8 +838,8 @@ __attribute__((always_inline)) INLINE static int cell_need_rebuild_for_pair(
   /* Is the cut-off radius plus the max distance the parts in both cells have */
   /* moved larger than the cell size ? */
   /* Note ci->dmin == cj->dmin */
-  return (kernel_gamma * max(ci->h_max, cj->h_max) + ci->dx_max_part +
-              cj->dx_max_part >
+  return (kernel_gamma * max(ci->hydro.h_max, cj->hydro.h_max) +
+              ci->hydro.dx_max_part + cj->hydro.dx_max_part >
           cj->dmin);
 }
 
@@ -734,10 +852,11 @@ __attribute__((always_inline)) INLINE static void cell_tag(struct cell *c) {
 #ifdef WITH_MPI
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->tag > 0) error("setting tag for already tagged cell");
+  if (c->mpi.tag > 0) error("setting tag for already tagged cell");
 #endif
 
-  if (c->tag < 0 && (c->tag = atomic_inc(&cell_next_tag)) > cell_max_tag)
+  if (c->mpi.tag < 0 &&
+      (c->mpi.tag = atomic_inc(&cell_next_tag)) > cell_max_tag)
     error("Ran out of cell tags.");
 #else
   error("SWIFT was not compiled with MPI enabled.");
diff --git a/src/clocks.c b/src/clocks.c
index c64276bf83f8b52d6d09aa4950737af2a12aa4f6..49297f5db1cc10a3d9f4537c5900610dded7ffba 100644
--- a/src/clocks.c
+++ b/src/clocks.c
@@ -263,6 +263,17 @@ const char *clocks_get_timesincestart(void) {
   return buffer;
 }
 
+/**
+ * Returns the wall-clock time since the start of execution in hours.
+ *
+ * Need to call clocks_set_cpufreq() to mark the start of execution.
+ *
+ * @result the time since the start of the execution
+ */
+double clocks_get_hours_since_start(void) {
+  return clocks_diff_ticks(getticks(), clocks_start) / (3600. * 1000.0);
+}
+
 /**
  * @brief return the cpu time used.
  *
diff --git a/src/clocks.h b/src/clocks.h
index d33e5a342a9b7024ee918a035547e8351b3dc726..ce08167bd504d47a76542870791057881c6d2f17 100644
--- a/src/clocks.h
+++ b/src/clocks.h
@@ -19,8 +19,13 @@
 #ifndef SWIFT_CLOCKS_H
 #define SWIFT_CLOCKS_H
 
+/* Config parameters. */
+#include "../config.h"
+
+/* System includes. */
 #include <sys/times.h>
-#include <time.h>
+
+/* Local includes */
 #include "cycle.h"
 
 /* Struct to record a time for the clocks functions. */
@@ -42,6 +47,7 @@ double clocks_from_ticks(ticks tics);
 ticks clocks_to_ticks(double interval);
 double clocks_diff_ticks(ticks tic, ticks toc);
 const char *clocks_get_timesincestart(void);
+double clocks_get_hours_since_start(void);
 
 double clocks_get_cputime_used(void);
 int clocks_random_seed(void);
diff --git a/src/collectgroup.c b/src/collectgroup.c
index c83d7bef3f03e672e8b5c9036e5daaab26b5d190..0b7b419b565612149fd2b295116b37aa65aa01e9 100644
--- a/src/collectgroup.c
+++ b/src/collectgroup.c
@@ -36,7 +36,8 @@
 
 /* Local collections for MPI reduces. */
 struct mpicollectgroup1 {
-  long long updates, g_updates, s_updates;
+  long long updated, g_updated, s_updated;
+  long long inhibited, g_inhibited, s_inhibited;
   integertime_t ti_hydro_end_min;
   integertime_t ti_gravity_end_min;
   int forcerebuild;
@@ -85,9 +86,12 @@ void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e) {
   e->ti_end_min = min(e->ti_hydro_end_min, e->ti_gravity_end_min);
   e->ti_end_max = max(e->ti_hydro_end_max, e->ti_gravity_end_max);
   e->ti_beg_max = max(e->ti_hydro_beg_max, e->ti_gravity_beg_max);
-  e->updates = grp1->updates;
-  e->g_updates = grp1->g_updates;
-  e->s_updates = grp1->s_updates;
+  e->updates = grp1->updated;
+  e->g_updates = grp1->g_updated;
+  e->s_updates = grp1->s_updated;
+  e->nr_inhibited_parts = grp1->inhibited;
+  e->nr_inhibited_gparts = grp1->g_inhibited;
+  e->nr_inhibited_sparts = grp1->s_inhibited;
   e->forcerebuild = grp1->forcerebuild;
 }
 
@@ -95,10 +99,16 @@ void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e) {
  * @brief Initialises a collectgroup1 struct ready for processing.
  *
  * @param grp1 The #collectgroup1 to initialise
- * @param updates the number of updated hydro particles on this node this step.
- * @param g_updates the number of updated gravity particles on this node this
+ * @param updated the number of updated hydro particles on this node this step.
+ * @param g_updated the number of updated gravity particles on this node this
+ * step.
+ * @param s_updated the number of updated star particles on this node this step.
+ * @param inhibited the number of inhibited hydro particles on this node this
+ * step.
+ * @param g_inhibited the number of inhibited gravity particles on this node
+ * this step.
+ * @param s_inhibited the number of inhibited star particles on this node this
  * step.
- * @param s_updates the number of updated star particles on this node this step.
  * @param ti_hydro_end_min the minimum end time for next hydro time step after
  * this step.
  * @param ti_hydro_end_max the maximum end time for next hydro time step after
@@ -113,17 +123,22 @@ void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e) {
  * after this step.
  * @param forcerebuild whether a rebuild is required after this step.
  */
-void collectgroup1_init(struct collectgroup1 *grp1, size_t updates,
-                        size_t g_updates, size_t s_updates,
+void collectgroup1_init(struct collectgroup1 *grp1, size_t updated,
+                        size_t g_updated, size_t s_updated, size_t inhibited,
+                        size_t g_inhibited, size_t s_inhibited,
                         integertime_t ti_hydro_end_min,
                         integertime_t ti_hydro_end_max,
                         integertime_t ti_hydro_beg_max,
                         integertime_t ti_gravity_end_min,
                         integertime_t ti_gravity_end_max,
                         integertime_t ti_gravity_beg_max, int forcerebuild) {
-  grp1->updates = updates;
-  grp1->g_updates = g_updates;
-  grp1->s_updates = s_updates;
+
+  grp1->updated = updated;
+  grp1->g_updated = g_updated;
+  grp1->s_updated = s_updated;
+  grp1->inhibited = inhibited;
+  grp1->g_inhibited = g_inhibited;
+  grp1->s_inhibited = s_inhibited;
   grp1->ti_hydro_end_min = ti_hydro_end_min;
   grp1->ti_hydro_end_max = ti_hydro_end_max;
   grp1->ti_hydro_beg_max = ti_hydro_beg_max;
@@ -147,9 +162,12 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) {
 
   /* Populate an MPI group struct and reduce this across all nodes. */
   struct mpicollectgroup1 mpigrp11;
-  mpigrp11.updates = grp1->updates;
-  mpigrp11.g_updates = grp1->g_updates;
-  mpigrp11.s_updates = grp1->s_updates;
+  mpigrp11.updated = grp1->updated;
+  mpigrp11.g_updated = grp1->g_updated;
+  mpigrp11.s_updated = grp1->s_updated;
+  mpigrp11.inhibited = grp1->inhibited;
+  mpigrp11.g_inhibited = grp1->g_inhibited;
+  mpigrp11.s_inhibited = grp1->s_inhibited;
   mpigrp11.ti_hydro_end_min = grp1->ti_hydro_end_min;
   mpigrp11.ti_gravity_end_min = grp1->ti_gravity_end_min;
   mpigrp11.forcerebuild = grp1->forcerebuild;
@@ -160,9 +178,12 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) {
     error("Failed to reduce mpicollection1.");
 
   /* And update. */
-  grp1->updates = mpigrp12.updates;
-  grp1->g_updates = mpigrp12.g_updates;
-  grp1->s_updates = mpigrp12.s_updates;
+  grp1->updated = mpigrp12.updated;
+  grp1->g_updated = mpigrp12.g_updated;
+  grp1->s_updated = mpigrp12.s_updated;
+  grp1->inhibited = mpigrp12.inhibited;
+  grp1->g_inhibited = mpigrp12.g_inhibited;
+  grp1->s_inhibited = mpigrp12.s_inhibited;
   grp1->ti_hydro_end_min = mpigrp12.ti_hydro_end_min;
   grp1->ti_gravity_end_min = mpigrp12.ti_gravity_end_min;
   grp1->forcerebuild = mpigrp12.forcerebuild;
@@ -182,9 +203,14 @@ static void doreduce1(struct mpicollectgroup1 *mpigrp11,
 
   /* Do what is needed for each part of the collection. */
   /* Sum of updates. */
-  mpigrp11->updates += mpigrp12->updates;
-  mpigrp11->g_updates += mpigrp12->g_updates;
-  mpigrp11->s_updates += mpigrp12->s_updates;
+  mpigrp11->updated += mpigrp12->updated;
+  mpigrp11->g_updated += mpigrp12->g_updated;
+  mpigrp11->s_updated += mpigrp12->s_updated;
+
+  /* Sum of inhibited */
+  mpigrp11->inhibited += mpigrp12->inhibited;
+  mpigrp11->g_inhibited += mpigrp12->g_inhibited;
+  mpigrp11->s_inhibited += mpigrp12->s_inhibited;
 
   /* Minimum end time. */
   mpigrp11->ti_hydro_end_min =
@@ -204,7 +230,7 @@ static void mpicollectgroup1_reduce(void *in, void *inout, int *len,
                                     MPI_Datatype *datatype) {
 
   for (int i = 0; i < *len; ++i)
-    doreduce1(&((struct mpicollectgroup1 *)inout)[0],
+    doreduce1(&((struct mpicollectgroup1 *)inout)[i],
               &((const struct mpicollectgroup1 *)in)[i]);
 }
 
diff --git a/src/collectgroup.h b/src/collectgroup.h
index 8bf8a9d1b75f9a5ddb3f19fa9cdb4103e044ea59..b6e8769ac993cc023ae402cdfc4b0169406f6181 100644
--- a/src/collectgroup.h
+++ b/src/collectgroup.h
@@ -35,7 +35,10 @@ struct engine;
 struct collectgroup1 {
 
   /* Number of particles updated */
-  long long updates, g_updates, s_updates;
+  long long updated, g_updated, s_updated;
+
+  /* Number of particles inhibited */
+  long long inhibited, g_inhibited, s_inhibited;
 
   /* Times for the time-step */
   integertime_t ti_hydro_end_min, ti_hydro_end_max, ti_hydro_beg_max;
@@ -47,8 +50,9 @@ struct collectgroup1 {
 
 void collectgroup_init(void);
 void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e);
-void collectgroup1_init(struct collectgroup1 *grp1, size_t updates,
-                        size_t g_updates, size_t s_updates,
+void collectgroup1_init(struct collectgroup1 *grp1, size_t updated,
+                        size_t g_updated, size_t s_updated, size_t inhibited,
+                        size_t g_inhibited, size_t s_inhibited,
                         integertime_t ti_hydro_end_min,
                         integertime_t ti_hydro_end_max,
                         integertime_t ti_hydro_beg_max,
diff --git a/src/common_io.c b/src/common_io.c
index 5c8a234c124fb95c08a4bd59d903745e03c5d92c..5ea80f651989022307c255c3ee935f701edfe3c5 100644
--- a/src/common_io.c
+++ b/src/common_io.c
@@ -378,7 +378,7 @@ void io_write_engine_policy(hid_t h_file, const struct engine* e) {
   const hid_t h_grp = H5Gcreate1(h_file, "/Policy", 0);
   if (h_grp < 0) error("Error while creating policy group");
 
-  for (int i = 1; i <= engine_maxpolicy; ++i)
+  for (int i = 1; i < engine_maxpolicy; ++i)
     if (e->policy & (1 << i))
       io_write_attribute_i(h_grp, engine_policy_names[i + 1], 1);
     else
@@ -634,9 +634,9 @@ void io_prepare_dm_gparts_mapper(void* restrict data, int Ndm, void* dummy) {
   /* Let's give all these gparts a negative id */
   for (int i = 0; i < Ndm; ++i) {
 
-    /* 0 or negative ids are not allowed */
-    if (gparts[i].id_or_neg_offset <= 0)
-      error("0 or negative ID for DM particle %i: ID=%lld", i,
+    /* Negative ids are not allowed */
+    if (gparts[i].id_or_neg_offset < 0)
+      error("Negative ID for DM particle %i: ID=%lld", i,
             gparts[i].id_or_neg_offset);
 
     /* Set gpart type */
@@ -787,35 +787,109 @@ void io_duplicate_stars_gparts(struct threadpool* tp,
 }
 
 /**
- * @brief Copy every DM #gpart into the dmparts array.
+ * @brief Copy every non-inhibited #part into the parts_written array.
+ *
+ * @param parts The array of #part containing all particles.
+ * @param xparts The array of #xpart containing all particles.
+ * @param parts_written The array of #part to fill with particles we want to
+ * write.
+ * @param xparts_written The array of #xpart  to fill with particles we want to
+ * write.
+ * @param Nparts The total number of #part.
+ * @param Nparts_written The total number of #part to write.
+ */
+void io_collect_parts_to_write(const struct part* restrict parts,
+                               const struct xpart* restrict xparts,
+                               struct part* restrict parts_written,
+                               struct xpart* restrict xparts_written,
+                               const size_t Nparts,
+                               const size_t Nparts_written) {
+
+  size_t count = 0;
+
+  /* Loop over all parts */
+  for (size_t i = 0; i < Nparts; ++i) {
+
+    /* And collect the ones that have not been removed */
+    if (parts[i].time_bin != time_bin_inhibited) {
+
+      parts_written[count] = parts[i];
+      xparts_written[count] = xparts[i];
+      count++;
+    }
+  }
+
+  /* Check that everything is fine */
+  if (count != Nparts_written)
+    error("Collected the wrong number of particles (%zu vs. %zu expected)",
+          count, Nparts_written);
+}
+
+/**
+ * @brief Copy every non-inhibited #spart into the sparts_written array.
+ *
+ * @param sparts The array of #spart containing all particles.
+ * @param sparts_written The array of #spart to fill with particles we want to
+ * write.
+ * @param Nsparts The total number of #part.
+ * @param Nsparts_written The total number of #part to write.
+ */
+void io_collect_sparts_to_write(const struct spart* restrict sparts,
+                                struct spart* restrict sparts_written,
+                                const size_t Nsparts,
+                                const size_t Nsparts_written) {
+
+  size_t count = 0;
+
+  /* Loop over all parts */
+  for (size_t i = 0; i < Nsparts; ++i) {
+
+    /* And collect the ones that have not been removed */
+    if (sparts[i].time_bin != time_bin_inhibited) {
+
+      sparts_written[count] = sparts[i];
+      count++;
+    }
+  }
+
+  /* Check that everything is fine */
+  if (count != Nsparts_written)
+    error("Collected the wrong number of s-particles (%zu vs. %zu expected)",
+          count, Nsparts_written);
+}
+
+/**
+ * @brief Copy every non-inhibited DM #gpart into the gparts_written array.
  *
  * @param gparts The array of #gpart containing all particles.
- * @param Ntot The number of #gpart.
- * @param dmparts The array of #gpart containg DM particles to be filled.
- * @param Ndm The number of DM particles.
+ * @param gparts_written The array of #gpart to fill with particles we want to
+ * write.
+ * @param Ngparts The total number of #part.
+ * @param Ngparts_written The total number of #part to write.
  */
-void io_collect_dm_gparts(const struct gpart* const gparts, size_t Ntot,
-                          struct gpart* const dmparts, size_t Ndm) {
+void io_collect_gparts_to_write(const struct gpart* restrict gparts,
+                                struct gpart* restrict gparts_written,
+                                const size_t Ngparts,
+                                const size_t Ngparts_written) {
 
   size_t count = 0;
 
-  /* Loop over all gparts */
-  for (size_t i = 0; i < Ntot; ++i) {
+  /* Loop over all parts */
+  for (size_t i = 0; i < Ngparts; ++i) {
 
-    /* message("i=%zd count=%zd id=%lld part=%p", i, count, gparts[i].id,
-     * gparts[i].part); */
+    /* And collect the ones that have not been removed */
+    if ((gparts[i].time_bin != time_bin_inhibited) &&
+        (gparts[i].type == swift_type_dark_matter)) {
 
-    /* And collect the DM ones */
-    if (gparts[i].type == swift_type_dark_matter) {
-      dmparts[count] = gparts[i];
+      gparts_written[count] = gparts[i];
       count++;
     }
   }
 
   /* Check that everything is fine */
-  if (count != Ndm)
-    error("Collected the wrong number of dm particles (%zu vs. %zu expected)",
-          count, Ndm);
+  if (count != Ngparts_written)
+    error("Collected the wrong number of s-particles (%zu vs. %zu expected)",
+          count, Ngparts_written);
 }
 
 /**
diff --git a/src/common_io.h b/src/common_io.h
index 7e0f1b74898f5cfdec5631c142231d30a0944f50..016c5138e18ae8636834c35d659e07d8fcd46e36 100644
--- a/src/common_io.h
+++ b/src/common_io.h
@@ -35,6 +35,7 @@
 struct part;
 struct gpart;
 struct spart;
+struct xpart;
 struct io_props;
 struct engine;
 struct threadpool;
@@ -56,12 +57,6 @@ enum IO_DATA_TYPE {
   CHAR
 };
 
-/**
- * @brief The different formats for when to run structure finding.
- *
- */
-enum io_stf_output_format { io_stf_steps = 0, io_stf_time };
-
 #if defined(HAVE_HDF5)
 
 hid_t io_hdf5_type(enum IO_DATA_TYPE type);
@@ -97,8 +92,20 @@ void io_copy_temp_buffer(void* temp, const struct engine* e,
 size_t io_sizeof_type(enum IO_DATA_TYPE type);
 int io_is_double_precision(enum IO_DATA_TYPE type);
 
-void io_collect_dm_gparts(const struct gpart* const gparts, size_t Ntot,
-                          struct gpart* const dmparts, size_t Ndm);
+void io_collect_parts_to_write(const struct part* restrict parts,
+                               const struct xpart* restrict xparts,
+                               struct part* restrict parts_written,
+                               struct xpart* restrict xparts_written,
+                               const size_t Nparts,
+                               const size_t Nparts_written);
+void io_collect_sparts_to_write(const struct spart* restrict sparts,
+                                struct spart* restrict sparts_written,
+                                const size_t Nsparts,
+                                const size_t Nsparts_written);
+void io_collect_gparts_to_write(const struct gpart* restrict gparts,
+                                struct gpart* restrict gparts_written,
+                                const size_t Ngparts,
+                                const size_t Ngparts_written);
 void io_prepare_dm_gparts(struct threadpool* tp, struct gpart* const gparts,
                           size_t Ndm);
 void io_duplicate_hydro_gparts(struct threadpool* tp, struct part* const parts,
diff --git a/src/const.h b/src/const.h
index 6c5b5299c08efb7935b046ecfd0b3d67b7dc4c7a..e417b8ca3827ef87396706c56df36bb9bd3aed75 100644
--- a/src/const.h
+++ b/src/const.h
@@ -21,13 +21,10 @@
 #define SWIFT_CONST_H
 
 /* SPH Viscosity constants. */
-#define const_viscosity_alpha 0.8f
-#define const_viscosity_alpha_min \
-  0.1f /* Values taken from (Price,2004), not used in legacy gadget mode */
-#define const_viscosity_alpha_max \
-  2.0f /* Values taken from (Price,2004), not used in legacy gadget mode */
-#define const_viscosity_length \
-  0.1f /* Values taken from (Price,2004), not used in legacy gadget mode */
+/* Cosmology default beta=3.0. Planetary default beta=4.0
+ * Alpha can be set in the parameter file.
+ * Beta is defined as in e.g. Price (2010) Eqn (103) */
+#define const_viscosity_beta 3.0f
 
 /* SPH Thermal conductivity constants. */
 #define const_conductivity_alpha \
diff --git a/src/cooling.h b/src/cooling.h
index 0fb04b9e484d989e746a254fc1934dc20033fb09..ae0d06a4733b31f8c0a2fe4db5329df1494f4aa4 100644
--- a/src/cooling.h
+++ b/src/cooling.h
@@ -34,6 +34,8 @@
 #include "./cooling/const_du/cooling.h"
 #elif defined(COOLING_CONST_LAMBDA)
 #include "./cooling/const_lambda/cooling.h"
+#elif defined(COOLING_COMPTON)
+#include "./cooling/Compton/cooling.h"
 #elif defined(COOLING_GRACKLE)
 #include "./cooling/grackle/cooling.h"
 #elif defined(COOLING_EAGLE)
diff --git a/src/cooling/Compton/cooling.h b/src/cooling/Compton/cooling.h
new file mode 100644
index 0000000000000000000000000000000000000000..da9225fe02fb6805f795938f86afd75ebdbef125
--- /dev/null
+++ b/src/cooling/Compton/cooling.h
@@ -0,0 +1,335 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_COOLING_COMPTON_H
+#define SWIFT_COOLING_COMPTON_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <float.h>
+#include <math.h>
+
+/* Local includes. */
+#include "const.h"
+#include "error.h"
+#include "hydro.h"
+#include "parser.h"
+#include "part.h"
+#include "physical_constants.h"
+#include "units.h"
+
+/**
+ * @brief Compute the mean molecular weight as a function of temperature for
+ * primordial gas.
+ *
+ * @param T The temperature of the gas [K].
+ * @param H_mass_fraction The hydrogen mass fraction of the gas.
+ * @param T_transition The temperature of the transition from HII to HI [K].
+ */
+__attribute__((always_inline, const)) INLINE static double
+mean_molecular_weight(const double T, const double H_mass_fraction,
+                      const double T_transition) {
+
+  if (T > T_transition)
+    return 4. / (8. - 5. * (1. - H_mass_fraction));
+  else
+    return 4. / (1. + 3. * H_mass_fraction);
+}
+
+/**
+ * @brief Compute the temperature for a given internal energy per unit mass
+ * assuming primordial gas.
+ *
+ * @param u_cgs The internal energy per unit mass of the gas [erg * g^-1].
+ * @param H_mass_fraction The hydrogen mass fraction of the gas.
+ * @param T_transition The temperature of the transition from HII to HI [K].
+ * @param m_H_cgs The mass of the Hydorgen atom [g].
+ * @param k_B_cgs The Boltzmann constant in cgs units [erg * K^-1]
+ * @return The temperature of the gas [K]
+ */
+__attribute__((always_inline, const)) INLINE static double
+temperature_from_internal_energy(const double u_cgs,
+                                 const double H_mass_fraction,
+                                 const double T_transition,
+                                 const double m_H_cgs, const double k_B_cgs) {
+
+  const double T_over_mu = hydro_gamma_minus_one * u_cgs * m_H_cgs / k_B_cgs;
+
+  const double mu_high =
+      mean_molecular_weight(T_transition + 1., H_mass_fraction, T_transition);
+  const double mu_low =
+      mean_molecular_weight(T_transition - 1., H_mass_fraction, T_transition);
+
+  if (T_over_mu > (T_transition + 1.) / mu_high)
+    return T_over_mu * mu_high;
+  else if (T_over_mu < (T_transition - 1.) / mu_low)
+    return T_over_mu * mu_low;
+  else
+    return T_transition;
+}
+
+/**
+ * @brief Calculates du/dt in CGS units for a particle.
+ *
+ *
+ * @param cosmo The current cosmological model.
+ * @param hydro_props The properties of the hydro scheme.
+ * @param cooling The #cooling_function_data used in the run.
+ * @param z The current redshift.
+ * @param u The current internal energy in internal units.
+ * @param p Pointer to the particle data.
+ * @return The change in energy per unit mass due to cooling for this particle
+ * in cgs units [erg * g^-1 * s^-1].
+ */
+__attribute__((always_inline)) INLINE static double Compton_cooling_rate_cgs(
+    const struct cosmology* cosmo, const struct hydro_props* hydro_props,
+    const struct cooling_function_data* cooling, const double z, const double u,
+    const struct part* p) {
+
+  /* Get particle density */
+  const double rho = hydro_get_physical_density(p, cosmo);
+  const double rho_cgs = rho * cooling->conv_factor_density_to_cgs;
+
+  /* Powers of (1 + z) */
+  const double zp1 = z + 1.;
+  const double zp1p2 = zp1 * zp1;
+  const double zp1p4 = zp1p2 * zp1p2; /* (1 + z)^4 */
+
+  /* CMB temperature at this redshift */
+  const double T_CMB = cooling->const_T_CMB_0 * zp1;
+
+  /* Gas properties */
+  const double H_mass_fraction = hydro_props->hydrogen_mass_fraction;
+  const double T_transition = hydro_props->hydrogen_ionization_temperature;
+
+  /* Particle temperature */
+  const double u_cgs = u * cooling->conv_factor_energy_to_cgs;
+  const double T = temperature_from_internal_energy(u_cgs, H_mass_fraction,
+                                                    T_transition, 1., 1.);
+  // MATTHIEU: to do: get H mass in cgs and k_B in cgs.
+
+  /* Electron abundance */
+  double electron_abundance = 0.;  // MATTHIEU: To do: compute X_e
+
+  /* Temperature difference with the CMB */
+  const double delta_T = T - T_CMB;
+
+  /* Electron density */
+  const double electron_density_cgs =
+      rho_cgs * electron_abundance * cooling->proton_mass_cgs_inv;
+
+  /* Compton formula */
+  return cooling->const_Compton_rate_cgs * delta_T * zp1p4 *
+         electron_density_cgs / rho_cgs;
+}
+
+/**
+ * @brief Apply the cooling function to a particle.
+ *
+ * @param phys_const The physical constants in internal units.
+ * @param us The internal system of units.
+ * @param cosmo The current cosmological model.
+ * @param hydro_props The properties of the hydro scheme.
+ * @param cooling The #cooling_function_data used in the run.
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the particle' extended data.
+ * @param dt The time-step of this particle.
+ * @param dt_therm The time-step operator used for thermal quantities.
+ */
+__attribute__((always_inline)) INLINE static void cooling_cool_part(
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct hydro_props* hydro_props,
+    const struct cooling_function_data* restrict cooling,
+    struct part* restrict p, struct xpart* restrict xp, const float dt,
+    const float dt_therm) {
+
+  /* Nothing to do here? */
+  if (dt == 0.) return;
+
+  /* Internal energy floor */
+  const float u_floor = hydro_props->minimal_internal_energy;
+
+  /* Current energy */
+  const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo);
+
+  /* Current du_dt in physical coordinates (internal units) */
+  const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo);
+
+  /* Calculate cooling du_dt (in cgs units) */
+  const double cooling_du_dt_cgs =
+      Compton_cooling_rate_cgs(cosmo, hydro_props, cooling, cosmo->z, u_old, p);
+
+  /* Convert to internal units */
+  float cooling_du_dt =
+      cooling_du_dt_cgs * cooling->conv_factor_energy_rate_from_cgs;
+
+  /* Add cosmological term */
+  cooling_du_dt *= cosmo->a * cosmo->a;
+
+  float total_du_dt = hydro_du_dt + cooling_du_dt;
+
+  /* We now need to check that we are not going to go below any of the limits */
+
+  /* First, check whether we may end up below the minimal energy after
+   * this step 1/2 kick + another 1/2 kick that could potentially be for
+   * a time-step twice as big. We hence check for 1.5 delta_t. */
+  if (u_old + total_du_dt * 1.5 * dt_therm < u_floor) {
+    total_du_dt = (u_floor - u_old) / (1.5f * dt_therm);
+  }
+
+  /* Second, check whether the energy used in the prediction could get negative.
+   * We need to check for the 1/2 dt kick followed by a full time-step drift
+   * that could potentially be for a time-step twice as big. We hence check
+   * for 2.5 delta_t but this time against 0 energy not the minimum */
+  if (u_old + total_du_dt * 2.5 * dt_therm < 0.) {
+    total_du_dt = -u_old / ((2.5f + 0.0001f) * dt_therm);
+  }
+
+  /* Update the internal energy time derivative */
+  hydro_set_physical_internal_energy_dt(p, cosmo, total_du_dt);
+
+  /* Store the radiated energy (assuming dt will not change) */
+  xp->cooling_data.radiated_energy +=
+      -hydro_get_mass(p) * (total_du_dt - hydro_du_dt) * dt_therm;
+}
+
+/**
+ * @brief Computes the time-step due to cooling for this particle.
+ *
+ * We impose no time-step limit.
+ *
+ * @param cooling The #cooling_function_data used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param cosmo The current cosmological model.
+ * @param hydro_props The properties of the hydro scheme.
+ * @param us The internal system of units.
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the extended data of the particle.
+ */
+__attribute__((always_inline)) INLINE static float cooling_timestep(
+    const struct cooling_function_data* restrict cooling,
+    const struct phys_const* restrict phys_const,
+    const struct cosmology* restrict cosmo,
+    const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props, const struct part* restrict p,
+    const struct xpart* restrict xp) {
+
+  return FLT_MAX;
+}
+
+/**
+ * @brief Sets the cooling properties of the (x-)particles to a valid start
+ * state.
+ *
+ * Nothing to do here. Just set the radiated energy counter to 0.
+ *
+ * @param phys_const The physical constants in internal units.
+ * @param cooling The properties of the cooling function.
+ * @param us The internal system of units.
+ * @param cosmo The current cosmological model.
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the extended particle data.
+ */
+__attribute__((always_inline)) INLINE static void cooling_first_init_part(
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, struct xpart* restrict xp) {
+
+  xp->cooling_data.radiated_energy = 0.f;
+}
+
+/**
+ * @brief Returns the total radiated energy by this particle.
+ *
+ * @param xp The extended particle data
+ */
+__attribute__((always_inline)) INLINE static float cooling_get_radiated_energy(
+    const struct xpart* restrict xp) {
+
+  return xp->cooling_data.radiated_energy;
+}
+
+/**
+ * @brief Initialises the cooling properties.
+ *
+ * @param parameter_file The parsed parameter file.
+ * @param us The current internal system of units.
+ * @param phys_const The physical constants in internal units.
+ * @param cooling The cooling properties to initialize
+ */
+static INLINE void cooling_init_backend(struct swift_params* parameter_file,
+                                        const struct unit_system* us,
+                                        const struct phys_const* phys_const,
+                                        struct cooling_function_data* cooling) {
+
+  /* Some useful conversion values */
+  cooling->conv_factor_density_to_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_DENSITY);
+  cooling->conv_factor_energy_to_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS);
+  cooling->conv_factor_energy_rate_from_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_TIME) /
+      units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS);
+
+  /* Useful constants */
+  cooling->proton_mass_cgs_inv =
+      1. / (phys_const->const_proton_mass *
+            units_cgs_conversion_factor(us, UNIT_CONV_MASS));
+
+  /* Temperature of the CMB in CGS */
+  const double T_CMB_0 = phys_const->const_T_CMB_0 *
+                         units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE);
+  cooling->const_T_CMB_0 = T_CMB_0; /* [K] */
+
+  /* Compute the coefficient at the front of the Compton cooling expression */
+  const double radiation_constant =
+      4. * phys_const->const_stefan_boltzmann / phys_const->const_speed_light_c;
+  const double compton_coefficient =
+      4. * radiation_constant * phys_const->const_thomson_cross_section *
+      phys_const->const_boltzmann_k /
+      (phys_const->const_electron_mass * phys_const->const_speed_light_c);
+  const float dimension_coefficient[5] = {1, 2, -3, 0, -5};
+
+  /* This should be ~1.0178085e-37 [g cm^2 s^-3 K^-5] */
+  const double compton_coefficient_cgs =
+      compton_coefficient *
+      units_general_cgs_conversion_factor(us, dimension_coefficient);
+
+  /* And now the Compton rate [g cm^2 s^-3 K^-1] == [erg s^-1 K^-1]*/
+  cooling->const_Compton_rate_cgs =
+      compton_coefficient_cgs * T_CMB_0 * T_CMB_0 * T_CMB_0 * T_CMB_0;
+}
+
+/**
+ * @brief Prints the properties of the cooling model to stdout.
+ *
+ * @param cooling The properties of the cooling function.
+ */
+static INLINE void cooling_print_backend(
+    const struct cooling_function_data* cooling) {
+
+  message("Cooling function is 'Compton cooling'.");
+}
+
+#endif /* SWIFT_COOLING_COMPTON_H */
diff --git a/src/cooling/Compton/cooling_io.h b/src/cooling/Compton/cooling_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..d020587c920f781450a5183954bc6c429e461512
--- /dev/null
+++ b/src/cooling/Compton/cooling_io.h
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_COOLING_IO_COMPTON_H
+#define SWIFT_COOLING_IO_COMPTON_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Local includes */
+#include "io_properties.h"
+
+#ifdef HAVE_HDF5
+
+/**
+ * @brief Writes the current model of cooling to the file
+ * @param h_grp The HDF5 group in which to write
+ * @param cooling the parameters of the cooling function.
+ */
+__attribute__((always_inline)) INLINE static void cooling_write_flavour(
+    hid_t h_grp, const struct cooling_function_data* cooling) {
+
+  io_write_attribute_s(h_grp, "Cooling Model", "Compton cooling");
+  io_write_attribute_d(h_grp, "Compton rate [erg s^-1 K^-1]",
+                       cooling->const_Compton_rate_cgs);
+}
+#endif
+
+/**
+ * @brief Specifies which particle fields to write to a dataset
+ *
+ * Nothing to write for this scheme.
+ *
+ * @param xparts The extended particle array.
+ * @param list The list of i/o properties to write.
+ * @param cooling The #cooling_function_data
+ *
+ * @return Returns the number of fields to write.
+ */
+__attribute__((always_inline)) INLINE static int cooling_write_particles(
+    const struct xpart* xparts, struct io_props* list,
+    const struct cooling_function_data* cooling) {
+
+  return 0;
+}
+
+#endif /* SWIFT_COOLING_IO_COMPTON_H */
diff --git a/src/cooling/Compton/cooling_struct.h b/src/cooling/Compton/cooling_struct.h
new file mode 100644
index 0000000000000000000000000000000000000000..1e09d492b1c8f6eb92f9d4f5faa00998dc2daec9
--- /dev/null
+++ b/src/cooling/Compton/cooling_struct.h
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_COOLING_STRUCT_COMPTON_H
+#define SWIFT_COOLING_STRUCT_COMPTON_H
+
+/**
+ * @brief Properties of the cooling function.
+ */
+struct cooling_function_data {
+
+  /*! Compton rate in cgs [g cm^2 s^-3 K^-1] */
+  double const_Compton_rate_cgs;
+
+  /*! Temperature of the CMB at redshift 0 in cgs [K] */
+  double const_T_CMB_0;
+
+  /*! Conversion factor from internal units to cgs for density */
+  double conv_factor_density_to_cgs;
+
+  /*! Conversion factor from internal units to cgs for internal energy */
+  double conv_factor_energy_to_cgs;
+
+  /*! Conversion factor from internal units from cgs for internal energy
+   * derivative */
+  double conv_factor_energy_rate_from_cgs;
+
+  /*! Inverse of the proton mass in cgs units [g^-1] */
+  double proton_mass_cgs_inv;
+};
+
+/**
+ * @brief Properties of the cooling stored in the particle data.
+ */
+struct cooling_xpart_data {
+
+  /*! Energy radiated away by this particle since the start of the run */
+  float radiated_energy;
+};
+
+#endif /* SWIFT_COOLING_STRUCT_COMPTON_H */
diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h
index 1c56572856a88d763d5ef7ca77e14d378891a264..8d74222c78deab0e6790a06dca847033556833a7 100644
--- a/src/cooling/EAGLE/cooling.h
+++ b/src/cooling/EAGLE/cooling.h
@@ -54,8 +54,10 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
+    const struct hydro_props* hydro_props,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, float dt) {}
+    struct part* restrict p, struct xpart* restrict xp, const float dt,
+    const float dt_therm) {}
 
 /**
  * @brief Computes the cooling time-step.
@@ -70,7 +72,9 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
     const struct cooling_function_data* restrict cooling,
     const struct phys_const* restrict phys_const,
     const struct cosmology* restrict cosmo,
-    const struct unit_system* restrict us, const struct part* restrict p) {
+    const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props, const struct part* restrict p,
+    const struct xpart* restrict xp) {
 
   return FLT_MAX;
 }
diff --git a/src/cooling/EAGLE/cooling_io.h b/src/cooling/EAGLE/cooling_io.h
index f98539605de5c231a821758e9bd8fdb89bd19a59..b8f89717b450719a90c13e26ec07a51738b35f49 100644
--- a/src/cooling/EAGLE/cooling_io.h
+++ b/src/cooling/EAGLE/cooling_io.h
@@ -28,13 +28,13 @@
 #ifdef HAVE_HDF5
 
 /**
- * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
+ * @brief Writes the current model of cooling to the file
+ * @param h_grp The HDF5 group in which to write
  */
 __attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
+    hid_t h_grp, const struct cooling_function_data* cooling) {
 
-  io_write_attribute_s(h_grpsph, "Cooling Model", "EAGLE");
+  io_write_attribute_s(h_grp, "Cooling Model", "EAGLE");
 }
 #endif
 
diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h
index b6fea7eea7b0fb208c4bffece425ec836d5df0c0..1c009886d2a08b62070162a80c4572aa43d35e3e 100644
--- a/src/cooling/const_du/cooling.h
+++ b/src/cooling/const_du/cooling.h
@@ -25,9 +25,9 @@
  * @file src/cooling/const_du/cooling.h
  * @brief Routines related to the "constant cooling" cooling function.
  *
- * This is the simplest possible cooling function. A constant cooling rate with
- * a minimal energy floor is applied. Should be used as a template for more
- * realistic functions.
+ * This is the simplest possible cooling function. A constant cooling rate
+ * (du/dt) with a minimal energy floor is applied. Should be used as a template
+ * for more realistic functions.
  */
 
 /* Config parameters. */
@@ -54,26 +54,30 @@
  * @param phys_const The physical constants in internal units.
  * @param us The internal system of units.
  * @param cosmo The current cosmological model.
+ * @param hydro_props The properties of the hydro scheme.
  * @param cooling The #cooling_function_data used in the run.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
  * @param dt The time-step of this particle.
+ * @param dt_therm The time-step operator used for thermal quantities.
  */
 __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
+    const struct hydro_props* hydro_props,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, float dt) {
+    struct part* restrict p, struct xpart* restrict xp, const float dt,
+    const float dt_therm) {
 
   /* Internal energy floor */
   const float u_floor = cooling->min_energy;
 
   /* Get current internal energy */
-  const float u_old = hydro_get_physical_internal_energy(p, cosmo);
+  const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo);
 
   /* Current du_dt */
-  const float hydro_du_dt = hydro_get_internal_energy_dt(p);
+  const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo);
 
   /* Get cooling function properties */
   float cooling_du_dt = -cooling->cooling_rate;
@@ -86,7 +90,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
   }
 
   /* Update the internal energy time derivative */
-  hydro_set_internal_energy_dt(p, hydro_du_dt + cooling_du_dt);
+  hydro_set_physical_internal_energy_dt(p, cosmo, hydro_du_dt + cooling_du_dt);
 
   /* Store the radiated energy */
   xp->cooling_data.radiated_energy += -hydro_get_mass(p) * cooling_du_dt * dt;
@@ -103,16 +107,21 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
  * @param phys_const The physical constants in internal units.
  * @param cosmo The current cosmological model.
  * @param us The internal system of units.
+ * @param hydro_props The properties of the hydro scheme.
  * @param p Pointer to the particle data.
+ * @param xp Pointer to the extedended particle data.
  */
 __attribute__((always_inline)) INLINE static float cooling_timestep(
     const struct cooling_function_data* restrict cooling,
     const struct phys_const* restrict phys_const,
     const struct cosmology* restrict cosmo,
-    const struct unit_system* restrict us, const struct part* restrict p) {
+    const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props, const struct part* restrict p,
+    const struct xpart* xp) {
 
   const float cooling_rate = cooling->cooling_rate;
-  const float internal_energy = hydro_get_physical_internal_energy(p, cosmo);
+  const float internal_energy =
+      hydro_get_physical_internal_energy(p, xp, cosmo);
   return cooling->cooling_tstep_mult * internal_energy / fabsf(cooling_rate);
 }
 
@@ -126,7 +135,10 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  *
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
+ * @param phys_const The physical constants in internal units.
  * @param cooling The properties of the cooling function.
+ * @param us The internal system of units.
+ * @param cosmo The current cosmological model.
  */
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
     const struct phys_const* restrict phys_const,
diff --git a/src/cooling/const_du/cooling_io.h b/src/cooling/const_du/cooling_io.h
index 52a943aca86e51665fd1841d7bcb8a100b046ed8..f4a327f14ec071bc62c4cf57bb118df71bab2b3e 100644
--- a/src/cooling/const_du/cooling_io.h
+++ b/src/cooling/const_du/cooling_io.h
@@ -21,6 +21,15 @@
 #ifndef SWIFT_COOLING_CONST_DU_IO_H
 #define SWIFT_COOLING_CONST_DU_IO_H
 
+/**
+ * @file src/cooling/const_du/cooling_io.h
+ * @brief i/o routines related to the "constant cooling" cooling function.
+ *
+ * This is the simplest possible cooling function. A constant cooling rate
+ * (du/dt) with a minimal energy floor is applied. Should be used as a template
+ * for more realistic functions.
+ */
+
 /* Config parameters. */
 #include "../config.h"
 
@@ -31,19 +40,20 @@
 
 /**
  * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
+ * @param h_grp The HDF5 group in which to write
+ * @param cooling the parameters of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
+    hid_t h_grp, const struct cooling_function_data* cooling) {
 
-  io_write_attribute_s(h_grpsph, "Cooling Model", "Constant du/dt");
+  io_write_attribute_s(h_grp, "Cooling Model", "Constant du/dt");
 }
 #endif
 
 /**
  * @brief Specifies which particle fields to write to a dataset
  *
- * @param parts The particle array.
+ * @param xparts The exended particle data array.
  * @param list The list of i/o properties to write.
  * @param cooling The #cooling_function_data
  *
diff --git a/src/cooling/const_du/cooling_struct.h b/src/cooling/const_du/cooling_struct.h
index cc00b001cf6b576266de02dac885f87d089bd8e4..94db6b6542cacda6dbdc43c6db6b9c2cac7961d6 100644
--- a/src/cooling/const_du/cooling_struct.h
+++ b/src/cooling/const_du/cooling_struct.h
@@ -23,11 +23,11 @@
 
 /**
  * @file src/cooling/const_du/cooling_struct.h
- * @brief Structure related to the "constant cooling" cooling function.
+ * @brief Structures related to the "constant cooling" cooling function.
  *
- * This is the simplest possible cooling function. A constant cooling rate with
- * a minimal energy floor is applied. Should be used as a template for more
- * realistic functions.
+ * This is the simplest possible cooling function. A constant cooling rate
+ * (du/dt) with a minimal energy floor is applied. Should be used as a template
+ * for more realistic functions.
  */
 
 /**
diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h
index f1a7abdbe14a39d98bbd01eb36ba870c8af0ee1a..83794edd0fd36c392cc64c14cad0c5e19b77f86a 100644
--- a/src/cooling/const_lambda/cooling.h
+++ b/src/cooling/const_lambda/cooling.h
@@ -1,8 +1,6 @@
 /*******************************************************************************
  * This file is part of SWIFT.
- * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk)
- *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- *                    Richard Bower (r.g.bower@durham.ac.uk)
+ * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
  *                    Stefan Arridge  (stefan.arridge@durham.ac.uk)
  *
  * This program is free software: you can redistribute it and/or modify
@@ -19,10 +17,17 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-
 #ifndef SWIFT_COOLING_CONST_LAMBDA_H
 #define SWIFT_COOLING_CONST_LAMBDA_H
 
+/**
+ * @file src/cooling/const_lambda/cooling.h
+ * @brief Routines related to the "constant lambda" cooling function.
+ *
+ * This model assumes a constant cooling rate Lambda irrespective of redshift
+ * or density.
+ */
+
 /* Config parameters. */
 #include "../config.h"
 
@@ -40,30 +45,39 @@
 #include "units.h"
 
 /**
- * @brief Calculates du/dt in code units for a particle.
+ * @brief Calculates du/dt in CGS units for a particle.
+ *
+ * The cooling rate is \f$\frac{du}{dt} = -\frac{\Lambda}{n_H^2}
+ * \frac{n_H^2}{\rho} \f$, where \f$ \frac{\Lambda}{n_H^2} \f$ is a constant in
+ * this model (lambda_nH2_cgs in #cooling_function_data).
+ * The returned value is in physical [erg * g^-1 * s^-1].
  *
- * @param phys_const The physical constants in internal units.
- * @param us The internal system of units.
  * @param cosmo The current cosmological model.
+ * @param hydro_props The properties of the hydro scheme.
  * @param cooling The #cooling_function_data used in the run.
- * @param p Pointer to the particle data..
+ * @param p Pointer to the particle data.
+ * @return The change in energy per unit mass due to cooling for this particle
+ * in cgs units [erg * g^-1 * s^-1].
  */
-__attribute__((always_inline)) INLINE static float cooling_rate(
-    const struct phys_const* const phys_const, const struct unit_system* us,
-    const struct cosmology* restrict cosmo,
+__attribute__((always_inline)) INLINE static double cooling_rate_cgs(
+    const struct cosmology* cosmo, const struct hydro_props* hydro_props,
     const struct cooling_function_data* cooling, const struct part* p) {
 
-  /* Get particle density */
-  const float rho = hydro_get_physical_density(p, cosmo);
+  /* Get particle density [g * cm^-3] */
+  const double rho = hydro_get_physical_density(p, cosmo);
+  const double rho_cgs = rho * cooling->conv_factor_density_to_cgs;
+
+  /* Get Hydrogen mass fraction */
+  const double X_H = hydro_props->hydrogen_mass_fraction;
 
-  /* Get cooling function properties */
-  const float X_H = cooling->hydrogen_mass_abundance;
+  /* Hydrogen number density (X_H * rho / m_p) [cm^-3] */
+  const double n_H_cgs = X_H * rho_cgs * cooling->proton_mass_cgs_inv;
 
-  /* Calculate du_dt */
-  const float du_dt = -cooling->lambda *
-                      (X_H * rho / phys_const->const_proton_mass) *
-                      (X_H * rho / phys_const->const_proton_mass) / rho;
-  return du_dt;
+  /* Calculate du_dt ((Lambda / n_H^2) * n_H^2 / rho) */
+  const double du_dt_cgs =
+      -cooling->lambda_nH2_cgs * n_H_cgs * n_H_cgs / rho_cgs;
+
+  return du_dt_cgs;
 }
 
 /**
@@ -72,75 +86,125 @@ __attribute__((always_inline)) INLINE static float cooling_rate(
  * @param phys_const The physical constants in internal units.
  * @param us The internal system of units.
  * @param cosmo The current cosmological model.
+ * @param hydro_props The properties of the hydro scheme.
  * @param cooling The #cooling_function_data used in the run.
  * @param p Pointer to the particle data.
+ * @param xp Pointer to the particle' extended data.
  * @param dt The time-step of this particle.
+ * @param dt_therm The time-step operator used for thermal quantities.
  */
 __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
+    const struct hydro_props* hydro_props,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, float dt) {
+    struct part* restrict p, struct xpart* restrict xp, const float dt,
+    const float dt_therm) {
+
+  /* Nothing to do here? */
+  if (dt == 0.) return;
 
   /* Internal energy floor */
-  const float u_floor = cooling->min_energy;
+  const float u_floor = hydro_props->minimal_internal_energy;
 
   /* Current energy */
-  const float u_old = hydro_get_physical_internal_energy(p, cosmo);
+  const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo);
+
+  /* Current du_dt in physical coordinates (internal units) */
+  const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo);
+
+  /* Calculate cooling du_dt (in cgs units) */
+  const double cooling_du_dt_cgs =
+      cooling_rate_cgs(cosmo, hydro_props, cooling, p);
+
+  /* Convert to internal units */
+  float cooling_du_dt =
+      cooling_du_dt_cgs * cooling->conv_factor_energy_rate_from_cgs;
+
+  /* Add cosmological term */
+  cooling_du_dt *= cosmo->a * cosmo->a;
 
-  /* Current du_dt */
-  const float hydro_du_dt = hydro_get_internal_energy_dt(p);
+  float total_du_dt = hydro_du_dt + cooling_du_dt;
 
-  /* Calculate cooling du_dt */
-  float cooling_du_dt = cooling_rate(phys_const, us, cosmo, cooling, p);
+  /* We now need to check that we are not going to go below any of the limits */
 
-  /* Integrate cooling equation to enforce energy floor */
-  /* Factor of 1.5 included since timestep could potentially double */
-  if (u_old + (hydro_du_dt + cooling_du_dt) * 1.5f * dt < u_floor) {
-    cooling_du_dt = -(u_old + 1.5f * dt * hydro_du_dt - u_floor) / (1.5f * dt);
+  /* First, check whether we may end up below the minimal energy after
+   * this step 1/2 kick + another 1/2 kick that could potentially be for
+   * a time-step twice as big. We hence check for 1.5 delta_t. */
+  if (u_old + total_du_dt * 1.5 * dt_therm < u_floor) {
+    total_du_dt = (u_floor - u_old) / (1.5f * dt_therm);
+  }
+
+  /* Second, check whether the energy used in the prediction could get negative.
+   * We need to check for the 1/2 dt kick followed by a full time-step drift
+   * that could potentially be for a time-step twice as big. We hence check
+   * for 2.5 delta_t but this time against 0 energy not the minimum */
+  if (u_old + total_du_dt * 2.5 * dt_therm < 0.) {
+    total_du_dt = -u_old / ((2.5f + 0.0001f) * dt_therm);
   }
 
   /* Update the internal energy time derivative */
-  hydro_set_internal_energy_dt(p, hydro_du_dt + cooling_du_dt);
+  hydro_set_physical_internal_energy_dt(p, cosmo, total_du_dt);
 
-  /* Store the radiated energy */
-  xp->cooling_data.radiated_energy += -hydro_get_mass(p) * cooling_du_dt * dt;
+  /* Store the radiated energy (assuming dt will not change) */
+  xp->cooling_data.radiated_energy +=
+      -hydro_get_mass(p) * (total_du_dt - hydro_du_dt) * dt_therm;
 }
 
 /**
- * @brief Computes the time-step due to cooling
+ * @brief Computes the time-step due to cooling for this particle.
+ *
+ * We compute a time-step \f$ \alpha \frac{u}{du/dt} \f$ in physical
+ * coordinates. \f$\alpha\f$ is a parameter of the cooling function.
  *
  * @param cooling The #cooling_function_data used in the run.
  * @param phys_const The physical constants in internal units.
  * @param cosmo The current cosmological model.
+ * @param hydro_props The properties of the hydro scheme.
  * @param us The internal system of units.
  * @param p Pointer to the particle data.
+ * @param xp Pointer to the extended data of the particle.
  */
 __attribute__((always_inline)) INLINE static float cooling_timestep(
     const struct cooling_function_data* restrict cooling,
     const struct phys_const* restrict phys_const,
     const struct cosmology* restrict cosmo,
-    const struct unit_system* restrict us, const struct part* restrict p) {
+    const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props, const struct part* restrict p,
+    const struct xpart* restrict xp) {
 
-  /* Get current internal energy */
-  const float u = hydro_get_physical_internal_energy(p, cosmo);
-  const float du_dt = cooling_rate(phys_const, us, cosmo, cooling, p);
+  /* Start with the case where there is no limit */
+  if (cooling->cooling_tstep_mult == FLT_MAX) return FLT_MAX;
 
-  /* If we are close to (or below) the energy floor, we ignore the condition */
-  if (u < 1.01f * cooling->min_energy)
+  /* Get current internal energy and cooling rate */
+  const float u = hydro_get_physical_internal_energy(p, xp, cosmo);
+  const double cooling_du_dt_cgs =
+      cooling_rate_cgs(cosmo, hydro_props, cooling, p);
+
+  /* Convert to internal units */
+  const float cooling_du_dt =
+      cooling_du_dt_cgs * cooling->conv_factor_energy_rate_from_cgs;
+
+  /* If we are close to (or below) the limit, we ignore the condition */
+  if (u < 1.01f * hydro_props->minimal_internal_energy || cooling_du_dt == 0.f)
     return FLT_MAX;
   else
-    return cooling->cooling_tstep_mult * u / fabsf(du_dt);
+    return cooling->cooling_tstep_mult * u / fabsf(cooling_du_dt);
 }
 
 /**
  * @brief Sets the cooling properties of the (x-)particles to a valid start
  * state.
  *
+ * Nothing to do here. Just set the radiated energy counter to 0.
+ *
+ * @param phys_const The physical constants in internal units.
+ * @param cooling The properties of the cooling function.
+ * @param us The internal system of units.
+ * @param cosmo The current cosmological model.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
- * @param cooling The properties of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
     const struct phys_const* restrict phys_const,
@@ -176,30 +240,23 @@ static INLINE void cooling_init_backend(struct swift_params* parameter_file,
                                         const struct phys_const* phys_const,
                                         struct cooling_function_data* cooling) {
 
-  const double lambda_cgs =
-      parser_get_param_double(parameter_file, "LambdaCooling:lambda_cgs");
-  const float min_temperature = parser_get_param_double(
-      parameter_file, "LambdaCooling:minimum_temperature");
-  cooling->hydrogen_mass_abundance = parser_get_param_double(
-      parameter_file, "LambdaCooling:hydrogen_mass_abundance");
-  cooling->mean_molecular_weight = parser_get_param_double(
-      parameter_file, "LambdaCooling:mean_molecular_weight");
-  cooling->cooling_tstep_mult = parser_get_param_double(
-      parameter_file, "LambdaCooling:cooling_tstep_mult");
-
-  /* convert minimum temperature into minimum internal energy */
-  const float u_floor =
-      phys_const->const_boltzmann_k * min_temperature /
-      (hydro_gamma_minus_one * cooling->mean_molecular_weight *
-       phys_const->const_proton_mass);
-
-  cooling->min_energy = u_floor;
-
-  /* convert lambda to code units */
-  cooling->lambda = lambda_cgs *
-                    units_cgs_conversion_factor(us, UNIT_CONV_TIME) /
-                    (units_cgs_conversion_factor(us, UNIT_CONV_ENERGY) *
-                     units_cgs_conversion_factor(us, UNIT_CONV_VOLUME));
+  /* Read in the cooling parameters */
+  cooling->lambda_nH2_cgs =
+      parser_get_param_double(parameter_file, "LambdaCooling:lambda_nH2_cgs");
+  cooling->cooling_tstep_mult = parser_get_opt_param_float(
+      parameter_file, "LambdaCooling:cooling_tstep_mult", FLT_MAX);
+
+  /* Some useful conversion values */
+  cooling->conv_factor_density_to_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_DENSITY);
+  cooling->conv_factor_energy_rate_from_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_TIME) /
+      units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS);
+
+  /* Useful constants */
+  cooling->proton_mass_cgs_inv =
+      1. / (phys_const->const_proton_mass *
+            units_cgs_conversion_factor(us, UNIT_CONV_MASS));
 }
 
 /**
@@ -211,11 +268,16 @@ static INLINE void cooling_print_backend(
     const struct cooling_function_data* cooling) {
 
   message(
-      "Cooling function is 'Constant lambda' with "
-      "(lambda,min_energy,hydrogen_mass_abundance,mean_molecular_weight) "
-      "=  (%g,%g,%g,%g)",
-      cooling->lambda, cooling->min_energy, cooling->hydrogen_mass_abundance,
-      cooling->mean_molecular_weight);
+      "Cooling function is 'Constant lambda' with Lambda/n_H^2=%g [erg * s^-1 "
+      "* "
+      "cm^3]",
+      cooling->lambda_nH2_cgs);
+
+  if (cooling->cooling_tstep_mult == FLT_MAX)
+    message("Cooling function time-step size is unlimited");
+  else
+    message("Cooling function time-step size limited to %f of u/(du/dt)",
+            cooling->cooling_tstep_mult);
 }
 
 #endif /* SWIFT_COOLING_CONST_LAMBDA_H */
diff --git a/src/cooling/const_lambda/cooling_io.h b/src/cooling/const_lambda/cooling_io.h
index 89c9471a291a4a6a5740a8c6c816913cbc6316a0..0dca5011ebe5bc6c2a4866387e9cf1ac0ba3447a 100644
--- a/src/cooling/const_lambda/cooling_io.h
+++ b/src/cooling/const_lambda/cooling_io.h
@@ -1,8 +1,6 @@
 /*******************************************************************************
  * This file is part of SWIFT.
- * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk)
- *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- *                    Richard Bower (r.g.bower@durham.ac.uk)
+ * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
  *                    Stefan Arridge  (stefan.arridge@durham.ac.uk)
  *
  * This program is free software: you can redistribute it and/or modify
@@ -22,6 +20,14 @@
 #ifndef SWIFT_COOLING_CONST_LAMBDA_IO_H
 #define SWIFT_COOLING_CONST_LAMBDA_IO_H
 
+/**
+ * @file src/cooling/const_lambda/cooling_io.h
+ * @brief i/o routines related to the "constant lambda" cooling function.
+ *
+ * This model assumes a constant cooling rate Lambda irrespective of redshift
+ * or density.
+ */
+
 /* Config parameters. */
 #include "../config.h"
 
@@ -31,20 +37,24 @@
 #ifdef HAVE_HDF5
 
 /**
- * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
+ * @brief Writes the current model of cooling to the file
+ * @param h_grp The HDF5 group in which to write
+ * @param cooling the parameters of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
+    hid_t h_grp, const struct cooling_function_data* cooling) {
 
-  io_write_attribute_s(h_grpsph, "Cooling Model", "Constant Lambda");
+  io_write_attribute_s(h_grp, "Cooling Model", "Constant Lambda");
+  io_write_attribute_d(h_grp, "Lambda/n_H^2 [cgs]", cooling->lambda_nH2_cgs);
 }
 #endif
 
 /**
  * @brief Specifies which particle fields to write to a dataset
  *
- * @param parts The particle array.
+ * Nothing to write for this scheme.
+ *
+ * @param xparts The extended particle array.
  * @param list The list of i/o properties to write.
  * @param cooling The #cooling_function_data
  *
@@ -53,6 +63,7 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
 __attribute__((always_inline)) INLINE static int cooling_write_particles(
     const struct xpart* xparts, struct io_props* list,
     const struct cooling_function_data* cooling) {
+
   return 0;
 }
 
diff --git a/src/cooling/const_lambda/cooling_struct.h b/src/cooling/const_lambda/cooling_struct.h
index 30d4e5e4af9c7bd139337709897d8111f88d2aa8..cc671a857887af90bda630e757af1b044b479e49 100644
--- a/src/cooling/const_lambda/cooling_struct.h
+++ b/src/cooling/const_lambda/cooling_struct.h
@@ -1,8 +1,6 @@
 /*******************************************************************************
  * This file is part of SWIFT.
- * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk)
- *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- *                    Richard Bower (r.g.bower@durham.ac.uk)
+ * Copyright (c) 2018 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
  *                    Stefan Arridge  (stefan.arridge@durham.ac.uk)
  *
  * This program is free software: you can redistribute it and/or modify
@@ -19,26 +17,34 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-
 #ifndef SWIFT_COOLING_STRUCT_CONST_LAMBDA_H
 #define SWIFT_COOLING_STRUCT_CONST_LAMBDA_H
 
+/**
+ * @file src/cooling/const_lambda/cooling_struct.h
+ * @brief Structures related to the "constant lambda" cooling function.
+ *
+ * This model assumes a constant cooling rate Lambda irrespective of redshift
+ * or density.
+ */
+
 /**
  * @brief Properties of the cooling function.
  */
 struct cooling_function_data {
 
-  /*! Cooling rate in internal units */
-  double lambda;
+  /*! Cooling rate / nH^2 in physical cgs units [erg * s^-1 * cm^3] */
+  double lambda_nH2_cgs;
 
-  /*! Fraction of gas mass that is Hydrogen. Used to calculate n_H */
-  float hydrogen_mass_abundance;
+  /*! Conversion factor from internal units to cgs for density */
+  double conv_factor_density_to_cgs;
 
-  /*! 'mu', used to convert min_temperature to min_internal energy */
-  float mean_molecular_weight;
+  /*! Conversion factor from internal units from cgs for internal energy
+   * derivative */
+  double conv_factor_energy_rate_from_cgs;
 
-  /*! Minimally allowed internal energy of all the particles */
-  float min_energy;
+  /*! Inverse of the proton mass in cgs units [g^-1] */
+  double proton_mass_cgs_inv;
 
   /*! Constant multiplication factor for time-step criterion */
   float cooling_tstep_mult;
diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h
index cb77b63294aacee425b917c1900eefd7ebfa5f34..479b1c8288cc12b12743c50684cb8b2af4d3a411 100644
--- a/src/cooling/grackle/cooling.h
+++ b/src/cooling/grackle/cooling.h
@@ -36,6 +36,7 @@
 
 /* Local includes. */
 #include "chemistry.h"
+#include "cooling_io.h"
 #include "error.h"
 #include "hydro.h"
 #include "parser.h"
@@ -491,6 +492,12 @@ __attribute__((always_inline)) INLINE static gr_float cooling_rate(
     const struct cooling_function_data* restrict cooling,
     const struct part* restrict p, struct xpart* restrict xp, double dt) {
 
+  if (cosmo->Omega_m != 0. || cosmo->Omega_r != 0. || cosmo->Omega_k != 0. ||
+      cosmo->Omega_lambda != 0. || cosmo->Omega_b != 0.)
+    error(
+        "Check cosmology factors (physical vs. co-moving and drifted vs. "
+        "un-drifted)!");
+
   /* set current time */
   code_units units = cooling->units;
   if (cooling->redshift == -1)
@@ -515,7 +522,8 @@ __attribute__((always_inline)) INLINE static gr_float cooling_rate(
 
   /* general particle data */
   gr_float density = hydro_get_physical_density(p, cosmo);
-  const double energy_before = hydro_get_physical_internal_energy(p, cosmo);
+  const double energy_before =
+      hydro_get_drifted_physical_internal_energy(p, cosmo);
   gr_float energy = energy_before;
 
   /* initialize density */
@@ -534,22 +542,11 @@ __attribute__((always_inline)) INLINE static gr_float cooling_rate(
 
   /* solve chemistry */
   chemistry_data chemistry_grackle = cooling->chemistry;
-  chemistry_data_storage my_rates = grackle_rates;
-  int error_code = _solve_chemistry(
-      &chemistry_grackle, &my_rates, &units, dt, data.grid_dx, data.grid_rank,
-      data.grid_dimension, data.grid_start, data.grid_end, data.density,
-      data.internal_energy, data.x_velocity, data.y_velocity, data.z_velocity,
-      data.HI_density, data.HII_density, data.HM_density, data.HeI_density,
-      data.HeII_density, data.HeIII_density, data.H2I_density,
-      data.H2II_density, data.DI_density, data.DII_density, data.HDI_density,
-      data.e_density, data.metal_density, data.volumetric_heating_rate,
-      data.specific_heating_rate, data.RT_heating_rate,
-      data.RT_HI_ionization_rate, data.RT_HeI_ionization_rate,
-      data.RT_HeII_ionization_rate, data.RT_H2_dissociation_rate, NULL);
-  if (error_code == 0) error("Error in solve_chemistry.");
-  // if (solve_chemistry(&units, &data, dt) == 0) {
-  //  error("Error in solve_chemistry.");
-  //}
+  chemistry_data_storage chemistry_rates = grackle_rates;
+  if (local_solve_chemistry(&chemistry_grackle, &chemistry_rates, &units, &data,
+                            dt) == 0) {
+    error("Error in solve_chemistry.");
+  }
 
   /* copy from grackle data to particle */
   cooling_copy_from_grackle(data, p, xp, density);
@@ -596,7 +593,8 @@ __attribute__((always_inline)) INLINE static gr_float cooling_time(
   data.grid_end = grid_end;
 
   /* general particle data */
-  const gr_float energy_before = hydro_get_physical_internal_energy(p, cosmo);
+  const gr_float energy_before =
+      hydro_get_drifted_physical_internal_energy(p, cosmo);
   gr_float density = hydro_get_physical_density(p, cosmo);
   gr_float energy = energy_before;
 
@@ -616,7 +614,10 @@ __attribute__((always_inline)) INLINE static gr_float cooling_time(
 
   /* Compute cooling time */
   gr_float cooling_time;
-  if (calculate_cooling_time(&units, &data, &cooling_time) == 0) {
+  chemistry_data chemistry_grackle = cooling->chemistry;
+  chemistry_data_storage chemistry_rates = grackle_rates;
+  if (local_calculate_cooling_time(&chemistry_grackle, &chemistry_rates, &units,
+                                   &data, &cooling_time) == 0) {
     error("Error in calculate_cooling_time.");
   }
 
@@ -641,13 +642,21 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
+    const struct hydro_props* hydro_props,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, double dt) {
+    struct part* restrict p, struct xpart* restrict xp, double dt,
+    double dt_therm) {
+
+  if (cosmo->Omega_m != 0. || cosmo->Omega_r != 0. || cosmo->Omega_k != 0. ||
+      cosmo->Omega_lambda != 0. || cosmo->Omega_b != 0.)
+    error(
+        "Check cosmology factors (physical vs. co-moving and drifted vs. "
+        "un-drifted)!");
 
   if (dt == 0.) return;
 
   /* Current du_dt */
-  const float hydro_du_dt = hydro_get_internal_energy_dt(p);
+  const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo);
 
   /* compute cooling rate */
   const float du_dt = cooling_rate(phys_const, us, cosmo, cooling, p, xp, dt);
@@ -656,7 +665,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
   xp->cooling_data.radiated_energy += -du_dt * dt * hydro_get_mass(p);
 
   /* Update the internal energy */
-  hydro_set_internal_energy_dt(p, hydro_du_dt + du_dt);
+  hydro_set_physical_internal_energy_dt(p, cosmo, hydro_du_dt + du_dt);
 }
 
 /**
@@ -674,7 +683,9 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
     const struct cooling_function_data* restrict cooling,
     const struct phys_const* restrict phys_const,
     const struct cosmology* restrict cosmo,
-    const struct unit_system* restrict us, const struct part* restrict p) {
+    const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props, const struct part* restrict p,
+    const struct xpart* restrict xp) {
 
   return FLT_MAX;
 }
diff --git a/src/cooling/grackle/cooling_io.h b/src/cooling/grackle/cooling_io.h
index faf84cf97d8449d54f2727ec26b16a9d81d117c6..684ab347e19c8ea5f1897a54f21951256ef5f50b 100644
--- a/src/cooling/grackle/cooling_io.h
+++ b/src/cooling/grackle/cooling_io.h
@@ -29,20 +29,20 @@
 #ifdef HAVE_HDF5
 
 /**
- * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
+ * @brief Writes the current model of cooling  to the file
+ * @param h_grp The HDF5 group in which to write
  */
 __attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
+    hid_t h_grp, const struct cooling_function_data* cooling) {
 
 #if COOLING_GRACKLE_MODE == 0
-  io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle");
+  io_write_attribute_s(h_grp, "Cooling Model", "Grackle");
 #elif COOLING_GRACKLE_MODE == 1
-  io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle1");
+  io_write_attribute_s(h_grp, "Cooling Model", "Grackle1");
 #elif COOLING_GRACKLE_MODE == 2
-  io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle2");
+  io_write_attribute_s(h_grp, "Cooling Model", "Grackle2");
 #elif COOLING_GRACKLE_MODE == 3
-  io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle3");
+  io_write_attribute_s(h_grp, "Cooling Model", "Grackle3");
 #else
   error("This function should be called only with one of the Grackle cooling.");
 #endif
diff --git a/src/cooling/grackle/cooling_struct.h b/src/cooling/grackle/cooling_struct.h
index b714690ce4688268723748b29506e458cccc4be9..6d4b37a6240446d580818e16ff887c8079e319a6 100644
--- a/src/cooling/grackle/cooling_struct.h
+++ b/src/cooling/grackle/cooling_struct.h
@@ -16,14 +16,14 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_COOLING_STRUCT_NONE_H
-#define SWIFT_COOLING_STRUCT_NONE_H
+#ifndef SWIFT_COOLING_STRUCT_GRACKLE_H
+#define SWIFT_COOLING_STRUCT_GRACKLE_H
+
+#include "../config.h"
 
 /* include grackle */
 #include <grackle.h>
 
-#include "../config.h"
-
 /**
  * @file src/cooling/none/cooling_struct.h
  * @brief Empty infrastructure for the cases without cooling function
@@ -113,4 +113,4 @@ struct cooling_xpart_data {
   float metal_frac;
 };
 
-#endif /* SWIFT_COOLING_STRUCT_NONE_H */
+#endif /* SWIFT_COOLING_STRUCT_GRACKLE_H */
diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h
index 0cc465adcdad8fe19afe4a9867e5d68a22ed9119..c21af9c3f1b491256e9f2a65f2f924fa606f665d 100644
--- a/src/cooling/none/cooling.h
+++ b/src/cooling/none/cooling.h
@@ -44,17 +44,21 @@
  * @param phys_const The physical constants in internal units.
  * @param us The internal system of units.
  * @param cosmo The current cosmological model.
+ * @param hydro_props The properties of the hydro scheme.
  * @param cooling The #cooling_function_data used in the run.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
  * @param dt The time-step of this particle.
+ * @param dt_therm The time-step operator used for thermal quantities.
  */
 __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
+    const struct hydro_props* hydro_props,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, float dt) {}
+    struct part* restrict p, struct xpart* restrict xp, const float dt,
+    const float dt_therm) {}
 
 /**
  * @brief Computes the cooling time-step.
@@ -64,14 +68,18 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
  * @param cooling The #cooling_function_data used in the run.
  * @param phys_const The physical constants in internal units.
  * @param cosmo The current cosmological model.
+ * @param hydro_props The properties of the hydro scheme.
  * @param us The internal system of units.
  * @param p Pointer to the particle data.
+ * @param xp Pointer to the extended data of the particle.
  */
 __attribute__((always_inline)) INLINE static float cooling_timestep(
     const struct cooling_function_data* restrict cooling,
     const struct phys_const* restrict phys_const,
     const struct cosmology* restrict cosmo,
-    const struct unit_system* restrict us, const struct part* restrict p) {
+    const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props, const struct part* restrict p,
+    const struct xpart* restrict xp) {
 
   return FLT_MAX;
 }
diff --git a/src/cooling/none/cooling_io.h b/src/cooling/none/cooling_io.h
index e4c84f506bcd31ff95ededb5be889fbf9a27261b..518c166480a0b81f6856c8a39e2a64d34369dc84 100644
--- a/src/cooling/none/cooling_io.h
+++ b/src/cooling/none/cooling_io.h
@@ -29,12 +29,13 @@
 
 /**
  * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
+ * @param h_grp The HDF5 group in which to write
+ * @param cooling the parameters of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
+    hid_t h_grp, const struct cooling_function_data* cooling) {
 
-  io_write_attribute_s(h_grpsph, "Cooling Model", "None");
+  io_write_attribute_s(h_grp, "Cooling Model", "None");
 }
 #endif
 
diff --git a/src/cooling_io.h b/src/cooling_io.h
index 88eeae2cabdaa8a0909977b84a7dbcf03145d988..1ced353d7ff8320a48731545300274c654a20744 100644
--- a/src/cooling_io.h
+++ b/src/cooling_io.h
@@ -29,6 +29,8 @@
 #include "./cooling/const_du/cooling_io.h"
 #elif defined(COOLING_CONST_LAMBDA)
 #include "./cooling/const_lambda/cooling_io.h"
+#elif defined(COOLING_COMPTON)
+#include "./cooling/Compton/cooling_io.h"
 #elif defined(COOLING_GRACKLE)
 #include "./cooling/grackle/cooling_io.h"
 #elif defined(COOLING_EAGLE)
diff --git a/src/cooling_struct.h b/src/cooling_struct.h
index 9c187d596e714fddaf60ae61323624569196ba70..93de8d1b7a0bcfd56d2b1a503aea1e8339bc8016 100644
--- a/src/cooling_struct.h
+++ b/src/cooling_struct.h
@@ -34,6 +34,8 @@
 #include "./cooling/const_du/cooling_struct.h"
 #elif defined(COOLING_CONST_LAMBDA)
 #include "./cooling/const_lambda/cooling_struct.h"
+#elif defined(COOLING_COMPTON)
+#include "./cooling/Compton/cooling_struct.h"
 #elif defined(COOLING_GRACKLE)
 #include "./cooling/grackle/cooling_struct.h"
 #elif defined(COOLING_EAGLE)
diff --git a/src/cosmology.c b/src/cosmology.c
index f209997dd3faae3ba884632ffa41cca5d6ae0710..4718ed5b316e514476e3ec38dd8771136f3a2f69 100644
--- a/src/cosmology.c
+++ b/src/cosmology.c
@@ -157,6 +157,8 @@ void cosmology_update(struct cosmology *c, const struct phys_const *phys_const,
       pow(a, -3. * hydro_gamma + 2.); /* 1 / a^(3*gamma - 2) */
   c->a_factor_mu =
       pow(a, 0.5 * (3. * hydro_gamma - 5.)); /* a^{(3*gamma - 5) / 2} */
+  c->a_factor_Balsara_eps =
+      pow(a, 0.5 * (1. - 3. * hydro_gamma)); /* a^{(1 - 3*gamma) / 2} */
 
   /* Redshift */
   c->z = a_inv - 1.;
@@ -487,6 +489,11 @@ void cosmology_init(struct swift_params *params, const struct unit_system *us,
   c->time_base = (c->log_a_end - c->log_a_begin) / max_nr_timesteps;
   c->time_base_inv = 1. / c->time_base;
 
+  /* If a_begin == a_end we hang */
+
+  if (c->a_begin >= c->a_end)
+    error("a_begin must be strictly before (and not equal to) a_end");
+
   /* Construct derived quantities */
 
   /* Curvature density (for closure) */
@@ -502,6 +509,10 @@ void cosmology_init(struct swift_params *params, const struct unit_system *us,
   c->H0 = H0_cgs * units_cgs_conversion_factor(us, UNIT_CONV_TIME);
   c->Hubble_time = 1. / c->H0;
 
+  /* Critical density at present day */
+  c->critical_density_0 =
+      3. * c->H0 * c->H0 / (8. * M_PI * phys_const->const_newton_G);
+
   /* Initialise the interpolation tables */
   c->drift_fac_interp_table = NULL;
   c->grav_kick_fac_interp_table = NULL;
@@ -543,6 +554,7 @@ void cosmology_init_no_cosmo(struct cosmology *c) {
   c->log_a_end = 0.;
 
   c->H = 0.;
+  c->H0 = 0.;
   c->a = 1.;
   c->z = 0.;
   c->a_inv = 1.;
@@ -552,10 +564,12 @@ void cosmology_init_no_cosmo(struct cosmology *c) {
   c->a_factor_pressure = 1.;
   c->a_factor_sound_speed = 1.;
   c->a_factor_mu = 1.;
+  c->a_factor_Balsara_eps = 1.;
   c->a_factor_hydro_accel = 1.;
   c->a_factor_grav_accel = 1.;
 
   c->critical_density = 0.;
+  c->critical_density_0 = 0.;
 
   c->time_step_factor = 1.;
 
diff --git a/src/cosmology.h b/src/cosmology.h
index 4556b039bd0e306dab37a05bc200c3aa2ab8a602..d6b7dfbdc854a66f89c5511a5076c4fb4a7a5d3f 100644
--- a/src/cosmology.h
+++ b/src/cosmology.h
@@ -54,10 +54,12 @@ struct cosmology {
   /*! Power of the scale-factor used for sound-speed conversion to physical */
   double a_factor_sound_speed;
 
-  /*! Power of the scale-factor used for relative velocities in viscosity term
-   */
+  /*! Power of the scale-factor used for relative velocities in visc. terms */
   double a_factor_mu;
 
+  /*! {ower of the scale-factor used for epsilon term in the Balsara switch */
+  double a_factor_Balsara_eps;
+
   /*! Power of the scale-factor used for gravity accelerations */
   double a_factor_grav_accel;
 
@@ -73,6 +75,9 @@ struct cosmology {
   /*! The critical density at the current redshift (in internal units) */
   double critical_density;
 
+  /*! The critical density at redshift 0 (in internal units) */
+  double critical_density_0;
+
   /*! Conversion factor from internal time-step size to cosmological step */
   double time_step_factor;
 
diff --git a/src/debug.c b/src/debug.c
index da8748df900869256bfbf4b1814e01bf5d60d804..a980ef9457a31d4b36f99926a24d7bc55822670a 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -172,8 +172,8 @@ int checkSpacehmax(struct space *s) {
   float cell_h_max = 0.0f;
   for (int k = 0; k < s->nr_cells; k++) {
     if (s->cells_top[k].nodeID == s->e->nodeID &&
-        s->cells_top[k].h_max > cell_h_max) {
-      cell_h_max = s->cells_top[k].h_max;
+        s->cells_top[k].hydro.h_max > cell_h_max) {
+      cell_h_max = s->cells_top[k].hydro.h_max;
     }
   }
 
@@ -191,9 +191,9 @@ int checkSpacehmax(struct space *s) {
   /* There is a problem. Hunt it down. */
   for (int k = 0; k < s->nr_cells; k++) {
     if (s->cells_top[k].nodeID == s->e->nodeID) {
-      if (s->cells_top[k].h_max > part_h_max) {
-        message("cell %d is inconsistent (%f > %f)", k, s->cells_top[k].h_max,
-                part_h_max);
+      if (s->cells_top[k].hydro.h_max > part_h_max) {
+        message("cell %d is inconsistent (%f > %f)", k,
+                s->cells_top[k].hydro.h_max, part_h_max);
       }
     }
   }
@@ -229,9 +229,9 @@ int checkCellhdxmax(const struct cell *c, int *depth) {
   const double loc_max[3] = {c->loc[0] + c->width[0], c->loc[1] + c->width[1],
                              c->loc[2] + c->width[2]};
 
-  const size_t nr_parts = c->count;
-  struct part *parts = c->parts;
-  struct xpart *xparts = c->xparts;
+  const size_t nr_parts = c->hydro.count;
+  struct part *parts = c->hydro.parts;
+  struct xpart *xparts = c->hydro.xparts;
   for (size_t k = 0; k < nr_parts; k++) {
 
     struct part *const p = &parts[k];
@@ -268,14 +268,15 @@ int checkCellhdxmax(const struct cell *c, int *depth) {
   }
 
   /* Check. */
-  if (c->h_max != h_max) {
-    message("%d Inconsistent h_max: cell %f != parts %f", *depth, c->h_max,
-            h_max);
+  if (c->hydro.h_max != h_max) {
+    message("%d Inconsistent h_max: cell %f != parts %f", *depth,
+            c->hydro.h_max, h_max);
     message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]);
     result = 0;
   }
-  if (c->dx_max_part != dx_max) {
-    message("%d Inconsistent dx_max: %f != %f", *depth, c->dx_max_part, dx_max);
+  if (c->hydro.dx_max_part != dx_max) {
+    message("%d Inconsistent dx_max: %f != %f", *depth, c->hydro.dx_max_part,
+            dx_max);
     message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]);
     result = 0;
   }
@@ -316,13 +317,13 @@ static void dumpCells_map(struct cell *c, void *data) {
 #endif
 
   /* Only cells with particles are dumped. */
-  if (c->count > 0 || c->gcount > 0 || c->scount > 0) {
+  if (c->hydro.count > 0 || c->grav.count > 0 || c->stars.count > 0) {
 
     /* In MPI mode we may only output cells with foreign partners.
      * These define the edges of the partitions. */
     int ismpiactive = 0;
 #if WITH_MPI
-    ismpiactive = (c->send_xv != NULL);
+    ismpiactive = (c->mpi.hydro.send_xv != NULL);
     if (mpiactive)
       mpiactive = ismpiactive;
     else
@@ -347,14 +348,14 @@ static void dumpCells_map(struct cell *c, void *data) {
       /* If requested we work out how many particles are active in this cell. */
       int pactcount = 0;
       if (pactive) {
-        const struct part *parts = c->parts;
-        for (int k = 0; k < c->count; k++)
+        const struct part *parts = c->hydro.parts;
+        for (int k = 0; k < c->hydro.count; k++)
           if (part_is_active(&parts[k], e)) pactcount++;
-        struct gpart *gparts = c->gparts;
-        for (int k = 0; k < c->gcount; k++)
+        struct gpart *gparts = c->grav.parts;
+        for (int k = 0; k < c->grav.count; k++)
           if (gpart_is_active(&gparts[k], e)) pactcount++;
-        struct spart *sparts = c->sparts;
-        for (int k = 0; k < c->scount; k++)
+        struct spart *sparts = c->stars.parts;
+        for (int k = 0; k < c->stars.count; k++)
           if (spart_is_active(&sparts[k], e)) pactcount++;
       }
 
@@ -362,9 +363,9 @@ static void dumpCells_map(struct cell *c, void *data) {
               "  %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6d %6d %6d %6d %6d %6d "
               "%6.1f %20lld %6d %6d %6d %6d %6d %6d %6d\n",
               c->loc[0], c->loc[1], c->loc[2], c->width[0], c->width[1],
-              c->width[2], e->step, c->count, c->gcount, c->scount, pactcount,
-              c->depth, ntasks, c->ti_hydro_end_min,
-              get_time_bin(c->ti_hydro_end_min), (c->super == c),
+              c->width[2], e->step, c->hydro.count, c->grav.count,
+              c->stars.count, pactcount, c->depth, ntasks, c->hydro.ti_end_min,
+              get_time_bin(c->hydro.ti_end_min), (c->super == c),
               (c->parent == NULL), cell_is_active_hydro(c, e), c->nodeID,
               c->nodeID == e->nodeID, ismpiactive);
     }
diff --git a/src/drift.h b/src/drift.h
index ff0fea744012b7143afed2a05b286d4646cdd69a..351b15381dde9a95af908003b1c8df01b56610fa 100644
--- a/src/drift.h
+++ b/src/drift.h
@@ -137,6 +137,12 @@ __attribute__((always_inline)) INLINE static void drift_spart(
   sp->x[0] += sp->v[0] * dt_drift;
   sp->x[1] += sp->v[1] * dt_drift;
   sp->x[2] += sp->v[2] * dt_drift;
+
+  /* Compute offsets since last cell construction */
+  for (int k = 0; k < 3; k++) {
+    const float dx = sp->v[k] * dt_drift;
+    sp->x_diff[k] -= dx;
+  }
 }
 
 #endif /* SWIFT_DRIFT_H */
diff --git a/src/dump.c b/src/dump.c
index ba50b517a72e71ab0ca5e791319c6336925762cb..9c519c2130b2612309e623b8234e3369214b52e2 100644
--- a/src/dump.c
+++ b/src/dump.c
@@ -48,17 +48,24 @@
  */
 void *dump_get(struct dump *d, size_t count, size_t *offset) {
   size_t local_offset = atomic_add(&d->count, count);
+#ifdef SWIFT_DEBUG_CHECKS
+  if (d->count > d->size) error("Dump file is too small.");
+#endif
   *offset = local_offset + d->file_offset;
   return (char *)d->data + local_offset;
 }
 
 /**
  * @brief Ensure that at least size bytes are available in the #dump.
+ *
+ * @param d The #dump.
+ * @param required_size The required size for the #dump
+ * @param increase_size If not enough size, increase by this amount
  */
-void dump_ensure(struct dump *d, size_t size) {
+void dump_ensure(struct dump *d, size_t required_size, size_t increase_size) {
 
   /* If we have enough space already, just bail. */
-  if (d->size - d->count > size) return;
+  if (d->size - d->count > required_size) return;
 
   /* Unmap the current data. */
   if (munmap(d->data, d->size) != 0) {
@@ -70,7 +77,7 @@ void dump_ensure(struct dump *d, size_t size) {
   const size_t trunc_count = d->count & d->page_mask;
   d->file_offset += trunc_count;
   d->count -= trunc_count;
-  d->size = (size * dump_grow_ensure_factor + ~d->page_mask) & d->page_mask;
+  d->size = (d->count + increase_size + ~d->page_mask) & d->page_mask;
 
   /* Re-allocate the file size. */
   if (posix_fallocate(d->fd, d->file_offset, d->size) != 0) {
@@ -121,7 +128,9 @@ void dump_close(struct dump *d) {
  */
 void dump_init(struct dump *d, const char *filename, size_t size) {
 
-  /* Create the output file. */
+  /* Create the output file.
+     The option O_RDWR seems to be required by mmap.
+  */
   if ((d->fd = open(filename, O_CREAT | O_RDWR, 0660)) == -1) {
     error("Failed to create dump file '%s' (%s).", filename, strerror(errno));
   }
diff --git a/src/dump.h b/src/dump.h
index 6857aa3a008a27e0e8ed23854d84f848ee0ca2be..021bc1e1dc22c178a893e42384c91fafdcf63112 100644
--- a/src/dump.h
+++ b/src/dump.h
@@ -27,9 +27,6 @@
 /* Standard headers */
 #include <stdlib.h>
 
-/* Some constants. */
-#define dump_grow_ensure_factor 10
-
 /** The dump struct. */
 struct dump {
 
@@ -54,7 +51,7 @@ struct dump {
 
 /* Function prototypes. */
 void dump_init(struct dump *d, const char *filename, size_t size);
-void dump_ensure(struct dump *d, size_t size);
+void dump_ensure(struct dump *d, size_t required_size, size_t increase_size);
 void dump_sync(struct dump *d);
 void dump_close(struct dump *d);
 void *dump_get(struct dump *d, size_t count, size_t *offset);
diff --git a/src/engine.c b/src/engine.c
index ac47c984c0ef4ef28fdcee3cbc99d22805e2b20c..816a02f193f63158975b545ad6ad0c8cc06c10e6 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -67,6 +67,8 @@
 #include "gravity.h"
 #include "gravity_cache.h"
 #include "hydro.h"
+#include "logger.h"
+#include "logger_io.h"
 #include "map.h"
 #include "memswap.h"
 #include "minmax.h"
@@ -112,6 +114,7 @@ const char *engine_policy_names[] = {"none",
                                      "stars",
                                      "fof search",
                                      "structure finding",
+                                     "star formation",
                                      "feedback"};
 
 /** The rank of the engine as a global variable (for messages). */
@@ -122,7 +125,8 @@ int engine_rank;
  */
 struct end_of_step_data {
 
-  size_t updates, g_updates, s_updates;
+  size_t updated, g_updated, s_updated;
+  size_t inhibited, g_inhibited, s_inhibited;
   integertime_t ti_hydro_end_min, ti_hydro_end_max, ti_hydro_beg_max;
   integertime_t ti_gravity_end_min, ti_gravity_end_max, ti_gravity_beg_max;
   struct engine *e;
@@ -151,335 +155,6 @@ void engine_addlink(struct engine *e, struct link **l, struct task *t) {
   res->next = atomic_swap(l, res);
 }
 
-/**
- * @brief Recursively add non-implicit star ghost tasks to a cell hierarchy.
- */
-void engine_add_stars_ghosts(struct engine *e, struct cell *c,
-                             struct task *stars_ghost_in,
-                             struct task *stars_ghost_out) {
-
-  /* If we have reached the leaf OR have to few particles to play with*/
-  if (!c->split || c->scount < engine_max_sparts_per_ghost) {
-
-    /* Add the ghost task and its dependencies */
-    struct scheduler *s = &e->sched;
-    c->stars_ghost = scheduler_addtask(s, task_type_stars_ghost,
-                                       task_subtype_none, 0, 0, c, NULL);
-    scheduler_addunlock(s, stars_ghost_in, c->stars_ghost);
-    scheduler_addunlock(s, c->stars_ghost, stars_ghost_out);
-  } else {
-    /* Keep recursing */
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL)
-        engine_add_stars_ghosts(e, c->progeny[k], stars_ghost_in,
-                                stars_ghost_out);
-  }
-}
-
-/**
- * @brief Recursively add non-implicit ghost tasks to a cell hierarchy.
- */
-void engine_add_ghosts(struct engine *e, struct cell *c, struct task *ghost_in,
-                       struct task *ghost_out) {
-
-  /* If we have reached the leaf OR have to few particles to play with*/
-  if (!c->split || c->count < engine_max_parts_per_ghost) {
-
-    /* Add the ghost task and its dependencies */
-    struct scheduler *s = &e->sched;
-    c->ghost =
-        scheduler_addtask(s, task_type_ghost, task_subtype_none, 0, 0, c, NULL);
-    scheduler_addunlock(s, ghost_in, c->ghost);
-    scheduler_addunlock(s, c->ghost, ghost_out);
-  } else {
-    /* Keep recursing */
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL)
-        engine_add_ghosts(e, c->progeny[k], ghost_in, ghost_out);
-  }
-}
-
-/**
- * @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
- * i.e. all the O(Npart) tasks -- timestep version
- *
- * Tasks are only created here. The dependencies will be added later on.
- *
- * Note that there is no need to recurse below the super-cell. Note also
- * that we only add tasks if the relevant particles are present in the cell.
- *
- * @param e The #engine.
- * @param c The #cell.
- */
-void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) {
-
-  struct scheduler *s = &e->sched;
-  const int is_with_cooling = (e->policy & engine_policy_cooling);
-
-  /* Are we in a super-cell ? */
-  if (c->super == c) {
-
-    /* Local tasks only... */
-    if (c->nodeID == e->nodeID) {
-
-      /* Add the two half kicks */
-      c->kick1 = scheduler_addtask(s, task_type_kick1, task_subtype_none, 0, 0,
-                                   c, NULL);
-
-      c->kick2 = scheduler_addtask(s, task_type_kick2, task_subtype_none, 0, 0,
-                                   c, NULL);
-
-      /* Add the time-step calculation task and its dependency */
-      c->timestep = scheduler_addtask(s, task_type_timestep, task_subtype_none,
-                                      0, 0, c, NULL);
-
-      /* Add the task finishing the force calculation */
-      c->end_force = scheduler_addtask(s, task_type_end_force,
-                                       task_subtype_none, 0, 0, c, NULL);
-
-      if (is_with_cooling) {
-
-        c->cooling = scheduler_addtask(s, task_type_cooling, task_subtype_none,
-                                       0, 0, c, NULL);
-
-        scheduler_addunlock(s, c->end_force, c->cooling);
-        scheduler_addunlock(s, c->cooling, c->kick2);
-
-      } else {
-        scheduler_addunlock(s, c->end_force, c->kick2);
-      }
-      scheduler_addunlock(s, c->kick2, c->timestep);
-      scheduler_addunlock(s, c->timestep, c->kick1);
-    }
-
-  } else { /* We are above the super-cell so need to go deeper */
-
-    /* Recurse. */
-    if (c->split)
-      for (int k = 0; k < 8; k++)
-        if (c->progeny[k] != NULL)
-          engine_make_hierarchical_tasks_common(e, c->progeny[k]);
-  }
-}
-
-/**
- * @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
- * i.e. all the O(Npart) tasks -- hydro version
- *
- * Tasks are only created here. The dependencies will be added later on.
- *
- * Note that there is no need to recurse below the super-cell. Note also
- * that we only add tasks if the relevant particles are present in the cell.
- *
- * @param e The #engine.
- * @param c The #cell.
- */
-void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) {
-
-  struct scheduler *s = &e->sched;
-  const int is_with_sourceterms = (e->policy & engine_policy_sourceterms);
-
-  /* Are we in a super-cell ? */
-  if (c->super_hydro == c) {
-
-    /* Add the sort task. */
-    c->sorts =
-        scheduler_addtask(s, task_type_sort, task_subtype_none, 0, 0, c, NULL);
-
-    /* Local tasks only... */
-    if (c->nodeID == e->nodeID) {
-
-      /* Add the drift task. */
-      c->drift_part = scheduler_addtask(s, task_type_drift_part,
-                                        task_subtype_none, 0, 0, c, NULL);
-
-      /* Generate the ghost tasks. */
-      c->ghost_in =
-          scheduler_addtask(s, task_type_ghost_in, task_subtype_none, 0,
-                            /* implicit = */ 1, c, NULL);
-      c->ghost_out =
-          scheduler_addtask(s, task_type_ghost_out, task_subtype_none, 0,
-                            /* implicit = */ 1, c, NULL);
-      engine_add_ghosts(e, c, c->ghost_in, c->ghost_out);
-
-#ifdef EXTRA_HYDRO_LOOP
-      /* Generate the extra ghost task. */
-      c->extra_ghost = scheduler_addtask(s, task_type_extra_ghost,
-                                         task_subtype_none, 0, 0, c, NULL);
-#endif
-
-      /* add source terms */
-      if (is_with_sourceterms) {
-        c->sourceterms = scheduler_addtask(s, task_type_sourceterms,
-                                           task_subtype_none, 0, 0, c, NULL);
-      }
-    }
-
-  } else { /* We are above the super-cell so need to go deeper */
-
-    /* Recurse. */
-    if (c->split)
-      for (int k = 0; k < 8; k++)
-        if (c->progeny[k] != NULL)
-          engine_make_hierarchical_tasks_hydro(e, c->progeny[k]);
-  }
-}
-
-/**
- * @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
- * i.e. all the O(Npart) tasks -- gravity version
- *
- * Tasks are only created here. The dependencies will be added later on.
- *
- * Note that there is no need to recurse below the super-cell. Note also
- * that we only add tasks if the relevant particles are present in the cell.
- *
- * @param e The #engine.
- * @param c The #cell.
- */
-void engine_make_hierarchical_tasks_gravity(struct engine *e, struct cell *c) {
-
-  struct scheduler *s = &e->sched;
-  const int periodic = e->s->periodic;
-  const int is_self_gravity = (e->policy & engine_policy_self_gravity);
-
-  /* Are we in a super-cell ? */
-  if (c->super_gravity == c) {
-
-    /* Local tasks only... */
-    if (c->nodeID == e->nodeID) {
-
-      c->drift_gpart = scheduler_addtask(s, task_type_drift_gpart,
-                                         task_subtype_none, 0, 0, c, NULL);
-
-      if (is_self_gravity) {
-
-        /* Initialisation of the multipoles */
-        c->init_grav = scheduler_addtask(s, task_type_init_grav,
-                                         task_subtype_none, 0, 0, c, NULL);
-
-        /* Gravity non-neighbouring pm calculations */
-        c->grav_long_range = scheduler_addtask(
-            s, task_type_grav_long_range, task_subtype_none, 0, 0, c, NULL);
-
-        /* Gravity recursive down-pass */
-        c->grav_down = scheduler_addtask(s, task_type_grav_down,
-                                         task_subtype_none, 0, 0, c, NULL);
-
-        /* Implicit tasks for the up and down passes */
-        c->init_grav_out = scheduler_addtask(s, task_type_init_grav_out,
-                                             task_subtype_none, 0, 1, c, NULL);
-        c->grav_down_in = scheduler_addtask(s, task_type_grav_down_in,
-                                            task_subtype_none, 0, 1, c, NULL);
-
-        /* Gravity mesh force propagation */
-        if (periodic)
-          c->grav_mesh = scheduler_addtask(s, task_type_grav_mesh,
-                                           task_subtype_none, 0, 0, c, NULL);
-
-        if (periodic) scheduler_addunlock(s, c->drift_gpart, c->grav_mesh);
-        if (periodic) scheduler_addunlock(s, c->grav_mesh, c->grav_down);
-        scheduler_addunlock(s, c->init_grav, c->grav_long_range);
-        scheduler_addunlock(s, c->grav_long_range, c->grav_down);
-        scheduler_addunlock(s, c->grav_down, c->super->end_force);
-
-        /* Link in the implicit tasks */
-        scheduler_addunlock(s, c->init_grav, c->init_grav_out);
-        scheduler_addunlock(s, c->grav_down_in, c->grav_down);
-      }
-    }
-  }
-
-  /* We are below the super-cell but not below the maximal splitting depth */
-  else if (c->super_gravity != NULL && c->depth < space_subdepth_grav) {
-
-    /* Local tasks only... */
-    if (c->nodeID == e->nodeID) {
-
-      if (is_self_gravity) {
-
-        c->init_grav_out = scheduler_addtask(s, task_type_init_grav_out,
-                                             task_subtype_none, 0, 1, c, NULL);
-
-        c->grav_down_in = scheduler_addtask(s, task_type_grav_down_in,
-                                            task_subtype_none, 0, 1, c, NULL);
-
-        scheduler_addunlock(s, c->parent->init_grav_out, c->init_grav_out);
-        scheduler_addunlock(s, c->grav_down_in, c->parent->grav_down_in);
-      }
-    }
-  }
-
-  /* Recurse but not below the maximal splitting depth */
-  if (c->split && c->depth <= space_subdepth_grav)
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL)
-        engine_make_hierarchical_tasks_gravity(e, c->progeny[k]);
-}
-
-/**
- * @brief Generate the stars hierarchical tasks for a hierarchy of cells -
- * i.e. all the O(Npart) tasks -- star version
- *
- * Tasks are only created here. The dependencies will be added later on.
- *
- * Note that there is no need to recurse below the super-cell. Note also
- * that we only add tasks if the relevant particles are present in the cell.
- *
- * @param e The #engine.
- * @param c The #cell.
- */
-void engine_make_hierarchical_tasks_stars(struct engine *e, struct cell *c) {
-
-  struct scheduler *s = &e->sched;
-
-  /* Are we in a super-cell ? */
-  if (c->super == c) {
-
-    /* Local tasks only... */
-    if (c->nodeID == e->nodeID) {
-
-      /* Generate the ghost tasks. */
-      c->stars_ghost_in =
-          scheduler_addtask(s, task_type_stars_ghost_in, task_subtype_none, 0,
-                            /* implicit = */ 1, c, NULL);
-      c->stars_ghost_out =
-          scheduler_addtask(s, task_type_stars_ghost_out, task_subtype_none, 0,
-                            /* implicit = */ 1, c, NULL);
-      engine_add_stars_ghosts(e, c, c->stars_ghost_in, c->stars_ghost_out);
-    }
-  } else { /* We are above the super-cell so need to go deeper */
-
-    /* Recurse. */
-    if (c->split)
-      for (int k = 0; k < 8; k++)
-        if (c->progeny[k] != NULL)
-          engine_make_hierarchical_tasks_stars(e, c->progeny[k]);
-  }
-}
-
-void engine_make_hierarchical_tasks_mapper(void *map_data, int num_elements,
-                                           void *extra_data) {
-  struct engine *e = (struct engine *)extra_data;
-  const int is_with_hydro = (e->policy & engine_policy_hydro);
-  const int is_with_self_gravity = (e->policy & engine_policy_self_gravity);
-  const int is_with_external_gravity =
-      (e->policy & engine_policy_external_gravity);
-  const int is_with_feedback = (e->policy & engine_policy_feedback);
-
-  for (int ind = 0; ind < num_elements; ind++) {
-    struct cell *c = &((struct cell *)map_data)[ind];
-    /* Make the common tasks (time integration) */
-    engine_make_hierarchical_tasks_common(e, c);
-    /* Add the hydro stuff */
-    if (is_with_hydro) engine_make_hierarchical_tasks_hydro(e, c);
-    /* And the gravity stuff */
-    if (is_with_self_gravity || is_with_external_gravity)
-      engine_make_hierarchical_tasks_gravity(e, c);
-    if (is_with_feedback) engine_make_hierarchical_tasks_stars(e, c);
-  }
-}
-
 #ifdef WITH_MPI
 /**
  * Do the exchange of one type of particles with all the other nodes.
@@ -722,7 +397,7 @@ struct savelink_mapper_data {
       for (int k = 0; k < counts[nodeID * nr_nodes + node]; k++) {             \
         if (parts[k + offset].gpart != NULL) {                                 \
           if (CHECKS)                                                          \
-            if (parts[k].gpart->id_or_neg_offset > 0)                          \
+            if (parts[k + offset].gpart->id_or_neg_offset > 0)                 \
               error("Trying to link a partnerless " #TYPE "!");                \
           parts[k + offset].gpart->id_or_neg_offset = -count;                  \
           count++;                                                             \
@@ -860,11 +535,87 @@ void engine_redistribute(struct engine *e) {
   struct space *s = e->s;
   struct cell *cells = s->cells_top;
   const int nr_cells = s->nr_cells;
+  struct xpart *xparts = s->xparts;
   struct part *parts = s->parts;
   struct gpart *gparts = s->gparts;
   struct spart *sparts = s->sparts;
   ticks tic = getticks();
 
+  size_t nr_parts = s->nr_parts;
+  size_t nr_gparts = s->nr_gparts;
+  size_t nr_sparts = s->nr_sparts;
+
+  /* Start by moving inhibited particles to the end of the arrays */
+  for (size_t k = 0; k < nr_parts; /* void */) {
+    if (parts[k].time_bin == time_bin_inhibited) {
+      nr_parts -= 1;
+
+      /* Swap the particle */
+      memswap(&parts[k], &parts[nr_parts], sizeof(struct part));
+
+      /* Swap the xpart */
+      memswap(&xparts[k], &xparts[nr_parts], sizeof(struct xpart));
+
+      /* Swap the link with the gpart */
+      if (parts[k].gpart != NULL) {
+        parts[k].gpart->id_or_neg_offset = -k;
+      }
+      if (parts[nr_parts].gpart != NULL) {
+        parts[nr_parts].gpart->id_or_neg_offset = -nr_parts;
+      }
+    } else {
+      k++;
+    }
+  }
+
+  /* Now move inhibited star particles to the end of the arrays */
+  for (size_t k = 0; k < nr_sparts; /* void */) {
+    if (sparts[k].time_bin == time_bin_inhibited) {
+      nr_sparts -= 1;
+
+      /* Swap the particle */
+      memswap(&s->sparts[k], &s->sparts[nr_sparts], sizeof(struct spart));
+
+      /* Swap the link with the gpart */
+      if (s->sparts[k].gpart != NULL) {
+        s->sparts[k].gpart->id_or_neg_offset = -k;
+      }
+      if (s->sparts[nr_sparts].gpart != NULL) {
+        s->sparts[nr_sparts].gpart->id_or_neg_offset = -nr_sparts;
+      }
+    } else {
+      k++;
+    }
+  }
+
+  /* Finally do the same with the gravity particles */
+  for (size_t k = 0; k < nr_gparts; /* void */) {
+    if (gparts[k].time_bin == time_bin_inhibited) {
+      nr_gparts -= 1;
+
+      /* Swap the particle */
+      memswap(&s->gparts[k], &s->gparts[nr_gparts], sizeof(struct gpart));
+
+      /* Swap the link with part/spart */
+      if (s->gparts[k].type == swift_type_gas) {
+        s->parts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k];
+      } else if (s->gparts[k].type == swift_type_stars) {
+        s->sparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k];
+      }
+      if (s->gparts[nr_gparts].type == swift_type_gas) {
+        s->parts[-s->gparts[nr_gparts].id_or_neg_offset].gpart =
+            &s->gparts[nr_gparts];
+      } else if (s->gparts[nr_gparts].type == swift_type_stars) {
+        s->sparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart =
+            &s->gparts[nr_gparts];
+      }
+    } else {
+      k++;
+    }
+  }
+
+  /* Now we are ready to deal with real particles and can start the exchange. */
+
   /* Allocate temporary arrays to store the counts of particles to be sent
    * and the destination of each particle */
   int *counts;
@@ -872,7 +623,7 @@ void engine_redistribute(struct engine *e) {
     error("Failed to allocate counts temporary buffer.");
 
   int *dest;
-  if ((dest = (int *)malloc(sizeof(int) * s->nr_parts)) == NULL)
+  if ((dest = (int *)malloc(sizeof(int) * nr_parts)) == NULL)
     error("Failed to allocate dest temporary buffer.");
 
   /* Simple index of node IDs, used for mappers over nodes. */
@@ -892,16 +643,16 @@ void engine_redistribute(struct engine *e) {
   redist_data.base = (void *)parts;
 
   threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_part, parts,
-                 s->nr_parts, sizeof(struct part), 0, &redist_data);
+                 nr_parts, sizeof(struct part), 0, &redist_data);
 
   /* Sort the particles according to their cell index. */
-  if (s->nr_parts > 0)
+  if (nr_parts > 0)
     space_parts_sort(s->parts, s->xparts, dest, &counts[nodeID * nr_nodes],
                      nr_nodes, 0);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Verify that the part have been sorted correctly. */
-  for (size_t k = 0; k < s->nr_parts; k++) {
+  for (size_t k = 0; k < nr_parts; k++) {
     const struct part *p = &s->parts[k];
 
     /* New cell index */
@@ -925,7 +676,7 @@ void engine_redistribute(struct engine *e) {
 
   /* We will need to re-link the gpart partners of parts, so save their
    * relative positions in the sent lists. */
-  if (s->nr_parts > 0 && s->nr_gparts > 0) {
+  if (nr_parts > 0 && nr_gparts > 0) {
 
     struct savelink_mapper_data savelink_data;
     savelink_data.nr_nodes = nr_nodes;
@@ -943,7 +694,7 @@ void engine_redistribute(struct engine *e) {
     error("Failed to allocate s_counts temporary buffer.");
 
   int *s_dest;
-  if ((s_dest = (int *)malloc(sizeof(int) * s->nr_sparts)) == NULL)
+  if ((s_dest = (int *)malloc(sizeof(int) * nr_sparts)) == NULL)
     error("Failed to allocate s_dest temporary buffer.");
 
   redist_data.counts = s_counts;
@@ -951,16 +702,16 @@ void engine_redistribute(struct engine *e) {
   redist_data.base = (void *)sparts;
 
   threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_spart, sparts,
-                 s->nr_sparts, sizeof(struct spart), 0, &redist_data);
+                 nr_sparts, sizeof(struct spart), 0, &redist_data);
 
   /* Sort the particles according to their cell index. */
-  if (s->nr_sparts > 0)
+  if (nr_sparts > 0)
     space_sparts_sort(s->sparts, s_dest, &s_counts[nodeID * nr_nodes], nr_nodes,
                       0);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Verify that the spart have been sorted correctly. */
-  for (size_t k = 0; k < s->nr_sparts; k++) {
+  for (size_t k = 0; k < nr_sparts; k++) {
     const struct spart *sp = &s->sparts[k];
 
     /* New cell index */
@@ -983,7 +734,7 @@ void engine_redistribute(struct engine *e) {
 #endif
 
   /* We need to re-link the gpart partners of sparts. */
-  if (s->nr_sparts > 0) {
+  if (nr_sparts > 0) {
 
     struct savelink_mapper_data savelink_data;
     savelink_data.nr_nodes = nr_nodes;
@@ -1001,7 +752,7 @@ void engine_redistribute(struct engine *e) {
     error("Failed to allocate g_gcount temporary buffer.");
 
   int *g_dest;
-  if ((g_dest = (int *)malloc(sizeof(int) * s->nr_gparts)) == NULL)
+  if ((g_dest = (int *)malloc(sizeof(int) * nr_gparts)) == NULL)
     error("Failed to allocate g_dest temporary buffer.");
 
   redist_data.counts = g_counts;
@@ -1009,16 +760,16 @@ void engine_redistribute(struct engine *e) {
   redist_data.base = (void *)gparts;
 
   threadpool_map(&e->threadpool, engine_redistribute_dest_mapper_gpart, gparts,
-                 s->nr_gparts, sizeof(struct gpart), 0, &redist_data);
+                 nr_gparts, sizeof(struct gpart), 0, &redist_data);
 
   /* Sort the gparticles according to their cell index. */
-  if (s->nr_gparts > 0)
+  if (nr_gparts > 0)
     space_gparts_sort(s->gparts, s->parts, s->sparts, g_dest,
                       &g_counts[nodeID * nr_nodes], nr_nodes);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Verify that the gpart have been sorted correctly. */
-  for (size_t k = 0; k < s->nr_gparts; k++) {
+  for (size_t k = 0; k < nr_gparts; k++) {
     const struct gpart *gp = &s->gparts[k];
 
     /* New cell index */
@@ -1093,49 +844,50 @@ void engine_redistribute(struct engine *e) {
   /* Now each node knows how many parts, sparts and gparts will be transferred
    * to every other node.
    * Get the new numbers of particles for this node. */
-  size_t nr_parts = 0, nr_gparts = 0, nr_sparts = 0;
-  for (int k = 0; k < nr_nodes; k++) nr_parts += counts[k * nr_nodes + nodeID];
+  size_t nr_parts_new = 0, nr_gparts_new = 0, nr_sparts_new = 0;
   for (int k = 0; k < nr_nodes; k++)
-    nr_gparts += g_counts[k * nr_nodes + nodeID];
+    nr_parts_new += counts[k * nr_nodes + nodeID];
   for (int k = 0; k < nr_nodes; k++)
-    nr_sparts += s_counts[k * nr_nodes + nodeID];
+    nr_gparts_new += g_counts[k * nr_nodes + nodeID];
+  for (int k = 0; k < nr_nodes; k++)
+    nr_sparts_new += s_counts[k * nr_nodes + nodeID];
 
   /* Now exchange the particles, type by type to keep the memory required
    * under control. */
 
   /* SPH particles. */
-  void *new_parts = engine_do_redistribute(counts, (char *)s->parts, nr_parts,
-                                           sizeof(struct part), part_align,
-                                           part_mpi_type, nr_nodes, nodeID);
+  void *new_parts = engine_do_redistribute(
+      counts, (char *)s->parts, nr_parts_new, sizeof(struct part), part_align,
+      part_mpi_type, nr_nodes, nodeID);
   free(s->parts);
   s->parts = (struct part *)new_parts;
-  s->nr_parts = nr_parts;
-  s->size_parts = engine_redistribute_alloc_margin * nr_parts;
+  s->nr_parts = nr_parts_new;
+  s->size_parts = engine_redistribute_alloc_margin * nr_parts_new;
 
   /* Extra SPH particle properties. */
-  new_parts = engine_do_redistribute(counts, (char *)s->xparts, nr_parts,
+  new_parts = engine_do_redistribute(counts, (char *)s->xparts, nr_parts_new,
                                      sizeof(struct xpart), xpart_align,
                                      xpart_mpi_type, nr_nodes, nodeID);
   free(s->xparts);
   s->xparts = (struct xpart *)new_parts;
 
   /* Gravity particles. */
-  new_parts = engine_do_redistribute(g_counts, (char *)s->gparts, nr_gparts,
+  new_parts = engine_do_redistribute(g_counts, (char *)s->gparts, nr_gparts_new,
                                      sizeof(struct gpart), gpart_align,
                                      gpart_mpi_type, nr_nodes, nodeID);
   free(s->gparts);
   s->gparts = (struct gpart *)new_parts;
-  s->nr_gparts = nr_gparts;
-  s->size_gparts = engine_redistribute_alloc_margin * nr_gparts;
+  s->nr_gparts = nr_gparts_new;
+  s->size_gparts = engine_redistribute_alloc_margin * nr_gparts_new;
 
   /* Star particles. */
-  new_parts = engine_do_redistribute(s_counts, (char *)s->sparts, nr_sparts,
+  new_parts = engine_do_redistribute(s_counts, (char *)s->sparts, nr_sparts_new,
                                      sizeof(struct spart), spart_align,
                                      spart_mpi_type, nr_nodes, nodeID);
   free(s->sparts);
   s->sparts = (struct spart *)new_parts;
-  s->nr_sparts = nr_sparts;
-  s->size_sparts = engine_redistribute_alloc_margin * nr_sparts;
+  s->nr_sparts = nr_sparts_new;
+  s->size_sparts = engine_redistribute_alloc_margin * nr_sparts_new;
 
   /* All particles have now arrived. Time for some final operations on the
      stuff we just received */
@@ -1162,7 +914,7 @@ void engine_redistribute(struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Verify that all parts are in the right place. */
-  for (size_t k = 0; k < nr_parts; k++) {
+  for (size_t k = 0; k < nr_parts_new; k++) {
     const int cid = cell_getid(s->cdim, s->parts[k].x[0] * s->iwidth[0],
                                s->parts[k].x[1] * s->iwidth[1],
                                s->parts[k].x[2] * s->iwidth[2]);
@@ -1170,7 +922,7 @@ void engine_redistribute(struct engine *e) {
       error("Received particle (%zu) that does not belong here (nodeID=%i).", k,
             cells[cid].nodeID);
   }
-  for (size_t k = 0; k < nr_gparts; k++) {
+  for (size_t k = 0; k < nr_gparts_new; k++) {
     const int cid = cell_getid(s->cdim, s->gparts[k].x[0] * s->iwidth[0],
                                s->gparts[k].x[1] * s->iwidth[1],
                                s->gparts[k].x[2] * s->iwidth[2]);
@@ -1178,7 +930,7 @@ void engine_redistribute(struct engine *e) {
       error("Received g-particle (%zu) that does not belong here (nodeID=%i).",
             k, cells[cid].nodeID);
   }
-  for (size_t k = 0; k < nr_sparts; k++) {
+  for (size_t k = 0; k < nr_sparts_new; k++) {
     const int cid = cell_getid(s->cdim, s->sparts[k].x[0] * s->iwidth[0],
                                s->sparts[k].x[1] * s->iwidth[1],
                                s->sparts[k].x[2] * s->iwidth[2]);
@@ -1188,8 +940,8 @@ void engine_redistribute(struct engine *e) {
   }
 
   /* Verify that the links are correct */
-  part_verify_links(s->parts, s->gparts, s->sparts, nr_parts, nr_gparts,
-                    nr_sparts, e->verbose);
+  part_verify_links(s->parts, s->gparts, s->sparts, nr_parts_new, nr_gparts_new,
+                    nr_sparts_new, e->verbose);
 #endif
 
   /* Be verbose about what just happened. */
@@ -1198,7 +950,7 @@ void engine_redistribute(struct engine *e) {
     for (int k = 0; k < nr_cells; k++)
       if (cells[k].nodeID == nodeID) my_cells += 1;
     message("node %i now has %zu parts, %zu sparts and %zu gparts in %i cells.",
-            nodeID, nr_parts, nr_sparts, nr_gparts, my_cells);
+            nodeID, nr_parts_new, nr_sparts_new, nr_gparts_new, my_cells);
   }
 
   /* Flag that a redistribute has taken place */
@@ -1365,94 +1117,95 @@ void engine_repartition_trigger(struct engine *e) {
 }
 
 /**
- * @brief Add send tasks for the hydro pairs to a hierarchy of cells.
+ * @brief Exchange cell structures with other nodes.
  *
  * @param e The #engine.
- * @param ci The sending #cell.
- * @param cj Dummy cell containing the nodeID of the receiving node.
- * @param t_xv The send_xv #task, if it has already been created.
- * @param t_rho The send_rho #task, if it has already been created.
- * @param t_gradient The send_gradient #task, if already created.
  */
-void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
-                                struct cell *cj, struct task *t_xv,
-                                struct task *t_rho, struct task *t_gradient) {
+void engine_exchange_cells(struct engine *e) {
 
 #ifdef WITH_MPI
-  struct link *l = NULL;
-  struct scheduler *s = &e->sched;
-  const int nodeID = cj->nodeID;
-
-  /* Check if any of the density tasks are for the target node. */
-  for (l = ci->density; l != NULL; l = l->next)
-    if (l->t->ci->nodeID == nodeID ||
-        (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
-      break;
-
-  /* If so, attach send tasks. */
-  if (l != NULL) {
-
-    /* Create the tasks and their dependencies? */
-    if (t_xv == NULL) {
-
-      /* Create a tag for this cell. */
-      if (ci->tag < 0) cell_tag(ci);
 
-      t_xv = scheduler_addtask(s, task_type_send, task_subtype_xv, ci->tag, 0,
-                               ci, cj);
-      t_rho = scheduler_addtask(s, task_type_send, task_subtype_rho, ci->tag, 0,
-                                ci, cj);
-#ifdef EXTRA_HYDRO_LOOP
-      t_gradient = scheduler_addtask(s, task_type_send, task_subtype_gradient,
-                                     ci->tag, 0, ci, cj);
-#endif
-
-#ifdef EXTRA_HYDRO_LOOP
-
-      scheduler_addunlock(s, t_gradient, ci->super->kick2);
+  struct space *s = e->s;
+  const int nr_proxies = e->nr_proxies;
+  const int with_gravity = e->policy & engine_policy_self_gravity;
+  const ticks tic = getticks();
 
-      scheduler_addunlock(s, ci->super_hydro->extra_ghost, t_gradient);
+  /* Exchange the cell structure with neighbouring ranks. */
+  proxy_cells_exchange(e->proxies, e->nr_proxies, e->s, with_gravity);
 
-      /* The send_rho task should unlock the super_hydro-cell's extra_ghost
-       * task. */
-      scheduler_addunlock(s, t_rho, ci->super_hydro->extra_ghost);
+  ticks tic2 = getticks();
 
-      /* The send_rho task depends on the cell's ghost task. */
-      scheduler_addunlock(s, ci->super_hydro->ghost_out, t_rho);
+  /* Count the number of particles we need to import and re-allocate
+     the buffer if needed. */
+  size_t count_parts_in = 0, count_gparts_in = 0, count_sparts_in = 0;
+  for (int k = 0; k < nr_proxies; k++)
+    for (int j = 0; j < e->proxies[k].nr_cells_in; j++) {
+      if (e->proxies[k].cells_in_type[j] & proxy_cell_type_hydro)
+        count_parts_in += e->proxies[k].cells_in[j]->hydro.count;
+      if (e->proxies[k].cells_in_type[j] & proxy_cell_type_gravity)
+        count_gparts_in += e->proxies[k].cells_in[j]->grav.count;
+      count_sparts_in += e->proxies[k].cells_in[j]->stars.count;
+    }
+  if (count_parts_in > s->size_parts_foreign) {
+    if (s->parts_foreign != NULL) free(s->parts_foreign);
+    s->size_parts_foreign = 1.1 * count_parts_in;
+    if (posix_memalign((void **)&s->parts_foreign, part_align,
+                       sizeof(struct part) * s->size_parts_foreign) != 0)
+      error("Failed to allocate foreign part data.");
+  }
+  if (count_gparts_in > s->size_gparts_foreign) {
+    if (s->gparts_foreign != NULL) free(s->gparts_foreign);
+    s->size_gparts_foreign = 1.1 * count_gparts_in;
+    if (posix_memalign((void **)&s->gparts_foreign, gpart_align,
+                       sizeof(struct gpart) * s->size_gparts_foreign) != 0)
+      error("Failed to allocate foreign gpart data.");
+  }
+  if (count_sparts_in > s->size_sparts_foreign) {
+    if (s->sparts_foreign != NULL) free(s->sparts_foreign);
+    s->size_sparts_foreign = 1.1 * count_sparts_in;
+    if (posix_memalign((void **)&s->sparts_foreign, spart_align,
+                       sizeof(struct spart) * s->size_sparts_foreign) != 0)
+      error("Failed to allocate foreign spart data.");
+  }
 
-      /* The send_xv task should unlock the super_hydro-cell's ghost task. */
-      scheduler_addunlock(s, t_xv, ci->super_hydro->ghost_in);
+  if (e->verbose)
+    message("Counting and allocating arrays took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
 
-#else
-      /* The send_rho task should unlock the super_hydro-cell's kick task. */
-      scheduler_addunlock(s, t_rho, ci->super->end_force);
+  tic2 = getticks();
 
-      /* The send_rho task depends on the cell's ghost task. */
-      scheduler_addunlock(s, ci->super_hydro->ghost_out, t_rho);
+  /* Unpack the cells and link to the particle data. */
+  struct part *parts = s->parts_foreign;
+  struct gpart *gparts = s->gparts_foreign;
+  struct spart *sparts = s->sparts_foreign;
+  for (int k = 0; k < nr_proxies; k++) {
+    for (int j = 0; j < e->proxies[k].nr_cells_in; j++) {
 
-      /* The send_xv task should unlock the super_hydro-cell's ghost task. */
-      scheduler_addunlock(s, t_xv, ci->super_hydro->ghost_in);
+      if (e->proxies[k].cells_in_type[j] & proxy_cell_type_hydro) {
+        cell_link_parts(e->proxies[k].cells_in[j], parts);
+        parts = &parts[e->proxies[k].cells_in[j]->hydro.count];
+      }
 
-#endif
+      if (e->proxies[k].cells_in_type[j] & proxy_cell_type_gravity) {
+        cell_link_gparts(e->proxies[k].cells_in[j], gparts);
+        gparts = &gparts[e->proxies[k].cells_in[j]->grav.count];
+      }
 
-      /* Drift before you send */
-      scheduler_addunlock(s, ci->super_hydro->drift_part, t_xv);
+      cell_link_sparts(e->proxies[k].cells_in[j], sparts);
+      sparts = &sparts[e->proxies[k].cells_in[j]->stars.count];
     }
-
-    /* Add them to the local cell. */
-    engine_addlink(e, &ci->send_xv, t_xv);
-    engine_addlink(e, &ci->send_rho, t_rho);
-#ifdef EXTRA_HYDRO_LOOP
-    engine_addlink(e, &ci->send_gradient, t_gradient);
-#endif
   }
+  s->nr_parts_foreign = parts - s->parts_foreign;
+  s->nr_gparts_foreign = gparts - s->gparts_foreign;
+  s->nr_sparts_foreign = sparts - s->sparts_foreign;
+
+  if (e->verbose)
+    message("Recursively linking arrays took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
 
-  /* Recurse? */
-  if (ci->split)
-    for (int k = 0; k < 8; k++)
-      if (ci->progeny[k] != NULL)
-        engine_addtasks_send_hydro(e, ci->progeny[k], cj, t_xv, t_rho,
-                                   t_gradient);
+  if (e->verbose)
+    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
+            clocks_getunit());
 
 #else
   error("SWIFT was not compiled with MPI support.");
@@ -1460,388 +1213,21 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
 }
 
 /**
- * @brief Add send tasks for the gravity pairs to a hierarchy of cells.
- *
- * @param e The #engine.
- * @param ci The sending #cell.
- * @param cj Dummy cell containing the nodeID of the receiving node.
- * @param t_grav The send_grav #task, if it has already been created.
- */
-void engine_addtasks_send_gravity(struct engine *e, struct cell *ci,
-                                  struct cell *cj, struct task *t_grav) {
-
-#ifdef WITH_MPI
-  struct link *l = NULL;
-  struct scheduler *s = &e->sched;
-  const int nodeID = cj->nodeID;
-
-  /* Check if any of the gravity tasks are for the target node. */
-  for (l = ci->grav; l != NULL; l = l->next)
-    if (l->t->ci->nodeID == nodeID ||
-        (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
-      break;
-
-  /* If so, attach send tasks. */
-  if (l != NULL) {
-
-    /* Create the tasks and their dependencies? */
-    if (t_grav == NULL) {
-
-      /* Create a tag for this cell. */
-      if (ci->tag < 0) cell_tag(ci);
-
-      t_grav = scheduler_addtask(s, task_type_send, task_subtype_gpart, ci->tag,
-                                 0, ci, cj);
-
-      /* The sends should unlock the down pass. */
-      scheduler_addunlock(s, t_grav, ci->super_gravity->grav_down);
-
-      /* Drift before you send */
-      scheduler_addunlock(s, ci->super_gravity->drift_gpart, t_grav);
-    }
-
-    /* Add them to the local cell. */
-    engine_addlink(e, &ci->send_grav, t_grav);
-  }
-
-  /* Recurse? */
-  if (ci->split)
-    for (int k = 0; k < 8; k++)
-      if (ci->progeny[k] != NULL)
-        engine_addtasks_send_gravity(e, ci->progeny[k], cj, t_grav);
-
-#else
-  error("SWIFT was not compiled with MPI support.");
-#endif
-}
-
-/**
- * @brief Add send tasks for the time-step to a hierarchy of cells.
- *
- * @param e The #engine.
- * @param ci The sending #cell.
- * @param cj Dummy cell containing the nodeID of the receiving node.
- * @param t_ti The send_ti #task, if it has already been created.
- */
-void engine_addtasks_send_timestep(struct engine *e, struct cell *ci,
-                                   struct cell *cj, struct task *t_ti) {
-
-#ifdef WITH_MPI
-  struct link *l = NULL;
-  struct scheduler *s = &e->sched;
-  const int nodeID = cj->nodeID;
-
-  /* Check if any of the gravity tasks are for the target node. */
-  for (l = ci->grav; l != NULL; l = l->next)
-    if (l->t->ci->nodeID == nodeID ||
-        (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
-      break;
-
-  /* Check whether instead any of the hydro tasks are for the target node. */
-  if (l == NULL)
-    for (l = ci->density; l != NULL; l = l->next)
-      if (l->t->ci->nodeID == nodeID ||
-          (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
-        break;
-
-  /* If found anything, attach send tasks. */
-  if (l != NULL) {
-
-    /* Create the tasks and their dependencies? */
-    if (t_ti == NULL) {
-
-      /* Create a tag for this cell. */
-      if (ci->tag < 0) cell_tag(ci);
-
-      t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend, ci->tag, 0,
-                               ci, cj);
-
-      /* The super-cell's timestep task should unlock the send_ti task. */
-      scheduler_addunlock(s, ci->super->timestep, t_ti);
-    }
-
-    /* Add them to the local cell. */
-    engine_addlink(e, &ci->send_ti, t_ti);
-  }
-
-  /* Recurse? */
-  if (ci->split)
-    for (int k = 0; k < 8; k++)
-      if (ci->progeny[k] != NULL)
-        engine_addtasks_send_timestep(e, ci->progeny[k], cj, t_ti);
-
-#else
-  error("SWIFT was not compiled with MPI support.");
-#endif
-}
-
-/**
- * @brief Add recv tasks for hydro pairs to a hierarchy of cells.
- *
- * @param e The #engine.
- * @param c The foreign #cell.
- * @param t_xv The recv_xv #task, if it has already been created.
- * @param t_rho The recv_rho #task, if it has already been created.
- * @param t_gradient The recv_gradient #task, if it has already been created.
- */
-void engine_addtasks_recv_hydro(struct engine *e, struct cell *c,
-                                struct task *t_xv, struct task *t_rho,
-                                struct task *t_gradient) {
-
-#ifdef WITH_MPI
-  struct scheduler *s = &e->sched;
-
-  /* Have we reached a level where there are any hydro tasks ? */
-  if (t_xv == NULL && c->density != NULL) {
-
-#ifdef SWIFT_DEBUG_CHECKS
-    /* Make sure this cell has a valid tag. */
-    if (c->tag < 0) error("Trying to receive from untagged cell.");
-#endif  // SWIFT_DEBUG_CHECKS
-
-    /* Create the tasks. */
-    t_xv = scheduler_addtask(s, task_type_recv, task_subtype_xv, c->tag, 0, c,
-                             NULL);
-    t_rho = scheduler_addtask(s, task_type_recv, task_subtype_rho, c->tag, 0, c,
-                              NULL);
-#ifdef EXTRA_HYDRO_LOOP
-    t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_gradient,
-                                   c->tag, 0, c, NULL);
-#endif
-  }
-
-  c->recv_xv = t_xv;
-  c->recv_rho = t_rho;
-  c->recv_gradient = t_gradient;
-
-  /* Add dependencies. */
-  if (c->sorts != NULL) scheduler_addunlock(s, t_xv, c->sorts);
-
-  for (struct link *l = c->density; l != NULL; l = l->next) {
-    scheduler_addunlock(s, t_xv, l->t);
-    scheduler_addunlock(s, l->t, t_rho);
-  }
-#ifdef EXTRA_HYDRO_LOOP
-  for (struct link *l = c->gradient; l != NULL; l = l->next) {
-    scheduler_addunlock(s, t_rho, l->t);
-    scheduler_addunlock(s, l->t, t_gradient);
-  }
-  for (struct link *l = c->force; l != NULL; l = l->next)
-    scheduler_addunlock(s, t_gradient, l->t);
-#else
-  for (struct link *l = c->force; l != NULL; l = l->next)
-    scheduler_addunlock(s, t_rho, l->t);
-#endif
-
-  /* Recurse? */
-  if (c->split)
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL)
-        engine_addtasks_recv_hydro(e, c->progeny[k], t_xv, t_rho, t_gradient);
-
-#else
-  error("SWIFT was not compiled with MPI support.");
-#endif
-}
-
-/**
- * @brief Add recv tasks for gravity pairs to a hierarchy of cells.
- *
- * @param e The #engine.
- * @param c The foreign #cell.
- * @param t_grav The recv_gpart #task, if it has already been created.
- */
-void engine_addtasks_recv_gravity(struct engine *e, struct cell *c,
-                                  struct task *t_grav) {
-
-#ifdef WITH_MPI
-  struct scheduler *s = &e->sched;
-
-  /* Have we reached a level where there are any gravity tasks ? */
-  if (t_grav == NULL && c->grav != NULL) {
-
-#ifdef SWIFT_DEBUG_CHECKS
-    /* Make sure this cell has a valid tag. */
-    if (c->tag < 0) error("Trying to receive from untagged cell.");
-#endif  // SWIFT_DEBUG_CHECKS
-
-    /* Create the tasks. */
-    t_grav = scheduler_addtask(s, task_type_recv, task_subtype_gpart, c->tag, 0,
-                               c, NULL);
-  }
-
-  c->recv_grav = t_grav;
-
-  for (struct link *l = c->grav; l != NULL; l = l->next)
-    scheduler_addunlock(s, t_grav, l->t);
-
-  /* Recurse? */
-  if (c->split)
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL)
-        engine_addtasks_recv_gravity(e, c->progeny[k], t_grav);
-
-#else
-  error("SWIFT was not compiled with MPI support.");
-#endif
-}
-
-/**
- * @brief Add recv tasks for gravity pairs to a hierarchy of cells.
- *
- * @param e The #engine.
- * @param c The foreign #cell.
- * @param t_ti The recv_ti #task, if already been created.
- */
-void engine_addtasks_recv_timestep(struct engine *e, struct cell *c,
-                                   struct task *t_ti) {
-
-#ifdef WITH_MPI
-  struct scheduler *s = &e->sched;
-
-  /* Have we reached a level where there are any self/pair tasks ? */
-  if (t_ti == NULL && (c->grav != NULL || c->density != NULL)) {
-
-#ifdef SWIFT_DEBUG_CHECKS
-    /* Make sure this cell has a valid tag. */
-    if (c->tag < 0) error("Trying to receive from untagged cell.");
-#endif  // SWIFT_DEBUG_CHECKS
-
-    t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend, c->tag, 0, c,
-                             NULL);
-  }
-
-  c->recv_ti = t_ti;
-
-  for (struct link *l = c->grav; l != NULL; l = l->next)
-    scheduler_addunlock(s, l->t, t_ti);
-
-  for (struct link *l = c->force; l != NULL; l = l->next)
-    scheduler_addunlock(s, l->t, t_ti);
-
-  /* Recurse? */
-  if (c->split)
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL)
-        engine_addtasks_recv_timestep(e, c->progeny[k], t_ti);
-
-#else
-  error("SWIFT was not compiled with MPI support.");
-#endif
-}
-
-/**
- * @brief Exchange cell structures with other nodes.
- *
- * @param e The #engine.
- */
-void engine_exchange_cells(struct engine *e) {
-
-#ifdef WITH_MPI
-
-  struct space *s = e->s;
-  const int nr_proxies = e->nr_proxies;
-  const int with_gravity = e->policy & engine_policy_self_gravity;
-  const ticks tic = getticks();
-
-  /* Exchange the cell structure with neighbouring ranks. */
-  proxy_cells_exchange(e->proxies, e->nr_proxies, e->s, with_gravity);
-
-  ticks tic2 = getticks();
-
-  /* Count the number of particles we need to import and re-allocate
-     the buffer if needed. */
-  size_t count_parts_in = 0, count_gparts_in = 0, count_sparts_in = 0;
-  for (int k = 0; k < nr_proxies; k++)
-    for (int j = 0; j < e->proxies[k].nr_cells_in; j++) {
-      if (e->proxies[k].cells_in_type[j] & proxy_cell_type_hydro)
-        count_parts_in += e->proxies[k].cells_in[j]->count;
-      if (e->proxies[k].cells_in_type[j] & proxy_cell_type_gravity)
-        count_gparts_in += e->proxies[k].cells_in[j]->gcount;
-      count_sparts_in += e->proxies[k].cells_in[j]->scount;
-    }
-  if (count_parts_in > s->size_parts_foreign) {
-    if (s->parts_foreign != NULL) free(s->parts_foreign);
-    s->size_parts_foreign = 1.1 * count_parts_in;
-    if (posix_memalign((void **)&s->parts_foreign, part_align,
-                       sizeof(struct part) * s->size_parts_foreign) != 0)
-      error("Failed to allocate foreign part data.");
-  }
-  if (count_gparts_in > s->size_gparts_foreign) {
-    if (s->gparts_foreign != NULL) free(s->gparts_foreign);
-    s->size_gparts_foreign = 1.1 * count_gparts_in;
-    if (posix_memalign((void **)&s->gparts_foreign, gpart_align,
-                       sizeof(struct gpart) * s->size_gparts_foreign) != 0)
-      error("Failed to allocate foreign gpart data.");
-  }
-  if (count_sparts_in > s->size_sparts_foreign) {
-    if (s->sparts_foreign != NULL) free(s->sparts_foreign);
-    s->size_sparts_foreign = 1.1 * count_sparts_in;
-    if (posix_memalign((void **)&s->sparts_foreign, spart_align,
-                       sizeof(struct spart) * s->size_sparts_foreign) != 0)
-      error("Failed to allocate foreign spart data.");
-  }
-
-  if (e->verbose)
-    message("Counting and allocating arrays took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  tic2 = getticks();
-
-  /* Unpack the cells and link to the particle data. */
-  struct part *parts = s->parts_foreign;
-  struct gpart *gparts = s->gparts_foreign;
-  struct spart *sparts = s->sparts_foreign;
-  for (int k = 0; k < nr_proxies; k++) {
-    for (int j = 0; j < e->proxies[k].nr_cells_in; j++) {
-
-      if (e->proxies[k].cells_in_type[j] & proxy_cell_type_hydro) {
-        cell_link_parts(e->proxies[k].cells_in[j], parts);
-        parts = &parts[e->proxies[k].cells_in[j]->count];
-      }
-
-      if (e->proxies[k].cells_in_type[j] & proxy_cell_type_gravity) {
-        cell_link_gparts(e->proxies[k].cells_in[j], gparts);
-        gparts = &gparts[e->proxies[k].cells_in[j]->gcount];
-      }
-
-      cell_link_sparts(e->proxies[k].cells_in[j], sparts);
-      sparts = &sparts[e->proxies[k].cells_in[j]->scount];
-    }
-  }
-  s->nr_parts_foreign = parts - s->parts_foreign;
-  s->nr_gparts_foreign = gparts - s->gparts_foreign;
-  s->nr_sparts_foreign = sparts - s->sparts_foreign;
-
-  if (e->verbose)
-    message("Recursively linking arrays took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  if (e->verbose)
-    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
-            clocks_getunit());
-
-#else
-  error("SWIFT was not compiled with MPI support.");
-#endif
-}
-
-/**
- * @brief Exchange straying particles with other nodes.
+ * @brief Exchange straying particles with other nodes.
  *
  * @param e The #engine.
  * @param offset_parts The index in the parts array as of which the foreign
- *        parts reside.
+ *        parts reside (i.e. the current number of local #part).
  * @param ind_part The foreign #cell ID of each part.
  * @param Npart The number of stray parts, contains the number of parts received
  *        on return.
  * @param offset_gparts The index in the gparts array as of which the foreign
- *        parts reside.
+ *        parts reside (i.e. the current number of local #gpart).
  * @param ind_gpart The foreign #cell ID of each gpart.
  * @param Ngpart The number of stray gparts, contains the number of gparts
  *        received on return.
  * @param offset_sparts The index in the sparts array as of which the foreign
- *        parts reside.
+ *        parts reside (i.e. the current number of local #spart).
  * @param ind_spart The foreign #cell ID of each spart.
  * @param Nspart The number of stray sparts, contains the number of sparts
  *        received on return.
@@ -1849,11 +1235,11 @@ void engine_exchange_cells(struct engine *e) {
  * Note that this function does not mess-up the linkage between parts and
  * gparts, i.e. the received particles have correct linkeage.
  */
-void engine_exchange_strays(struct engine *e, size_t offset_parts,
-                            int *ind_part, size_t *Npart, size_t offset_gparts,
-                            int *ind_gpart, size_t *Ngpart,
-                            size_t offset_sparts, int *ind_spart,
-                            size_t *Nspart) {
+void engine_exchange_strays(struct engine *e, const size_t offset_parts,
+                            const int *ind_part, size_t *Npart,
+                            const size_t offset_gparts, const int *ind_gpart,
+                            size_t *Ngpart, const size_t offset_sparts,
+                            const int *ind_spart, size_t *Nspart) {
 
 #ifdef WITH_MPI
 
@@ -1869,6 +1255,10 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
 
   /* Put the parts into the corresponding proxies. */
   for (size_t k = 0; k < *Npart; k++) {
+
+    /* Ignore the particles we want to get rid of (inhibited, ...). */
+    if (ind_part[k] == -1) continue;
+
     /* Get the target node and proxy ID. */
     const int node_id = e->s->cells_top[ind_part[k]].nodeID;
     if (node_id < 0 || node_id >= e->nr_nodes)
@@ -1889,6 +1279,11 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
           -e->proxies[pid].nr_parts_out;
     }
 
+#ifdef SWIFT_DEBUG_CHECKS
+    if (s->parts[offset_parts + k].time_bin == time_bin_inhibited)
+      error("Attempting to exchange an inhibited particle");
+#endif
+
     /* Load the part and xpart into the proxy. */
     proxy_parts_load(&e->proxies[pid], &s->parts[offset_parts + k],
                      &s->xparts[offset_parts + k], 1);
@@ -1896,17 +1291,23 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
 
   /* Put the sparts into the corresponding proxies. */
   for (size_t k = 0; k < *Nspart; k++) {
+
+    /* Ignore the particles we want to get rid of (inhibited, ...). */
+    if (ind_spart[k] == -1) continue;
+
+    /* Get the target node and proxy ID. */
     const int node_id = e->s->cells_top[ind_spart[k]].nodeID;
     if (node_id < 0 || node_id >= e->nr_nodes)
       error("Bad node ID %i.", node_id);
     const int pid = e->proxy_ind[node_id];
-    if (pid < 0)
+    if (pid < 0) {
       error(
           "Do not have a proxy for the requested nodeID %i for part with "
           "id=%lld, x=[%e,%e,%e].",
           node_id, s->sparts[offset_sparts + k].id,
           s->sparts[offset_sparts + k].x[0], s->sparts[offset_sparts + k].x[1],
           s->sparts[offset_sparts + k].x[2]);
+    }
 
     /* Re-link the associated gpart with the buffer offset of the spart. */
     if (s->sparts[offset_sparts + k].gpart != NULL) {
@@ -1914,23 +1315,39 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
           -e->proxies[pid].nr_sparts_out;
     }
 
+#ifdef SWIFT_DEBUG_CHECKS
+    if (s->sparts[offset_sparts + k].time_bin == time_bin_inhibited)
+      error("Attempting to exchange an inhibited particle");
+#endif
+
     /* Load the spart into the proxy */
     proxy_sparts_load(&e->proxies[pid], &s->sparts[offset_sparts + k], 1);
   }
 
   /* Put the gparts into the corresponding proxies. */
   for (size_t k = 0; k < *Ngpart; k++) {
+
+    /* Ignore the particles we want to get rid of (inhibited, ...). */
+    if (ind_gpart[k] == -1) continue;
+
+    /* Get the target node and proxy ID. */
     const int node_id = e->s->cells_top[ind_gpart[k]].nodeID;
     if (node_id < 0 || node_id >= e->nr_nodes)
       error("Bad node ID %i.", node_id);
     const int pid = e->proxy_ind[node_id];
-    if (pid < 0)
+    if (pid < 0) {
       error(
           "Do not have a proxy for the requested nodeID %i for part with "
           "id=%lli, x=[%e,%e,%e].",
           node_id, s->gparts[offset_gparts + k].id_or_neg_offset,
           s->gparts[offset_gparts + k].x[0], s->gparts[offset_gparts + k].x[1],
           s->gparts[offset_gparts + k].x[2]);
+    }
+
+#ifdef SWIFT_DEBUG_CHECKS
+    if (s->gparts[offset_gparts + k].time_bin == time_bin_inhibited)
+      error("Attempting to exchange an inhibited particle");
+#endif
 
     /* Load the gpart into the proxy */
     proxy_gparts_load(&e->proxies[pid], &s->gparts[offset_gparts + k], 1);
@@ -1993,6 +1410,8 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
     free(s->xparts);
     s->parts = parts_new;
     s->xparts = xparts_new;
+
+    /* Reset the links */
     for (size_t k = 0; k < offset_parts; k++) {
       if (s->parts[k].gpart != NULL) {
         s->parts[k].gpart->id_or_neg_offset = -k;
@@ -2009,6 +1428,8 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
     memcpy(sparts_new, s->sparts, sizeof(struct spart) * offset_sparts);
     free(s->sparts);
     s->sparts = sparts_new;
+
+    /* Reset the links */
     for (size_t k = 0; k < offset_sparts; k++) {
       if (s->sparts[k].gpart != NULL) {
         s->sparts[k].gpart->id_or_neg_offset = -k;
@@ -2026,6 +1447,7 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
     free(s->gparts);
     s->gparts = gparts_new;
 
+    /* Reset the links */
     for (size_t k = 0; k < offset_gparts; k++) {
       if (s->gparts[k].type == swift_type_gas) {
         s->parts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k];
@@ -2196,19 +1618,16 @@ void engine_exchange_top_multipoles(struct engine *e) {
   /* Each node (space) has constructed its own top-level multipoles.
    * We now need to make sure every other node has a copy of everything.
    *
-   * WARNING: Adult stuff ahead: don't do this at home!
-   *
-   * Since all nodes have their top-level multi-poles computed
-   * and all foreign ones set to 0 (all bytes), we can gather all the m-poles
-   * by doing a bit-wise OR reduction across all the nodes directly in
-   * place inside the multi-poles_top array.
-   * This only works if the foreign m-poles on every nodes are zeroed and no
-   * multi-pole is present on more than one node (two things guaranteed by the
-   * domain decomposition).
+   * We use our home-made reduction operation that simply performs a XOR
+   * operation on the multipoles. Since only local multipoles are non-zero and
+   * each multipole is only present once, the bit-by-bit XOR will
+   * create the desired result.
    */
-  MPI_Allreduce(MPI_IN_PLACE, e->s->multipoles_top,
-                e->s->nr_cells * sizeof(struct gravity_tensors), MPI_BYTE,
-                MPI_BOR, MPI_COMM_WORLD);
+  int err = MPI_Allreduce(MPI_IN_PLACE, e->s->multipoles_top, e->s->nr_cells,
+                          multipole_mpi_type, multipole_mpi_reduce_op,
+                          MPI_COMM_WORLD);
+  if (err != MPI_SUCCESS)
+    mpi_error(err, "Failed to all-reduce the top-level multipoles.");
 
 #ifdef SWIFT_DEBUG_CHECKS
   long long counter = 0;
@@ -2217,6 +1636,9 @@ void engine_exchange_top_multipoles(struct engine *e) {
   for (int i = 0; i < e->s->nr_cells; ++i) {
     const struct gravity_tensors *m = &e->s->multipoles_top[i];
     counter += m->m_pole.num_gpart;
+    if (m->m_pole.num_gpart < 0) {
+      error("m->m_pole.num_gpart is negative: %lld", m->m_pole.num_gpart);
+    }
     if (m->m_pole.M_000 > 0.) {
       if (m->CoM[0] < 0. || m->CoM[0] > e->s->dim[0])
         error("Invalid multipole position in X");
@@ -2227,7 +1649,10 @@ void engine_exchange_top_multipoles(struct engine *e) {
     }
   }
   if (counter != e->total_nr_gparts)
-    error("Total particles in multipoles inconsistent with engine");
+    error(
+        "Total particles in multipoles inconsistent with engine.\n "
+        "  counter = %lld, nr_gparts = %lld",
+        counter, e->total_nr_gparts);
 #endif
 
 #else
@@ -2243,1954 +1668,148 @@ void engine_exchange_proxy_multipoles(struct engine *e) {
 
   /* Start by counting the number of cells to send and receive */
   int count_send_cells = 0;
-  int count_recv_cells = 0;
-  int count_send_requests = 0;
-  int count_recv_requests = 0;
-
-  /* Loop over the proxies. */
-  for (int pid = 0; pid < e->nr_proxies; pid++) {
-
-    /* Get a handle on the proxy. */
-    const struct proxy *p = &e->proxies[pid];
-
-    /* Now collect the number of requests associated */
-    count_recv_requests += p->nr_cells_in;
-    count_send_requests += p->nr_cells_out;
-
-    /* And the actual number of things we are going to ship */
-    for (int k = 0; k < p->nr_cells_in; k++)
-      count_recv_cells += p->cells_in[k]->pcell_size;
-
-    for (int k = 0; k < p->nr_cells_out; k++)
-      count_send_cells += p->cells_out[k]->pcell_size;
-  }
-
-  /* Allocate the buffers for the packed data */
-  struct gravity_tensors *buffer_send = NULL;
-  if (posix_memalign((void **)&buffer_send, SWIFT_CACHE_ALIGNMENT,
-                     count_send_cells * sizeof(struct gravity_tensors)) != 0)
-    error("Unable to allocate memory for multipole transactions");
-
-  struct gravity_tensors *buffer_recv = NULL;
-  if (posix_memalign((void **)&buffer_recv, SWIFT_CACHE_ALIGNMENT,
-                     count_recv_cells * sizeof(struct gravity_tensors)) != 0)
-    error("Unable to allocate memory for multipole transactions");
-
-  /* Also allocate the MPI requests */
-  const int count_requests = count_send_requests + count_recv_requests;
-  MPI_Request *requests =
-      (MPI_Request *)malloc(sizeof(MPI_Request) * count_requests);
-  if (requests == NULL) error("Unable to allocate memory for MPI requests");
-
-  int this_request = 0;
-  int this_recv = 0;
-  int this_send = 0;
-
-  /* Loop over the proxies to issue the receives. */
-  for (int pid = 0; pid < e->nr_proxies; pid++) {
-
-    /* Get a handle on the proxy. */
-    const struct proxy *p = &e->proxies[pid];
-
-    for (int k = 0; k < p->nr_cells_in; k++) {
-
-      const int num_elements = p->cells_in[k]->pcell_size;
-
-      /* Receive everything */
-      MPI_Irecv(&buffer_recv[this_recv], num_elements, multipole_mpi_type,
-                p->cells_in[k]->nodeID, p->cells_in[k]->tag, MPI_COMM_WORLD,
-                &requests[this_request]);
-
-      /* Move to the next slot in the buffers */
-      this_recv += num_elements;
-      this_request++;
-    }
-
-    /* Loop over the proxies to issue the sends. */
-    for (int k = 0; k < p->nr_cells_out; k++) {
-
-      /* Number of multipoles in this cell hierarchy */
-      const int num_elements = p->cells_out[k]->pcell_size;
-
-      /* Let's pack everything recursively */
-      cell_pack_multipoles(p->cells_out[k], &buffer_send[this_send]);
-
-      /* Send everything (note the use of cells_in[0] to get the correct node
-       * ID. */
-      MPI_Isend(&buffer_send[this_send], num_elements, multipole_mpi_type,
-                p->cells_in[0]->nodeID, p->cells_out[k]->tag, MPI_COMM_WORLD,
-                &requests[this_request]);
-
-      /* Move to the next slot in the buffers */
-      this_send += num_elements;
-      this_request++;
-    }
-  }
-
-  /* Wait for all the requests to arrive home */
-  MPI_Status *stats = (MPI_Status *)malloc(count_requests * sizeof(MPI_Status));
-  int res;
-  if ((res = MPI_Waitall(count_requests, requests, stats)) != MPI_SUCCESS) {
-    for (int k = 0; k < count_requests; ++k) {
-      char buff[MPI_MAX_ERROR_STRING];
-      MPI_Error_string(stats[k].MPI_ERROR, buff, &res);
-      message("request from source %i, tag %i has error '%s'.",
-              stats[k].MPI_SOURCE, stats[k].MPI_TAG, buff);
-    }
-    error("Failed during waitall for multipole data.");
-  }
-
-  /* Let's now unpack the multipoles at the right place */
-  this_recv = 0;
-  for (int pid = 0; pid < e->nr_proxies; pid++) {
-
-    /* Get a handle on the proxy. */
-    const struct proxy *p = &e->proxies[pid];
-
-    for (int k = 0; k < p->nr_cells_in; k++) {
-
-      const int num_elements = p->cells_in[k]->pcell_size;
-
-#ifdef SWIFT_DEBUG_CHECKS
-
-      /* Check that the first element (top-level cell's multipole) matches what
-       * we received */
-      if (p->cells_in[k]->multipole->m_pole.num_gpart !=
-          buffer_recv[this_recv].m_pole.num_gpart)
-        error("Current: M_000=%e num_gpart=%lld\n New: M_000=%e num_gpart=%lld",
-              p->cells_in[k]->multipole->m_pole.M_000,
-              p->cells_in[k]->multipole->m_pole.num_gpart,
-              buffer_recv[this_recv].m_pole.M_000,
-              buffer_recv[this_recv].m_pole.num_gpart);
-#endif
-
-      /* Unpack recursively */
-      cell_unpack_multipoles(p->cells_in[k], &buffer_recv[this_recv]);
-
-      /* Move to the next slot in the buffers */
-      this_recv += num_elements;
-    }
-  }
-
-  /* Free everything */
-  free(stats);
-  free(buffer_send);
-  free(buffer_recv);
-  free(requests);
-
-  /* How much time did this take? */
-  if (e->verbose)
-    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
-            clocks_getunit());
-#else
-  error("SWIFT was not compiled with MPI support.");
-#endif
-}
-
-/**
- * @brief Constructs the top-level tasks for the short-range gravity
- * and long-range gravity interactions.
- *
- * - All top-cells get a self task.
- * - All pairs within range according to the multipole acceptance
- *   criterion get a pair task.
- */
-void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements,
-                                           void *extra_data) {
-
-  struct engine *e = ((struct engine **)extra_data)[0];
-  struct space *s = e->s;
-  struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
-  const int periodic = s->periodic;
-  const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
-  const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]};
-  struct cell *cells = s->cells_top;
-  const double theta_crit = e->gravity_properties->theta_crit;
-  const double max_distance = e->mesh->r_cut_max;
-
-  /* Compute how many cells away we need to walk */
-  const double distance = 2.5 * cells[0].width[0] / theta_crit;
-  int delta = (int)(distance / cells[0].width[0]) + 1;
-  int delta_m = delta;
-  int delta_p = delta;
-
-  /* Special case where every cell is in range of every other one */
-  if (delta >= cdim[0] / 2) {
-    if (cdim[0] % 2 == 0) {
-      delta_m = cdim[0] / 2;
-      delta_p = cdim[0] / 2 - 1;
-    } else {
-      delta_m = cdim[0] / 2;
-      delta_p = cdim[0] / 2;
-    }
-  }
-
-  /* Loop through the elements, which are just byte offsets from NULL. */
-  for (int ind = 0; ind < num_elements; ind++) {
-
-    /* Get the cell index. */
-    const int cid = (size_t)(map_data) + ind;
-
-    /* Integer indices of the cell in the top-level grid */
-    const int i = cid / (cdim[1] * cdim[2]);
-    const int j = (cid / cdim[2]) % cdim[1];
-    const int k = cid % cdim[2];
-
-    /* Get the cell */
-    struct cell *ci = &cells[cid];
-
-    /* Skip cells without gravity particles */
-    if (ci->gcount == 0) continue;
-
-    /* Is that cell local ? */
-    if (ci->nodeID != nodeID) continue;
-
-    /* If the cells is local build a self-interaction */
-    scheduler_addtask(sched, task_type_self, task_subtype_grav, 0, 0, ci, NULL);
-
-    /* Recover the multipole information */
-    const struct gravity_tensors *const multi_i = ci->multipole;
-    const double CoM_i[3] = {multi_i->CoM[0], multi_i->CoM[1], multi_i->CoM[2]};
-
-#ifdef SWIFT_DEBUG_CHECKS
-    if (cell_getid(cdim, i, j, k) != cid)
-      error("Incorrect calculation of indices (i,j,k)=(%d,%d,%d) cid=%d", i, j,
-            k, cid);
-
-    if (multi_i->r_max != multi_i->r_max_rebuild)
-      error(
-          "Multipole size not equal ot it's size after rebuild. But we just "
-          "rebuilt...");
-#endif
-
-    /* Loop over every other cell within (Manhattan) range delta */
-    for (int x = -delta_m; x <= delta_p; x++) {
-      int ii = i + x;
-      if (ii >= cdim[0])
-        ii -= cdim[0];
-      else if (ii < 0)
-        ii += cdim[0];
-      for (int y = -delta_m; y <= delta_p; y++) {
-        int jj = j + y;
-        if (jj >= cdim[1])
-          jj -= cdim[1];
-        else if (jj < 0)
-          jj += cdim[1];
-        for (int z = -delta_m; z <= delta_p; z++) {
-          int kk = k + z;
-          if (kk >= cdim[2])
-            kk -= cdim[2];
-          else if (kk < 0)
-            kk += cdim[2];
-
-          /* Get the cell */
-          const int cjd = cell_getid(cdim, ii, jj, kk);
-          struct cell *cj = &cells[cjd];
-
-#ifdef SWIFT_DEBUG_CHECKS
-          const int iii = cjd / (cdim[1] * cdim[2]);
-          const int jjj = (cjd / cdim[2]) % cdim[1];
-          const int kkk = cjd % cdim[2];
-
-          if (ii != iii || jj != jjj || kk != kkk)
-            error(
-                "Incorrect calculation of indices (iii,jjj,kkk)=(%d,%d,%d) "
-                "cjd=%d",
-                iii, jjj, kkk, cjd);
-#endif
-
-          /* Avoid duplicates of local pairs*/
-          if (cid <= cjd && cj->nodeID == nodeID) continue;
-
-          /* Skip cells without gravity particles */
-          if (cj->gcount == 0) continue;
-
-          /* Recover the multipole information */
-          const struct gravity_tensors *const multi_j = cj->multipole;
-
-          /* Get the distance between the CoMs */
-          double dx = CoM_i[0] - multi_j->CoM[0];
-          double dy = CoM_i[1] - multi_j->CoM[1];
-          double dz = CoM_i[2] - multi_j->CoM[2];
-
-          /* Apply BC */
-          if (periodic) {
-            dx = nearest(dx, dim[0]);
-            dy = nearest(dy, dim[1]);
-            dz = nearest(dz, dim[2]);
-          }
-          const double r2 = dx * dx + dy * dy + dz * dz;
-
-          /* Minimal distance between any pair of particles */
-          const double min_radius =
-              sqrt(r2) - (multi_i->r_max + multi_j->r_max);
-
-          /* Are we beyond the distance where the truncated forces are 0 ?*/
-          if (periodic && min_radius > max_distance) continue;
-
-          /* Are the cells too close for a MM interaction ? */
-          if (!cell_can_use_pair_mm_rebuild(ci, cj, e, s)) {
-
-            /* Ok, we need to add a direct pair calculation */
-            scheduler_addtask(sched, task_type_pair, task_subtype_grav, 0, 0,
-                              ci, cj);
-          }
-        }
-      }
-    }
-  }
-}
-
-/**
- * @brief Constructs the top-level tasks for the short-range gravity
- * interactions (master function).
- *
- * - Create the FFT task and the array of gravity ghosts.
- * - Call the mapper function to create the other tasks.
- *
- * @param e The #engine.
- */
-void engine_make_self_gravity_tasks(struct engine *e) {
-
-  struct space *s = e->s;
-  struct task **ghosts = NULL;
-
-  /* Create the multipole self and pair tasks. */
-  void *extra_data[2] = {e, ghosts};
-  threadpool_map(&e->threadpool, engine_make_self_gravity_tasks_mapper, NULL,
-                 s->nr_cells, 1, 0, extra_data);
-}
-
-/**
- * @brief Constructs the top-level tasks for the external gravity.
- *
- * @param e The #engine.
- */
-void engine_make_external_gravity_tasks(struct engine *e) {
-
-  struct space *s = e->s;
-  struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
-  struct cell *cells = s->cells_top;
-  const int nr_cells = s->nr_cells;
-
-  for (int cid = 0; cid < nr_cells; ++cid) {
-
-    struct cell *ci = &cells[cid];
-
-    /* Skip cells without gravity particles */
-    if (ci->gcount == 0) continue;
-
-    /* Is that neighbour local ? */
-    if (ci->nodeID != nodeID) continue;
-
-    /* If the cell is local, build a self-interaction */
-    scheduler_addtask(sched, task_type_self, task_subtype_external_grav, 0, 0,
-                      ci, NULL);
-  }
-}
-
-/**
- * @brief Constructs the top-level pair tasks for the first hydro loop over
- * neighbours
- *
- * Here we construct all the tasks for all possible neighbouring non-empty
- * local cells in the hierarchy. No dependencies are being added thus far.
- * Additional loop over neighbours can later be added by simply duplicating
- * all the tasks created by this function.
- *
- * @param map_data Offset of first two indices disguised as a pointer.
- * @param num_elements Number of cells to traverse.
- * @param extra_data The #engine.
- */
-void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements,
-                                        void *extra_data) {
-
-  /* Extract the engine pointer. */
-  struct engine *e = (struct engine *)extra_data;
-
-  struct space *s = e->s;
-  struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
-  const int *cdim = s->cdim;
-  struct cell *cells = s->cells_top;
-
-  /* Loop through the elements, which are just byte offsets from NULL. */
-  for (int ind = 0; ind < num_elements; ind++) {
-
-    /* Get the cell index. */
-    const int cid = (size_t)(map_data) + ind;
-    const int i = cid / (cdim[1] * cdim[2]);
-    const int j = (cid / cdim[2]) % cdim[1];
-    const int k = cid % cdim[2];
-
-    /* Get the cell */
-    struct cell *ci = &cells[cid];
-
-    /* Skip cells without hydro particles */
-    if (ci->count == 0) continue;
-
-    /* If the cells is local build a self-interaction */
-    if (ci->nodeID == nodeID)
-      scheduler_addtask(sched, task_type_self, task_subtype_density, 0, 0, ci,
-                        NULL);
-
-    /* Now loop over all the neighbours of this cell */
-    for (int ii = -1; ii < 2; ii++) {
-      int iii = i + ii;
-      if (!s->periodic && (iii < 0 || iii >= cdim[0])) continue;
-      iii = (iii + cdim[0]) % cdim[0];
-      for (int jj = -1; jj < 2; jj++) {
-        int jjj = j + jj;
-        if (!s->periodic && (jjj < 0 || jjj >= cdim[1])) continue;
-        jjj = (jjj + cdim[1]) % cdim[1];
-        for (int kk = -1; kk < 2; kk++) {
-          int kkk = k + kk;
-          if (!s->periodic && (kkk < 0 || kkk >= cdim[2])) continue;
-          kkk = (kkk + cdim[2]) % cdim[2];
-
-          /* Get the neighbouring cell */
-          const int cjd = cell_getid(cdim, iii, jjj, kkk);
-          struct cell *cj = &cells[cjd];
-
-          /* Is that neighbour local and does it have particles ? */
-          if (cid >= cjd || cj->count == 0 ||
-              (ci->nodeID != nodeID && cj->nodeID != nodeID))
-            continue;
-
-          /* Construct the pair task */
-          const int sid = sortlistID[(kk + 1) + 3 * ((jj + 1) + 3 * (ii + 1))];
-          scheduler_addtask(sched, task_type_pair, task_subtype_density, sid, 0,
-                            ci, cj);
-        }
-      }
-    }
-  }
-}
-
-/**
- * @brief Constructs the top-level pair tasks for the star loop over
- * neighbours
- *
- * Here we construct all the tasks for all possible neighbouring non-empty
- * local cells in the hierarchy. No dependencies are being added thus far.
- * Additional loop over neighbours can later be added by simply duplicating
- * all the tasks created by this function.
- *
- * @param map_data Offset of first two indices disguised as a pointer.
- * @param num_elements Number of cells to traverse.
- * @param extra_data The #engine.
- */
-void engine_make_starsloop_tasks_mapper(void *map_data, int num_elements,
-                                        void *extra_data) {
-
-  /* Extract the engine pointer. */
-  struct engine *e = (struct engine *)extra_data;
-
-  struct space *s = e->s;
-  struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
-  const int *cdim = s->cdim;
-  struct cell *cells = s->cells_top;
-
-  /* Loop through the elements, which are just byte offsets from NULL. */
-  for (int ind = 0; ind < num_elements; ind++) {
-
-    /* Get the cell index. */
-    const int cid = (size_t)(map_data) + ind;
-    const int i = cid / (cdim[1] * cdim[2]);
-    const int j = (cid / cdim[2]) % cdim[1];
-    const int k = cid % cdim[2];
-
-    /* Get the cell */
-    struct cell *ci = &cells[cid];
-
-    /* Skip cells without star particles */
-    if (ci->scount == 0) continue;
-
-    /* If the cells is local build a self-interaction */
-    if (ci->nodeID == nodeID)
-      scheduler_addtask(sched, task_type_self, task_subtype_stars_density, 0, 0,
-                        ci, NULL);
-
-    /* Now loop over all the neighbours of this cell */
-    for (int ii = -1; ii < 2; ii++) {
-      int iii = i + ii;
-      if (!s->periodic && (iii < 0 || iii >= cdim[0])) continue;
-      iii = (iii + cdim[0]) % cdim[0];
-      for (int jj = -1; jj < 2; jj++) {
-        int jjj = j + jj;
-        if (!s->periodic && (jjj < 0 || jjj >= cdim[1])) continue;
-        jjj = (jjj + cdim[1]) % cdim[1];
-        for (int kk = -1; kk < 2; kk++) {
-          int kkk = k + kk;
-          if (!s->periodic && (kkk < 0 || kkk >= cdim[2])) continue;
-          kkk = (kkk + cdim[2]) % cdim[2];
-
-          /* Get the neighbouring cell */
-          const int cjd = cell_getid(cdim, iii, jjj, kkk);
-          struct cell *cj = &cells[cjd];
-
-          /* Is that neighbour local and does it have particles ? */
-          if (cid >= cjd || cj->count == 0 ||
-              (ci->nodeID != nodeID && cj->nodeID != nodeID))
-            continue;
-
-          /* Construct the pair task */
-          const int sid = sortlistID[(kk + 1) + 3 * ((jj + 1) + 3 * (ii + 1))];
-          scheduler_addtask(sched, task_type_pair, task_subtype_stars_density,
-                            sid, 0, ci, cj);
-        }
-      }
-    }
-  }
-}
-
-/**
- * @brief Counts the tasks associated with one cell and constructs the links
- *
- * For each hydrodynamic and gravity task, construct the links with
- * the corresponding cell.  Similarly, construct the dependencies for
- * all the sorting tasks.
- */
-void engine_count_and_link_tasks_mapper(void *map_data, int num_elements,
-                                        void *extra_data) {
-
-  struct engine *e = (struct engine *)extra_data;
-  struct scheduler *const sched = &e->sched;
-
-  for (int ind = 0; ind < num_elements; ind++) {
-    struct task *t = &((struct task *)map_data)[ind];
-
-    struct cell *ci = t->ci;
-    struct cell *cj = t->cj;
-    const enum task_types t_type = t->type;
-    const enum task_subtypes t_subtype = t->subtype;
-
-    /* Link sort tasks to all the higher sort task. */
-    if (t_type == task_type_sort) {
-      for (struct cell *finger = t->ci->parent; finger != NULL;
-           finger = finger->parent)
-        if (finger->sorts != NULL) scheduler_addunlock(sched, t, finger->sorts);
-    }
-
-    /* Link self tasks to cells. */
-    else if (t_type == task_type_self) {
-      atomic_inc(&ci->nr_tasks);
-
-      if (t_subtype == task_subtype_density) {
-        engine_addlink(e, &ci->density, t);
-      } else if (t_subtype == task_subtype_grav) {
-        engine_addlink(e, &ci->grav, t);
-      } else if (t_subtype == task_subtype_external_grav) {
-        engine_addlink(e, &ci->grav, t);
-      } else if (t->subtype == task_subtype_stars_density) {
-        engine_addlink(e, &ci->stars_density, t);
-      }
-
-      /* Link pair tasks to cells. */
-    } else if (t_type == task_type_pair) {
-      atomic_inc(&ci->nr_tasks);
-      atomic_inc(&cj->nr_tasks);
-
-      if (t_subtype == task_subtype_density) {
-        engine_addlink(e, &ci->density, t);
-        engine_addlink(e, &cj->density, t);
-      } else if (t_subtype == task_subtype_grav) {
-        engine_addlink(e, &ci->grav, t);
-        engine_addlink(e, &cj->grav, t);
-      } else if (t->subtype == task_subtype_stars_density) {
-        engine_addlink(e, &ci->stars_density, t);
-        engine_addlink(e, &cj->stars_density, t);
-      }
-#ifdef SWIFT_DEBUG_CHECKS
-      else if (t_subtype == task_subtype_external_grav) {
-        error("Found a pair/external-gravity task...");
-      }
-#endif
-
-      /* Link sub-self tasks to cells. */
-    } else if (t_type == task_type_sub_self) {
-      atomic_inc(&ci->nr_tasks);
-
-      if (t_subtype == task_subtype_density) {
-        engine_addlink(e, &ci->density, t);
-      } else if (t_subtype == task_subtype_grav) {
-        engine_addlink(e, &ci->grav, t);
-      } else if (t_subtype == task_subtype_external_grav) {
-        engine_addlink(e, &ci->grav, t);
-      } else if (t->subtype == task_subtype_stars_density) {
-        engine_addlink(e, &ci->stars_density, t);
-      }
-
-      /* Link sub-pair tasks to cells. */
-    } else if (t_type == task_type_sub_pair) {
-      atomic_inc(&ci->nr_tasks);
-      atomic_inc(&cj->nr_tasks);
-
-      if (t_subtype == task_subtype_density) {
-        engine_addlink(e, &ci->density, t);
-        engine_addlink(e, &cj->density, t);
-      } else if (t_subtype == task_subtype_grav) {
-        engine_addlink(e, &ci->grav, t);
-        engine_addlink(e, &cj->grav, t);
-      } else if (t->subtype == task_subtype_stars_density) {
-        engine_addlink(e, &ci->stars_density, t);
-        engine_addlink(e, &cj->stars_density, t);
-      }
-#ifdef SWIFT_DEBUG_CHECKS
-      else if (t_subtype == task_subtype_external_grav) {
-        error("Found a sub-pair/external-gravity task...");
-      }
-#endif
-
-      /* Multipole-multipole interaction of progenies */
-    } else if (t_type == task_type_grav_mm) {
-
-      atomic_inc(&ci->nr_mm_tasks);
-      atomic_inc(&cj->nr_mm_tasks);
-      engine_addlink(e, &ci->grav_mm, t);
-      engine_addlink(e, &cj->grav_mm, t);
-    }
-  }
-}
-
-/**
- * @brief Creates all the task dependencies for the gravity
- *
- * @param e The #engine
- */
-void engine_link_gravity_tasks(struct engine *e) {
-
-  struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
-  const int nr_tasks = sched->nr_tasks;
-
-  for (int k = 0; k < nr_tasks; k++) {
-
-    /* Get a pointer to the task. */
-    struct task *t = &sched->tasks[k];
-
-    if (t->type == task_type_none) continue;
-
-    /* Get the cells we act on */
-    struct cell *ci = t->ci;
-    struct cell *cj = t->cj;
-    const enum task_types t_type = t->type;
-    const enum task_subtypes t_subtype = t->subtype;
-
-    struct cell *ci_parent = (ci->parent != NULL) ? ci->parent : ci;
-    struct cell *cj_parent =
-        (cj != NULL && cj->parent != NULL) ? cj->parent : cj;
-
-/* Node ID (if running with MPI) */
-#ifdef WITH_MPI
-    const int ci_nodeID = ci->nodeID;
-    const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1;
-#else
-    const int ci_nodeID = nodeID;
-    const int cj_nodeID = nodeID;
-#endif
-
-    /* Self-interaction for self-gravity? */
-    if (t_type == task_type_self && t_subtype == task_subtype_grav) {
-
-#ifdef SWIFT_DEBUG_CHECKS
-      if (ci_nodeID != nodeID) error("Non-local self task");
-#endif
-
-      /* drift ---+-> gravity --> grav_down */
-      /* init  --/    */
-      scheduler_addunlock(sched, ci->super_gravity->drift_gpart, t);
-      scheduler_addunlock(sched, ci_parent->init_grav_out, t);
-      scheduler_addunlock(sched, t, ci_parent->grav_down_in);
-    }
-
-    /* Self-interaction for external gravity ? */
-    if (t_type == task_type_self && t_subtype == task_subtype_external_grav) {
-
-#ifdef SWIFT_DEBUG_CHECKS
-      if (ci_nodeID != nodeID) error("Non-local self task");
-#endif
-
-      /* drift -----> gravity --> end_force */
-      scheduler_addunlock(sched, ci->super_gravity->drift_gpart, t);
-      scheduler_addunlock(sched, t, ci->super->end_force);
-    }
-
-    /* Otherwise, pair interaction? */
-    else if (t_type == task_type_pair && t_subtype == task_subtype_grav) {
-
-      if (ci_nodeID == nodeID) {
-
-        /* drift ---+-> gravity --> grav_down */
-        /* init  --/    */
-        scheduler_addunlock(sched, ci->super_gravity->drift_gpart, t);
-        scheduler_addunlock(sched, ci_parent->init_grav_out, t);
-        scheduler_addunlock(sched, t, ci_parent->grav_down_in);
-      }
-      if (cj_nodeID == nodeID) {
-
-        /* drift ---+-> gravity --> grav_down */
-        /* init  --/    */
-        if (ci->super_gravity != cj->super_gravity) /* Avoid double unlock */
-          scheduler_addunlock(sched, cj->super_gravity->drift_gpart, t);
-
-        if (ci_parent != cj_parent) { /* Avoid double unlock */
-          scheduler_addunlock(sched, cj_parent->init_grav_out, t);
-          scheduler_addunlock(sched, t, cj_parent->grav_down_in);
-        }
-      }
-    }
-
-    /* Otherwise, sub-self interaction? */
-    else if (t_type == task_type_sub_self && t_subtype == task_subtype_grav) {
-
-#ifdef SWIFT_DEBUG_CHECKS
-      if (ci_nodeID != nodeID) error("Non-local sub-self task");
-#endif
-      /* drift ---+-> gravity --> grav_down */
-      /* init  --/    */
-      scheduler_addunlock(sched, ci->super_gravity->drift_gpart, t);
-      scheduler_addunlock(sched, ci_parent->init_grav_out, t);
-      scheduler_addunlock(sched, t, ci_parent->grav_down_in);
-    }
-
-    /* Sub-self-interaction for external gravity ? */
-    else if (t_type == task_type_sub_self &&
-             t_subtype == task_subtype_external_grav) {
-
-#ifdef SWIFT_DEBUG_CHECKS
-      if (ci_nodeID != nodeID) error("Non-local sub-self task");
-#endif
-
-      /* drift -----> gravity --> end_force */
-      scheduler_addunlock(sched, ci->super_gravity->drift_gpart, t);
-      scheduler_addunlock(sched, t, ci->super->end_force);
-    }
-
-    /* Otherwise, sub-pair interaction? */
-    else if (t_type == task_type_sub_pair && t_subtype == task_subtype_grav) {
-
-      if (ci_nodeID == nodeID) {
-
-        /* drift ---+-> gravity --> grav_down */
-        /* init  --/    */
-        scheduler_addunlock(sched, ci->super_gravity->drift_gpart, t);
-        scheduler_addunlock(sched, ci_parent->init_grav_out, t);
-        scheduler_addunlock(sched, t, ci_parent->grav_down_in);
-      }
-      if (cj_nodeID == nodeID) {
-
-        /* drift ---+-> gravity --> grav_down */
-        /* init  --/    */
-        if (ci->super_gravity != cj->super_gravity) /* Avoid double unlock */
-          scheduler_addunlock(sched, cj->super_gravity->drift_gpart, t);
-
-        if (ci_parent != cj_parent) { /* Avoid double unlock */
-          scheduler_addunlock(sched, cj_parent->init_grav_out, t);
-          scheduler_addunlock(sched, t, cj_parent->grav_down_in);
-        }
-      }
-    }
-
-    /* Otherwise M-M interaction? */
-    else if (t_type == task_type_grav_mm) {
-
-      if (ci_nodeID == nodeID) {
-
-        /* init -----> gravity --> grav_down */
-        scheduler_addunlock(sched, ci_parent->init_grav_out, t);
-        scheduler_addunlock(sched, t, ci_parent->grav_down_in);
-      }
-      if (cj_nodeID == nodeID) {
-
-        /* init -----> gravity --> grav_down */
-        if (ci_parent != cj_parent) { /* Avoid double unlock */
-          scheduler_addunlock(sched, cj_parent->init_grav_out, t);
-          scheduler_addunlock(sched, t, cj_parent->grav_down_in);
-        }
-      }
-    }
-  }
-}
-
-#ifdef EXTRA_HYDRO_LOOP
-
-/**
- * @brief Creates the dependency network for the hydro tasks of a given cell.
- *
- * @param sched The #scheduler.
- * @param density The density task to link.
- * @param gradient The gradient task to link.
- * @param force The force task to link.
- * @param c The cell.
- * @param with_cooling Do we have a cooling task ?
- */
-static inline void engine_make_hydro_loops_dependencies(
-    struct scheduler *sched, struct task *density, struct task *gradient,
-    struct task *force, struct cell *c, int with_cooling) {
-
-  /* density loop --> ghost --> gradient loop --> extra_ghost */
-  /* extra_ghost --> force loop  */
-  scheduler_addunlock(sched, density, c->super_hydro->ghost_in);
-  scheduler_addunlock(sched, c->super_hydro->ghost_out, gradient);
-  scheduler_addunlock(sched, gradient, c->super_hydro->extra_ghost);
-  scheduler_addunlock(sched, c->super_hydro->extra_ghost, force);
-}
-
-#else
-
-/**
- * @brief Creates the dependency network for the hydro tasks of a given cell.
- *
- * @param sched The #scheduler.
- * @param density The density task to link.
- * @param force The force task to link.
- * @param c The cell.
- * @param with_cooling Are we running with cooling switched on ?
- */
-static inline void engine_make_hydro_loops_dependencies(struct scheduler *sched,
-                                                        struct task *density,
-                                                        struct task *force,
-                                                        struct cell *c,
-                                                        int with_cooling) {
-  /* density loop --> ghost --> force loop */
-  scheduler_addunlock(sched, density, c->super_hydro->ghost_in);
-  scheduler_addunlock(sched, c->super_hydro->ghost_out, force);
-}
-
-#endif
-/**
- * @brief Creates the dependency network for the stars tasks of a given cell.
- *
- * @param sched The #scheduler.
- * @param density The density task to link.
- * @param c The cell.
- */
-static inline void engine_make_stars_loops_dependencies(struct scheduler *sched,
-                                                        struct task *density,
-                                                        struct cell *c) {
-  /* density loop --> ghost */
-  scheduler_addunlock(sched, density, c->super->stars_ghost_in);
-}
-
-/**
- * @brief Duplicates the first hydro loop and construct all the
- * dependencies for the hydro part
- *
- * This is done by looping over all the previously constructed tasks
- * and adding another task involving the same cells but this time
- * corresponding to the second hydro loop over neighbours.
- * With all the relevant tasks for a given cell available, we construct
- * all the dependencies for that cell.
- */
-void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
-                                              void *extra_data) {
-
-  struct engine *e = (struct engine *)extra_data;
-  struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
-  const int with_cooling = (e->policy & engine_policy_cooling);
-
-  for (int ind = 0; ind < num_elements; ind++) {
-    struct task *t = &((struct task *)map_data)[ind];
-
-    /* Sort tasks depend on the drift of the cell. */
-    if (t->type == task_type_sort && t->ci->nodeID == engine_rank) {
-      scheduler_addunlock(sched, t->ci->super_hydro->drift_part, t);
-    }
-
-    /* Self-interaction? */
-    else if (t->type == task_type_self && t->subtype == task_subtype_density) {
-
-      /* Make the self-density tasks depend on the drift only. */
-      scheduler_addunlock(sched, t->ci->super_hydro->drift_part, t);
-
-#ifdef EXTRA_HYDRO_LOOP
-      /* Start by constructing the task for the second  and third hydro loop. */
-      struct task *t2 = scheduler_addtask(
-          sched, task_type_self, task_subtype_gradient, 0, 0, t->ci, NULL);
-      struct task *t3 = scheduler_addtask(
-          sched, task_type_self, task_subtype_force, 0, 0, t->ci, NULL);
-
-      /* Add the link between the new loops and the cell */
-      engine_addlink(e, &t->ci->gradient, t2);
-      engine_addlink(e, &t->ci->force, t3);
-
-      /* Now, build all the dependencies for the hydro */
-      engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci,
-                                           with_cooling);
-      scheduler_addunlock(sched, t3, t->ci->super->end_force);
-#else
-
-      /* Start by constructing the task for the second hydro loop */
-      struct task *t2 = scheduler_addtask(
-          sched, task_type_self, task_subtype_force, 0, 0, t->ci, NULL);
-
-      /* Add the link between the new loop and the cell */
-      engine_addlink(e, &t->ci->force, t2);
-
-      /* Now, build all the dependencies for the hydro */
-      engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling);
-      scheduler_addunlock(sched, t2, t->ci->super->end_force);
-#endif
-    }
-
-    /* Otherwise, pair interaction? */
-    else if (t->type == task_type_pair && t->subtype == task_subtype_density) {
-
-      /* Make all density tasks depend on the drift and the sorts. */
-      if (t->ci->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->ci->super_hydro->drift_part, t);
-      scheduler_addunlock(sched, t->ci->super_hydro->sorts, t);
-      if (t->ci->super_hydro != t->cj->super_hydro) {
-        if (t->cj->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->cj->super_hydro->drift_part, t);
-        scheduler_addunlock(sched, t->cj->super_hydro->sorts, t);
-      }
-
-#ifdef EXTRA_HYDRO_LOOP
-      /* Start by constructing the task for the second and third hydro loop */
-      struct task *t2 = scheduler_addtask(
-          sched, task_type_pair, task_subtype_gradient, 0, 0, t->ci, t->cj);
-      struct task *t3 = scheduler_addtask(
-          sched, task_type_pair, task_subtype_force, 0, 0, t->ci, t->cj);
-
-      /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->gradient, t2);
-      engine_addlink(e, &t->cj->gradient, t2);
-      engine_addlink(e, &t->ci->force, t3);
-      engine_addlink(e, &t->cj->force, t3);
-
-      /* Now, build all the dependencies for the hydro for the cells */
-      /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci,
-                                             with_cooling);
-        scheduler_addunlock(sched, t3, t->ci->super->end_force);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->super_hydro != t->cj->super_hydro)
-          engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->cj,
-                                               with_cooling);
-        if (t->ci->super != t->cj->super)
-          scheduler_addunlock(sched, t3, t->cj->super->end_force);
-      }
-
-#else
-
-      /* Start by constructing the task for the second hydro loop */
-      struct task *t2 = scheduler_addtask(
-          sched, task_type_pair, task_subtype_force, 0, 0, t->ci, t->cj);
-
-      /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->force, t2);
-      engine_addlink(e, &t->cj->force, t2);
-
-      /* Now, build all the dependencies for the hydro for the cells */
-      /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling);
-        scheduler_addunlock(sched, t2, t->ci->super->end_force);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->super_hydro != t->cj->super_hydro)
-          engine_make_hydro_loops_dependencies(sched, t, t2, t->cj,
-                                               with_cooling);
-        if (t->ci->super != t->cj->super)
-          scheduler_addunlock(sched, t2, t->cj->super->end_force);
-      }
-
-#endif
-
-    }
-
-    /* Otherwise, sub-self interaction? */
-    else if (t->type == task_type_sub_self &&
-             t->subtype == task_subtype_density) {
-
-      /* Make all density tasks depend on the drift and sorts. */
-      scheduler_addunlock(sched, t->ci->super_hydro->drift_part, t);
-      scheduler_addunlock(sched, t->ci->super_hydro->sorts, t);
-
-#ifdef EXTRA_HYDRO_LOOP
-
-      /* Start by constructing the task for the second and third hydro loop */
-      struct task *t2 =
-          scheduler_addtask(sched, task_type_sub_self, task_subtype_gradient,
-                            t->flags, 0, t->ci, t->cj);
-      struct task *t3 =
-          scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
-                            t->flags, 0, t->ci, t->cj);
-
-      /* Add the link between the new loop and the cell */
-      engine_addlink(e, &t->ci->gradient, t2);
-      engine_addlink(e, &t->ci->force, t3);
-
-      /* Now, build all the dependencies for the hydro for the cells */
-      /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci,
-                                             with_cooling);
-        scheduler_addunlock(sched, t3, t->ci->super->end_force);
-      }
-
-#else
-      /* Start by constructing the task for the second hydro loop */
-      struct task *t2 =
-          scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
-                            t->flags, 0, t->ci, t->cj);
-
-      /* Add the link between the new loop and the cell */
-      engine_addlink(e, &t->ci->force, t2);
-
-      /* Now, build all the dependencies for the hydro for the cells */
-      /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling);
-        scheduler_addunlock(sched, t2, t->ci->super->end_force);
-      }
-#endif
-    }
-
-    /* Otherwise, sub-pair interaction? */
-    else if (t->type == task_type_sub_pair &&
-             t->subtype == task_subtype_density) {
-
-      /* Make all density tasks depend on the drift. */
-      if (t->ci->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->ci->super_hydro->drift_part, t);
-      scheduler_addunlock(sched, t->ci->super_hydro->sorts, t);
-      if (t->ci->super_hydro != t->cj->super_hydro) {
-        if (t->cj->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->cj->super_hydro->drift_part, t);
-        scheduler_addunlock(sched, t->cj->super_hydro->sorts, t);
-      }
-
-#ifdef EXTRA_HYDRO_LOOP
-
-      /* Start by constructing the task for the second and third hydro loop */
-      struct task *t2 =
-          scheduler_addtask(sched, task_type_sub_pair, task_subtype_gradient,
-                            t->flags, 0, t->ci, t->cj);
-      struct task *t3 =
-          scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
-                            t->flags, 0, t->ci, t->cj);
-
-      /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->gradient, t2);
-      engine_addlink(e, &t->cj->gradient, t2);
-      engine_addlink(e, &t->ci->force, t3);
-      engine_addlink(e, &t->cj->force, t3);
-
-      /* Now, build all the dependencies for the hydro for the cells */
-      /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci,
-                                             with_cooling);
-        scheduler_addunlock(sched, t3, t->ci->super->end_force);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->super_hydro != t->cj->super_hydro)
-          engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->cj,
-                                               with_cooling);
-        if (t->ci->super != t->cj->super)
-          scheduler_addunlock(sched, t3, t->cj->super->end_force);
-      }
-
-#else
-      /* Start by constructing the task for the second hydro loop */
-      struct task *t2 =
-          scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
-                            t->flags, 0, t->ci, t->cj);
-
-      /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->force, t2);
-      engine_addlink(e, &t->cj->force, t2);
-
-      /* Now, build all the dependencies for the hydro for the cells */
-      /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling);
-        scheduler_addunlock(sched, t2, t->ci->super->end_force);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->super_hydro != t->cj->super_hydro)
-          engine_make_hydro_loops_dependencies(sched, t, t2, t->cj,
-                                               with_cooling);
-        if (t->ci->super != t->cj->super)
-          scheduler_addunlock(sched, t2, t->cj->super->end_force);
-      }
-#endif
-    }
-  }
-}
-
-/**
- * @brief Duplicates the first hydro loop and construct all the
- * dependencies for the stars
- *
- * This is done by looping over all the previously constructed tasks
- * and adding another task involving the same cells but this time
- * corresponding to the second hydro loop over neighbours.
- * With all the relevant tasks for a given cell available, we construct
- * all the dependencies for that cell.
- */
-void engine_link_stars_tasks_mapper(void *map_data, int num_elements,
-                                    void *extra_data) {
-
-  struct engine *e = (struct engine *)extra_data;
-  struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
-
-  for (int ind = 0; ind < num_elements; ind++) {
-    struct task *t = &((struct task *)map_data)[ind];
-
-    /* Self-interaction? */
-    if (t->type == task_type_self && t->subtype == task_subtype_stars_density) {
-
-      /* Make the self-density tasks depend on the drifts. */
-      scheduler_addunlock(sched, t->ci->super->drift_part, t);
-
-      scheduler_addunlock(sched, t->ci->super->drift_gpart, t);
-
-      /* Now, build all the dependencies for the stars */
-      engine_make_stars_loops_dependencies(sched, t, t->ci);
-      scheduler_addunlock(sched, t->ci->stars_ghost_out,
-                          t->ci->super->end_force);
-    }
-
-    /* Otherwise, pair interaction? */
-    else if (t->type == task_type_pair &&
-             t->subtype == task_subtype_stars_density) {
-
-      /* Make all density tasks depend on the drift and the sorts. */
-      if (t->ci->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->ci->super->drift_part, t);
-      scheduler_addunlock(sched, t->ci->super->sorts, t);
-      if (t->ci->super != t->cj->super) {
-        if (t->cj->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->cj->super->drift_part, t);
-        scheduler_addunlock(sched, t->cj->super->sorts, t);
-      }
-
-      /* Now, build all the dependencies for the stars for the cells */
-      /* that are local and are not descendant of the same super-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_stars_loops_dependencies(sched, t, t->ci);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->super != t->cj->super)
-          engine_make_stars_loops_dependencies(sched, t, t->cj);
-      }
-
-    }
-
-    /* Otherwise, sub-self interaction? */
-    else if (t->type == task_type_sub_self &&
-             t->subtype == task_subtype_stars_density) {
-
-      /* Make all density tasks depend on the drift and sorts. */
-      scheduler_addunlock(sched, t->ci->super->drift_part, t);
-      scheduler_addunlock(sched, t->ci->super->sorts, t);
-
-      /* Now, build all the dependencies for the stars for the cells */
-      /* that are local and are not descendant of the same super-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_stars_loops_dependencies(sched, t, t->ci);
-      } else
-        error("oo");
-    }
-
-    /* Otherwise, sub-pair interaction? */
-    else if (t->type == task_type_sub_pair &&
-             t->subtype == task_subtype_stars_density) {
-
-      /* Make all density tasks depend on the drift. */
-      if (t->ci->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->ci->super->drift_part, t);
-      scheduler_addunlock(sched, t->ci->super->sorts, t);
-      if (t->ci->super != t->cj->super) {
-        if (t->cj->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->cj->super->drift_part, t);
-        scheduler_addunlock(sched, t->cj->super->sorts, t);
-      }
-
-      /* Now, build all the dependencies for the stars for the cells */
-      /* that are local and are not descendant of the same super-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_stars_loops_dependencies(sched, t, t->ci);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->super != t->cj->super)
-          engine_make_stars_loops_dependencies(sched, t, t->cj);
-      }
-    }
-  }
-}
-
-/**
- * @brief Fill the #space's task list.
- *
- * @param e The #engine we are working with.
- */
-void engine_maketasks(struct engine *e) {
-
-  struct space *s = e->s;
-  struct scheduler *sched = &e->sched;
-  struct cell *cells = s->cells_top;
-  const int nr_cells = s->nr_cells;
-  const ticks tic = getticks();
-
-  /* Re-set the scheduler. */
-  scheduler_reset(sched, engine_estimate_nr_tasks(e));
-
-  ticks tic2 = getticks();
-
-  /* Construct the first hydro loop over neighbours */
-  if (e->policy & engine_policy_hydro)
-    threadpool_map(&e->threadpool, engine_make_hydroloop_tasks_mapper, NULL,
-                   s->nr_cells, 1, 0, e);
-
-  if (e->verbose)
-    message("Making hydro tasks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  tic2 = getticks();
-
-  /* Construct the stars hydro loop over neighbours */
-  if (e->policy & engine_policy_feedback) {
-    threadpool_map(&e->threadpool, engine_make_starsloop_tasks_mapper, NULL,
-                   s->nr_cells, 1, 0, e);
-  }
-
-  /* Add the self gravity tasks. */
-  if (e->policy & engine_policy_self_gravity) engine_make_self_gravity_tasks(e);
-
-  if (e->verbose)
-    message("Making gravity tasks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  /* Add the external gravity tasks. */
-  if (e->policy & engine_policy_external_gravity)
-    engine_make_external_gravity_tasks(e);
-
-  if (e->sched.nr_tasks == 0 && (s->nr_gparts > 0 || s->nr_parts > 0))
-    error("We have particles but no hydro or gravity tasks were created.");
-
-  /* Free the old list of cell-task links. */
-  if (e->links != NULL) free(e->links);
-  e->size_links = 0;
-
-/* The maximum number of links is the
- * number of cells (s->tot_cells) times the number of neighbours (26) times
- * the number of interaction types, so 26 * 2 (density, force) pairs
- * and 2 (density, force) self.
- */
-#ifdef EXTRA_HYDRO_LOOP
-  const size_t hydro_tasks_per_cell = 27 * 3;
-#else
-  const size_t hydro_tasks_per_cell = 27 * 2;
-#endif
-  const size_t self_grav_tasks_per_cell = 125;
-  const size_t ext_grav_tasks_per_cell = 1;
-  const size_t stars_tasks_per_cell = 15;
-
-  if (e->policy & engine_policy_hydro)
-    e->size_links += s->tot_cells * hydro_tasks_per_cell;
-  if (e->policy & engine_policy_external_gravity)
-    e->size_links += s->tot_cells * ext_grav_tasks_per_cell;
-  if (e->policy & engine_policy_self_gravity)
-    e->size_links += s->tot_cells * self_grav_tasks_per_cell;
-  if (e->policy & engine_policy_stars)
-    e->size_links += s->tot_cells * stars_tasks_per_cell;
-
-  /* Allocate the new link list */
-  if ((e->links = (struct link *)malloc(sizeof(struct link) * e->size_links)) ==
-      NULL)
-    error("Failed to allocate cell-task links.");
-  e->nr_links = 0;
-
-  tic2 = getticks();
-
-  /* Split the tasks. */
-  scheduler_splittasks(sched);
-
-  if (e->verbose)
-    message("Splitting tasks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-#ifdef SWIFT_DEBUG_CHECKS
-  /* Verify that we are not left with invalid tasks */
-  for (int i = 0; i < e->sched.nr_tasks; ++i) {
-    const struct task *t = &e->sched.tasks[i];
-    if (t->ci == NULL && t->cj != NULL && !t->skip) error("Invalid task");
-  }
-#endif
-
-  tic2 = getticks();
-
-  /* Count the number of tasks associated with each cell and
-     store the density tasks in each cell, and make each sort
-     depend on the sorts of its progeny. */
-  threadpool_map(&e->threadpool, engine_count_and_link_tasks_mapper,
-                 sched->tasks, sched->nr_tasks, sizeof(struct task), 0, e);
-
-  if (e->verbose)
-    message("Counting and linking tasks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  tic2 = getticks();
-
-  /* Re-set the tag counter. MPI tags are defined for top-level cells in
-   * cell_set_super_mapper. */
-#ifdef WITH_MPI
-  cell_next_tag = 0;
-#endif
-
-  /* Now that the self/pair tasks are at the right level, set the super
-   * pointers. */
-  threadpool_map(&e->threadpool, cell_set_super_mapper, cells, nr_cells,
-                 sizeof(struct cell), 0, e);
-
-  if (e->verbose)
-    message("Setting super-pointers took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  /* Append hierarchical tasks to each cell. */
-  threadpool_map(&e->threadpool, engine_make_hierarchical_tasks_mapper, cells,
-                 nr_cells, sizeof(struct cell), 0, e);
-
-  tic2 = getticks();
-
-  /* Run through the tasks and make force tasks for each density task.
-     Each force task depends on the cell ghosts and unlocks the kick task
-     of its super-cell. */
-  if (e->policy & engine_policy_hydro)
-    threadpool_map(&e->threadpool, engine_make_extra_hydroloop_tasks_mapper,
-                   sched->tasks, sched->nr_tasks, sizeof(struct task), 0, e);
-
-  if (e->verbose)
-    message("Making extra hydroloop tasks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  tic2 = getticks();
-
-  /* Add the dependencies for the gravity stuff */
-  if (e->policy & (engine_policy_self_gravity | engine_policy_external_gravity))
-    engine_link_gravity_tasks(e);
-
-  if (e->verbose)
-    message("Linking gravity tasks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  tic2 = getticks();
-
-  if (e->policy & engine_policy_stars)
-    threadpool_map(&e->threadpool, engine_link_stars_tasks_mapper, sched->tasks,
-                   sched->nr_tasks, sizeof(struct task), 0, e);
-
-  if (e->verbose)
-    message("Linking stars tasks took %.3f %s (including reweight).",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-#ifdef WITH_MPI
-  if (e->policy & engine_policy_feedback)
-    error("Cannot run stellar feedback with MPI (yet).");
-
-  /* Add the communication tasks if MPI is being used. */
-  if (e->policy & engine_policy_mpi) {
-
-    tic2 = getticks();
-
-    /* Loop over the proxies and add the send tasks, which also generates the
-     * cell tags for super-cells. */
-    for (int pid = 0; pid < e->nr_proxies; pid++) {
-
-      /* Get a handle on the proxy. */
-      struct proxy *p = &e->proxies[pid];
-
-      for (int k = 0; k < p->nr_cells_out; k++)
-        engine_addtasks_send_timestep(e, p->cells_out[k], p->cells_in[0], NULL);
-
-      /* Loop through the proxy's outgoing cells and add the
-         send tasks for the cells in the proxy that have a hydro connection. */
-      if (e->policy & engine_policy_hydro)
-        for (int k = 0; k < p->nr_cells_out; k++)
-          if (p->cells_out_type[k] & proxy_cell_type_hydro)
-            engine_addtasks_send_hydro(e, p->cells_out[k], p->cells_in[0], NULL,
-                                       NULL, NULL);
-
-      /* Loop through the proxy's outgoing cells and add the
-         send tasks for the cells in the proxy that have a gravity connection.
-         */
-      if (e->policy & engine_policy_self_gravity)
-        for (int k = 0; k < p->nr_cells_out; k++)
-          if (p->cells_out_type[k] & proxy_cell_type_gravity)
-            engine_addtasks_send_gravity(e, p->cells_out[k], p->cells_in[0],
-                                         NULL);
-    }
-
-    if (e->verbose)
-      message("Creating send tasks took %.3f %s.",
-              clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-    tic2 = getticks();
-
-    /* Exchange the cell tags. */
-    proxy_tags_exchange(e->proxies, e->nr_proxies, s);
-
-    if (e->verbose)
-      message("Exchanging cell tags took %.3f %s.",
-              clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-    tic2 = getticks();
-
-    /* Loop over the proxies and add the recv tasks, which relies on having the
-     * cell tags. */
-    for (int pid = 0; pid < e->nr_proxies; pid++) {
-
-      /* Get a handle on the proxy. */
-      struct proxy *p = &e->proxies[pid];
-
-      for (int k = 0; k < p->nr_cells_in; k++)
-        engine_addtasks_recv_timestep(e, p->cells_in[k], NULL);
-
-      /* Loop through the proxy's incoming cells and add the
-         recv tasks for the cells in the proxy that have a hydro connection. */
-      if (e->policy & engine_policy_hydro)
-        for (int k = 0; k < p->nr_cells_in; k++)
-          if (p->cells_in_type[k] & proxy_cell_type_hydro)
-            engine_addtasks_recv_hydro(e, p->cells_in[k], NULL, NULL, NULL);
-
-      /* Loop through the proxy's incoming cells and add the
-         recv tasks for the cells in the proxy that have a gravity connection.
-         */
-      if (e->policy & engine_policy_self_gravity)
-        for (int k = 0; k < p->nr_cells_in; k++)
-          if (p->cells_in_type[k] & proxy_cell_type_gravity)
-            engine_addtasks_recv_gravity(e, p->cells_in[k], NULL);
-    }
-
-    if (e->verbose)
-      message("Creating recv tasks took %.3f %s.",
-              clocks_from_ticks(getticks() - tic2), clocks_getunit());
-  }
-#endif
-
-  tic2 = getticks();
-
-  /* Set the unlocks per task. */
-  scheduler_set_unlocks(sched);
-
-  if (e->verbose)
-    message("Setting unlocks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  tic2 = getticks();
-
-  /* Rank the tasks. */
-  scheduler_ranktasks(sched);
-
-  if (e->verbose)
-    message("Ranking the tasks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  /* Weight the tasks. */
-  scheduler_reweight(sched, e->verbose);
-
-  /* Set the tasks age. */
-  e->tasks_age = 0;
-
-  if (e->verbose)
-    message("took %.3f %s (including reweight).",
-            clocks_from_ticks(getticks() - tic), clocks_getunit());
-}
-
-/**
- * @brief Mark tasks to be un-skipped and set the sort flags accordingly.
- *        Threadpool mapper function.
- *
- * @param map_data pointer to the tasks
- * @param num_elements number of tasks
- * @param extra_data pointer to int that will define if a rebuild is needed.
- */
-void engine_marktasks_mapper(void *map_data, int num_elements,
-                             void *extra_data) {
-  /* Unpack the arguments. */
-  struct task *tasks = (struct task *)map_data;
-  size_t *rebuild_space = &((size_t *)extra_data)[1];
-  struct scheduler *s = (struct scheduler *)(((size_t *)extra_data)[2]);
-  struct engine *e = (struct engine *)((size_t *)extra_data)[0];
-  const int nodeID = e->nodeID;
-
-  for (int ind = 0; ind < num_elements; ind++) {
-
-    /* Get basic task information */
-    struct task *t = &tasks[ind];
-    const enum task_types t_type = t->type;
-    const enum task_subtypes t_subtype = t->subtype;
-
-    /* Single-cell task? */
-    if (t_type == task_type_self || t_type == task_type_sub_self) {
-
-      /* Local pointer. */
-      struct cell *ci = t->ci;
-
-      if (ci->nodeID != engine_rank) error("Non-local self task found");
-
-      /* Activate the hydro drift */
-      if (t_type == task_type_self && t_subtype == task_subtype_density) {
-        if (cell_is_active_hydro(ci, e)) {
-          scheduler_activate(s, t);
-          cell_activate_drift_part(ci, s);
-        }
-      }
-
-      /* Store current values of dx_max and h_max. */
-      else if (t_type == task_type_sub_self &&
-               t_subtype == task_subtype_density) {
-        if (cell_is_active_hydro(ci, e)) {
-          scheduler_activate(s, t);
-          cell_activate_subcell_hydro_tasks(ci, NULL, s);
-        }
-      }
-
-      else if (t_type == task_type_self && t_subtype == task_subtype_force) {
-        if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
-      }
-
-      else if (t_type == task_type_sub_self &&
-               t_subtype == task_subtype_force) {
-        if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
-      }
-
-#ifdef EXTRA_HYDRO_LOOP
-      else if (t_type == task_type_self && t_subtype == task_subtype_gradient) {
-        if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
-      }
-
-      else if (t_type == task_type_sub_self &&
-               t_subtype == task_subtype_gradient) {
-        if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
-      }
-#endif
-
-      /* Activate the star density */
-      else if (t_type == task_type_self &&
-               t_subtype == task_subtype_stars_density) {
-        if (cell_is_active_stars(ci, e)) {
-          scheduler_activate(s, t);
-          cell_activate_drift_part(ci, s);
-          cell_activate_drift_gpart(ci, s);
-        }
-      }
-
-      /* Store current values of dx_max and h_max. */
-      else if (t_type == task_type_sub_self &&
-               t_subtype == task_subtype_stars_density) {
-        if (cell_is_active_stars(ci, e)) {
-          scheduler_activate(s, t);
-          cell_activate_subcell_stars_tasks(ci, NULL, s);
-        }
-      }
-
-      /* Activate the gravity drift */
-      else if (t_type == task_type_self && t_subtype == task_subtype_grav) {
-        if (cell_is_active_gravity(ci, e)) {
-          scheduler_activate(s, t);
-          cell_activate_subcell_grav_tasks(t->ci, NULL, s);
-        }
-      }
-
-      /* Activate the gravity drift */
-      else if (t_type == task_type_self &&
-               t_subtype == task_subtype_external_grav) {
-        if (cell_is_active_gravity(ci, e)) {
-          scheduler_activate(s, t);
-          cell_activate_drift_gpart(t->ci, s);
-        }
-      }
-
-#ifdef SWIFT_DEBUG_CHECKS
-      else {
-        error("Invalid task type / sub-type encountered");
-      }
-#endif
-    }
-
-    /* Pair? */
-    else if (t_type == task_type_pair || t_type == task_type_sub_pair) {
-
-      /* Local pointers. */
-      struct cell *ci = t->ci;
-      struct cell *cj = t->cj;
-#ifdef WITH_MPI
-      const int ci_nodeID = ci->nodeID;
-      const int cj_nodeID = cj->nodeID;
-#else
-      const int ci_nodeID = nodeID;
-      const int cj_nodeID = nodeID;
-#endif
-      const int ci_active_hydro = cell_is_active_hydro(ci, e);
-      const int cj_active_hydro = cell_is_active_hydro(cj, e);
-      const int ci_active_gravity = cell_is_active_gravity(ci, e);
-      const int cj_active_gravity = cell_is_active_gravity(cj, e);
-      const int ci_active_stars = cell_is_active_stars(ci, e);
-      const int cj_active_stars = cell_is_active_stars(cj, e);
-
-      /* Only activate tasks that involve a local active cell. */
-      if ((t_subtype == task_subtype_density ||
-           t_subtype == task_subtype_gradient ||
-           t_subtype == task_subtype_force) &&
-          ((ci_active_hydro && ci_nodeID == nodeID) ||
-           (cj_active_hydro && cj_nodeID == nodeID))) {
-
-        scheduler_activate(s, t);
-
-        /* Set the correct sorting flags */
-        if (t_type == task_type_pair &&
-            (t_subtype == task_subtype_density ||
-             t_subtype == task_subtype_stars_density)) {
-
-          /* Store some values. */
-          atomic_or(&ci->requires_sorts, 1 << t->flags);
-          atomic_or(&cj->requires_sorts, 1 << t->flags);
-          ci->dx_max_sort_old = ci->dx_max_sort;
-          cj->dx_max_sort_old = cj->dx_max_sort;
-
-          /* Activate the hydro drift tasks. */
-          if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
-          if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s);
-
-          /* Check the sorts and activate them if needed. */
-          cell_activate_sorts(ci, t->flags, s);
-          cell_activate_sorts(cj, t->flags, s);
-
-        }
-
-        /* Store current values of dx_max and h_max. */
-        else if (t_type == task_type_sub_pair &&
-                 (t_subtype == task_subtype_density ||
-                  t_subtype == task_subtype_stars_density)) {
-          cell_activate_subcell_hydro_tasks(t->ci, t->cj, s);
-        }
-      }
-
-      /* Stars */
-      if (t_subtype == task_subtype_stars_density &&
-          ((ci_active_stars && ci->nodeID == engine_rank) ||
-           (cj_active_stars && cj->nodeID == engine_rank))) {
-
-        scheduler_activate(s, t);
-
-        /* Set the correct sorting flags */
-        if (t_type == task_type_pair) {
-
-          /* Store some values. */
-          atomic_or(&ci->requires_sorts, 1 << t->flags);
-          atomic_or(&cj->requires_sorts, 1 << t->flags);
-          ci->dx_max_sort_old = ci->dx_max_sort;
-          cj->dx_max_sort_old = cj->dx_max_sort;
-
-          /* Activate the hydro drift tasks. */
-          if (ci_nodeID == nodeID) {
-            cell_activate_drift_part(ci, s);
-            cell_activate_drift_gpart(ci, s);
-          }
-          if (cj_nodeID == nodeID) {
-            cell_activate_drift_part(cj, s);
-            cell_activate_drift_gpart(cj, s);
-          }
-
-          /* Check the sorts and activate them if needed. */
-          cell_activate_sorts(ci, t->flags, s);
-          cell_activate_sorts(cj, t->flags, s);
-
-        }
-
-        /* Store current values of dx_max and h_max. */
-        else if (t_type == task_type_sub_pair) {
-          cell_activate_subcell_stars_tasks(t->ci, t->cj, s);
-        }
-      }
-
-      if ((t_subtype == task_subtype_grav) &&
-          ((ci_active_gravity && ci_nodeID == nodeID) ||
-           (cj_active_gravity && cj_nodeID == nodeID))) {
-
-        scheduler_activate(s, t);
-
-        if (t_type == task_type_pair && t_subtype == task_subtype_grav) {
-          /* Activate the gravity drift */
-          cell_activate_subcell_grav_tasks(t->ci, t->cj, s);
-        }
-
-#ifdef SWIFT_DEBUG_CHECKS
-        else if (t_type == task_type_sub_pair &&
-                 t_subtype == task_subtype_grav) {
-          error("Invalid task sub-type encountered");
-        }
-#endif
-      }
-
-      /* Only interested in density tasks as of here. */
-      if (t_subtype == task_subtype_density) {
-
-        /* Too much particle movement? */
-        if (cell_need_rebuild_for_pair(ci, cj)) *rebuild_space = 1;
-
-#ifdef WITH_MPI
-        /* Activate the send/recv tasks. */
-        if (ci_nodeID != nodeID) {
-
-          /* If the local cell is active, receive data from the foreign cell. */
-          if (cj_active_hydro) {
-            scheduler_activate(s, ci->recv_xv);
-            if (ci_active_hydro) {
-              scheduler_activate(s, ci->recv_rho);
-#ifdef EXTRA_HYDRO_LOOP
-              scheduler_activate(s, ci->recv_gradient);
-#endif
-            }
-          }
-
-          /* If the foreign cell is active, we want its ti_end values. */
-          if (ci_active_hydro) scheduler_activate(s, ci->recv_ti);
-
-          /* Is the foreign cell active and will need stuff from us? */
-          if (ci_active_hydro) {
-
-            struct link *l = scheduler_activate_send(s, cj->send_xv, ci_nodeID);
-
-            /* Drift the cell which will be sent at the level at which it is
-               sent, i.e. drift the cell specified in the send task (l->t)
-               itself. */
-            cell_activate_drift_part(l->t->ci, s);
-
-            /* If the local cell is also active, more stuff will be needed. */
-            if (cj_active_hydro) {
-              scheduler_activate_send(s, cj->send_rho, ci_nodeID);
-
-#ifdef EXTRA_HYDRO_LOOP
-              scheduler_activate_send(s, cj->send_gradient, ci_nodeID);
-#endif
-            }
-          }
-
-          /* If the local cell is active, send its ti_end values. */
-          if (cj_active_hydro)
-            scheduler_activate_send(s, cj->send_ti, ci_nodeID);
-
-        } else if (cj_nodeID != nodeID) {
-
-          /* If the local cell is active, receive data from the foreign cell. */
-          if (ci_active_hydro) {
-            scheduler_activate(s, cj->recv_xv);
-            if (cj_active_hydro) {
-              scheduler_activate(s, cj->recv_rho);
-#ifdef EXTRA_HYDRO_LOOP
-              scheduler_activate(s, cj->recv_gradient);
-#endif
-            }
-          }
-
-          /* If the foreign cell is active, we want its ti_end values. */
-          if (cj_active_hydro) scheduler_activate(s, cj->recv_ti);
-
-          /* Is the foreign cell active and will need stuff from us? */
-          if (cj_active_hydro) {
-
-            struct link *l = scheduler_activate_send(s, ci->send_xv, cj_nodeID);
-
-            /* Drift the cell which will be sent at the level at which it is
-               sent, i.e. drift the cell specified in the send task (l->t)
-               itself. */
-            cell_activate_drift_part(l->t->ci, s);
-
-            /* If the local cell is also active, more stuff will be needed. */
-            if (ci_active_hydro) {
-
-              scheduler_activate_send(s, ci->send_rho, cj_nodeID);
-
-#ifdef EXTRA_HYDRO_LOOP
-              scheduler_activate_send(s, ci->send_gradient, cj_nodeID);
-#endif
-            }
-          }
-
-          /* If the local cell is active, send its ti_end values. */
-          if (ci_active_hydro)
-            scheduler_activate_send(s, ci->send_ti, cj_nodeID);
-        }
-#endif
-      }
+  int count_recv_cells = 0;
+  int count_send_requests = 0;
+  int count_recv_requests = 0;
 
-      /* Only interested in stars_density tasks as of here. */
-      if (t->subtype == task_subtype_stars_density) {
+  /* Loop over the proxies. */
+  for (int pid = 0; pid < e->nr_proxies; pid++) {
 
-        /* Too much particle movement? */
-        if (cell_need_rebuild_for_pair(ci, cj)) *rebuild_space = 1;
+    /* Get a handle on the proxy. */
+    const struct proxy *p = &e->proxies[pid];
 
-        // LOIC: Need implementing MPI case
-      }
+    /* Now collect the number of requests associated */
+    count_recv_requests += p->nr_cells_in;
+    count_send_requests += p->nr_cells_out;
 
-      /* Only interested in gravity tasks as of here. */
-      if (t_subtype == task_subtype_grav) {
+    /* And the actual number of things we are going to ship */
+    for (int k = 0; k < p->nr_cells_in; k++)
+      count_recv_cells += p->cells_in[k]->mpi.pcell_size;
 
-#ifdef WITH_MPI
-        /* Activate the send/recv tasks. */
-        if (ci_nodeID != nodeID) {
+    for (int k = 0; k < p->nr_cells_out; k++)
+      count_send_cells += p->cells_out[k]->mpi.pcell_size;
+  }
 
-          /* If the local cell is active, receive data from the foreign cell. */
-          if (cj_active_gravity) scheduler_activate(s, ci->recv_grav);
+  /* Allocate the buffers for the packed data */
+  struct gravity_tensors *buffer_send = NULL;
+  if (posix_memalign((void **)&buffer_send, SWIFT_CACHE_ALIGNMENT,
+                     count_send_cells * sizeof(struct gravity_tensors)) != 0)
+    error("Unable to allocate memory for multipole transactions");
 
-          /* If the foreign cell is active, we want its ti_end values. */
-          if (ci_active_gravity) scheduler_activate(s, ci->recv_ti);
+  struct gravity_tensors *buffer_recv = NULL;
+  if (posix_memalign((void **)&buffer_recv, SWIFT_CACHE_ALIGNMENT,
+                     count_recv_cells * sizeof(struct gravity_tensors)) != 0)
+    error("Unable to allocate memory for multipole transactions");
 
-          /* Is the foreign cell active and will need stuff from us? */
-          if (ci_active_gravity) {
+  /* Also allocate the MPI requests */
+  const int count_requests = count_send_requests + count_recv_requests;
+  MPI_Request *requests =
+      (MPI_Request *)malloc(sizeof(MPI_Request) * count_requests);
+  if (requests == NULL) error("Unable to allocate memory for MPI requests");
 
-            struct link *l =
-                scheduler_activate_send(s, cj->send_grav, ci_nodeID);
+  int this_request = 0;
+  int this_recv = 0;
+  int this_send = 0;
 
-            /* Drift the cell which will be sent at the level at which it is
-               sent, i.e. drift the cell specified in the send task (l->t)
-               itself. */
-            cell_activate_drift_gpart(l->t->ci, s);
-          }
+  /* Loop over the proxies to issue the receives. */
+  for (int pid = 0; pid < e->nr_proxies; pid++) {
 
-          /* If the local cell is active, send its ti_end values. */
-          if (cj_active_gravity)
-            scheduler_activate_send(s, cj->send_ti, ci_nodeID);
+    /* Get a handle on the proxy. */
+    const struct proxy *p = &e->proxies[pid];
 
-        } else if (cj_nodeID != nodeID) {
+    for (int k = 0; k < p->nr_cells_in; k++) {
 
-          /* If the local cell is active, receive data from the foreign cell. */
-          if (ci_active_gravity) scheduler_activate(s, cj->recv_grav);
+      const int num_elements = p->cells_in[k]->mpi.pcell_size;
 
-          /* If the foreign cell is active, we want its ti_end values. */
-          if (cj_active_gravity) scheduler_activate(s, cj->recv_ti);
+      /* Receive everything */
+      MPI_Irecv(&buffer_recv[this_recv], num_elements, multipole_mpi_type,
+                p->cells_in[k]->nodeID, p->cells_in[k]->mpi.tag, MPI_COMM_WORLD,
+                &requests[this_request]);
 
-          /* Is the foreign cell active and will need stuff from us? */
-          if (cj_active_gravity) {
+      /* Move to the next slot in the buffers */
+      this_recv += num_elements;
+      this_request++;
+    }
 
-            struct link *l =
-                scheduler_activate_send(s, ci->send_grav, cj_nodeID);
+    /* Loop over the proxies to issue the sends. */
+    for (int k = 0; k < p->nr_cells_out; k++) {
 
-            /* Drift the cell which will be sent at the level at which it is
-               sent, i.e. drift the cell specified in the send task (l->t)
-               itself. */
-            cell_activate_drift_gpart(l->t->ci, s);
-          }
+      /* Number of multipoles in this cell hierarchy */
+      const int num_elements = p->cells_out[k]->mpi.pcell_size;
 
-          /* If the local cell is active, send its ti_end values. */
-          if (ci_active_gravity)
-            scheduler_activate_send(s, ci->send_ti, cj_nodeID);
-        }
-#endif
-      }
-    }
+      /* Let's pack everything recursively */
+      cell_pack_multipoles(p->cells_out[k], &buffer_send[this_send]);
 
-    /* End force ? */
-    else if (t_type == task_type_end_force) {
+      /* Send everything (note the use of cells_in[0] to get the correct node
+       * ID. */
+      MPI_Isend(&buffer_send[this_send], num_elements, multipole_mpi_type,
+                p->cells_in[0]->nodeID, p->cells_out[k]->mpi.tag,
+                MPI_COMM_WORLD, &requests[this_request]);
 
-      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
-        scheduler_activate(s, t);
+      /* Move to the next slot in the buffers */
+      this_send += num_elements;
+      this_request++;
     }
+  }
 
-    /* Kick ? */
-    else if (t_type == task_type_kick1 || t_type == task_type_kick2) {
-
-      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
-        scheduler_activate(s, t);
+  /* Wait for all the requests to arrive home */
+  MPI_Status *stats = (MPI_Status *)malloc(count_requests * sizeof(MPI_Status));
+  int res;
+  if ((res = MPI_Waitall(count_requests, requests, stats)) != MPI_SUCCESS) {
+    for (int k = 0; k < count_requests; ++k) {
+      char buff[MPI_MAX_ERROR_STRING];
+      MPI_Error_string(stats[k].MPI_ERROR, buff, &res);
+      message("request from source %i, tag %i has error '%s'.",
+              stats[k].MPI_SOURCE, stats[k].MPI_TAG, buff);
     }
+    error("Failed during waitall for multipole data.");
+  }
 
-    /* Hydro ghost tasks ? */
-    else if (t_type == task_type_ghost || t_type == task_type_extra_ghost ||
-             t_type == task_type_ghost_in || t_type == task_type_ghost_out) {
-      if (cell_is_active_hydro(t->ci, e)) scheduler_activate(s, t);
-    }
+  /* Let's now unpack the multipoles at the right place */
+  this_recv = 0;
+  for (int pid = 0; pid < e->nr_proxies; pid++) {
 
-    /* Gravity stuff ? */
-    else if (t_type == task_type_grav_down || t_type == task_type_grav_mesh ||
-             t_type == task_type_grav_long_range ||
-             t_type == task_type_init_grav ||
-             t_type == task_type_init_grav_out ||
-             t_type == task_type_grav_down_in) {
-      if (cell_is_active_gravity(t->ci, e)) scheduler_activate(s, t);
-    }
+    /* Get a handle on the proxy. */
+    const struct proxy *p = &e->proxies[pid];
 
-    /* Multipole - Multipole interaction task */
-    else if (t_type == task_type_grav_mm) {
+    for (int k = 0; k < p->nr_cells_in; k++) {
 
-      /* Local pointers. */
-      const struct cell *ci = t->ci;
-      const struct cell *cj = t->cj;
-#ifdef WITH_MPI
-      const int ci_nodeID = ci->nodeID;
-      const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1;
-#else
-      const int ci_nodeID = nodeID;
-      const int cj_nodeID = nodeID;
-#endif
-      const int ci_active_gravity = cell_is_active_gravity_mm(ci, e);
-      const int cj_active_gravity = cell_is_active_gravity_mm(cj, e);
+      const int num_elements = p->cells_in[k]->mpi.pcell_size;
 
-      if ((ci_active_gravity && ci_nodeID == nodeID) ||
-          (cj_active_gravity && cj_nodeID == nodeID))
-        scheduler_activate(s, t);
-    }
+#ifdef SWIFT_DEBUG_CHECKS
 
-    /* Star ghost tasks ? */
-    else if (t_type == task_type_stars_ghost ||
-             t_type == task_type_stars_ghost_in ||
-             t_type == task_type_stars_ghost_out) {
-      if (cell_is_active_stars(t->ci, e)) scheduler_activate(s, t);
-    }
+      /* Check that the first element (top-level cell's multipole) matches what
+       * we received */
+      if (p->cells_in[k]->grav.multipole->m_pole.num_gpart !=
+          buffer_recv[this_recv].m_pole.num_gpart)
+        error("Current: M_000=%e num_gpart=%lld\n New: M_000=%e num_gpart=%lld",
+              p->cells_in[k]->grav.multipole->m_pole.M_000,
+              p->cells_in[k]->grav.multipole->m_pole.num_gpart,
+              buffer_recv[this_recv].m_pole.M_000,
+              buffer_recv[this_recv].m_pole.num_gpart);
+#endif
 
-    /* Time-step? */
-    else if (t_type == task_type_timestep) {
-      t->ci->updated = 0;
-      t->ci->g_updated = 0;
-      t->ci->s_updated = 0;
-      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
-        scheduler_activate(s, t);
-    }
+      /* Unpack recursively */
+      cell_unpack_multipoles(p->cells_in[k], &buffer_recv[this_recv]);
 
-    /* Subgrid tasks */
-    else if (t_type == task_type_cooling) {
-      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
-        scheduler_activate(s, t);
+      /* Move to the next slot in the buffers */
+      this_recv += num_elements;
     }
   }
-}
-
-/**
- * @brief Mark tasks to be un-skipped and set the sort flags accordingly.
- *
- * @return 1 if the space has to be rebuilt, 0 otherwise.
- */
-int engine_marktasks(struct engine *e) {
-
-  struct scheduler *s = &e->sched;
-  const ticks tic = getticks();
-  int rebuild_space = 0;
 
-  /* Run through the tasks and mark as skip or not. */
-  size_t extra_data[3] = {(size_t)e, (size_t)rebuild_space, (size_t)&e->sched};
-  threadpool_map(&e->threadpool, engine_marktasks_mapper, s->tasks, s->nr_tasks,
-                 sizeof(struct task), 0, extra_data);
-  rebuild_space = extra_data[1];
+  /* Free everything */
+  free(stats);
+  free(buffer_send);
+  free(buffer_recv);
+  free(requests);
 
+  /* How much time did this take? */
   if (e->verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
-
-  /* All is well... */
-  return rebuild_space;
+#else
+  error("SWIFT was not compiled with MPI support.");
+#endif
 }
 
 /**
@@ -4297,6 +1916,9 @@ int engine_estimate_nr_tasks(struct engine *e) {
   if (e->policy & engine_policy_stars) {
     n1 += 2;
   }
+#if defined(WITH_LOGGER)
+  n1 += 1;
+#endif
 
 #ifdef WITH_MPI
 
@@ -4310,7 +1932,7 @@ int engine_estimate_nr_tasks(struct engine *e) {
     struct cell *c = &e->s->cells_top[k];
 
     /* Any cells with particles will have tasks (local & foreign). */
-    int nparts = c->count + c->gcount + c->scount;
+    int nparts = c->hydro.count + c->grav.count + c->stars.count;
     if (nparts > 0) {
       ntop++;
       ncells++;
@@ -4351,10 +1973,12 @@ int engine_estimate_nr_tasks(struct engine *e) {
  * @brief Rebuild the space and tasks.
  *
  * @param e The #engine.
+ * @param repartitioned Did we just redistribute?
  * @param clean_smoothing_length_values Are we cleaning up the values of
  * the smoothing lengths before building the tasks ?
  */
-void engine_rebuild(struct engine *e, int clean_smoothing_length_values) {
+void engine_rebuild(struct engine *e, int repartitioned,
+                    int clean_smoothing_length_values) {
 
   const ticks tic = getticks();
 
@@ -4363,10 +1987,23 @@ void engine_rebuild(struct engine *e, int clean_smoothing_length_values) {
   e->restarting = 0;
 
   /* Re-build the space. */
-  space_rebuild(e->s, e->verbose);
+  space_rebuild(e->s, repartitioned, e->verbose);
 
-  /* Construct the list of purely local cells */
-  space_list_local_cells(e->s);
+  /* Update the global counters of particles */
+  long long num_particles[3] = {e->s->nr_parts, e->s->nr_gparts,
+                                e->s->nr_sparts};
+#ifdef WITH_MPI
+  MPI_Allreduce(MPI_IN_PLACE, num_particles, 3, MPI_LONG_LONG, MPI_SUM,
+                MPI_COMM_WORLD);
+#endif
+  e->total_nr_parts = num_particles[0];
+  e->total_nr_gparts = num_particles[1];
+  e->total_nr_sparts = num_particles[2];
+
+  /* Flag that there are no inhibited particles */
+  e->nr_inhibited_parts = 0;
+  e->nr_inhibited_gparts = 0;
+  e->nr_inhibited_sparts = 0;
 
   /* Re-compute the mesh forces */
   if ((e->policy & engine_policy_self_gravity) && e->s->periodic)
@@ -4411,7 +2048,7 @@ void engine_rebuild(struct engine *e, int clean_smoothing_length_values) {
   engine_maketasks(e);
 
   /* Make the list of top-level cells that have tasks */
-  space_list_cells_with_tasks(e->s);
+  space_list_useful_top_level_cells(e->s);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that all cells have been drifted to the current time.
@@ -4457,15 +2094,22 @@ void engine_prepare(struct engine *e) {
   const ticks tic = getticks();
 
   int drifted_all = 0;
+  int repartitioned = 0;
 
   /* Unskip active tasks and check for rebuild */
   if (!e->forcerebuild && !e->forcerepart && !e->restarting) engine_unskip(e);
 
+  const ticks tic2 = getticks();
+
 #ifdef WITH_MPI
   MPI_Allreduce(MPI_IN_PLACE, &e->forcerebuild, 1, MPI_INT, MPI_MAX,
                 MPI_COMM_WORLD);
 #endif
 
+  if (e->verbose)
+    message("Communicating rebuild flag took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
   /* Do we need repartitioning ? */
   if (e->forcerepart) {
 
@@ -4475,6 +2119,7 @@ void engine_prepare(struct engine *e) {
 
     /* And repartition */
     engine_repartition(e);
+    repartitioned = 1;
   }
 
   /* Do we need rebuilding ? */
@@ -4484,7 +2129,7 @@ void engine_prepare(struct engine *e) {
     if (!e->restarting && !drifted_all) engine_drift_all(e);
 
     /* And rebuild */
-    engine_rebuild(e, 0);
+    engine_rebuild(e, repartitioned, 0);
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -4540,13 +2185,14 @@ void engine_collect_end_of_step_recurse(struct cell *c,
 
 /* Skip super-cells (Their values are already set) */
 #ifdef WITH_MPI
-  if (c->timestep != NULL || c->recv_ti != NULL) return;
+  if (c->timestep != NULL || c->mpi.recv_ti != NULL) return;
 #else
   if (c->timestep != NULL) return;
 #endif /* WITH_MPI */
 
   /* Counters for the different quantities. */
   size_t updated = 0, g_updated = 0, s_updated = 0;
+  size_t inhibited = 0, g_inhibited = 0, s_inhibited = 0;
   integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0,
                 ti_hydro_beg_max = 0;
   integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0,
@@ -4555,41 +2201,49 @@ void engine_collect_end_of_step_recurse(struct cell *c,
   /* Collect the values from the progeny. */
   for (int k = 0; k < 8; k++) {
     struct cell *cp = c->progeny[k];
-    if (cp != NULL && (cp->count > 0 || cp->gcount > 0 || cp->scount > 0)) {
+    if (cp != NULL &&
+        (cp->hydro.count > 0 || cp->grav.count > 0 || cp->stars.count > 0)) {
 
       /* Recurse */
       engine_collect_end_of_step_recurse(cp, e);
 
       /* And update */
-      ti_hydro_end_min = min(ti_hydro_end_min, cp->ti_hydro_end_min);
-      ti_hydro_end_max = max(ti_hydro_end_max, cp->ti_hydro_end_max);
-      ti_hydro_beg_max = max(ti_hydro_beg_max, cp->ti_hydro_beg_max);
+      ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min);
+      ti_hydro_end_max = max(ti_hydro_end_max, cp->hydro.ti_end_max);
+      ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max);
 
-      ti_gravity_end_min = min(ti_gravity_end_min, cp->ti_gravity_end_min);
-      ti_gravity_end_max = max(ti_gravity_end_max, cp->ti_gravity_end_max);
-      ti_gravity_beg_max = max(ti_gravity_beg_max, cp->ti_gravity_beg_max);
+      ti_gravity_end_min = min(ti_gravity_end_min, cp->grav.ti_end_min);
+      ti_gravity_end_max = max(ti_gravity_end_max, cp->grav.ti_end_max);
+      ti_gravity_beg_max = max(ti_gravity_beg_max, cp->grav.ti_beg_max);
 
-      updated += cp->updated;
-      g_updated += cp->g_updated;
-      s_updated += cp->s_updated;
+      updated += cp->hydro.updated;
+      g_updated += cp->grav.updated;
+      s_updated += cp->stars.updated;
+
+      inhibited += cp->hydro.inhibited;
+      g_inhibited += cp->grav.inhibited;
+      s_inhibited += cp->stars.inhibited;
 
       /* Collected, so clear for next time. */
-      cp->updated = 0;
-      cp->g_updated = 0;
-      cp->s_updated = 0;
+      cp->hydro.updated = 0;
+      cp->grav.updated = 0;
+      cp->stars.updated = 0;
     }
   }
 
   /* Store the collected values in the cell. */
-  c->ti_hydro_end_min = ti_hydro_end_min;
-  c->ti_hydro_end_max = ti_hydro_end_max;
-  c->ti_hydro_beg_max = ti_hydro_beg_max;
-  c->ti_gravity_end_min = ti_gravity_end_min;
-  c->ti_gravity_end_max = ti_gravity_end_max;
-  c->ti_gravity_beg_max = ti_gravity_beg_max;
-  c->updated = updated;
-  c->g_updated = g_updated;
-  c->s_updated = s_updated;
+  c->hydro.ti_end_min = ti_hydro_end_min;
+  c->hydro.ti_end_max = ti_hydro_end_max;
+  c->hydro.ti_beg_max = ti_hydro_beg_max;
+  c->grav.ti_end_min = ti_gravity_end_min;
+  c->grav.ti_end_max = ti_gravity_end_max;
+  c->grav.ti_beg_max = ti_gravity_beg_max;
+  c->hydro.updated = updated;
+  c->grav.updated = g_updated;
+  c->stars.updated = s_updated;
+  c->hydro.inhibited = inhibited;
+  c->grav.inhibited = g_inhibited;
+  c->stars.inhibited = s_inhibited;
 }
 
 /**
@@ -4612,7 +2266,8 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements,
   int *local_cells = (int *)map_data;
 
   /* Local collectible */
-  size_t updates = 0, g_updates = 0, s_updates = 0;
+  size_t updated = 0, g_updated = 0, s_updated = 0;
+  size_t inhibited = 0, g_inhibited = 0, s_inhibited = 0;
   integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0,
                 ti_hydro_beg_max = 0;
   integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0,
@@ -4621,39 +2276,47 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements,
   for (int ind = 0; ind < num_elements; ind++) {
     struct cell *c = &s->cells_top[local_cells[ind]];
 
-    if (c->count > 0 || c->gcount > 0 || c->scount > 0) {
+    if (c->hydro.count > 0 || c->grav.count > 0 || c->stars.count > 0) {
 
       /* Make the top-cells recurse */
       engine_collect_end_of_step_recurse(c, e);
 
       /* And aggregate */
-      if (c->ti_hydro_end_min > e->ti_current)
-        ti_hydro_end_min = min(ti_hydro_end_min, c->ti_hydro_end_min);
-      ti_hydro_end_max = max(ti_hydro_end_max, c->ti_hydro_end_max);
-      ti_hydro_beg_max = max(ti_hydro_beg_max, c->ti_hydro_beg_max);
+      if (c->hydro.ti_end_min > e->ti_current)
+        ti_hydro_end_min = min(ti_hydro_end_min, c->hydro.ti_end_min);
+      ti_hydro_end_max = max(ti_hydro_end_max, c->hydro.ti_end_max);
+      ti_hydro_beg_max = max(ti_hydro_beg_max, c->hydro.ti_beg_max);
 
-      if (c->ti_gravity_end_min > e->ti_current)
-        ti_gravity_end_min = min(ti_gravity_end_min, c->ti_gravity_end_min);
-      ti_gravity_end_max = max(ti_gravity_end_max, c->ti_gravity_end_max);
-      ti_gravity_beg_max = max(ti_gravity_beg_max, c->ti_gravity_beg_max);
+      if (c->grav.ti_end_min > e->ti_current)
+        ti_gravity_end_min = min(ti_gravity_end_min, c->grav.ti_end_min);
+      ti_gravity_end_max = max(ti_gravity_end_max, c->grav.ti_end_max);
+      ti_gravity_beg_max = max(ti_gravity_beg_max, c->grav.ti_beg_max);
 
-      updates += c->updated;
-      g_updates += c->g_updated;
-      s_updates += c->s_updated;
+      updated += c->hydro.updated;
+      g_updated += c->grav.updated;
+      s_updated += c->stars.updated;
+
+      inhibited += c->hydro.inhibited;
+      g_inhibited += c->grav.inhibited;
+      s_inhibited += c->stars.inhibited;
 
       /* Collected, so clear for next time. */
-      c->updated = 0;
-      c->g_updated = 0;
-      c->s_updated = 0;
+      c->hydro.updated = 0;
+      c->grav.updated = 0;
+      c->stars.updated = 0;
     }
   }
 
   /* Let's write back to the global data.
    * We use the space lock to garanty single access*/
   if (lock_lock(&s->lock) == 0) {
-    data->updates += updates;
-    data->g_updates += g_updates;
-    data->s_updates += s_updates;
+    data->updated += updated;
+    data->g_updated += g_updated;
+    data->s_updated += s_updated;
+
+    data->inhibited += inhibited;
+    data->g_inhibited += g_inhibited;
+    data->s_inhibited += s_inhibited;
 
     if (ti_hydro_end_min > e->ti_current)
       data->ti_hydro_end_min = min(ti_hydro_end_min, data->ti_hydro_end_min);
@@ -4692,9 +2355,10 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements,
 void engine_collect_end_of_step(struct engine *e, int apply) {
 
   const ticks tic = getticks();
-  const struct space *s = e->s;
+  struct space *s = e->s;
   struct end_of_step_data data;
-  data.updates = 0, data.g_updates = 0, data.s_updates = 0;
+  data.updated = 0, data.g_updated = 0, data.s_updated = 0;
+  data.inhibited = 0, data.g_inhibited = 0, data.s_inhibited = 0;
   data.ti_hydro_end_min = max_nr_timesteps, data.ti_hydro_end_max = 0,
   data.ti_hydro_beg_max = 0;
   data.ti_gravity_end_min = max_nr_timesteps, data.ti_gravity_end_max = 0,
@@ -4706,12 +2370,17 @@ void engine_collect_end_of_step(struct engine *e, int apply) {
                  s->local_cells_with_tasks_top, s->nr_local_cells_with_tasks,
                  sizeof(int), 0, &data);
 
+  /* Store the local number of inhibited particles */
+  s->nr_inhibited_parts = data.inhibited;
+  s->nr_inhibited_gparts = data.g_inhibited;
+  s->nr_inhibited_sparts = data.s_inhibited;
+
   /* Store these in the temporary collection group. */
-  collectgroup1_init(&e->collect_group1, data.updates, data.g_updates,
-                     data.s_updates, data.ti_hydro_end_min,
-                     data.ti_hydro_end_max, data.ti_hydro_beg_max,
-                     data.ti_gravity_end_min, data.ti_gravity_end_max,
-                     data.ti_gravity_beg_max, e->forcerebuild);
+  collectgroup1_init(
+      &e->collect_group1, data.updated, data.g_updated, data.s_updated,
+      data.inhibited, data.g_inhibited, data.s_inhibited, data.ti_hydro_end_min,
+      data.ti_hydro_end_max, data.ti_hydro_beg_max, data.ti_gravity_end_min,
+      data.ti_gravity_end_max, data.ti_gravity_beg_max, e->forcerebuild);
 
 /* Aggregate collective data from the different nodes for this step. */
 #ifdef WITH_MPI
@@ -4736,21 +2405,37 @@ void engine_collect_end_of_step(struct engine *e, int apply) {
             in_i[1], e->collect_group1.ti_gravity_end_min);
 
     long long in_ll[3], out_ll[3];
-    out_ll[0] = data.updates;
-    out_ll[1] = data.g_updates;
-    out_ll[2] = data.s_updates;
+    out_ll[0] = data.updated;
+    out_ll[1] = data.g_updated;
+    out_ll[2] = data.s_updated;
+    if (MPI_Allreduce(out_ll, in_ll, 3, MPI_LONG_LONG_INT, MPI_SUM,
+                      MPI_COMM_WORLD) != MPI_SUCCESS)
+      error("Failed to aggregate particle counts.");
+    if (in_ll[0] != (long long)e->collect_group1.updated)
+      error("Failed to get same updated, is %lld, should be %lld", in_ll[0],
+            e->collect_group1.updated);
+    if (in_ll[1] != (long long)e->collect_group1.g_updated)
+      error("Failed to get same g_updated, is %lld, should be %lld", in_ll[1],
+            e->collect_group1.g_updated);
+    if (in_ll[2] != (long long)e->collect_group1.s_updated)
+      error("Failed to get same s_updated, is %lld, should be %lld", in_ll[2],
+            e->collect_group1.s_updated);
+
+    out_ll[0] = data.inhibited;
+    out_ll[1] = data.g_inhibited;
+    out_ll[2] = data.s_inhibited;
     if (MPI_Allreduce(out_ll, in_ll, 3, MPI_LONG_LONG_INT, MPI_SUM,
                       MPI_COMM_WORLD) != MPI_SUCCESS)
       error("Failed to aggregate particle counts.");
-    if (in_ll[0] != (long long)e->collect_group1.updates)
-      error("Failed to get same updates, is %lld, should be %lld", in_ll[0],
-            e->collect_group1.updates);
-    if (in_ll[1] != (long long)e->collect_group1.g_updates)
-      error("Failed to get same g_updates, is %lld, should be %lld", in_ll[1],
-            e->collect_group1.g_updates);
-    if (in_ll[2] != (long long)e->collect_group1.s_updates)
-      error("Failed to get same s_updates, is %lld, should be %lld", in_ll[2],
-            e->collect_group1.s_updates);
+    if (in_ll[0] != (long long)e->collect_group1.inhibited)
+      error("Failed to get same inhibited, is %lld, should be %lld", in_ll[0],
+            e->collect_group1.inhibited);
+    if (in_ll[1] != (long long)e->collect_group1.g_inhibited)
+      error("Failed to get same g_inhibited, is %lld, should be %lld", in_ll[1],
+            e->collect_group1.g_inhibited);
+    if (in_ll[2] != (long long)e->collect_group1.s_inhibited)
+      error("Failed to get same s_inhibited, is %lld, should be %lld", in_ll[2],
+            e->collect_group1.s_inhibited);
 
     int buff = 0;
     if (MPI_Allreduce(&e->forcerebuild, &buff, 1, MPI_INT, MPI_MAX,
@@ -4979,7 +2664,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
   if (e->nodeID == 0) message("Computing initial gas densities.");
 
   /* Construct all cells and tasks to start everything */
-  engine_rebuild(e, clean_h_values);
+  engine_rebuild(e, 0, clean_h_values);
 
   /* No time integration. We just want the density and ghosts */
   engine_skip_force_and_kick(e);
@@ -4992,6 +2677,15 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
   space_init_gparts(s, e->verbose);
   space_init_sparts(s, e->verbose);
 
+#ifdef WITH_LOGGER
+  /* Mark the first time step in the particle logger file. */
+  logger_log_timestamp(e->logger, e->ti_current, e->time,
+                       &e->logger->timestamp_offset);
+  /* Make sure that we have enough space in the particle logger file
+   * to store the particles in current time step. */
+  logger_ensure_size(e->logger, e->total_nr_parts, e->total_nr_gparts, 0);
+#endif
+
   /* Now, launch the calculation */
   TIMER_TIC;
   engine_launch(e);
@@ -5020,7 +2714,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
   long long num_gpart_mpole = 0;
   if (e->policy & engine_policy_self_gravity) {
     for (int i = 0; i < e->s->nr_cells; ++i)
-      num_gpart_mpole += e->s->cells_top[i].multipole->m_pole.num_gpart;
+      num_gpart_mpole += e->s->cells_top[i].grav.multipole->m_pole.num_gpart;
     if (num_gpart_mpole != e->total_nr_gparts)
       error(
           "Top-level multipoles don't contain the total number of gpart "
@@ -5054,6 +2748,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
 #endif
 
   if (e->nodeID == 0) scheduler_write_dependencies(&e->sched, e->verbose);
+  if (e->nodeID == 0) scheduler_write_task_level(&e->sched);
 
   /* Run the 0th time-step */
   TIMER_TIC2;
@@ -5124,12 +2819,27 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
   if (s->cells_top != NULL && s->nr_parts > 0) {
     for (int i = 0; i < s->nr_cells; i++) {
       struct cell *c = &s->cells_top[i];
-      if (c->nodeID == engine_rank && c->count > 0) {
-        float part_h_max = c->parts[0].h;
-        for (int k = 1; k < c->count; k++) {
-          if (c->parts[k].h > part_h_max) part_h_max = c->parts[k].h;
+      if (c->nodeID == engine_rank && c->hydro.count > 0) {
+        float part_h_max = c->hydro.parts[0].h;
+        for (int k = 1; k < c->hydro.count; k++) {
+          if (c->hydro.parts[k].h > part_h_max)
+            part_h_max = c->hydro.parts[k].h;
+        }
+        c->hydro.h_max = max(part_h_max, c->hydro.h_max);
+      }
+    }
+  }
+
+  if (s->cells_top != NULL && s->nr_sparts > 0) {
+    for (int i = 0; i < s->nr_cells; i++) {
+      struct cell *c = &s->cells_top[i];
+      if (c->nodeID == engine_rank && c->stars.count > 0) {
+        float spart_h_max = c->stars.parts[0].h;
+        for (int k = 1; k < c->stars.count; k++) {
+          if (c->stars.parts[k].h > spart_h_max)
+            spart_h_max = c->stars.parts[k].h;
         }
-        c->h_max = max(part_h_max, c->h_max);
+        c->stars.h_max = max(spart_h_max, c->stars.h_max);
       }
     }
   }
@@ -5189,7 +2899,7 @@ void engine_step(struct engine *e) {
   }
 
   /* We need some cells to exist but not the whole task stuff. */
-  if (e->restarting) space_rebuild(e->s, e->verbose);
+  if (e->restarting) space_rebuild(e->s, 0, e->verbose);
 
   /* Move forward in time */
   e->ti_old = e->ti_current;
@@ -5228,6 +2938,15 @@ void engine_step(struct engine *e) {
        ((double)e->total_nr_gparts) * e->gravity_properties->rebuild_frequency))
     e->forcerebuild = 1;
 
+#ifdef WITH_LOGGER
+  /* Mark the current time step in the particle logger file. */
+  logger_log_timestamp(e->logger, e->ti_current, e->time,
+                       &e->logger->timestamp_offset);
+  /* Make sure that we have enough space in the particle logger file
+   * to store the particles in current time step. */
+  logger_ensure_size(e->logger, e->total_nr_parts, e->total_nr_gparts, 0);
+#endif
+
   /* Are we drifting everything (a la Gadget/GIZMO) ? */
   if (e->policy & engine_policy_drift_all && !e->forcerebuild)
     engine_drift_all(e);
@@ -5260,7 +2979,7 @@ void engine_step(struct engine *e) {
   long long num_gpart_mpole = 0;
   if (e->policy & engine_policy_self_gravity) {
     for (int i = 0; i < e->s->nr_cells; ++i)
-      num_gpart_mpole += e->s->cells_top[i].multipole->m_pole.num_gpart;
+      num_gpart_mpole += e->s->cells_top[i].grav.multipole->m_pole.num_gpart;
     if (num_gpart_mpole != e->total_nr_gparts)
       error(
           "Multipoles don't contain the total number of gpart mpoles=%lld "
@@ -5289,9 +3008,9 @@ void engine_step(struct engine *e) {
   /* Collect information about the next time-step */
   engine_collect_end_of_step(e, 1);
   e->forcerebuild = e->collect_group1.forcerebuild;
-  e->updates_since_rebuild += e->collect_group1.updates;
-  e->g_updates_since_rebuild += e->collect_group1.g_updates;
-  e->s_updates_since_rebuild += e->collect_group1.s_updates;
+  e->updates_since_rebuild += e->collect_group1.updated;
+  e->g_updates_since_rebuild += e->collect_group1.g_updated;
+  e->s_updates_since_rebuild += e->collect_group1.s_updated;
 
 #ifdef SWIFT_DEBUG_CHECKS
   if (e->ti_end_min == e->ti_current && e->ti_end_min < max_nr_timesteps)
@@ -5329,6 +3048,9 @@ void engine_step(struct engine *e) {
  */
 void engine_check_for_dumps(struct engine *e) {
 
+  const int with_stf = (e->policy & engine_policy_structure_finding);
+  const int stf_time_output = (e->stf_output_freq_format == io_stf_time);
+
   /* Save some statistics ? */
   int save_stats = 0;
   if (e->ti_end_min > e->ti_next_stats && e->ti_next_stats > 0) save_stats = 1;
@@ -5345,13 +3067,11 @@ void engine_check_for_dumps(struct engine *e) {
 
   /* Do we want to perform structure finding? */
   int run_stf = 0;
-  if ((e->policy & engine_policy_structure_finding)) {
-    if (e->stf_output_freq_format == io_stf_steps &&
-        e->step % e->delta_step_stf == 0)
-      run_stf = 1;
-    else if (e->stf_output_freq_format == io_stf_time &&
-             e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0)
-      run_stf = 1;
+  if (with_stf && stf_time_output) {
+    if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) run_stf = 1;
+  }
+  if (with_stf && !stf_time_output) {
+    if (e->step % e->delta_step_stf == 0) run_stf = 1;
   }
   /* Do we want to perform a FOF search? */
   if (e->policy & engine_policy_fof)
@@ -5380,23 +3100,36 @@ void engine_check_for_dumps(struct engine *e) {
         /* Let's fake that we are at the common dump time */
         e->ti_current = e->ti_next_snapshot;
         e->max_active_bin = 0;
-        if (!(e->policy & engine_policy_cosmology))
-          e->time = e->ti_next_snapshot * e->time_base + e->time_begin;
+        if ((e->policy & engine_policy_cosmology)) {
+          cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
+          e->time = e->cosmology->time;
+        } else {
+          e->time = e->ti_next_stats * e->time_base + e->time_begin;
+        }
 
         /* Drift everyone */
         engine_drift_all(e);
 
         /* Dump everything */
         engine_print_stats(e);
+#ifdef WITH_LOGGER
+        /* Write a file containing the offsets in the particle logger. */
+        engine_dump_index(e);
+#else
         engine_dump_snapshot(e);
+#endif
 
       } else if (e->ti_next_stats < e->ti_next_snapshot) {
 
         /* Let's fake that we are at the stats dump time */
         e->ti_current = e->ti_next_stats;
         e->max_active_bin = 0;
-        if (!(e->policy & engine_policy_cosmology))
+        if ((e->policy & engine_policy_cosmology)) {
+          cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
+          e->time = e->cosmology->time;
+        } else {
           e->time = e->ti_next_stats * e->time_base + e->time_begin;
+        }
 
         /* Drift everyone */
         engine_drift_all(e);
@@ -5414,21 +3147,35 @@ void engine_check_for_dumps(struct engine *e) {
         engine_drift_all(e);
 
         /* Dump snapshot */
+#ifdef WITH_LOGGER
+        /* Write a file containing the offsets in the particle logger. */
+        engine_dump_index(e);
+#else
         engine_dump_snapshot(e);
+#endif
 
       } else if (e->ti_next_stats > e->ti_next_snapshot) {
 
         /* Let's fake that we are at the snapshot dump time */
         e->ti_current = e->ti_next_snapshot;
         e->max_active_bin = 0;
-        if (!(e->policy & engine_policy_cosmology))
-          e->time = e->ti_next_snapshot * e->time_base + e->time_begin;
+        if ((e->policy & engine_policy_cosmology)) {
+          cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
+          e->time = e->cosmology->time;
+        } else {
+          e->time = e->ti_next_stats * e->time_base + e->time_begin;
+        }
 
         /* Drift everyone */
         engine_drift_all(e);
 
         /* Dump snapshot */
+#ifdef WITH_LOGGER
+        /* Write a file containing the offsets in the particle logger. */
+        engine_dump_index(e);
+#else
         engine_dump_snapshot(e);
+#endif
 
         /* Let's fake that we are at the stats dump time */
         e->ti_current = e->ti_next_stats;
@@ -5452,14 +3199,23 @@ void engine_check_for_dumps(struct engine *e) {
       /* Let's fake that we are at the snapshot dump time */
       e->ti_current = e->ti_next_snapshot;
       e->max_active_bin = 0;
-      if (!(e->policy & engine_policy_cosmology))
-        e->time = e->ti_next_snapshot * e->time_base + e->time_begin;
+      if ((e->policy & engine_policy_cosmology)) {
+        cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
+        e->time = e->cosmology->time;
+      } else {
+        e->time = e->ti_next_stats * e->time_base + e->time_begin;
+      }
 
       /* Drift everyone */
       engine_drift_all(e);
 
       /* Dump... */
+#ifdef WITH_LOGGER
+      /* Write a file containing the offsets in the particle logger. */
+      engine_dump_index(e);
+#else
       engine_dump_snapshot(e);
+#endif
 
       /* ... and find the next output time */
       engine_compute_next_snapshot_time(e);
@@ -5469,8 +3225,12 @@ void engine_check_for_dumps(struct engine *e) {
       /* Let's fake that we are at the stats dump time */
       e->ti_current = e->ti_next_stats;
       e->max_active_bin = 0;
-      if (!(e->policy & engine_policy_cosmology))
+      if ((e->policy & engine_policy_cosmology)) {
+        cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
+        e->time = e->cosmology->time;
+      } else {
         e->time = e->ti_next_stats * e->time_base + e->time_begin;
+      }
 
       /* Drift everyone */
       engine_drift_all(e);
@@ -5485,10 +3245,25 @@ void engine_check_for_dumps(struct engine *e) {
     /* Perform structure finding? */
     if (run_stf) {
 
-    // MATTHIEU: Add a drift_all here. And check the order with the other i/o
-    // options.
-
 #ifdef HAVE_VELOCIRAPTOR
+
+      // MATTHIEU: Check the order with the other i/o options.
+      if (!dump_snapshot && !save_stats) {
+
+        /* Let's fake that we are at the stats dump time */
+        e->ti_current = e->ti_next_stf;
+        e->max_active_bin = 0;
+        if ((e->policy & engine_policy_cosmology)) {
+          cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
+          e->time = e->cosmology->time;
+        } else {
+          e->time = e->ti_next_stats * e->time_base + e->time_begin;
+        }
+
+        /* Drift everyone */
+        engine_drift_all(e);
+      }
+
       velociraptor_init(e);
       velociraptor_invoke(e);
 
@@ -5524,14 +3299,15 @@ void engine_check_for_dumps(struct engine *e) {
 
     /* Do we want to perform structure finding? */
     run_stf = 0;
-    if ((e->policy & engine_policy_structure_finding) &&
-        e->stf_output_freq_format == io_stf_time) {
+    if (with_stf && stf_time_output) {
       if (e->ti_end_min > e->ti_next_stf && e->ti_next_stf > 0) run_stf = 1;
     }
   }
 
   /* Restore the information we stored */
   e->ti_current = ti_current;
+  if (e->policy & engine_policy_cosmology)
+    cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
   e->max_active_bin = max_active_bin;
   e->time = time;
 }
@@ -5647,12 +3423,38 @@ void engine_unskip(struct engine *e) {
 void engine_do_drift_all_mapper(void *map_data, int num_elements,
                                 void *extra_data) {
 
-  struct engine *e = (struct engine *)extra_data;
-  struct cell *cells = (struct cell *)map_data;
+  const struct engine *e = (const struct engine *)extra_data;
+  const int restarting = e->restarting;
+  struct space *s = e->s;
+  struct cell *cells_top;
+  int *local_cells_with_tasks_top;
+
+  if (restarting) {
+
+    /* When restarting, we loop over all top-level cells */
+    cells_top = (struct cell *)map_data;
+    local_cells_with_tasks_top = NULL;
+
+  } else {
+
+    /* In any other case, we use th list of local cells with tasks */
+    cells_top = s->cells_top;
+    local_cells_with_tasks_top = (int *)map_data;
+  }
 
   for (int ind = 0; ind < num_elements; ind++) {
-    struct cell *c = &cells[ind];
-    if (c != NULL && c->nodeID == e->nodeID) {
+
+    struct cell *c;
+
+    /* When restarting, the list of local cells with tasks does not
+       yet exist. We use the raw list of top-level cells instead */
+    if (restarting)
+      c = &cells_top[ind];
+    else
+      c = &cells_top[local_cells_with_tasks_top[ind]];
+
+    if (c->nodeID == e->nodeID) {
+
       /* Drift all the particles */
       cell_drift_part(c, e, 1);
 
@@ -5688,8 +3490,19 @@ void engine_drift_all(struct engine *e) {
   }
 #endif
 
-  threadpool_map(&e->threadpool, engine_do_drift_all_mapper, e->s->cells_top,
-                 e->s->nr_cells, sizeof(struct cell), 0, e);
+  if (!e->restarting) {
+
+    /* Normal case: We have a list of local cells with tasks to play with */
+    threadpool_map(&e->threadpool, engine_do_drift_all_mapper,
+                   e->s->local_cells_with_tasks_top,
+                   e->s->nr_local_cells_with_tasks, sizeof(int), 0, e);
+  } else {
+
+    /* When restarting, the list of local cells with tasks does not yet
+       exist. We use the raw list of top-level cells instead */
+    threadpool_map(&e->threadpool, engine_do_drift_all_mapper, e->s->cells_top,
+                   e->s->nr_cells, sizeof(struct cell), 0, e);
+  }
 
   /* Synchronize particle positions */
   space_synchronize_particle_positions(e->s);
@@ -5726,7 +3539,7 @@ void engine_do_drift_top_multipoles_mapper(void *map_data, int num_elements,
     if (c != NULL) {
 
       /* Drift the multipole at this level only */
-      if (c->ti_old_multipole != e->ti_current) cell_drift_multipole(c, e);
+      if (c->grav.ti_old_multipole != e->ti_current) cell_drift_multipole(c, e);
     }
   }
 }
@@ -6022,7 +3835,7 @@ void engine_makeproxies(struct engine *e) {
                 proxy_addcell_out(&proxies[pid], &cells[cid], proxy_type);
 
                 /* Store info about where to send the cell */
-                cells[cid].sendto |= (1ULL << pid);
+                cells[cid].mpi.sendto |= (1ULL << pid);
               }
 
               /* Same for the symmetric case? */
@@ -6049,7 +3862,7 @@ void engine_makeproxies(struct engine *e) {
                 proxy_addcell_out(&proxies[pid], &cells[cjd], proxy_type);
 
                 /* Store info about where to send the cell */
-                cells[cjd].sendto |= (1ULL << pid);
+                cells[cjd].mpi.sendto |= (1ULL << pid);
               }
             }
           }
@@ -6225,6 +4038,42 @@ void engine_dump_snapshot(struct engine *e) {
             (float)clocks_diff(&time1, &time2), clocks_getunit());
 }
 
+/**
+ * @brief Writes an index file with the current state of the engine
+ *
+ * @param e The #engine.
+ */
+void engine_dump_index(struct engine *e) {
+
+#if defined(WITH_LOGGER)
+  struct clocks_time time1, time2;
+  clocks_gettime(&time1);
+
+  if (e->verbose) {
+    if (e->policy & engine_policy_cosmology)
+      message("Writing index at a=%e",
+              exp(e->ti_current * e->time_base) * e->cosmology->a_begin);
+    else
+      message("Writing index at t=%e",
+              e->ti_current * e->time_base + e->time_begin);
+  }
+
+  /* Dump... */
+  write_index_single(e, e->logger->base_name, e->internal_units,
+                     e->snapshot_units);
+
+  /* Flag that we dumped a snapshot */
+  e->step_props |= engine_step_prop_logger_index;
+
+  clocks_gettime(&time2);
+  if (e->verbose)
+    message("writing particle indices took %.3f %s.",
+            (float)clocks_diff(&time1, &time2), clocks_getunit());
+#else
+  error("SWIFT was not compiled with the logger");
+#endif
+}
+
 #ifdef HAVE_SETAFFINITY
 /**
  * @brief Returns the initial affinity the main thread is using.
@@ -6390,6 +4239,11 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params,
   e->last_repartition = 0;
 #endif
 
+#if defined(WITH_LOGGER)
+  e->logger = (struct logger *)malloc(sizeof(struct logger));
+  logger_init(e->logger, params);
+#endif
+
   /* Make the space link back to the engine. */
   s->e = e;
 
@@ -6425,8 +4279,8 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params,
         parser_get_opt_param_double(params, "StructureFinding:time_first", 0.);
     e->a_first_stf_output = parser_get_opt_param_double(
         params, "StructureFinding:scale_factor_first", 0.1);
-    e->stf_output_freq_format =
-        parser_get_param_int(params, "StructureFinding:output_time_format");
+    e->stf_output_freq_format = (enum io_stf_output_format)parser_get_param_int(
+        params, "StructureFinding:output_time_format");
 
     if (e->stf_output_freq_format == io_stf_steps) {
       e->delta_step_stf =
@@ -6847,7 +4701,14 @@ void engine_config(int restart, struct engine *e, struct swift_params *params,
                 MPI_COMM_WORLD);
 #endif
 
-  /* Find the time of the first snapshot  output */
+#if defined(WITH_LOGGER)
+  if (e->nodeID == 0)
+    message(
+        "WARNING: There is currently no way of predicting the output "
+        "size, please use it carefully");
+#endif
+
+  /* Find the time of the first snapshot output */
   engine_compute_next_snapshot_time(e);
 
   /* Find the time of the first statistics output */
@@ -6887,6 +4748,7 @@ void engine_config(int restart, struct engine *e, struct swift_params *params,
 /* Construct types for MPI communications */
 #ifdef WITH_MPI
   part_create_mpi_types();
+  multipole_create_mpi_types();
   stats_create_mpi_type();
   proxy_create_mpi_type();
   task_create_mpi_comms();
@@ -6991,7 +4853,12 @@ void engine_config(int restart, struct engine *e, struct swift_params *params,
     }
   }
 
-/* Free the affinity stuff */
+#ifdef WITH_LOGGER
+  /* Write the particle logger header */
+  logger_write_file_header(e->logger, e);
+#endif
+
+  /* Free the affinity stuff */
 #if defined(HAVE_SETAFFINITY)
   if (with_aff) {
     free(cpuid);
@@ -7403,20 +5270,17 @@ void engine_clean(struct engine *e) {
   }
   free(e->runners);
   free(e->snapshot_units);
-  if (e->output_list_snapshots) {
-    output_list_clean(e->output_list_snapshots);
-    free(e->output_list_snapshots);
-  }
-  if (e->output_list_stats) {
-    output_list_clean(e->output_list_stats);
-    free(e->output_list_stats);
-  }
-  if (e->output_list_stf) {
-    output_list_clean(e->output_list_stf);
-    free(e->output_list_stf);
-  }
+
+  output_list_clean(&e->output_list_snapshots);
+  output_list_clean(&e->output_list_stats);
+  output_list_clean(&e->output_list_stf);
+
   free(e->links);
   free(e->cell_loc);
+#if defined(WITH_LOGGER)
+  logger_clean(e->logger);
+  free(e->logger);
+#endif
   scheduler_clean(&e->sched);
   space_clean(e->s);
   threadpool_clean(&e->threadpool);
diff --git a/src/engine.h b/src/engine.h
index aed9c141d8ac5fc8bda155d27a78b0657ffe2118..4b924aef372c00ceec0d9ac3b36b20380b6f0d49 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -39,6 +39,7 @@
 #include "collectgroup.h"
 #include "cooling_struct.h"
 #include "fof.h"
+#include "dump.h"
 #include "gravity_properties.h"
 #include "mesh_gravity.h"
 #include "parser.h"
@@ -50,6 +51,7 @@
 #include "space.h"
 #include "task.h"
 #include "units.h"
+#include "velociraptor_interface.h"
 
 /**
  * @brief The different policies the #engine can follow.
@@ -73,11 +75,12 @@ enum engine_policy {
   engine_policy_sourceterms = (1 << 14),
   engine_policy_stars = (1 << 15),
   engine_policy_structure_finding = (1 << 16),
-  engine_policy_feedback = (1 << 17),
-  engine_policy_fof = (1 << 18)
+  engine_policy_star_formation = (1 << 17),
+  engine_policy_feedback = (1 << 18),
+  engine_policy_fof = (1 << 19)
 };
-#define engine_maxpolicy 18
-extern const char *engine_policy_names[];
+#define engine_maxpolicy 20
+extern const char *engine_policy_names[engine_maxpolicy + 1];
 
 /**
  * @brief The different unusual events that can take place in a time-step.
@@ -89,7 +92,8 @@ enum engine_step_properties {
   engine_step_prop_repartition = (1 << 2),
   engine_step_prop_statistics = (1 << 3),
   engine_step_prop_snapshot = (1 << 4),
-  engine_step_prop_restarts = (1 << 5)
+  engine_step_prop_restarts = (1 << 5),
+  engine_step_prop_logger_index = (1 << 6)
 };
 
 /* Some constants */
@@ -205,6 +209,15 @@ struct engine {
   /* Total numbers of particles in the system. */
   long long total_nr_parts, total_nr_gparts, total_nr_sparts;
 
+  /* The total number of inhibted particles in the system. */
+  long long nr_inhibited_parts, nr_inhibited_gparts, nr_inhibited_sparts;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Total number of particles removed from the system since the last rebuild */
+  long long count_inhibited_parts, count_inhibited_gparts,
+      count_inhibited_sparts;
+#endif
+
   /* Total mass in the simulation */
   double total_mass;
 
@@ -232,7 +245,7 @@ struct engine {
   int snapshot_output_count;
 
   /* Structure finding information */
-  int stf_output_freq_format;
+  enum io_stf_output_format stf_output_freq_format;
   int delta_step_stf;
   double a_first_stf_output;
   double time_first_stf_output;
@@ -306,6 +319,10 @@ struct engine {
   int forcerepart;
   struct repartition *reparttype;
 
+#ifdef WITH_LOGGER
+  struct logger *logger;
+#endif
+
   /* How many steps have we done with the same set of tasks? */
   int tasks_age;
 
@@ -379,7 +396,7 @@ struct engine {
   int restart_max_tasks;
 };
 
-/* Function prototypes. */
+/* Function prototypes, engine.c. */
 void engine_addlink(struct engine *e, struct link **l, struct task *t);
 void engine_barrier(struct engine *e);
 void engine_compute_next_snapshot_time(struct engine *e);
@@ -409,19 +426,19 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params,
 void engine_config(int restart, struct engine *e, struct swift_params *params,
                    int nr_nodes, int nodeID, int nr_threads, int with_aff,
                    int verbose, const char *restart_file);
+void engine_dump_index(struct engine *e);
 void engine_launch(struct engine *e);
 void engine_prepare(struct engine *e);
 void engine_init_particles(struct engine *e, int flag_entropy_ICs,
                            int clean_h_values, int compute_init_accel);
 void engine_step(struct engine *e);
-void engine_maketasks(struct engine *e);
 void engine_split(struct engine *e, struct partition *initial_partition);
-void engine_exchange_strays(struct engine *e, size_t offset_parts,
-                            int *ind_part, size_t *Npart, size_t offset_gparts,
-                            int *ind_gpart, size_t *Ngpart,
-                            size_t offset_sparts, int *ind_spart,
-                            size_t *Nspart);
-void engine_rebuild(struct engine *e, int clean_h_values);
+void engine_exchange_strays(struct engine *e, const size_t offset_parts,
+                            const int *ind_part, size_t *Npart,
+                            const size_t offset_gparts, const int *ind_gpart,
+                            size_t *Ngpart, const size_t offset_sparts,
+                            const int *ind_spart, size_t *Nspart);
+void engine_rebuild(struct engine *e, int redistributed, int clean_h_values);
 void engine_repartition(struct engine *e);
 void engine_repartition_trigger(struct engine *e);
 void engine_makeproxies(struct engine *e);
@@ -433,6 +450,12 @@ void engine_unpin(void);
 void engine_clean(struct engine *e);
 int engine_estimate_nr_tasks(struct engine *e);
 
+/* Function prototypes, engine_maketasks.c. */
+void engine_maketasks(struct engine *e);
+
+/* Function prototypes, engine_marktasks.c. */
+int engine_marktasks(struct engine *e);
+
 #ifdef HAVE_SETAFFINITY
 cpu_set_t *engine_entry_affinity(void);
 #endif
diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c
new file mode 100644
index 0000000000000000000000000000000000000000..e4431a8cf14998774623e3575ecfdae08333e556
--- /dev/null
+++ b/src/engine_maketasks.c
@@ -0,0 +1,2090 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
+ *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2015 Peter W. Draper (p.w.draper@durham.ac.uk)
+ *                    Angus Lepper (angus.lepper@ed.ac.uk)
+ *               2016 John A. Regan (john.a.regan@durham.ac.uk)
+ *                    Tom Theuns (tom.theuns@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <stdlib.h>
+#include <unistd.h>
+
+/* MPI headers. */
+#ifdef WITH_MPI
+#include <mpi.h>
+#endif
+
+/* Load the profiler header, if needed. */
+#ifdef WITH_PROFILER
+#include <gperftools/profiler.h>
+#endif
+
+/* This object's header. */
+#include "engine.h"
+
+/* Local headers. */
+#include "atomic.h"
+#include "cell.h"
+#include "clocks.h"
+#include "cycle.h"
+#include "debug.h"
+#include "error.h"
+#include "proxy.h"
+#include "timers.h"
+
+/**
+ * @brief Add send tasks for the gravity pairs to a hierarchy of cells.
+ *
+ * @param e The #engine.
+ * @param ci The sending #cell.
+ * @param cj Dummy cell containing the nodeID of the receiving node.
+ * @param t_grav The send_grav #task, if it has already been created.
+ */
+void engine_addtasks_send_gravity(struct engine *e, struct cell *ci,
+                                  struct cell *cj, struct task *t_grav) {
+
+#ifdef WITH_MPI
+  struct link *l = NULL;
+  struct scheduler *s = &e->sched;
+  const int nodeID = cj->nodeID;
+
+  /* Check if any of the gravity tasks are for the target node. */
+  for (l = ci->grav.grav; l != NULL; l = l->next)
+    if (l->t->ci->nodeID == nodeID ||
+        (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
+      break;
+
+  /* If so, attach send tasks. */
+  if (l != NULL) {
+
+    /* Create the tasks and their dependencies? */
+    if (t_grav == NULL) {
+
+      /* Create a tag for this cell. */
+      if (ci->mpi.tag < 0) cell_tag(ci);
+
+      t_grav = scheduler_addtask(s, task_type_send, task_subtype_gpart,
+                                 ci->mpi.tag, 0, ci, cj);
+
+      /* The sends should unlock the down pass. */
+      scheduler_addunlock(s, t_grav, ci->grav.super->grav.down);
+
+      /* Drift before you send */
+      scheduler_addunlock(s, ci->grav.super->grav.drift, t_grav);
+    }
+
+    /* Add them to the local cell. */
+    engine_addlink(e, &ci->mpi.grav.send, t_grav);
+  }
+
+  /* Recurse? */
+  if (ci->split)
+    for (int k = 0; k < 8; k++)
+      if (ci->progeny[k] != NULL)
+        engine_addtasks_send_gravity(e, ci->progeny[k], cj, t_grav);
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+#endif
+}
+
+/**
+ * @brief Add send tasks for the hydro pairs to a hierarchy of cells.
+ *
+ * @param e The #engine.
+ * @param ci The sending #cell.
+ * @param cj Dummy cell containing the nodeID of the receiving node.
+ * @param t_xv The send_xv #task, if it has already been created.
+ * @param t_rho The send_rho #task, if it has already been created.
+ * @param t_gradient The send_gradient #task, if already created.
+ */
+void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
+                                struct cell *cj, struct task *t_xv,
+                                struct task *t_rho, struct task *t_gradient) {
+
+#ifdef WITH_MPI
+  struct link *l = NULL;
+  struct scheduler *s = &e->sched;
+  const int nodeID = cj->nodeID;
+
+  /* Check if any of the density tasks are for the target node. */
+  for (l = ci->hydro.density; l != NULL; l = l->next)
+    if (l->t->ci->nodeID == nodeID ||
+        (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
+      break;
+
+  /* If so, attach send tasks. */
+  if (l != NULL) {
+
+    /* Create the tasks and their dependencies? */
+    if (t_xv == NULL) {
+
+      /* Create a tag for this cell. */
+      if (ci->mpi.tag < 0) cell_tag(ci);
+
+      t_xv = scheduler_addtask(s, task_type_send, task_subtype_xv, ci->mpi.tag,
+                               0, ci, cj);
+      t_rho = scheduler_addtask(s, task_type_send, task_subtype_rho,
+                                ci->mpi.tag, 0, ci, cj);
+#ifdef EXTRA_HYDRO_LOOP
+      t_gradient = scheduler_addtask(s, task_type_send, task_subtype_gradient,
+                                     ci->mpi.tag, 0, ci, cj);
+#endif
+
+#ifdef EXTRA_HYDRO_LOOP
+
+      scheduler_addunlock(s, t_gradient, ci->super->kick2);
+
+      scheduler_addunlock(s, ci->hydro.super->hydro.extra_ghost, t_gradient);
+
+      /* The send_rho task should unlock the super_hydro-cell's extra_ghost
+       * task. */
+      scheduler_addunlock(s, t_rho, ci->hydro.super->hydro.extra_ghost);
+
+      /* The send_rho task depends on the cell's ghost task. */
+      scheduler_addunlock(s, ci->hydro.super->hydro.ghost_out, t_rho);
+
+      /* The send_xv task should unlock the super_hydro-cell's ghost task. */
+      scheduler_addunlock(s, t_xv, ci->hydro.super->hydro.ghost_in);
+
+#else
+      /* The send_rho task should unlock the super_hydro-cell's kick task. */
+      scheduler_addunlock(s, t_rho, ci->super->end_force);
+
+      /* The send_rho task depends on the cell's ghost task. */
+      scheduler_addunlock(s, ci->hydro.super->hydro.ghost_out, t_rho);
+
+      /* The send_xv task should unlock the super_hydro-cell's ghost task. */
+      scheduler_addunlock(s, t_xv, ci->hydro.super->hydro.ghost_in);
+
+#endif
+
+      /* Drift before you send */
+      scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_xv);
+    }
+
+    /* Add them to the local cell. */
+    engine_addlink(e, &ci->mpi.hydro.send_xv, t_xv);
+    engine_addlink(e, &ci->mpi.hydro.send_rho, t_rho);
+#ifdef EXTRA_HYDRO_LOOP
+    engine_addlink(e, &ci->mpi.hydro.send_gradient, t_gradient);
+#endif
+  }
+
+  /* Recurse? */
+  if (ci->split)
+    for (int k = 0; k < 8; k++)
+      if (ci->progeny[k] != NULL)
+        engine_addtasks_send_hydro(e, ci->progeny[k], cj, t_xv, t_rho,
+                                   t_gradient);
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+#endif
+}
+
+/**
+ * @brief Add send tasks for the time-step to a hierarchy of cells.
+ *
+ * @param e The #engine.
+ * @param ci The sending #cell.
+ * @param cj Dummy cell containing the nodeID of the receiving node.
+ * @param t_ti The send_ti #task, if it has already been created.
+ */
+void engine_addtasks_send_timestep(struct engine *e, struct cell *ci,
+                                   struct cell *cj, struct task *t_ti) {
+
+#ifdef WITH_MPI
+  struct link *l = NULL;
+  struct scheduler *s = &e->sched;
+  const int nodeID = cj->nodeID;
+
+  /* Check if any of the gravity tasks are for the target node. */
+  for (l = ci->grav.grav; l != NULL; l = l->next)
+    if (l->t->ci->nodeID == nodeID ||
+        (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
+      break;
+
+  /* Check whether instead any of the hydro tasks are for the target node. */
+  if (l == NULL)
+    for (l = ci->hydro.density; l != NULL; l = l->next)
+      if (l->t->ci->nodeID == nodeID ||
+          (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
+        break;
+
+  /* If found anything, attach send tasks. */
+  if (l != NULL) {
+
+    /* Create the tasks and their dependencies? */
+    if (t_ti == NULL) {
+
+      /* Create a tag for this cell. */
+      if (ci->mpi.tag < 0) cell_tag(ci);
+
+      t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend,
+                               ci->mpi.tag, 0, ci, cj);
+
+      /* The super-cell's timestep task should unlock the send_ti task. */
+      scheduler_addunlock(s, ci->super->timestep, t_ti);
+    }
+
+    /* Add them to the local cell. */
+    engine_addlink(e, &ci->mpi.send_ti, t_ti);
+  }
+
+  /* Recurse? */
+  if (ci->split)
+    for (int k = 0; k < 8; k++)
+      if (ci->progeny[k] != NULL)
+        engine_addtasks_send_timestep(e, ci->progeny[k], cj, t_ti);
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+#endif
+}
+
+/**
+ * @brief Add recv tasks for hydro pairs to a hierarchy of cells.
+ *
+ * @param e The #engine.
+ * @param c The foreign #cell.
+ * @param t_xv The recv_xv #task, if it has already been created.
+ * @param t_rho The recv_rho #task, if it has already been created.
+ * @param t_gradient The recv_gradient #task, if it has already been created.
+ */
+void engine_addtasks_recv_hydro(struct engine *e, struct cell *c,
+                                struct task *t_xv, struct task *t_rho,
+                                struct task *t_gradient) {
+
+#ifdef WITH_MPI
+  struct scheduler *s = &e->sched;
+
+  /* Have we reached a level where there are any hydro tasks ? */
+  if (t_xv == NULL && c->hydro.density != NULL) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Make sure this cell has a valid tag. */
+    if (c->mpi.tag < 0) error("Trying to receive from untagged cell.");
+#endif  // SWIFT_DEBUG_CHECKS
+
+    /* Create the tasks. */
+    t_xv = scheduler_addtask(s, task_type_recv, task_subtype_xv, c->mpi.tag, 0,
+                             c, NULL);
+    t_rho = scheduler_addtask(s, task_type_recv, task_subtype_rho, c->mpi.tag,
+                              0, c, NULL);
+#ifdef EXTRA_HYDRO_LOOP
+    t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_gradient,
+                                   c->mpi.tag, 0, c, NULL);
+#endif
+  }
+
+  c->mpi.hydro.recv_xv = t_xv;
+  c->mpi.hydro.recv_rho = t_rho;
+  c->mpi.hydro.recv_gradient = t_gradient;
+
+  /* Add dependencies. */
+  if (c->hydro.sorts != NULL) scheduler_addunlock(s, t_xv, c->hydro.sorts);
+
+  for (struct link *l = c->hydro.density; l != NULL; l = l->next) {
+    scheduler_addunlock(s, t_xv, l->t);
+    scheduler_addunlock(s, l->t, t_rho);
+  }
+#ifdef EXTRA_HYDRO_LOOP
+  for (struct link *l = c->hydro.gradient; l != NULL; l = l->next) {
+    scheduler_addunlock(s, t_rho, l->t);
+    scheduler_addunlock(s, l->t, t_gradient);
+  }
+  for (struct link *l = c->hydro.force; l != NULL; l = l->next)
+    scheduler_addunlock(s, t_gradient, l->t);
+#else
+  for (struct link *l = c->hydro.force; l != NULL; l = l->next)
+    scheduler_addunlock(s, t_rho, l->t);
+#endif
+
+  /* Recurse? */
+  if (c->split)
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL)
+        engine_addtasks_recv_hydro(e, c->progeny[k], t_xv, t_rho, t_gradient);
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+#endif
+}
+
+/**
+ * @brief Add recv tasks for gravity pairs to a hierarchy of cells.
+ *
+ * @param e The #engine.
+ * @param c The foreign #cell.
+ * @param t_grav The recv_gpart #task, if it has already been created.
+ */
+void engine_addtasks_recv_gravity(struct engine *e, struct cell *c,
+                                  struct task *t_grav) {
+
+#ifdef WITH_MPI
+  struct scheduler *s = &e->sched;
+
+  /* Have we reached a level where there are any gravity tasks ? */
+  if (t_grav == NULL && c->grav.grav != NULL) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Make sure this cell has a valid tag. */
+    if (c->mpi.tag < 0) error("Trying to receive from untagged cell.");
+#endif  // SWIFT_DEBUG_CHECKS
+
+    /* Create the tasks. */
+    t_grav = scheduler_addtask(s, task_type_recv, task_subtype_gpart,
+                               c->mpi.tag, 0, c, NULL);
+  }
+
+  c->mpi.grav.recv = t_grav;
+
+  for (struct link *l = c->grav.grav; l != NULL; l = l->next)
+    scheduler_addunlock(s, t_grav, l->t);
+
+  /* Recurse? */
+  if (c->split)
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL)
+        engine_addtasks_recv_gravity(e, c->progeny[k], t_grav);
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+#endif
+}
+
+/**
+ * @brief Add recv tasks for gravity pairs to a hierarchy of cells.
+ *
+ * @param e The #engine.
+ * @param c The foreign #cell.
+ * @param t_ti The recv_ti #task, if already been created.
+ */
+void engine_addtasks_recv_timestep(struct engine *e, struct cell *c,
+                                   struct task *t_ti) {
+
+#ifdef WITH_MPI
+  struct scheduler *s = &e->sched;
+
+  /* Have we reached a level where there are any self/pair tasks ? */
+  if (t_ti == NULL && (c->grav.grav != NULL || c->hydro.density != NULL)) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Make sure this cell has a valid tag. */
+    if (c->mpi.tag < 0) error("Trying to receive from untagged cell.");
+#endif  // SWIFT_DEBUG_CHECKS
+
+    t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend, c->mpi.tag,
+                             0, c, NULL);
+  }
+
+  c->mpi.recv_ti = t_ti;
+
+  for (struct link *l = c->grav.grav; l != NULL; l = l->next)
+    scheduler_addunlock(s, l->t, t_ti);
+
+  for (struct link *l = c->hydro.force; l != NULL; l = l->next)
+    scheduler_addunlock(s, l->t, t_ti);
+
+  /* Recurse? */
+  if (c->split)
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL)
+        engine_addtasks_recv_timestep(e, c->progeny[k], t_ti);
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+#endif
+}
+
+/**
+ * @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
+ * i.e. all the O(Npart) tasks -- timestep version
+ *
+ * Tasks are only created here. The dependencies will be added later on.
+ *
+ * Note that there is no need to recurse below the super-cell. Note also
+ * that we only add tasks if the relevant particles are present in the cell.
+ *
+ * @param e The #engine.
+ * @param c The #cell.
+ */
+void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) {
+
+  struct scheduler *s = &e->sched;
+  const int is_with_cooling = (e->policy & engine_policy_cooling);
+  const int is_with_star_formation = (e->policy & engine_policy_star_formation);
+
+  /* Are we in a super-cell ? */
+  if (c->super == c) {
+
+    /* Local tasks only... */
+    if (c->nodeID == e->nodeID) {
+
+      /* Add the two half kicks */
+      c->kick1 = scheduler_addtask(s, task_type_kick1, task_subtype_none, 0, 0,
+                                   c, NULL);
+
+#if defined(WITH_LOGGER)
+      c->logger = scheduler_addtask(s, task_type_logger, task_subtype_none, 0,
+                                    0, c, NULL);
+#endif
+
+      c->kick2 = scheduler_addtask(s, task_type_kick2, task_subtype_none, 0, 0,
+                                   c, NULL);
+
+      /* Add the time-step calculation task and its dependency */
+      c->timestep = scheduler_addtask(s, task_type_timestep, task_subtype_none,
+                                      0, 0, c, NULL);
+
+      /* Add the task finishing the force calculation */
+      c->end_force = scheduler_addtask(s, task_type_end_force,
+                                       task_subtype_none, 0, 0, c, NULL);
+
+      if (is_with_cooling) {
+
+        c->hydro.cooling = scheduler_addtask(s, task_type_cooling,
+                                             task_subtype_none, 0, 0, c, NULL);
+
+        scheduler_addunlock(s, c->end_force, c->hydro.cooling);
+        scheduler_addunlock(s, c->hydro.cooling, c->kick2);
+
+      } else {
+        scheduler_addunlock(s, c->end_force, c->kick2);
+      }
+
+      if (is_with_star_formation) {
+
+        c->hydro.star_formation = scheduler_addtask(
+            s, task_type_star_formation, task_subtype_none, 0, 0, c, NULL);
+
+        scheduler_addunlock(s, c->kick2, c->hydro.star_formation);
+        scheduler_addunlock(s, c->hydro.star_formation, c->timestep);
+
+      } else {
+        scheduler_addunlock(s, c->kick2, c->timestep);
+      }
+      scheduler_addunlock(s, c->timestep, c->kick1);
+
+#if defined(WITH_LOGGER)
+      scheduler_addunlock(s, c->kick1, c->logger);
+#endif
+    }
+
+  } else { /* We are above the super-cell so need to go deeper */
+
+    /* Recurse. */
+    if (c->split)
+      for (int k = 0; k < 8; k++)
+        if (c->progeny[k] != NULL)
+          engine_make_hierarchical_tasks_common(e, c->progeny[k]);
+  }
+}
+
+/**
+ * @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
+ * i.e. all the O(Npart) tasks -- gravity version
+ *
+ * Tasks are only created here. The dependencies will be added later on.
+ *
+ * Note that there is no need to recurse below the super-cell. Note also
+ * that we only add tasks if the relevant particles are present in the cell.
+ *
+ * @param e The #engine.
+ * @param c The #cell.
+ */
+void engine_make_hierarchical_tasks_gravity(struct engine *e, struct cell *c) {
+
+  struct scheduler *s = &e->sched;
+  const int periodic = e->s->periodic;
+  const int is_self_gravity = (e->policy & engine_policy_self_gravity);
+
+  /* Are we in a super-cell ? */
+  if (c->grav.super == c) {
+
+    /* Local tasks only... */
+    if (c->nodeID == e->nodeID) {
+
+      c->grav.drift = scheduler_addtask(s, task_type_drift_gpart,
+                                        task_subtype_none, 0, 0, c, NULL);
+
+      if (is_self_gravity) {
+
+        /* Initialisation of the multipoles */
+        c->grav.init = scheduler_addtask(s, task_type_init_grav,
+                                         task_subtype_none, 0, 0, c, NULL);
+
+        /* Gravity non-neighbouring pm calculations */
+        c->grav.long_range = scheduler_addtask(
+            s, task_type_grav_long_range, task_subtype_none, 0, 0, c, NULL);
+
+        /* Gravity recursive down-pass */
+        c->grav.down = scheduler_addtask(s, task_type_grav_down,
+                                         task_subtype_none, 0, 0, c, NULL);
+
+        /* Implicit tasks for the up and down passes */
+        c->grav.init_out = scheduler_addtask(s, task_type_init_grav_out,
+                                             task_subtype_none, 0, 1, c, NULL);
+        c->grav.down_in = scheduler_addtask(s, task_type_grav_down_in,
+                                            task_subtype_none, 0, 1, c, NULL);
+
+        /* Gravity mesh force propagation */
+        if (periodic)
+          c->grav.mesh = scheduler_addtask(s, task_type_grav_mesh,
+                                           task_subtype_none, 0, 0, c, NULL);
+
+        if (periodic) scheduler_addunlock(s, c->grav.drift, c->grav.mesh);
+        if (periodic) scheduler_addunlock(s, c->grav.mesh, c->grav.down);
+        scheduler_addunlock(s, c->grav.init, c->grav.long_range);
+        scheduler_addunlock(s, c->grav.long_range, c->grav.down);
+        scheduler_addunlock(s, c->grav.down, c->super->end_force);
+
+        /* Link in the implicit tasks */
+        scheduler_addunlock(s, c->grav.init, c->grav.init_out);
+        scheduler_addunlock(s, c->grav.down_in, c->grav.down);
+      }
+    }
+  }
+
+  /* We are below the super-cell but not below the maximal splitting depth */
+  else if (c->grav.super != NULL && c->depth < space_subdepth_grav) {
+
+    /* Local tasks only... */
+    if (c->nodeID == e->nodeID) {
+
+      if (is_self_gravity) {
+
+        c->grav.init_out = scheduler_addtask(s, task_type_init_grav_out,
+                                             task_subtype_none, 0, 1, c, NULL);
+
+        c->grav.down_in = scheduler_addtask(s, task_type_grav_down_in,
+                                            task_subtype_none, 0, 1, c, NULL);
+
+        scheduler_addunlock(s, c->parent->grav.init_out, c->grav.init_out);
+        scheduler_addunlock(s, c->grav.down_in, c->parent->grav.down_in);
+      }
+    }
+  }
+
+  /* Recurse but not below the maximal splitting depth */
+  if (c->split && c->depth <= space_subdepth_grav)
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL)
+        engine_make_hierarchical_tasks_gravity(e, c->progeny[k]);
+}
+
+/**
+ * @brief Recursively add non-implicit star ghost tasks to a cell hierarchy.
+ */
+void engine_add_stars_ghosts(struct engine *e, struct cell *c,
+                             struct task *stars_ghost_in,
+                             struct task *stars_ghost_out) {
+
+  /* If we have reached the leaf OR have to few particles to play with*/
+  if (!c->split || c->stars.count < engine_max_sparts_per_ghost) {
+
+    /* Add the ghost task and its dependencies */
+    struct scheduler *s = &e->sched;
+    c->stars.ghost = scheduler_addtask(s, task_type_stars_ghost,
+                                       task_subtype_none, 0, 0, c, NULL);
+    scheduler_addunlock(s, stars_ghost_in, c->stars.ghost);
+    scheduler_addunlock(s, c->stars.ghost, stars_ghost_out);
+  } else {
+    /* Keep recursing */
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL)
+        engine_add_stars_ghosts(e, c->progeny[k], stars_ghost_in,
+                                stars_ghost_out);
+  }
+}
+
+/**
+ * @brief Recursively add non-implicit ghost tasks to a cell hierarchy.
+ */
+void engine_add_ghosts(struct engine *e, struct cell *c, struct task *ghost_in,
+                       struct task *ghost_out) {
+
+  /* If we have reached the leaf OR have to few particles to play with*/
+  if (!c->split || c->hydro.count < engine_max_parts_per_ghost) {
+
+    /* Add the ghost task and its dependencies */
+    struct scheduler *s = &e->sched;
+    c->hydro.ghost =
+        scheduler_addtask(s, task_type_ghost, task_subtype_none, 0, 0, c, NULL);
+    scheduler_addunlock(s, ghost_in, c->hydro.ghost);
+    scheduler_addunlock(s, c->hydro.ghost, ghost_out);
+  } else {
+    /* Keep recursing */
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL)
+        engine_add_ghosts(e, c->progeny[k], ghost_in, ghost_out);
+  }
+}
+
+/**
+ * @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
+ * i.e. all the O(Npart) tasks -- hydro version
+ *
+ * Tasks are only created here. The dependencies will be added later on.
+ *
+ * Note that there is no need to recurse below the super-cell. Note also
+ * that we only add tasks if the relevant particles are present in the cell.
+ *
+ * @param e The #engine.
+ * @param c The #cell.
+ */
+void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) {
+
+  struct scheduler *s = &e->sched;
+  const int is_with_sourceterms = (e->policy & engine_policy_sourceterms);
+
+  /* Are we in a super-cell ? */
+  if (c->hydro.super == c) {
+
+    /* Add the sort task. */
+    c->hydro.sorts =
+        scheduler_addtask(s, task_type_sort, task_subtype_none, 0, 0, c, NULL);
+
+    /* Local tasks only... */
+    if (c->nodeID == e->nodeID) {
+
+      /* Add the drift task. */
+      c->hydro.drift = scheduler_addtask(s, task_type_drift_part,
+                                         task_subtype_none, 0, 0, c, NULL);
+
+      /* Generate the ghost tasks. */
+      c->hydro.ghost_in =
+          scheduler_addtask(s, task_type_ghost_in, task_subtype_none, 0,
+                            /* implicit = */ 1, c, NULL);
+      c->hydro.ghost_out =
+          scheduler_addtask(s, task_type_ghost_out, task_subtype_none, 0,
+                            /* implicit = */ 1, c, NULL);
+      engine_add_ghosts(e, c, c->hydro.ghost_in, c->hydro.ghost_out);
+
+#ifdef EXTRA_HYDRO_LOOP
+      /* Generate the extra ghost task. */
+      c->hydro.extra_ghost = scheduler_addtask(
+          s, task_type_extra_ghost, task_subtype_none, 0, 0, c, NULL);
+#endif
+
+      /* add source terms */
+      if (is_with_sourceterms) {
+        c->sourceterms = scheduler_addtask(s, task_type_sourceterms,
+                                           task_subtype_none, 0, 0, c, NULL);
+      }
+    }
+
+  } else { /* We are above the super-cell so need to go deeper */
+
+    /* Recurse. */
+    if (c->split)
+      for (int k = 0; k < 8; k++)
+        if (c->progeny[k] != NULL)
+          engine_make_hierarchical_tasks_hydro(e, c->progeny[k]);
+  }
+}
+
+/**
+ * @brief Generate the stars hierarchical tasks for a hierarchy of cells -
+ * i.e. all the O(Npart) tasks -- star version
+ *
+ * Tasks are only created here. The dependencies will be added later on.
+ *
+ * Note that there is no need to recurse below the super-cell. Note also
+ * that we only add tasks if the relevant particles are present in the cell.
+ *
+ * @param e The #engine.
+ * @param c The #cell.
+ */
+void engine_make_hierarchical_tasks_stars(struct engine *e, struct cell *c) {
+
+  struct scheduler *s = &e->sched;
+
+  /* Are we in a super-cell ? */
+  if (c->super == c) {
+
+    /* Local tasks only... */
+    if (c->nodeID == e->nodeID) {
+
+      /* Generate the ghost tasks. */
+      c->stars.ghost_in =
+          scheduler_addtask(s, task_type_stars_ghost_in, task_subtype_none, 0,
+                            /* implicit = */ 1, c, NULL);
+      c->stars.ghost_out =
+          scheduler_addtask(s, task_type_stars_ghost_out, task_subtype_none, 0,
+                            /* implicit = */ 1, c, NULL);
+      engine_add_stars_ghosts(e, c, c->stars.ghost_in, c->stars.ghost_out);
+    }
+  } else { /* We are above the super-cell so need to go deeper */
+
+    /* Recurse. */
+    if (c->split)
+      for (int k = 0; k < 8; k++)
+        if (c->progeny[k] != NULL)
+          engine_make_hierarchical_tasks_stars(e, c->progeny[k]);
+  }
+}
+
+/**
+ * @brief Constructs the top-level tasks for the short-range gravity
+ * and long-range gravity interactions.
+ *
+ * - All top-cells get a self task.
+ * - All pairs within range according to the multipole acceptance
+ *   criterion get a pair task.
+ */
+void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements,
+                                           void *extra_data) {
+
+  struct engine *e = ((struct engine **)extra_data)[0];
+  struct space *s = e->s;
+  struct scheduler *sched = &e->sched;
+  const int nodeID = e->nodeID;
+  const int periodic = s->periodic;
+  const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
+  const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]};
+  struct cell *cells = s->cells_top;
+  const double theta_crit = e->gravity_properties->theta_crit;
+  const double max_distance = e->mesh->r_cut_max;
+
+  /* Compute how many cells away we need to walk */
+  const double distance = 2.5 * cells[0].width[0] / theta_crit;
+  int delta = (int)(distance / cells[0].width[0]) + 1;
+  int delta_m = delta;
+  int delta_p = delta;
+
+  /* Special case where every cell is in range of every other one */
+  if (delta >= cdim[0] / 2) {
+    if (cdim[0] % 2 == 0) {
+      delta_m = cdim[0] / 2;
+      delta_p = cdim[0] / 2 - 1;
+    } else {
+      delta_m = cdim[0] / 2;
+      delta_p = cdim[0] / 2;
+    }
+  }
+
+  /* Loop through the elements, which are just byte offsets from NULL. */
+  for (int ind = 0; ind < num_elements; ind++) {
+
+    /* Get the cell index. */
+    const int cid = (size_t)(map_data) + ind;
+
+    /* Integer indices of the cell in the top-level grid */
+    const int i = cid / (cdim[1] * cdim[2]);
+    const int j = (cid / cdim[2]) % cdim[1];
+    const int k = cid % cdim[2];
+
+    /* Get the cell */
+    struct cell *ci = &cells[cid];
+
+    /* Skip cells without gravity particles */
+    if (ci->grav.count == 0) continue;
+
+    /* Is that cell local ? */
+    if (ci->nodeID != nodeID) continue;
+
+    /* If the cells is local build a self-interaction */
+    scheduler_addtask(sched, task_type_self, task_subtype_grav, 0, 0, ci, NULL);
+
+    /* Recover the multipole information */
+    const struct gravity_tensors *const multi_i = ci->grav.multipole;
+    const double CoM_i[3] = {multi_i->CoM[0], multi_i->CoM[1], multi_i->CoM[2]};
+
+#ifdef SWIFT_DEBUG_CHECKS
+    if (cell_getid(cdim, i, j, k) != cid)
+      error("Incorrect calculation of indices (i,j,k)=(%d,%d,%d) cid=%d", i, j,
+            k, cid);
+
+    if (multi_i->r_max != multi_i->r_max_rebuild)
+      error(
+          "Multipole size not equal ot it's size after rebuild. But we just "
+          "rebuilt...");
+#endif
+
+    /* Loop over every other cell within (Manhattan) range delta */
+    for (int x = -delta_m; x <= delta_p; x++) {
+      int ii = i + x;
+      if (ii >= cdim[0])
+        ii -= cdim[0];
+      else if (ii < 0)
+        ii += cdim[0];
+      for (int y = -delta_m; y <= delta_p; y++) {
+        int jj = j + y;
+        if (jj >= cdim[1])
+          jj -= cdim[1];
+        else if (jj < 0)
+          jj += cdim[1];
+        for (int z = -delta_m; z <= delta_p; z++) {
+          int kk = k + z;
+          if (kk >= cdim[2])
+            kk -= cdim[2];
+          else if (kk < 0)
+            kk += cdim[2];
+
+          /* Get the cell */
+          const int cjd = cell_getid(cdim, ii, jj, kk);
+          struct cell *cj = &cells[cjd];
+
+#ifdef SWIFT_DEBUG_CHECKS
+          const int iii = cjd / (cdim[1] * cdim[2]);
+          const int jjj = (cjd / cdim[2]) % cdim[1];
+          const int kkk = cjd % cdim[2];
+
+          if (ii != iii || jj != jjj || kk != kkk)
+            error(
+                "Incorrect calculation of indices (iii,jjj,kkk)=(%d,%d,%d) "
+                "cjd=%d",
+                iii, jjj, kkk, cjd);
+#endif
+
+          /* Avoid duplicates of local pairs*/
+          if (cid <= cjd && cj->nodeID == nodeID) continue;
+
+          /* Skip cells without gravity particles */
+          if (cj->grav.count == 0) continue;
+
+          /* Recover the multipole information */
+          const struct gravity_tensors *const multi_j = cj->grav.multipole;
+
+          /* Get the distance between the CoMs */
+          double dx = CoM_i[0] - multi_j->CoM[0];
+          double dy = CoM_i[1] - multi_j->CoM[1];
+          double dz = CoM_i[2] - multi_j->CoM[2];
+
+          /* Apply BC */
+          if (periodic) {
+            dx = nearest(dx, dim[0]);
+            dy = nearest(dy, dim[1]);
+            dz = nearest(dz, dim[2]);
+          }
+          const double r2 = dx * dx + dy * dy + dz * dz;
+
+          /* Minimal distance between any pair of particles */
+          const double min_radius =
+              sqrt(r2) - (multi_i->r_max + multi_j->r_max);
+
+          /* Are we beyond the distance where the truncated forces are 0 ?*/
+          if (periodic && min_radius > max_distance) continue;
+
+          /* Are the cells too close for a MM interaction ? */
+          if (!cell_can_use_pair_mm_rebuild(ci, cj, e, s)) {
+
+            /* Ok, we need to add a direct pair calculation */
+            scheduler_addtask(sched, task_type_pair, task_subtype_grav, 0, 0,
+                              ci, cj);
+          }
+        }
+      }
+    }
+  }
+}
+
+void engine_make_hierarchical_tasks_mapper(void *map_data, int num_elements,
+                                           void *extra_data) {
+  struct engine *e = (struct engine *)extra_data;
+  const int is_with_hydro = (e->policy & engine_policy_hydro);
+  const int is_with_self_gravity = (e->policy & engine_policy_self_gravity);
+  const int is_with_external_gravity =
+      (e->policy & engine_policy_external_gravity);
+  const int is_with_feedback = (e->policy & engine_policy_feedback);
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct cell *c = &((struct cell *)map_data)[ind];
+    /* Make the common tasks (time integration) */
+    engine_make_hierarchical_tasks_common(e, c);
+    /* Add the hydro stuff */
+    if (is_with_hydro) engine_make_hierarchical_tasks_hydro(e, c);
+    /* And the gravity stuff */
+    if (is_with_self_gravity || is_with_external_gravity)
+      engine_make_hierarchical_tasks_gravity(e, c);
+    if (is_with_feedback) engine_make_hierarchical_tasks_stars(e, c);
+  }
+}
+
+/**
+ * @brief Constructs the top-level tasks for the short-range gravity
+ * interactions (master function).
+ *
+ * - Create the FFT task and the array of gravity ghosts.
+ * - Call the mapper function to create the other tasks.
+ *
+ * @param e The #engine.
+ */
+void engine_make_self_gravity_tasks(struct engine *e) {
+
+  struct space *s = e->s;
+  struct task **ghosts = NULL;
+
+  /* Create the multipole self and pair tasks. */
+  void *extra_data[2] = {e, ghosts};
+  threadpool_map(&e->threadpool, engine_make_self_gravity_tasks_mapper, NULL,
+                 s->nr_cells, 1, 0, extra_data);
+}
+
+/**
+ * @brief Constructs the top-level tasks for the external gravity.
+ *
+ * @param e The #engine.
+ */
+void engine_make_external_gravity_tasks(struct engine *e) {
+
+  struct space *s = e->s;
+  struct scheduler *sched = &e->sched;
+  const int nodeID = e->nodeID;
+  struct cell *cells = s->cells_top;
+  const int nr_cells = s->nr_cells;
+
+  for (int cid = 0; cid < nr_cells; ++cid) {
+
+    struct cell *ci = &cells[cid];
+
+    /* Skip cells without gravity particles */
+    if (ci->grav.count == 0) continue;
+
+    /* Is that neighbour local ? */
+    if (ci->nodeID != nodeID) continue;
+
+    /* If the cell is local, build a self-interaction */
+    scheduler_addtask(sched, task_type_self, task_subtype_external_grav, 0, 0,
+                      ci, NULL);
+  }
+}
+
+/**
+ * @brief Counts the tasks associated with one cell and constructs the links
+ *
+ * For each hydrodynamic and gravity task, construct the links with
+ * the corresponding cell.  Similarly, construct the dependencies for
+ * all the sorting tasks.
+ */
+void engine_count_and_link_tasks_mapper(void *map_data, int num_elements,
+                                        void *extra_data) {
+
+  struct engine *e = (struct engine *)extra_data;
+  struct scheduler *const sched = &e->sched;
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct task *t = &((struct task *)map_data)[ind];
+
+    struct cell *ci = t->ci;
+    struct cell *cj = t->cj;
+    const enum task_types t_type = t->type;
+    const enum task_subtypes t_subtype = t->subtype;
+
+    /* Link sort tasks to all the higher sort task. */
+    if (t_type == task_type_sort) {
+      for (struct cell *finger = t->ci->parent; finger != NULL;
+           finger = finger->parent)
+        if (finger->hydro.sorts != NULL)
+          scheduler_addunlock(sched, t, finger->hydro.sorts);
+    }
+
+    /* Link self tasks to cells. */
+    else if (t_type == task_type_self) {
+      atomic_inc(&ci->nr_tasks);
+
+      if (t_subtype == task_subtype_density) {
+        engine_addlink(e, &ci->hydro.density, t);
+      } else if (t_subtype == task_subtype_grav) {
+        engine_addlink(e, &ci->grav.grav, t);
+      } else if (t_subtype == task_subtype_external_grav) {
+        engine_addlink(e, &ci->grav.grav, t);
+      } else if (t->subtype == task_subtype_stars_density) {
+        engine_addlink(e, &ci->stars.density, t);
+      }
+
+      /* Link pair tasks to cells. */
+    } else if (t_type == task_type_pair) {
+      atomic_inc(&ci->nr_tasks);
+      atomic_inc(&cj->nr_tasks);
+
+      if (t_subtype == task_subtype_density) {
+        engine_addlink(e, &ci->hydro.density, t);
+        engine_addlink(e, &cj->hydro.density, t);
+      } else if (t_subtype == task_subtype_grav) {
+        engine_addlink(e, &ci->grav.grav, t);
+        engine_addlink(e, &cj->grav.grav, t);
+      } else if (t->subtype == task_subtype_stars_density) {
+        engine_addlink(e, &ci->stars.density, t);
+        engine_addlink(e, &cj->stars.density, t);
+      }
+#ifdef SWIFT_DEBUG_CHECKS
+      else if (t_subtype == task_subtype_external_grav) {
+        error("Found a pair/external-gravity task...");
+      }
+#endif
+
+      /* Link sub-self tasks to cells. */
+    } else if (t_type == task_type_sub_self) {
+      atomic_inc(&ci->nr_tasks);
+
+      if (t_subtype == task_subtype_density) {
+        engine_addlink(e, &ci->hydro.density, t);
+      } else if (t_subtype == task_subtype_grav) {
+        engine_addlink(e, &ci->grav.grav, t);
+      } else if (t_subtype == task_subtype_external_grav) {
+        engine_addlink(e, &ci->grav.grav, t);
+      } else if (t->subtype == task_subtype_stars_density) {
+        engine_addlink(e, &ci->stars.density, t);
+      }
+
+      /* Link sub-pair tasks to cells. */
+    } else if (t_type == task_type_sub_pair) {
+      atomic_inc(&ci->nr_tasks);
+      atomic_inc(&cj->nr_tasks);
+
+      if (t_subtype == task_subtype_density) {
+        engine_addlink(e, &ci->hydro.density, t);
+        engine_addlink(e, &cj->hydro.density, t);
+      } else if (t_subtype == task_subtype_grav) {
+        engine_addlink(e, &ci->grav.grav, t);
+        engine_addlink(e, &cj->grav.grav, t);
+      } else if (t->subtype == task_subtype_stars_density) {
+        engine_addlink(e, &ci->stars.density, t);
+        engine_addlink(e, &cj->stars.density, t);
+      }
+#ifdef SWIFT_DEBUG_CHECKS
+      else if (t_subtype == task_subtype_external_grav) {
+        error("Found a sub-pair/external-gravity task...");
+      }
+#endif
+
+      /* Multipole-multipole interaction of progenies */
+    } else if (t_type == task_type_grav_mm) {
+
+      atomic_inc(&ci->grav.nr_mm_tasks);
+      atomic_inc(&cj->grav.nr_mm_tasks);
+      engine_addlink(e, &ci->grav.mm, t);
+      engine_addlink(e, &cj->grav.mm, t);
+    }
+  }
+}
+
+/**
+ * @brief Creates all the task dependencies for the gravity
+ *
+ * @param e The #engine
+ */
+void engine_link_gravity_tasks(struct engine *e) {
+
+  struct scheduler *sched = &e->sched;
+  const int nodeID = e->nodeID;
+  const int nr_tasks = sched->nr_tasks;
+
+  for (int k = 0; k < nr_tasks; k++) {
+
+    /* Get a pointer to the task. */
+    struct task *t = &sched->tasks[k];
+
+    if (t->type == task_type_none) continue;
+
+    /* Get the cells we act on */
+    struct cell *ci = t->ci;
+    struct cell *cj = t->cj;
+    const enum task_types t_type = t->type;
+    const enum task_subtypes t_subtype = t->subtype;
+
+    struct cell *ci_parent = (ci->parent != NULL) ? ci->parent : ci;
+    struct cell *cj_parent =
+        (cj != NULL && cj->parent != NULL) ? cj->parent : cj;
+
+/* Node ID (if running with MPI) */
+#ifdef WITH_MPI
+    const int ci_nodeID = ci->nodeID;
+    const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1;
+#else
+    const int ci_nodeID = nodeID;
+    const int cj_nodeID = nodeID;
+#endif
+
+    /* Self-interaction for self-gravity? */
+    if (t_type == task_type_self && t_subtype == task_subtype_grav) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (ci_nodeID != nodeID) error("Non-local self task");
+#endif
+
+      /* drift ---+-> gravity --> grav_down */
+      /* init  --/    */
+      scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
+      scheduler_addunlock(sched, ci_parent->grav.init_out, t);
+      scheduler_addunlock(sched, t, ci_parent->grav.down_in);
+    }
+
+    /* Self-interaction for external gravity ? */
+    if (t_type == task_type_self && t_subtype == task_subtype_external_grav) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (ci_nodeID != nodeID) error("Non-local self task");
+#endif
+
+      /* drift -----> gravity --> end_force */
+      scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
+      scheduler_addunlock(sched, t, ci->super->end_force);
+    }
+
+    /* Otherwise, pair interaction? */
+    else if (t_type == task_type_pair && t_subtype == task_subtype_grav) {
+
+      if (ci_nodeID == nodeID) {
+
+        /* drift ---+-> gravity --> grav_down */
+        /* init  --/    */
+        scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
+        scheduler_addunlock(sched, ci_parent->grav.init_out, t);
+        scheduler_addunlock(sched, t, ci_parent->grav.down_in);
+      }
+      if (cj_nodeID == nodeID) {
+
+        /* drift ---+-> gravity --> grav_down */
+        /* init  --/    */
+        if (ci->grav.super != cj->grav.super) /* Avoid double unlock */
+          scheduler_addunlock(sched, cj->grav.super->grav.drift, t);
+
+        if (ci_parent != cj_parent) { /* Avoid double unlock */
+          scheduler_addunlock(sched, cj_parent->grav.init_out, t);
+          scheduler_addunlock(sched, t, cj_parent->grav.down_in);
+        }
+      }
+    }
+
+    /* Otherwise, sub-self interaction? */
+    else if (t_type == task_type_sub_self && t_subtype == task_subtype_grav) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (ci_nodeID != nodeID) error("Non-local sub-self task");
+#endif
+      /* drift ---+-> gravity --> grav_down */
+      /* init  --/    */
+      scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
+      scheduler_addunlock(sched, ci_parent->grav.init_out, t);
+      scheduler_addunlock(sched, t, ci_parent->grav.down_in);
+    }
+
+    /* Sub-self-interaction for external gravity ? */
+    else if (t_type == task_type_sub_self &&
+             t_subtype == task_subtype_external_grav) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (ci_nodeID != nodeID) error("Non-local sub-self task");
+#endif
+
+      /* drift -----> gravity --> end_force */
+      scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
+      scheduler_addunlock(sched, t, ci->super->end_force);
+    }
+
+    /* Otherwise, sub-pair interaction? */
+    else if (t_type == task_type_sub_pair && t_subtype == task_subtype_grav) {
+
+      if (ci_nodeID == nodeID) {
+
+        /* drift ---+-> gravity --> grav_down */
+        /* init  --/    */
+        scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
+        scheduler_addunlock(sched, ci_parent->grav.init_out, t);
+        scheduler_addunlock(sched, t, ci_parent->grav.down_in);
+      }
+      if (cj_nodeID == nodeID) {
+
+        /* drift ---+-> gravity --> grav_down */
+        /* init  --/    */
+        if (ci->grav.super != cj->grav.super) /* Avoid double unlock */
+          scheduler_addunlock(sched, cj->grav.super->grav.drift, t);
+
+        if (ci_parent != cj_parent) { /* Avoid double unlock */
+          scheduler_addunlock(sched, cj_parent->grav.init_out, t);
+          scheduler_addunlock(sched, t, cj_parent->grav.down_in);
+        }
+      }
+    }
+
+    /* Otherwise M-M interaction? */
+    else if (t_type == task_type_grav_mm) {
+
+      if (ci_nodeID == nodeID) {
+
+        /* init -----> gravity --> grav_down */
+        scheduler_addunlock(sched, ci_parent->grav.init_out, t);
+        scheduler_addunlock(sched, t, ci_parent->grav.down_in);
+      }
+      if (cj_nodeID == nodeID) {
+
+        /* init -----> gravity --> grav_down */
+        if (ci_parent != cj_parent) { /* Avoid double unlock */
+          scheduler_addunlock(sched, cj_parent->grav.init_out, t);
+          scheduler_addunlock(sched, t, cj_parent->grav.down_in);
+        }
+      }
+    }
+  }
+}
+
+#ifdef EXTRA_HYDRO_LOOP
+
+/**
+ * @brief Creates the dependency network for the hydro tasks of a given cell.
+ *
+ * @param sched The #scheduler.
+ * @param density The density task to link.
+ * @param gradient The gradient task to link.
+ * @param force The force task to link.
+ * @param c The cell.
+ * @param with_cooling Do we have a cooling task ?
+ */
+static inline void engine_make_hydro_loops_dependencies(
+    struct scheduler *sched, struct task *density, struct task *gradient,
+    struct task *force, struct cell *c, int with_cooling) {
+
+  /* density loop --> ghost --> gradient loop --> extra_ghost */
+  /* extra_ghost --> force loop  */
+  scheduler_addunlock(sched, density, c->hydro.super->hydro.ghost_in);
+  scheduler_addunlock(sched, c->hydro.super->hydro.ghost_out, gradient);
+  scheduler_addunlock(sched, gradient, c->hydro.super->hydro.extra_ghost);
+  scheduler_addunlock(sched, c->hydro.super->hydro.extra_ghost, force);
+}
+
+#else
+
+/**
+ * @brief Creates the dependency network for the hydro tasks of a given cell.
+ *
+ * @param sched The #scheduler.
+ * @param density The density task to link.
+ * @param force The force task to link.
+ * @param c The cell.
+ * @param with_cooling Are we running with cooling switched on ?
+ */
+static inline void engine_make_hydro_loops_dependencies(struct scheduler *sched,
+                                                        struct task *density,
+                                                        struct task *force,
+                                                        struct cell *c,
+                                                        int with_cooling) {
+  /* density loop --> ghost --> force loop */
+  scheduler_addunlock(sched, density, c->hydro.super->hydro.ghost_in);
+  scheduler_addunlock(sched, c->hydro.super->hydro.ghost_out, force);
+}
+
+#endif
+/**
+ * @brief Creates the dependency network for the stars tasks of a given cell.
+ *
+ * @param sched The #scheduler.
+ * @param density The density task to link.
+ * @param c The cell.
+ */
+static inline void engine_make_stars_loops_dependencies(struct scheduler *sched,
+                                                        struct task *density,
+                                                        struct cell *c) {
+  /* density loop --> ghost */
+  scheduler_addunlock(sched, density, c->super->stars.ghost_in);
+}
+
+/**
+ * @brief Duplicates the first hydro loop and construct all the
+ * dependencies for the hydro part
+ *
+ * This is done by looping over all the previously constructed tasks
+ * and adding another task involving the same cells but this time
+ * corresponding to the second hydro loop over neighbours.
+ * With all the relevant tasks for a given cell available, we construct
+ * all the dependencies for that cell.
+ */
+void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
+                                              void *extra_data) {
+
+  struct engine *e = (struct engine *)extra_data;
+  struct scheduler *sched = &e->sched;
+  const int nodeID = e->nodeID;
+  const int with_cooling = (e->policy & engine_policy_cooling);
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct task *t = &((struct task *)map_data)[ind];
+
+    /* Sort tasks depend on the drift of the cell. */
+    if (t->type == task_type_sort && t->ci->nodeID == engine_rank) {
+      scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
+    }
+
+    /* Self-interaction? */
+    else if (t->type == task_type_self && t->subtype == task_subtype_density) {
+
+      /* Make the self-density tasks depend on the drift only. */
+      scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
+
+#ifdef EXTRA_HYDRO_LOOP
+      /* Start by constructing the task for the second  and third hydro loop. */
+      struct task *t2 = scheduler_addtask(
+          sched, task_type_self, task_subtype_gradient, 0, 0, t->ci, NULL);
+      struct task *t3 = scheduler_addtask(
+          sched, task_type_self, task_subtype_force, 0, 0, t->ci, NULL);
+
+      /* Add the link between the new loops and the cell */
+      engine_addlink(e, &t->ci->hydro.gradient, t2);
+      engine_addlink(e, &t->ci->hydro.force, t3);
+
+      /* Now, build all the dependencies for the hydro */
+      engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci,
+                                           with_cooling);
+      scheduler_addunlock(sched, t3, t->ci->super->end_force);
+#else
+
+      /* Start by constructing the task for the second hydro loop */
+      struct task *t2 = scheduler_addtask(
+          sched, task_type_self, task_subtype_force, 0, 0, t->ci, NULL);
+
+      /* Add the link between the new loop and the cell */
+      engine_addlink(e, &t->ci->hydro.force, t2);
+
+      /* Now, build all the dependencies for the hydro */
+      engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling);
+      scheduler_addunlock(sched, t2, t->ci->super->end_force);
+#endif
+    }
+
+    /* Otherwise, pair interaction? */
+    else if (t->type == task_type_pair && t->subtype == task_subtype_density) {
+
+      /* Make all density tasks depend on the drift and the sorts. */
+      if (t->ci->nodeID == engine_rank)
+        scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
+      scheduler_addunlock(sched, t->ci->hydro.super->hydro.sorts, t);
+      if (t->ci->hydro.super != t->cj->hydro.super) {
+        if (t->cj->nodeID == engine_rank)
+          scheduler_addunlock(sched, t->cj->hydro.super->hydro.drift, t);
+        scheduler_addunlock(sched, t->cj->hydro.super->hydro.sorts, t);
+      }
+
+#ifdef EXTRA_HYDRO_LOOP
+      /* Start by constructing the task for the second and third hydro loop */
+      struct task *t2 = scheduler_addtask(
+          sched, task_type_pair, task_subtype_gradient, 0, 0, t->ci, t->cj);
+      struct task *t3 = scheduler_addtask(
+          sched, task_type_pair, task_subtype_force, 0, 0, t->ci, t->cj);
+
+      /* Add the link between the new loop and both cells */
+      engine_addlink(e, &t->ci->hydro.gradient, t2);
+      engine_addlink(e, &t->cj->hydro.gradient, t2);
+      engine_addlink(e, &t->ci->hydro.force, t3);
+      engine_addlink(e, &t->cj->hydro.force, t3);
+
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super_hydro-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci,
+                                             with_cooling);
+        scheduler_addunlock(sched, t3, t->ci->super->end_force);
+      }
+      if (t->cj->nodeID == nodeID) {
+        if (t->ci->hydro.super != t->cj->hydro.super)
+          engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->cj,
+                                               with_cooling);
+        if (t->ci->super != t->cj->super)
+          scheduler_addunlock(sched, t3, t->cj->super->end_force);
+      }
+
+#else
+
+      /* Start by constructing the task for the second hydro loop */
+      struct task *t2 = scheduler_addtask(
+          sched, task_type_pair, task_subtype_force, 0, 0, t->ci, t->cj);
+
+      /* Add the link between the new loop and both cells */
+      engine_addlink(e, &t->ci->hydro.force, t2);
+      engine_addlink(e, &t->cj->hydro.force, t2);
+
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super_hydro-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling);
+        scheduler_addunlock(sched, t2, t->ci->super->end_force);
+      }
+      if (t->cj->nodeID == nodeID) {
+        if (t->ci->hydro.super != t->cj->hydro.super)
+          engine_make_hydro_loops_dependencies(sched, t, t2, t->cj,
+                                               with_cooling);
+        if (t->ci->super != t->cj->super)
+          scheduler_addunlock(sched, t2, t->cj->super->end_force);
+      }
+
+#endif
+
+    }
+
+    /* Otherwise, sub-self interaction? */
+    else if (t->type == task_type_sub_self &&
+             t->subtype == task_subtype_density) {
+
+      /* Make all density tasks depend on the drift and sorts. */
+      scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
+      scheduler_addunlock(sched, t->ci->hydro.super->hydro.sorts, t);
+
+#ifdef EXTRA_HYDRO_LOOP
+
+      /* Start by constructing the task for the second and third hydro loop */
+      struct task *t2 =
+          scheduler_addtask(sched, task_type_sub_self, task_subtype_gradient,
+                            t->flags, 0, t->ci, t->cj);
+      struct task *t3 =
+          scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
+                            t->flags, 0, t->ci, t->cj);
+
+      /* Add the link between the new loop and the cell */
+      engine_addlink(e, &t->ci->hydro.gradient, t2);
+      engine_addlink(e, &t->ci->hydro.force, t3);
+
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super_hydro-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci,
+                                             with_cooling);
+        scheduler_addunlock(sched, t3, t->ci->super->end_force);
+      }
+
+#else
+      /* Start by constructing the task for the second hydro loop */
+      struct task *t2 =
+          scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
+                            t->flags, 0, t->ci, t->cj);
+
+      /* Add the link between the new loop and the cell */
+      engine_addlink(e, &t->ci->hydro.force, t2);
+
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super_hydro-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling);
+        scheduler_addunlock(sched, t2, t->ci->super->end_force);
+      }
+#endif
+    }
+
+    /* Otherwise, sub-pair interaction? */
+    else if (t->type == task_type_sub_pair &&
+             t->subtype == task_subtype_density) {
+
+      /* Make all density tasks depend on the drift. */
+      if (t->ci->nodeID == engine_rank)
+        scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
+      scheduler_addunlock(sched, t->ci->hydro.super->hydro.sorts, t);
+      if (t->ci->hydro.super != t->cj->hydro.super) {
+        if (t->cj->nodeID == engine_rank)
+          scheduler_addunlock(sched, t->cj->hydro.super->hydro.drift, t);
+        scheduler_addunlock(sched, t->cj->hydro.super->hydro.sorts, t);
+      }
+
+#ifdef EXTRA_HYDRO_LOOP
+
+      /* Start by constructing the task for the second and third hydro loop */
+      struct task *t2 =
+          scheduler_addtask(sched, task_type_sub_pair, task_subtype_gradient,
+                            t->flags, 0, t->ci, t->cj);
+      struct task *t3 =
+          scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
+                            t->flags, 0, t->ci, t->cj);
+
+      /* Add the link between the new loop and both cells */
+      engine_addlink(e, &t->ci->hydro.gradient, t2);
+      engine_addlink(e, &t->cj->hydro.gradient, t2);
+      engine_addlink(e, &t->ci->hydro.force, t3);
+      engine_addlink(e, &t->cj->hydro.force, t3);
+
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super_hydro-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci,
+                                             with_cooling);
+        scheduler_addunlock(sched, t3, t->ci->super->end_force);
+      }
+      if (t->cj->nodeID == nodeID) {
+        if (t->ci->hydro.super != t->cj->hydro.super)
+          engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->cj,
+                                               with_cooling);
+        if (t->ci->super != t->cj->super)
+          scheduler_addunlock(sched, t3, t->cj->super->end_force);
+      }
+
+#else
+      /* Start by constructing the task for the second hydro loop */
+      struct task *t2 =
+          scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
+                            t->flags, 0, t->ci, t->cj);
+
+      /* Add the link between the new loop and both cells */
+      engine_addlink(e, &t->ci->hydro.force, t2);
+      engine_addlink(e, &t->cj->hydro.force, t2);
+
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super_hydro-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t->ci, with_cooling);
+        scheduler_addunlock(sched, t2, t->ci->super->end_force);
+      }
+      if (t->cj->nodeID == nodeID) {
+        if (t->ci->hydro.super != t->cj->hydro.super)
+          engine_make_hydro_loops_dependencies(sched, t, t2, t->cj,
+                                               with_cooling);
+        if (t->ci->super != t->cj->super)
+          scheduler_addunlock(sched, t2, t->cj->super->end_force);
+      }
+#endif
+    }
+  }
+}
+
+/**
+ * @brief Creates all the task dependencies for the stars
+ *
+ * @param map_data The tasks
+ * @param num_elements number of tasks
+ * @param extra_data The #engine
+ */
+void engine_link_stars_tasks_mapper(void *map_data, int num_elements,
+                                    void *extra_data) {
+
+  struct engine *e = (struct engine *)extra_data;
+  struct scheduler *sched = &e->sched;
+  const int nodeID = e->nodeID;
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct task *t = &((struct task *)map_data)[ind];
+
+    /* Self-interaction? */
+    if (t->type == task_type_self && t->subtype == task_subtype_stars_density) {
+
+      /* Make the self-density tasks depend on the drifts. */
+      scheduler_addunlock(sched, t->ci->super->hydro.drift, t);
+
+      scheduler_addunlock(sched, t->ci->super->grav.drift, t);
+
+      /* Now, build all the dependencies for the stars */
+      engine_make_stars_loops_dependencies(sched, t, t->ci);
+      if (t->ci == t->ci->super)
+        scheduler_addunlock(sched, t->ci->super->stars.ghost_out,
+                            t->ci->super->end_force);
+    }
+
+    /* Otherwise, pair interaction? */
+    else if (t->type == task_type_pair &&
+             t->subtype == task_subtype_stars_density) {
+
+      /* Make all density tasks depend on the drift and the sorts. */
+      if (t->ci->nodeID == engine_rank)
+        scheduler_addunlock(sched, t->ci->super->hydro.drift, t);
+      scheduler_addunlock(sched, t->ci->super->hydro.sorts, t);
+      if (t->ci->super != t->cj->super) {
+        if (t->cj->nodeID == engine_rank)
+          scheduler_addunlock(sched, t->cj->super->hydro.drift, t);
+        scheduler_addunlock(sched, t->cj->super->hydro.sorts, t);
+      }
+
+      /* Now, build all the dependencies for the stars for the cells */
+      /* that are local and are not descendant of the same super-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_stars_loops_dependencies(sched, t, t->ci);
+      }
+      if (t->cj->nodeID == nodeID) {
+        if (t->ci->super != t->cj->super)
+          engine_make_stars_loops_dependencies(sched, t, t->cj);
+      }
+
+    }
+
+    /* Otherwise, sub-self interaction? */
+    else if (t->type == task_type_sub_self &&
+             t->subtype == task_subtype_stars_density) {
+
+      /* Make all density tasks depend on the drift and sorts. */
+      scheduler_addunlock(sched, t->ci->super->hydro.drift, t);
+      scheduler_addunlock(sched, t->ci->super->hydro.sorts, t);
+
+      /* Now, build all the dependencies for the stars for the cells */
+      /* that are local and are not descendant of the same super-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_stars_loops_dependencies(sched, t, t->ci);
+      } else
+        error("oo");
+    }
+
+    /* Otherwise, sub-pair interaction? */
+    else if (t->type == task_type_sub_pair &&
+             t->subtype == task_subtype_stars_density) {
+
+      /* Make all density tasks depend on the drift. */
+      if (t->ci->nodeID == engine_rank)
+        scheduler_addunlock(sched, t->ci->super->hydro.drift, t);
+      scheduler_addunlock(sched, t->ci->super->hydro.sorts, t);
+      if (t->ci->super != t->cj->super) {
+        if (t->cj->nodeID == engine_rank)
+          scheduler_addunlock(sched, t->cj->super->hydro.drift, t);
+        scheduler_addunlock(sched, t->cj->super->hydro.sorts, t);
+      }
+
+      /* Now, build all the dependencies for the stars for the cells */
+      /* that are local and are not descendant of the same super-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_stars_loops_dependencies(sched, t, t->ci);
+      }
+      if (t->cj->nodeID == nodeID) {
+        if (t->ci->super != t->cj->super)
+          engine_make_stars_loops_dependencies(sched, t, t->cj);
+      }
+    }
+  }
+}
+
+/**
+ * @brief Constructs the top-level pair tasks for the star loop over
+ * neighbours
+ *
+ * Here we construct all the tasks for all possible neighbouring non-empty
+ * local cells in the hierarchy. No dependencies are being added thus far.
+ * Additional loop over neighbours can later be added by simply duplicating
+ * all the tasks created by this function.
+ *
+ * @param map_data Offset of first two indices disguised as a pointer.
+ * @param num_elements Number of cells to traverse.
+ * @param extra_data The #engine.
+ */
+void engine_make_starsloop_tasks_mapper(void *map_data, int num_elements,
+                                        void *extra_data) {
+
+  /* Extract the engine pointer. */
+  struct engine *e = (struct engine *)extra_data;
+
+  struct space *s = e->s;
+  struct scheduler *sched = &e->sched;
+  const int nodeID = e->nodeID;
+  const int *cdim = s->cdim;
+  struct cell *cells = s->cells_top;
+
+  /* Loop through the elements, which are just byte offsets from NULL. */
+  for (int ind = 0; ind < num_elements; ind++) {
+
+    /* Get the cell index. */
+    const int cid = (size_t)(map_data) + ind;
+    const int i = cid / (cdim[1] * cdim[2]);
+    const int j = (cid / cdim[2]) % cdim[1];
+    const int k = cid % cdim[2];
+
+    /* Get the cell */
+    struct cell *ci = &cells[cid];
+
+    /* Skip cells without star particles */
+    if (ci->stars.count == 0) continue;
+
+    /* If the cells is local build a self-interaction */
+    if (ci->nodeID == nodeID)
+      scheduler_addtask(sched, task_type_self, task_subtype_stars_density, 0, 0,
+                        ci, NULL);
+
+    /* Now loop over all the neighbours of this cell */
+    for (int ii = -1; ii < 2; ii++) {
+      int iii = i + ii;
+      if (!s->periodic && (iii < 0 || iii >= cdim[0])) continue;
+      iii = (iii + cdim[0]) % cdim[0];
+      for (int jj = -1; jj < 2; jj++) {
+        int jjj = j + jj;
+        if (!s->periodic && (jjj < 0 || jjj >= cdim[1])) continue;
+        jjj = (jjj + cdim[1]) % cdim[1];
+        for (int kk = -1; kk < 2; kk++) {
+          int kkk = k + kk;
+          if (!s->periodic && (kkk < 0 || kkk >= cdim[2])) continue;
+          kkk = (kkk + cdim[2]) % cdim[2];
+
+          /* Get the neighbouring cell */
+          const int cjd = cell_getid(cdim, iii, jjj, kkk);
+          struct cell *cj = &cells[cjd];
+
+          /* Is that neighbour local and does it have particles ? */
+          if (cid >= cjd || cj->hydro.count == 0 ||
+              (ci->nodeID != nodeID && cj->nodeID != nodeID))
+            continue;
+
+          /* Construct the pair task */
+          const int sid = sortlistID[(kk + 1) + 3 * ((jj + 1) + 3 * (ii + 1))];
+          scheduler_addtask(sched, task_type_pair, task_subtype_stars_density,
+                            sid, 0, ci, cj);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * @brief Constructs the top-level pair tasks for the first hydro loop over
+ * neighbours
+ *
+ * Here we construct all the tasks for all possible neighbouring non-empty
+ * local cells in the hierarchy. No dependencies are being added thus far.
+ * Additional loop over neighbours can later be added by simply duplicating
+ * all the tasks created by this function.
+ *
+ * @param map_data Offset of first two indices disguised as a pointer.
+ * @param num_elements Number of cells to traverse.
+ * @param extra_data The #engine.
+ */
+void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements,
+                                        void *extra_data) {
+
+  /* Extract the engine pointer. */
+  struct engine *e = (struct engine *)extra_data;
+
+  struct space *s = e->s;
+  struct scheduler *sched = &e->sched;
+  const int nodeID = e->nodeID;
+  const int *cdim = s->cdim;
+  struct cell *cells = s->cells_top;
+
+  /* Loop through the elements, which are just byte offsets from NULL. */
+  for (int ind = 0; ind < num_elements; ind++) {
+
+    /* Get the cell index. */
+    const int cid = (size_t)(map_data) + ind;
+    const int i = cid / (cdim[1] * cdim[2]);
+    const int j = (cid / cdim[2]) % cdim[1];
+    const int k = cid % cdim[2];
+
+    /* Get the cell */
+    struct cell *ci = &cells[cid];
+
+    /* Skip cells without hydro particles */
+    if (ci->hydro.count == 0) continue;
+
+    /* If the cells is local build a self-interaction */
+    if (ci->nodeID == nodeID)
+      scheduler_addtask(sched, task_type_self, task_subtype_density, 0, 0, ci,
+                        NULL);
+
+    /* Now loop over all the neighbours of this cell */
+    for (int ii = -1; ii < 2; ii++) {
+      int iii = i + ii;
+      if (!s->periodic && (iii < 0 || iii >= cdim[0])) continue;
+      iii = (iii + cdim[0]) % cdim[0];
+      for (int jj = -1; jj < 2; jj++) {
+        int jjj = j + jj;
+        if (!s->periodic && (jjj < 0 || jjj >= cdim[1])) continue;
+        jjj = (jjj + cdim[1]) % cdim[1];
+        for (int kk = -1; kk < 2; kk++) {
+          int kkk = k + kk;
+          if (!s->periodic && (kkk < 0 || kkk >= cdim[2])) continue;
+          kkk = (kkk + cdim[2]) % cdim[2];
+
+          /* Get the neighbouring cell */
+          const int cjd = cell_getid(cdim, iii, jjj, kkk);
+          struct cell *cj = &cells[cjd];
+
+          /* Is that neighbour local and does it have particles ? */
+          if (cid >= cjd || cj->hydro.count == 0 ||
+              (ci->nodeID != nodeID && cj->nodeID != nodeID))
+            continue;
+
+          /* Construct the pair task */
+          const int sid = sortlistID[(kk + 1) + 3 * ((jj + 1) + 3 * (ii + 1))];
+          scheduler_addtask(sched, task_type_pair, task_subtype_density, sid, 0,
+                            ci, cj);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * @brief Fill the #space's task list.
+ *
+ * @param e The #engine we are working with.
+ */
+void engine_maketasks(struct engine *e) {
+
+  struct space *s = e->s;
+  struct scheduler *sched = &e->sched;
+  struct cell *cells = s->cells_top;
+  const int nr_cells = s->nr_cells;
+  const ticks tic = getticks();
+
+  /* Re-set the scheduler. */
+  scheduler_reset(sched, engine_estimate_nr_tasks(e));
+
+  ticks tic2 = getticks();
+
+  /* Construct the first hydro loop over neighbours */
+  if (e->policy & engine_policy_hydro)
+    threadpool_map(&e->threadpool, engine_make_hydroloop_tasks_mapper, NULL,
+                   s->nr_cells, 1, 0, e);
+
+  if (e->verbose)
+    message("Making hydro tasks took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+  tic2 = getticks();
+
+  /* Construct the stars hydro loop over neighbours */
+  if (e->policy & engine_policy_feedback) {
+    threadpool_map(&e->threadpool, engine_make_starsloop_tasks_mapper, NULL,
+                   s->nr_cells, 1, 0, e);
+  }
+
+  /* Add the self gravity tasks. */
+  if (e->policy & engine_policy_self_gravity) engine_make_self_gravity_tasks(e);
+
+  if (e->verbose)
+    message("Making gravity tasks took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+  /* Add the external gravity tasks. */
+  if (e->policy & engine_policy_external_gravity)
+    engine_make_external_gravity_tasks(e);
+
+  if (e->sched.nr_tasks == 0 && (s->nr_gparts > 0 || s->nr_parts > 0))
+    error("We have particles but no hydro or gravity tasks were created.");
+
+  /* Free the old list of cell-task links. */
+  if (e->links != NULL) free(e->links);
+  e->size_links = 0;
+
+/* The maximum number of links is the
+ * number of cells (s->tot_cells) times the number of neighbours (26) times
+ * the number of interaction types, so 26 * 2 (density, force) pairs
+ * and 2 (density, force) self.
+ */
+#ifdef EXTRA_HYDRO_LOOP
+  const size_t hydro_tasks_per_cell = 27 * 3;
+#else
+  const size_t hydro_tasks_per_cell = 27 * 2;
+#endif
+  const size_t self_grav_tasks_per_cell = 125;
+  const size_t ext_grav_tasks_per_cell = 1;
+  const size_t stars_tasks_per_cell = 15;
+
+  if (e->policy & engine_policy_hydro)
+    e->size_links += s->tot_cells * hydro_tasks_per_cell;
+  if (e->policy & engine_policy_external_gravity)
+    e->size_links += s->tot_cells * ext_grav_tasks_per_cell;
+  if (e->policy & engine_policy_self_gravity)
+    e->size_links += s->tot_cells * self_grav_tasks_per_cell;
+  if (e->policy & engine_policy_stars)
+    e->size_links += s->tot_cells * stars_tasks_per_cell;
+
+  /* Allocate the new link list */
+  if ((e->links = (struct link *)malloc(sizeof(struct link) * e->size_links)) ==
+      NULL)
+    error("Failed to allocate cell-task links.");
+  e->nr_links = 0;
+
+  tic2 = getticks();
+
+  /* Split the tasks. */
+  scheduler_splittasks(sched);
+
+  if (e->verbose)
+    message("Splitting tasks took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Verify that we are not left with invalid tasks */
+  for (int i = 0; i < e->sched.nr_tasks; ++i) {
+    const struct task *t = &e->sched.tasks[i];
+    if (t->ci == NULL && t->cj != NULL && !t->skip) error("Invalid task");
+  }
+#endif
+
+  tic2 = getticks();
+
+  /* Count the number of tasks associated with each cell and
+     store the density tasks in each cell, and make each sort
+     depend on the sorts of its progeny. */
+  threadpool_map(&e->threadpool, engine_count_and_link_tasks_mapper,
+                 sched->tasks, sched->nr_tasks, sizeof(struct task), 0, e);
+
+  if (e->verbose)
+    message("Counting and linking tasks took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+  tic2 = getticks();
+
+  /* Re-set the tag counter. MPI tags are defined for top-level cells in
+   * cell_set_super_mapper. */
+#ifdef WITH_MPI
+  cell_next_tag = 0;
+#endif
+
+  /* Now that the self/pair tasks are at the right level, set the super
+   * pointers. */
+  threadpool_map(&e->threadpool, cell_set_super_mapper, cells, nr_cells,
+                 sizeof(struct cell), 0, e);
+
+  if (e->verbose)
+    message("Setting super-pointers took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+  /* Append hierarchical tasks to each cell. */
+  threadpool_map(&e->threadpool, engine_make_hierarchical_tasks_mapper, cells,
+                 nr_cells, sizeof(struct cell), 0, e);
+
+  tic2 = getticks();
+
+  /* Run through the tasks and make force tasks for each density task.
+     Each force task depends on the cell ghosts and unlocks the kick task
+     of its super-cell. */
+  if (e->policy & engine_policy_hydro)
+    threadpool_map(&e->threadpool, engine_make_extra_hydroloop_tasks_mapper,
+                   sched->tasks, sched->nr_tasks, sizeof(struct task), 0, e);
+
+  if (e->verbose)
+    message("Making extra hydroloop tasks took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+  tic2 = getticks();
+
+  /* Add the dependencies for the gravity stuff */
+  if (e->policy & (engine_policy_self_gravity | engine_policy_external_gravity))
+    engine_link_gravity_tasks(e);
+
+  if (e->verbose)
+    message("Linking gravity tasks took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+  tic2 = getticks();
+
+  if (e->policy & engine_policy_stars)
+    threadpool_map(&e->threadpool, engine_link_stars_tasks_mapper, sched->tasks,
+                   sched->nr_tasks, sizeof(struct task), 0, e);
+
+  if (e->verbose)
+    message("Linking stars tasks took %.3f %s (including reweight).",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+#ifdef WITH_MPI
+  if (e->policy & engine_policy_feedback)
+    error("Cannot run stellar feedback with MPI (yet).");
+
+  /* Add the communication tasks if MPI is being used. */
+  if (e->policy & engine_policy_mpi) {
+
+    tic2 = getticks();
+
+    /* Loop over the proxies and add the send tasks, which also generates the
+     * cell tags for super-cells. */
+    for (int pid = 0; pid < e->nr_proxies; pid++) {
+
+      /* Get a handle on the proxy. */
+      struct proxy *p = &e->proxies[pid];
+
+      for (int k = 0; k < p->nr_cells_out; k++)
+        engine_addtasks_send_timestep(e, p->cells_out[k], p->cells_in[0], NULL);
+
+      /* Loop through the proxy's outgoing cells and add the
+         send tasks for the cells in the proxy that have a hydro connection. */
+      if (e->policy & engine_policy_hydro)
+        for (int k = 0; k < p->nr_cells_out; k++)
+          if (p->cells_out_type[k] & proxy_cell_type_hydro)
+            engine_addtasks_send_hydro(e, p->cells_out[k], p->cells_in[0], NULL,
+                                       NULL, NULL);
+
+      /* Loop through the proxy's outgoing cells and add the
+         send tasks for the cells in the proxy that have a gravity connection.
+         */
+      if (e->policy & engine_policy_self_gravity)
+        for (int k = 0; k < p->nr_cells_out; k++)
+          if (p->cells_out_type[k] & proxy_cell_type_gravity)
+            engine_addtasks_send_gravity(e, p->cells_out[k], p->cells_in[0],
+                                         NULL);
+    }
+
+    if (e->verbose)
+      message("Creating send tasks took %.3f %s.",
+              clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+    tic2 = getticks();
+
+    /* Exchange the cell tags. */
+    proxy_tags_exchange(e->proxies, e->nr_proxies, s);
+
+    if (e->verbose)
+      message("Exchanging cell tags took %.3f %s.",
+              clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+    tic2 = getticks();
+
+    /* Loop over the proxies and add the recv tasks, which relies on having the
+     * cell tags. */
+    for (int pid = 0; pid < e->nr_proxies; pid++) {
+
+      /* Get a handle on the proxy. */
+      struct proxy *p = &e->proxies[pid];
+
+      for (int k = 0; k < p->nr_cells_in; k++)
+        engine_addtasks_recv_timestep(e, p->cells_in[k], NULL);
+
+      /* Loop through the proxy's incoming cells and add the
+         recv tasks for the cells in the proxy that have a hydro connection. */
+      if (e->policy & engine_policy_hydro)
+        for (int k = 0; k < p->nr_cells_in; k++)
+          if (p->cells_in_type[k] & proxy_cell_type_hydro)
+            engine_addtasks_recv_hydro(e, p->cells_in[k], NULL, NULL, NULL);
+
+      /* Loop through the proxy's incoming cells and add the
+         recv tasks for the cells in the proxy that have a gravity connection.
+         */
+      if (e->policy & engine_policy_self_gravity)
+        for (int k = 0; k < p->nr_cells_in; k++)
+          if (p->cells_in_type[k] & proxy_cell_type_gravity)
+            engine_addtasks_recv_gravity(e, p->cells_in[k], NULL);
+    }
+
+    if (e->verbose)
+      message("Creating recv tasks took %.3f %s.",
+              clocks_from_ticks(getticks() - tic2), clocks_getunit());
+  }
+#endif
+
+  tic2 = getticks();
+
+  /* Set the unlocks per task. */
+  scheduler_set_unlocks(sched);
+
+  if (e->verbose)
+    message("Setting unlocks took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+  tic2 = getticks();
+
+  /* Rank the tasks. */
+  scheduler_ranktasks(sched);
+
+  if (e->verbose)
+    message("Ranking the tasks took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
+
+  /* Weight the tasks. */
+  scheduler_reweight(sched, e->verbose);
+
+  /* Set the tasks age. */
+  e->tasks_age = 0;
+
+  if (e->verbose)
+    message("took %.3f %s (including reweight).",
+            clocks_from_ticks(getticks() - tic), clocks_getunit());
+}
diff --git a/src/engine_marktasks.c b/src/engine_marktasks.c
new file mode 100644
index 0000000000000000000000000000000000000000..c891fb2ca7aa66538adbbb8e1fd694f3af6acf07
--- /dev/null
+++ b/src/engine_marktasks.c
@@ -0,0 +1,552 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
+ *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2015 Peter W. Draper (p.w.draper@durham.ac.uk)
+ *                    Angus Lepper (angus.lepper@ed.ac.uk)
+ *               2016 John A. Regan (john.a.regan@durham.ac.uk)
+ *                    Tom Theuns (tom.theuns@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <stdlib.h>
+#include <unistd.h>
+
+/* MPI headers. */
+#ifdef WITH_MPI
+#include <mpi.h>
+#endif
+
+/* Load the profiler header, if needed. */
+#ifdef WITH_PROFILER
+#include <gperftools/profiler.h>
+#endif
+
+/* This object's header. */
+#include "engine.h"
+
+/* Local headers. */
+#include "active.h"
+#include "atomic.h"
+#include "cell.h"
+#include "clocks.h"
+#include "cycle.h"
+#include "debug.h"
+#include "error.h"
+#include "proxy.h"
+#include "timers.h"
+
+/**
+ * @brief Mark tasks to be un-skipped and set the sort flags accordingly.
+ *        Threadpool mapper function.
+ *
+ * @param map_data pointer to the tasks
+ * @param num_elements number of tasks
+ * @param extra_data pointer to int that will define if a rebuild is needed.
+ */
+void engine_marktasks_mapper(void *map_data, int num_elements,
+                             void *extra_data) {
+  /* Unpack the arguments. */
+  struct task *tasks = (struct task *)map_data;
+  size_t *rebuild_space = &((size_t *)extra_data)[1];
+  struct scheduler *s = (struct scheduler *)(((size_t *)extra_data)[2]);
+  struct engine *e = (struct engine *)((size_t *)extra_data)[0];
+  const int nodeID = e->nodeID;
+
+  for (int ind = 0; ind < num_elements; ind++) {
+
+    /* Get basic task information */
+    struct task *t = &tasks[ind];
+    const enum task_types t_type = t->type;
+    const enum task_subtypes t_subtype = t->subtype;
+
+    /* Single-cell task? */
+    if (t_type == task_type_self || t_type == task_type_sub_self) {
+
+      /* Local pointer. */
+      struct cell *ci = t->ci;
+
+      if (ci->nodeID != engine_rank) error("Non-local self task found");
+
+      /* Activate the hydro drift */
+      if (t_type == task_type_self && t_subtype == task_subtype_density) {
+        if (cell_is_active_hydro(ci, e)) {
+          scheduler_activate(s, t);
+          cell_activate_drift_part(ci, s);
+        }
+      }
+
+      /* Store current values of dx_max and h_max. */
+      else if (t_type == task_type_sub_self &&
+               t_subtype == task_subtype_density) {
+        if (cell_is_active_hydro(ci, e)) {
+          scheduler_activate(s, t);
+          cell_activate_subcell_hydro_tasks(ci, NULL, s);
+        }
+      }
+
+      else if (t_type == task_type_self && t_subtype == task_subtype_force) {
+        if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
+      }
+
+      else if (t_type == task_type_sub_self &&
+               t_subtype == task_subtype_force) {
+        if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
+      }
+
+#ifdef EXTRA_HYDRO_LOOP
+      else if (t_type == task_type_self && t_subtype == task_subtype_gradient) {
+        if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
+      }
+
+      else if (t_type == task_type_sub_self &&
+               t_subtype == task_subtype_gradient) {
+        if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
+      }
+#endif
+
+      /* Activate the star density */
+      else if (t_type == task_type_self &&
+               t_subtype == task_subtype_stars_density) {
+        if (cell_is_active_stars(ci, e)) {
+          scheduler_activate(s, t);
+          cell_activate_drift_part(ci, s);
+          cell_activate_drift_gpart(ci, s);
+        }
+      }
+
+      /* Store current values of dx_max and h_max. */
+      else if (t_type == task_type_sub_self &&
+               t_subtype == task_subtype_stars_density) {
+        if (cell_is_active_stars(ci, e)) {
+          scheduler_activate(s, t);
+          cell_activate_subcell_stars_tasks(ci, NULL, s);
+        }
+      }
+
+      /* Activate the gravity drift */
+      else if (t_type == task_type_self && t_subtype == task_subtype_grav) {
+        if (cell_is_active_gravity(ci, e)) {
+          scheduler_activate(s, t);
+          cell_activate_subcell_grav_tasks(t->ci, NULL, s);
+        }
+      }
+
+      /* Activate the gravity drift */
+      else if (t_type == task_type_self &&
+               t_subtype == task_subtype_external_grav) {
+        if (cell_is_active_gravity(ci, e)) {
+          scheduler_activate(s, t);
+          cell_activate_drift_gpart(t->ci, s);
+        }
+      }
+
+#ifdef SWIFT_DEBUG_CHECKS
+      else {
+        error("Invalid task type / sub-type encountered");
+      }
+#endif
+    }
+
+    /* Pair? */
+    else if (t_type == task_type_pair || t_type == task_type_sub_pair) {
+
+      /* Local pointers. */
+      struct cell *ci = t->ci;
+      struct cell *cj = t->cj;
+#ifdef WITH_MPI
+      const int ci_nodeID = ci->nodeID;
+      const int cj_nodeID = cj->nodeID;
+#else
+      const int ci_nodeID = nodeID;
+      const int cj_nodeID = nodeID;
+#endif
+      const int ci_active_hydro = cell_is_active_hydro(ci, e);
+      const int cj_active_hydro = cell_is_active_hydro(cj, e);
+      const int ci_active_gravity = cell_is_active_gravity(ci, e);
+      const int cj_active_gravity = cell_is_active_gravity(cj, e);
+      const int ci_active_stars = cell_is_active_stars(ci, e);
+      const int cj_active_stars = cell_is_active_stars(cj, e);
+
+      /* Only activate tasks that involve a local active cell. */
+      if ((t_subtype == task_subtype_density ||
+           t_subtype == task_subtype_gradient ||
+           t_subtype == task_subtype_force) &&
+          ((ci_active_hydro && ci_nodeID == nodeID) ||
+           (cj_active_hydro && cj_nodeID == nodeID))) {
+
+        scheduler_activate(s, t);
+
+        /* Set the correct sorting flags */
+        if (t_type == task_type_pair &&
+            (t_subtype == task_subtype_density ||
+             t_subtype == task_subtype_stars_density)) {
+
+          /* Store some values. */
+          atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+          atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+          ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+          cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
+
+          /* Activate the hydro drift tasks. */
+          if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
+          if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s);
+
+          /* Check the sorts and activate them if needed. */
+          cell_activate_sorts(ci, t->flags, s);
+          cell_activate_sorts(cj, t->flags, s);
+
+        }
+
+        /* Store current values of dx_max and h_max. */
+        else if (t_type == task_type_sub_pair &&
+                 (t_subtype == task_subtype_density ||
+                  t_subtype == task_subtype_stars_density)) {
+          cell_activate_subcell_hydro_tasks(t->ci, t->cj, s);
+        }
+      }
+
+      /* Stars */
+      if (t_subtype == task_subtype_stars_density &&
+          ((ci_active_stars && ci->nodeID == engine_rank) ||
+           (cj_active_stars && cj->nodeID == engine_rank))) {
+
+        scheduler_activate(s, t);
+
+        /* Set the correct sorting flags */
+        if (t_type == task_type_pair) {
+
+          /* Store some values. */
+          atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+          atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+          ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+          cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
+
+          /* Activate the hydro drift tasks. */
+          if (ci_nodeID == nodeID) {
+            cell_activate_drift_part(ci, s);
+            cell_activate_drift_gpart(ci, s);
+          }
+          if (cj_nodeID == nodeID) {
+            cell_activate_drift_part(cj, s);
+            cell_activate_drift_gpart(cj, s);
+          }
+
+          /* Check the sorts and activate them if needed. */
+          cell_activate_sorts(ci, t->flags, s);
+          cell_activate_sorts(cj, t->flags, s);
+
+        }
+
+        /* Store current values of dx_max and h_max. */
+        else if (t_type == task_type_sub_pair) {
+          cell_activate_subcell_stars_tasks(t->ci, t->cj, s);
+        }
+      }
+
+      if ((t_subtype == task_subtype_grav) &&
+          ((ci_active_gravity && ci_nodeID == nodeID) ||
+           (cj_active_gravity && cj_nodeID == nodeID))) {
+
+        scheduler_activate(s, t);
+
+        if (t_type == task_type_pair && t_subtype == task_subtype_grav) {
+          /* Activate the gravity drift */
+          cell_activate_subcell_grav_tasks(t->ci, t->cj, s);
+        }
+
+#ifdef SWIFT_DEBUG_CHECKS
+        else if (t_type == task_type_sub_pair &&
+                 t_subtype == task_subtype_grav) {
+          error("Invalid task sub-type encountered");
+        }
+#endif
+      }
+
+      /* Only interested in density tasks as of here. */
+      if (t_subtype == task_subtype_density) {
+
+        /* Too much particle movement? */
+        if (cell_need_rebuild_for_pair(ci, cj)) *rebuild_space = 1;
+
+#ifdef WITH_MPI
+        /* Activate the send/recv tasks. */
+        if (ci_nodeID != nodeID) {
+
+          /* If the local cell is active, receive data from the foreign cell. */
+          if (cj_active_hydro) {
+            scheduler_activate(s, ci->mpi.hydro.recv_xv);
+            if (ci_active_hydro) {
+              scheduler_activate(s, ci->mpi.hydro.recv_rho);
+#ifdef EXTRA_HYDRO_LOOP
+              scheduler_activate(s, ci->mpi.hydro.recv_gradient);
+#endif
+            }
+          }
+
+          /* If the foreign cell is active, we want its ti_end values. */
+          if (ci_active_hydro) scheduler_activate(s, ci->mpi.recv_ti);
+
+          /* Is the foreign cell active and will need stuff from us? */
+          if (ci_active_hydro) {
+
+            struct link *l =
+                scheduler_activate_send(s, cj->mpi.hydro.send_xv, ci_nodeID);
+
+            /* Drift the cell which will be sent at the level at which it is
+               sent, i.e. drift the cell specified in the send task (l->t)
+               itself. */
+            cell_activate_drift_part(l->t->ci, s);
+
+            /* If the local cell is also active, more stuff will be needed. */
+            if (cj_active_hydro) {
+              scheduler_activate_send(s, cj->mpi.hydro.send_rho, ci_nodeID);
+
+#ifdef EXTRA_HYDRO_LOOP
+              scheduler_activate_send(s, cj->mpi.hydro.send_gradient,
+                                      ci_nodeID);
+#endif
+            }
+          }
+
+          /* If the local cell is active, send its ti_end values. */
+          if (cj_active_hydro)
+            scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID);
+
+        } else if (cj_nodeID != nodeID) {
+
+          /* If the local cell is active, receive data from the foreign cell. */
+          if (ci_active_hydro) {
+            scheduler_activate(s, cj->mpi.hydro.recv_xv);
+            if (cj_active_hydro) {
+              scheduler_activate(s, cj->mpi.hydro.recv_rho);
+#ifdef EXTRA_HYDRO_LOOP
+              scheduler_activate(s, cj->mpi.hydro.recv_gradient);
+#endif
+            }
+          }
+
+          /* If the foreign cell is active, we want its ti_end values. */
+          if (cj_active_hydro) scheduler_activate(s, cj->mpi.recv_ti);
+
+          /* Is the foreign cell active and will need stuff from us? */
+          if (cj_active_hydro) {
+
+            struct link *l =
+                scheduler_activate_send(s, ci->mpi.hydro.send_xv, cj_nodeID);
+
+            /* Drift the cell which will be sent at the level at which it is
+               sent, i.e. drift the cell specified in the send task (l->t)
+               itself. */
+            cell_activate_drift_part(l->t->ci, s);
+
+            /* If the local cell is also active, more stuff will be needed. */
+            if (ci_active_hydro) {
+
+              scheduler_activate_send(s, ci->mpi.hydro.send_rho, cj_nodeID);
+
+#ifdef EXTRA_HYDRO_LOOP
+              scheduler_activate_send(s, ci->mpi.hydro.send_gradient,
+                                      cj_nodeID);
+#endif
+            }
+          }
+
+          /* If the local cell is active, send its ti_end values. */
+          if (ci_active_hydro)
+            scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID);
+        }
+#endif
+      }
+
+      /* Only interested in stars_density tasks as of here. */
+      if (t->subtype == task_subtype_stars_density) {
+
+        /* Too much particle movement? */
+        if (cell_need_rebuild_for_pair(ci, cj)) *rebuild_space = 1;
+
+        // LOIC: Need implementing MPI case
+      }
+
+      /* Only interested in gravity tasks as of here. */
+      if (t_subtype == task_subtype_grav) {
+
+#ifdef WITH_MPI
+        /* Activate the send/recv tasks. */
+        if (ci_nodeID != nodeID) {
+
+          /* If the local cell is active, receive data from the foreign cell. */
+          if (cj_active_gravity) scheduler_activate(s, ci->mpi.grav.recv);
+
+          /* If the foreign cell is active, we want its ti_end values. */
+          if (ci_active_gravity) scheduler_activate(s, ci->mpi.recv_ti);
+
+          /* Is the foreign cell active and will need stuff from us? */
+          if (ci_active_gravity) {
+
+            struct link *l =
+                scheduler_activate_send(s, cj->mpi.grav.send, ci_nodeID);
+
+            /* Drift the cell which will be sent at the level at which it is
+               sent, i.e. drift the cell specified in the send task (l->t)
+               itself. */
+            cell_activate_drift_gpart(l->t->ci, s);
+          }
+
+          /* If the local cell is active, send its ti_end values. */
+          if (cj_active_gravity)
+            scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID);
+
+        } else if (cj_nodeID != nodeID) {
+
+          /* If the local cell is active, receive data from the foreign cell. */
+          if (ci_active_gravity) scheduler_activate(s, cj->mpi.grav.recv);
+
+          /* If the foreign cell is active, we want its ti_end values. */
+          if (cj_active_gravity) scheduler_activate(s, cj->mpi.recv_ti);
+
+          /* Is the foreign cell active and will need stuff from us? */
+          if (cj_active_gravity) {
+
+            struct link *l =
+                scheduler_activate_send(s, ci->mpi.grav.send, cj_nodeID);
+
+            /* Drift the cell which will be sent at the level at which it is
+               sent, i.e. drift the cell specified in the send task (l->t)
+               itself. */
+            cell_activate_drift_gpart(l->t->ci, s);
+          }
+
+          /* If the local cell is active, send its ti_end values. */
+          if (ci_active_gravity)
+            scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID);
+        }
+#endif
+      }
+    }
+
+    /* End force ? */
+    else if (t_type == task_type_end_force) {
+
+      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
+        scheduler_activate(s, t);
+    }
+
+    /* Kick ? */
+    else if (t_type == task_type_kick1 || t_type == task_type_kick2) {
+
+      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
+        scheduler_activate(s, t);
+    }
+
+    /* Hydro ghost tasks ? */
+    else if (t_type == task_type_ghost || t_type == task_type_extra_ghost ||
+             t_type == task_type_ghost_in || t_type == task_type_ghost_out) {
+      if (cell_is_active_hydro(t->ci, e)) scheduler_activate(s, t);
+    }
+
+    /* logger tasks ? */
+    else if (t->type == task_type_logger) {
+      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e) ||
+          cell_is_active_stars(t->ci, e))
+        scheduler_activate(s, t);
+    }
+
+    /* Gravity stuff ? */
+    else if (t_type == task_type_grav_down || t_type == task_type_grav_mesh ||
+             t_type == task_type_grav_long_range ||
+             t_type == task_type_init_grav ||
+             t_type == task_type_init_grav_out ||
+             t_type == task_type_grav_down_in) {
+      if (cell_is_active_gravity(t->ci, e)) scheduler_activate(s, t);
+    }
+
+    /* Multipole - Multipole interaction task */
+    else if (t_type == task_type_grav_mm) {
+
+      /* Local pointers. */
+      const struct cell *ci = t->ci;
+      const struct cell *cj = t->cj;
+#ifdef WITH_MPI
+      const int ci_nodeID = ci->nodeID;
+      const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1;
+#else
+      const int ci_nodeID = nodeID;
+      const int cj_nodeID = nodeID;
+#endif
+      const int ci_active_gravity = cell_is_active_gravity_mm(ci, e);
+      const int cj_active_gravity = cell_is_active_gravity_mm(cj, e);
+
+      if ((ci_active_gravity && ci_nodeID == nodeID) ||
+          (cj_active_gravity && cj_nodeID == nodeID))
+        scheduler_activate(s, t);
+    }
+
+    /* Star ghost tasks ? */
+    else if (t_type == task_type_stars_ghost ||
+             t_type == task_type_stars_ghost_in ||
+             t_type == task_type_stars_ghost_out) {
+      if (cell_is_active_stars(t->ci, e)) scheduler_activate(s, t);
+    }
+
+    /* Time-step? */
+    else if (t_type == task_type_timestep) {
+      t->ci->hydro.updated = 0;
+      t->ci->grav.updated = 0;
+      t->ci->stars.updated = 0;
+      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
+        scheduler_activate(s, t);
+    }
+
+    /* Subgrid tasks */
+    else if (t_type == task_type_cooling) {
+      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
+        scheduler_activate(s, t);
+    } else if (t_type == task_type_star_formation) {
+      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
+        scheduler_activate(s, t);
+    }
+  }
+}
+
+/**
+ * @brief Mark tasks to be un-skipped and set the sort flags accordingly.
+ *
+ * @return 1 if the space has to be rebuilt, 0 otherwise.
+ */
+int engine_marktasks(struct engine *e) {
+
+  struct scheduler *s = &e->sched;
+  const ticks tic = getticks();
+  int rebuild_space = 0;
+
+  /* Run through the tasks and mark as skip or not. */
+  size_t extra_data[3] = {(size_t)e, (size_t)rebuild_space, (size_t)&e->sched};
+  threadpool_map(&e->threadpool, engine_marktasks_mapper, s->tasks, s->nr_tasks,
+                 sizeof(struct task), 0, extra_data);
+  rebuild_space = extra_data[1];
+
+  if (e->verbose)
+    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
+            clocks_getunit());
+
+  /* All is well... */
+  return rebuild_space;
+}
diff --git a/src/equation_of_state/planetary/hm80.h b/src/equation_of_state/planetary/hm80.h
index 5e80c240018756cb57cc8974df4974a6cc53724a..38e2c9e4022387ee5ab79fafbedc6fc0dc47f49d 100644
--- a/src/equation_of_state/planetary/hm80.h
+++ b/src/equation_of_state/planetary/hm80.h
@@ -86,21 +86,19 @@ INLINE static void load_table_HM80(struct HM80_params *mat, char *table_file) {
 
   // Load table contents from file
   FILE *f = fopen(table_file, "r");
-  int c;
+  if (f == NULL) error("Failed to open the HM80 EoS file '%s'", table_file);
 
   // Ignore header lines
   char buffer[100];
   for (int i = 0; i < 4; i++) {
     if (fgets(buffer, 100, f) == NULL)
-      error("Something incorrect happening with the file header.");
+      error("Failed to read the HM80 EoS file header %s", table_file);
   }
 
   // Table properties
-  c = fscanf(f, "%f %f %d %f %f %d", &mat->log_rho_min, &mat->log_rho_max,
-             &mat->num_rho, &mat->log_u_min, &mat->log_u_max, &mat->num_u);
-  if (c != 6) {
-    error("Failed to read EOS table %s", table_file);
-  }
+  int c = fscanf(f, "%f %f %d %f %f %d", &mat->log_rho_min, &mat->log_rho_max,
+                 &mat->num_rho, &mat->log_u_min, &mat->log_u_max, &mat->num_u);
+  if (c != 6) error("Failed to read the HM80 EoS table %s", table_file);
   mat->log_rho_step =
       (mat->log_rho_max - mat->log_rho_min) / (mat->num_rho - 1);
   mat->log_u_step = (mat->log_u_max - mat->log_u_min) / (mat->num_u - 1);
@@ -115,9 +113,7 @@ INLINE static void load_table_HM80(struct HM80_params *mat, char *table_file) {
   for (int i_rho = 0; i_rho < mat->num_rho; i_rho++) {
     for (int i_u = 0; i_u < mat->num_u; i_u++) {
       c = fscanf(f, "%f", &mat->table_log_P_rho_u[i_rho * mat->num_u + i_u]);
-      if (c != 1) {
-        error("Failed to read EOS table");
-      }
+      if (c != 1) error("Failed to read the HM80 EoS table %s", table_file);
     }
   }
   fclose(f);
diff --git a/src/equation_of_state/planetary/sesame.h b/src/equation_of_state/planetary/sesame.h
index d958c9b9d09ffe37eefd77ad0384d85bf8c055dd..11c16964602b28c0d1a080b6c262ff20c1f5b9cb 100644
--- a/src/equation_of_state/planetary/sesame.h
+++ b/src/equation_of_state/planetary/sesame.h
@@ -82,21 +82,19 @@ INLINE static void load_table_SESAME(struct SESAME_params *mat,
 
   // Load table contents from file
   FILE *f = fopen(table_file, "r");
-  int c;
+  if (f == NULL) error("Failed to open the SESAME EoS file '%s'", table_file);
 
   // Ignore header lines
   char buffer[100];
   for (int i = 0; i < 5; i++) {
     if (fgets(buffer, 100, f) == NULL)
-      error("Something incorrect happening with the file header.");
+      error("Failed to read the SESAME EoS file header %s", table_file);
   }
   float ignore;
 
   // Table properties
-  c = fscanf(f, "%d %d", &mat->num_rho, &mat->num_T);
-  if (c != 2) {
-    error("Failed to read EOS table %s", table_file);
-  }
+  int c = fscanf(f, "%d %d", &mat->num_rho, &mat->num_T);
+  if (c != 2) error("Failed to read the SESAME EoS table %s", table_file);
 
   // Ignore the first elements of rho = 0, T = 0
   mat->num_rho--;
@@ -118,23 +116,17 @@ INLINE static void load_table_SESAME(struct SESAME_params *mat,
     // Ignore the first elements of rho = 0, T = 0
     if (i_rho == -1) {
       c = fscanf(f, "%f", &ignore);
-      if (c != 1) {
-        error("Failed to read EOS table %s", table_file);
-      }
+      if (c != 1) error("Failed to read the SESAME EoS table %s", table_file);
     } else {
       c = fscanf(f, "%f", &mat->table_log_rho[i_rho]);
-      if (c != 1) {
-        error("Failed to read EOS table %s", table_file);
-      }
+      if (c != 1) error("Failed to read the SESAME EoS table %s", table_file);
     }
   }
 
   // Temperatures (ignored)
   for (int i_T = -1; i_T < mat->num_T; i_T++) {
     c = fscanf(f, "%f", &ignore);
-    if (c != 1) {
-      error("Failed to read EOS table %s", table_file);
-    }
+    if (c != 1) error("Failed to read the SESAME EoS table %s", table_file);
   }
 
   // Sp. int. energies (not log yet), pressures, sound speeds, and entropies
@@ -143,18 +135,14 @@ INLINE static void load_table_SESAME(struct SESAME_params *mat,
       // Ignore the first elements of rho = 0, T = 0
       if ((i_T == -1) || (i_rho == -1)) {
         c = fscanf(f, "%f %f %f %f", &ignore, &ignore, &ignore, &ignore);
-        if (c != 4) {
-          error("Failed to read EOS table %s", table_file);
-        }
+        if (c != 4) error("Failed to read the SESAME EoS table %s", table_file);
       } else {
         c = fscanf(f, "%f %f %f %f",
                    &mat->table_log_u_rho_T[i_rho * mat->num_T + i_T],
                    &mat->table_P_rho_T[i_rho * mat->num_T + i_T],
                    &mat->table_c_rho_T[i_rho * mat->num_T + i_T],
                    &mat->table_s_rho_T[i_rho * mat->num_T + i_T]);
-        if (c != 4) {
-          error("Failed to read EOS table %s", table_file);
-        }
+        if (c != 4) error("Failed to read the SESAME EoS table %s", table_file);
       }
     }
   }
diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h
index 83decb0cfb189316018a465796de8377d8749bec..d446844e8ffc862fd3be0688302ebb3a2efab8fa 100644
--- a/src/gravity/Default/gravity.h
+++ b/src/gravity/Default/gravity.h
@@ -22,6 +22,7 @@
 
 #include <float.h>
 
+/* Local includes. */
 #include "cosmology.h"
 #include "gravity_properties.h"
 #include "kernel_gravity.h"
diff --git a/src/gravity_cache.h b/src/gravity_cache.h
index 821f044429b445c28ff8ae39b8dc65304dd2b42d..6453d1eb92814f0e20cf25fa5996b920e523812d 100644
--- a/src/gravity_cache.h
+++ b/src/gravity_cache.h
@@ -208,12 +208,20 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate(
 
   /* Fill the input caches */
   for (int i = 0; i < gcount; ++i) {
+
     x[i] = (float)(gparts[i].x[0] - shift[0]);
     y[i] = (float)(gparts[i].x[1] - shift[1]);
     z[i] = (float)(gparts[i].x[2] - shift[2]);
     epsilon[i] = gravity_get_softening(&gparts[i], grav_props);
-    m[i] = gparts[i].mass;
-    active[i] = (int)(gparts[i].time_bin <= max_active_bin);
+
+    /* Make a dummy particle out of the inhibted ones */
+    if (gparts[i].time_bin == time_bin_inhibited) {
+      m[i] = 0.f;
+      active[i] = 0;
+    } else {
+      m[i] = gparts[i].mass;
+      active[i] = (int)(gparts[i].time_bin <= max_active_bin);
+    }
 
     /* Distance to the CoM of the other cell. */
     float dx = x[i] - CoM[0];
@@ -294,8 +302,15 @@ gravity_cache_populate_no_mpole(const timebin_t max_active_bin,
     y[i] = (float)(gparts[i].x[1] - shift[1]);
     z[i] = (float)(gparts[i].x[2] - shift[2]);
     epsilon[i] = gravity_get_softening(&gparts[i], grav_props);
-    m[i] = gparts[i].mass;
-    active[i] = (int)(gparts[i].time_bin <= max_active_bin);
+
+    /* Make a dummy particle out of the inhibted ones */
+    if (gparts[i].time_bin == time_bin_inhibited) {
+      m[i] = 0.f;
+      active[i] = 0;
+    } else {
+      m[i] = gparts[i].mass;
+      active[i] = (int)(gparts[i].time_bin <= max_active_bin);
+    }
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
diff --git a/src/gravity_properties.c b/src/gravity_properties.c
index 40cd7cecb10eab7ebbe7b59d34fb15dccac8b620..fffbf22ec187f179f0e80b7121beaa3a96de0260 100644
--- a/src/gravity_properties.c
+++ b/src/gravity_properties.c
@@ -39,7 +39,8 @@
 #define gravity_props_default_rebuild_frequency 0.01f
 
 void gravity_props_init(struct gravity_props *p, struct swift_params *params,
-                        const struct cosmology *cosmo, int with_cosmology) {
+                        const struct cosmology *cosmo, int with_cosmology,
+                        int periodic) {
 
   /* Tree updates */
   p->rebuild_frequency =
@@ -50,24 +51,31 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params,
     error("Invalid tree rebuild frequency. Must be in [0., 1.]");
 
   /* Tree-PM parameters */
-  p->mesh_size = parser_get_param_int(params, "Gravity:mesh_side_length");
-  p->a_smooth = parser_get_opt_param_float(params, "Gravity:a_smooth",
-                                           gravity_props_default_a_smooth);
-  p->r_cut_max_ratio = parser_get_opt_param_float(
-      params, "Gravity:r_cut_max", gravity_props_default_r_cut_max);
-  p->r_cut_min_ratio = parser_get_opt_param_float(
-      params, "Gravity:r_cut_min", gravity_props_default_r_cut_min);
-
-  /* Some basic checks */
-  if (p->mesh_size % 2 != 0)
-    error("The mesh side-length must be an even number.");
-
-  if (p->a_smooth <= 0.)
-    error("The mesh smoothing scale 'a_smooth' must be > 0.");
-
-  if (2. * p->a_smooth * p->r_cut_max_ratio > p->mesh_size)
-    error("Mesh too small given r_cut_max. Should be at least %d cells wide.",
-          (int)(2. * p->a_smooth * p->r_cut_max_ratio) + 1);
+  if (periodic) {
+    p->mesh_size = parser_get_param_int(params, "Gravity:mesh_side_length");
+    p->a_smooth = parser_get_opt_param_float(params, "Gravity:a_smooth",
+                                             gravity_props_default_a_smooth);
+    p->r_cut_max_ratio = parser_get_opt_param_float(
+        params, "Gravity:r_cut_max", gravity_props_default_r_cut_max);
+    p->r_cut_min_ratio = parser_get_opt_param_float(
+        params, "Gravity:r_cut_min", gravity_props_default_r_cut_min);
+
+    /* Some basic checks of what we read */
+    if (p->mesh_size % 2 != 0)
+      error("The mesh side-length must be an even number.");
+
+    if (p->a_smooth <= 0.)
+      error("The mesh smoothing scale 'a_smooth' must be > 0.");
+
+    if (2. * p->a_smooth * p->r_cut_max_ratio > p->mesh_size)
+      error("Mesh too small given r_cut_max. Should be at least %d cells wide.",
+            (int)(2. * p->a_smooth * p->r_cut_max_ratio) + 1);
+  } else {
+    p->mesh_size = 0;
+    p->a_smooth = 0.f;
+    p->r_cut_min_ratio = 0.f;
+    p->r_cut_max_ratio = 0.f;
+  }
 
   /* Time integration */
   p->eta = parser_get_param_float(params, "Gravity:eta");
diff --git a/src/gravity_properties.h b/src/gravity_properties.h
index 62dbab3605fb2dcfc4ae65e54c0b5f913d714c16..0cabd9958efa2bb23524d03632f90fdd1f1c8306 100644
--- a/src/gravity_properties.h
+++ b/src/gravity_properties.h
@@ -88,7 +88,8 @@ struct gravity_props {
 
 void gravity_props_print(const struct gravity_props *p);
 void gravity_props_init(struct gravity_props *p, struct swift_params *params,
-                        const struct cosmology *cosmo, int with_cosmology);
+                        const struct cosmology *cosmo, int with_cosmology,
+                        int periodic);
 void gravity_update(struct gravity_props *p, const struct cosmology *cosmo);
 
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h
index f5e71a8762c54faca7d165eb26199d0e30b486ac..4252f2787aefcec058b8fa956eaa0351b8f41d57 100644
--- a/src/hydro/Default/hydro.h
+++ b/src/hydro/Default/hydro.h
@@ -32,27 +32,59 @@
 #include <float.h>
 
 /**
- * @brief Returns the comoving internal energy of a particle
+ * @brief Returns the comoving internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * @param p The particle of interest
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float
-hydro_get_comoving_internal_energy(const struct part *restrict p) {
+hydro_get_comoving_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp) {
 
-  return p->u;
+  return xp->u_full;
 }
 
 /**
- * @brief Returns the physical internal energy of a particle
+ * @brief Returns the physical internal energy of a particle at the last
+ * time the particle was kicked.
  *
- * @param p The particle of interest
+ * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float
 hydro_get_physical_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp,
                                    const struct cosmology *cosmo) {
 
-  return cosmo->a_factor_internal_energy * p->u;
+  return xp->u_full * cosmo->a_factor_internal_energy;
+}
+
+/**
+ * @brief Returns the comoving internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_internal_energy(const struct part *restrict p) {
+
+  return p->u;
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_internal_energy(const struct part *restrict p,
+                                           const struct cosmology *cosmo) {
+
+  return p->u * cosmo->a_factor_internal_energy;
 }
 
 /**
@@ -80,24 +112,57 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
 }
 
 /**
- * @brief Returns the comoving entropy of a particle
+ * @brief Returns the comoving entropy of a particle at the last
+ * time the particle was kicked.
  *
- * @param p The particle of interest
+ * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
-    const struct part *restrict p) {
+    const struct part *restrict p, const struct xpart *restrict xp) {
 
-  return gas_entropy_from_internal_energy(p->rho, p->u);
+  return gas_entropy_from_internal_energy(p->rho, xp->u_full);
 }
 
 /**
- * @brief Returns the physical entropy of a particle
+ * @brief Returns the physical entropy of a particle at the last
+ * time the particle was kicked.
  *
- * @param p The particle of interest
+ * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
-    const struct part *restrict p, const struct cosmology *cosmo) {
+    const struct part *restrict p, const struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return gas_entropy_from_internal_energy(p->rho, xp->u_full);
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_entropy(const struct part *restrict p) {
+
+  return gas_entropy_from_internal_energy(p->rho, p->u);
+}
+
+/**
+ * @brief Returns the physical entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_entropy(const struct part *restrict p,
+                                   const struct cosmology *cosmo) {
 
   /* Note: no cosmological conversion required here with our choice of
    * coordinates. */
@@ -201,12 +266,27 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
  *
  * @param p The particle of interest
  */
-__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
-    const struct part *restrict p) {
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy_dt(const struct part *restrict p) {
 
   return p->force.u_dt;
 }
 
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest
+ * @param cosmo Cosmology data structure
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy_dt(const struct part *restrict p,
+                                      const struct cosmology *cosmo) {
+
+  return p->force.u_dt * cosmo->a_factor_internal_energy;
+}
+
 /**
  * @brief Returns the time derivative of internal energy of a particle
  *
@@ -215,12 +295,29 @@ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
  * @param p The particle of interest.
  * @param du_dt The new time derivative of the internal energy.
  */
-__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt(
-    struct part *restrict p, float du_dt) {
+__attribute__((always_inline)) INLINE static void
+hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) {
 
   p->force.u_dt = du_dt;
 }
 
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest.
+ * @param cosmo Cosmology data structure
+ * @param du_dt The new time derivative of the internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_physical_internal_energy_dt(struct part *restrict p,
+                                      const struct cosmology *cosmo,
+                                      float du_dt) {
+
+  p->force.u_dt = du_dt * cosmo->a_factor_internal_energy;
+}
+
 /**
  * @brief Computes the hydro time-step of a given particle
  *
@@ -361,12 +458,14 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  * @param cosmo The current cosmological model.
+ * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
-    const struct cosmology *cosmo, const float dt_alpha) {
+    const struct cosmology *cosmo, const struct hydro_props *hydro_props,
+    const float dt_alpha) {
   const float fac_mu = cosmo->a_factor_mu;
 
   /* Some smoothing length multiples. */
@@ -395,6 +494,9 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
   p->force.balsara =
       normDiv_v / (normDiv_v + normRot_v + 0.0001f * fac_mu * fc * h_inv);
 
+  /* Set the AV property */
+  p->alpha = hydro_props->viscosity.alpha;
+
   /* Viscosity parameter decay time */
   /* const float tau = h / (2.f * const_viscosity_length * p->force.soundspeed);
    */
@@ -403,8 +505,10 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
   /* const float S = max(-normDiv_v, 0.f); */
 
   /* Compute the particle's viscosity parameter time derivative */
-  /* const float alpha_dot = (const_viscosity_alpha_min - p->alpha) / tau + */
-  /*                         (const_viscosity_alpha_max - p->alpha) * S; */
+  /* const float alpha_dot = (hydro_props->viscosity.alpha_max) - p->alpha) /
+   * tau + */
+  /*                         (hydro_props->viscosity.alpha_max - p->alpha) * S;
+   */
 
   /* Update particle's viscosity paramter */
   /* p->alpha += alpha_dot * (p->ti_end - p->ti_begin) * timeBase; */  // MATTHIEU
diff --git a/src/hydro/Default/hydro_iact.h b/src/hydro/Default/hydro_iact.h
index 658b4aba83085610a49bb9d2579d4f20c70d6c5b..72808874c3fc6b58005d0e3ad450eafea8aa4b4d 100644
--- a/src/hydro/Default/hydro_iact.h
+++ b/src/hydro/Default/hydro_iact.h
@@ -226,7 +226,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   omega_ij = min(fac_mu * dvdr, 0.f);
 
   /* Compute signal velocity */
-  v_sig = pi->force.soundspeed + pj->force.soundspeed - 2.0f * omega_ij;
+  v_sig = pi->force.soundspeed + pj->force.soundspeed -
+          const_viscosity_beta * omega_ij;
 
   /* Compute viscosity parameter */
   alpha_ij = -0.5f * (pi->alpha + pj->alpha);
@@ -335,7 +336,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   omega_ij = min(fac_mu * dvdr, 0.f);
 
   /* Compute signal velocity */
-  v_sig = pi->force.soundspeed + pj->force.soundspeed - 2.0f * omega_ij;
+  v_sig = pi->force.soundspeed + pj->force.soundspeed -
+          const_viscosity_beta * omega_ij;
 
   /* Compute viscosity parameter */
   alpha_ij = -0.5f * (pi->alpha + pj->alpha);
diff --git a/src/hydro/Default/hydro_io.h b/src/hydro/Default/hydro_io.h
index d47c96fbf32e1ee00346888aaf2e8afabc22abc3..69919c202223fdecc197a87178e59767c02ee16e 100644
--- a/src/hydro/Default/hydro_io.h
+++ b/src/hydro/Default/hydro_io.h
@@ -55,6 +55,17 @@ INLINE static void hydro_read_particles(struct part* parts,
   list[7] = io_make_input_field("Density", FLOAT, 1, OPTIONAL,
                                 UNIT_CONV_DENSITY, parts, rho);
 }
+INLINE static void convert_S(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
+
+  ret[0] = hydro_get_comoving_entropy(p, xp);
+}
+
+INLINE static void convert_P(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
+
+  ret[0] = hydro_get_comoving_pressure(p);
+}
 
 INLINE static void convert_part_pos(const struct engine* e,
                                     const struct part* p,
@@ -128,7 +139,7 @@ INLINE static void hydro_write_particles(const struct part* parts,
                                          struct io_props* list,
                                          int* num_fields) {
 
-  *num_fields = 8;
+  *num_fields = 10;
 
   /* List what we want to write */
   list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3,
@@ -146,7 +157,13 @@ INLINE static void hydro_write_particles(const struct part* parts,
                                  UNIT_CONV_NO_UNITS, parts, id);
   list[6] =
       io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho);
-  list[7] = io_make_output_field_convert_part("Potential", FLOAT, 1,
+  list[7] = io_make_output_field_convert_part("Entropy", FLOAT, 1,
+                                              UNIT_CONV_ENTROPY_PER_UNIT_MASS,
+                                              parts, xparts, convert_S);
+  list[8] = io_make_output_field_convert_part(
+      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P);
+
+  list[0] = io_make_output_field_convert_part("Potential", FLOAT, 1,
                                               UNIT_CONV_POTENTIAL, parts,
                                               xparts, convert_part_potential);
 }
@@ -166,13 +183,6 @@ INLINE static void hydro_write_flavour(hid_t h_grpsph) {
       h_grpsph, "Viscosity Model",
       "Morris & Monaghan (1997), Rosswog, Davies, Thielemann & "
       "Piran (2000) with additional Balsara (1995) switch");
-  io_write_attribute_f(h_grpsph, "Viscosity alpha_min",
-                       const_viscosity_alpha_min);
-  io_write_attribute_f(h_grpsph, "Viscosity alpha_max",
-                       const_viscosity_alpha_max);
-  io_write_attribute_f(h_grpsph, "Viscosity beta", 2.f);
-  io_write_attribute_f(h_grpsph, "Viscosity decay length",
-                       const_viscosity_length);
 
   /* Time integration properties */
   io_write_attribute_f(h_grpsph, "Maximal Delta u change over dt",
diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h
index 0dcd2cf01e9d3b93194fab96af836b6dd045d1f9..9765ced22bb8e6291c2a189fc0e0f27419239c08 100644
--- a/src/hydro/Gadget2/hydro.h
+++ b/src/hydro/Gadget2/hydro.h
@@ -42,26 +42,59 @@
 #include "minmax.h"
 
 /**
- * @brief Returns the comoving internal energy of a particle
+ * @brief Returns the comoving internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * @param p The particle of interest
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float
-hydro_get_comoving_internal_energy(const struct part *restrict p) {
+hydro_get_comoving_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp) {
 
-  return gas_internal_energy_from_entropy(p->rho, p->entropy);
+  return gas_internal_energy_from_entropy(p->rho, xp->entropy_full);
 }
 
 /**
- * @brief Returns the physical internal energy of a particle
+ * @brief Returns the physical internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float
 hydro_get_physical_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp,
                                    const struct cosmology *cosmo) {
 
+  return gas_internal_energy_from_entropy(p->rho * cosmo->a3_inv,
+                                          xp->entropy_full);
+}
+
+/**
+ * @brief Returns the comoving internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_internal_energy(const struct part *restrict p) {
+
+  return gas_internal_energy_from_entropy(p->rho, p->entropy);
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_internal_energy(const struct part *restrict p,
+                                           const struct cosmology *cosmo) {
+
   return gas_internal_energy_from_entropy(p->rho * cosmo->a3_inv, p->entropy);
 }
 
@@ -79,7 +112,8 @@ __attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure(
 /**
  * @brief Returns the physical pressure of a particle
  *
- * @param p The particle of interest
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
     const struct part *restrict p, const struct cosmology *cosmo) {
@@ -88,24 +122,57 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
 }
 
 /**
- * @brief Returns the comoving entropy of a particle
+ * @brief Returns the comoving entropy of a particle at the last
+ * time the particle was kicked.
  *
  * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
-    const struct part *restrict p) {
+    const struct part *restrict p, const struct xpart *restrict xp) {
 
-  return p->entropy;
+  return xp->entropy_full;
 }
 
 /**
- * @brief Returns the physical entropy of a particle
+ * @brief Returns the physical entropy of a particl at the last
+ * time the particle was kicked.
  *
  * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
-    const struct part *restrict p, const struct cosmology *cosmo) {
+    const struct part *restrict p, const struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return xp->entropy_full;
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_entropy(const struct part *restrict p) {
+
+  return p->entropy;
+}
+
+/**
+ * @brief Returns the physical entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_entropy(const struct part *restrict p,
+                                   const struct cosmology *cosmo) {
 
   /* Note: no cosmological conversion required here with our choice of
    * coordinates. */
@@ -204,32 +271,66 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
 }
 
 /**
- * @brief Returns the time derivative of internal energy of a particle
+ * @brief Returns the time derivative of co-moving internal energy of a particle
  *
  * We assume a constant density.
  *
  * @param p The particle of interest
  */
-__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
-    const struct part *restrict p) {
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy_dt(const struct part *restrict p) {
 
   return gas_internal_energy_from_entropy(p->rho, p->entropy_dt);
 }
 
 /**
- * @brief Returns the time derivative of internal energy of a particle
+ * @brief Returns the time derivative of physical internal energy of a particle
  *
  * We assume a constant density.
  *
  * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy_dt(const struct part *restrict p,
+                                      const struct cosmology *cosmo) {
+
+  return gas_internal_energy_from_entropy(p->rho * cosmo->a3_inv,
+                                          p->entropy_dt);
+}
+
+/**
+ * @brief Sets the time derivative of the co-moving internal energy of a
+ * particle
+ *
+ * We assume a constant density for the conversion to entropy.
+ *
+ * @param p The particle of interest.
  * @param du_dt The new time derivative of the internal energy.
  */
-__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt(
-    struct part *restrict p, float du_dt) {
+__attribute__((always_inline)) INLINE static void
+hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) {
 
   p->entropy_dt = gas_entropy_from_internal_energy(p->rho, du_dt);
 }
 
+/**
+ * @brief Sets the time derivative of the physical internal energy of a particle
+ *
+ * We assume a constant density for the conversion to entropy.
+ *
+ * @param p The particle of interest.
+ * @param cosmo Cosmology data structure
+ * @param du_dt The time derivative of the internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_physical_internal_energy_dt(struct part *restrict p,
+                                      const struct cosmology *restrict cosmo,
+                                      float du_dt) {
+  p->entropy_dt =
+      gas_entropy_from_internal_energy(p->rho * cosmo->a3_inv, du_dt);
+}
+
 /**
  * @brief Computes the hydro time-step of a given particle
  *
@@ -371,25 +472,31 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  * @param cosmo The current cosmological model.
+ * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
-    const struct cosmology *cosmo, const float dt_alpha) {
+    const struct cosmology *cosmo, const struct hydro_props *hydro_props,
+    const float dt_alpha) {
 
-  const float fac_mu = cosmo->a_factor_mu;
+  const float fac_Balsara_eps = cosmo->a_factor_Balsara_eps;
 
-  /* Inverse of the physical density */
+  /* Inverse of the co-moving density */
   const float rho_inv = 1.f / p->rho;
 
+  /* Inverse of the smoothing length */
+  const float h_inv = 1.f / p->h;
+
   /* Compute the norm of the curl */
   const float curl_v = sqrtf(p->density.rot_v[0] * p->density.rot_v[0] +
                              p->density.rot_v[1] * p->density.rot_v[1] +
                              p->density.rot_v[2] * p->density.rot_v[2]);
 
-  /* Compute the norm of div v */
-  const float abs_div_v = fabsf(p->density.div_v);
+  /* Compute the norm of div v including the Hubble flow term */
+  const float div_physical_v = p->density.div_v + 3.f * cosmo->H;
+  const float abs_div_physical_v = fabsf(div_physical_v);
 
   /* Compute the pressure */
   const float pressure = gas_pressure_from_entropy(p->rho, p->entropy);
@@ -401,8 +508,11 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
   const float P_over_rho2 = pressure * rho_inv * rho_inv;
 
   /* Compute the Balsara switch */
-  const float balsara =
-      abs_div_v / (abs_div_v + curl_v + 0.0001f * fac_mu * soundspeed / p->h);
+  /* Pre-multiply in the AV factor; hydro_props are not passed to the iact
+   * functions */
+  const float balsara = hydro_props->viscosity.alpha * abs_div_physical_v /
+                        (abs_div_physical_v + curl_v +
+                         0.0001f * fac_Balsara_eps * soundspeed * h_inv);
 
   /* Compute the "grad h" term */
   const float omega_inv =
@@ -532,6 +642,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param p The particle to act upon
  * @param xp The particle extended data to act upon
  * @param dt_therm The time-step for this kick (for thermodynamic quantities)
+ * @param dt_grav The time-step for this kick (for gravity forces)
+ * @param dt_hydro The time-step for this kick (for hydro forces)
+ * @param dt_kick_corr The time-step for this kick (for correction of the kick)
  * @param cosmo The cosmological model.
  * @param hydro_props The constants used in the scheme
  */
@@ -547,12 +660,13 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
   xp->entropy_full += p->entropy_dt * dt_therm;
 
   /* Apply the minimal energy limit */
-  const float density = p->rho * cosmo->a3_inv;
-  const float min_energy = hydro_props->minimal_internal_energy;
-  const float min_entropy =
-      gas_entropy_from_internal_energy(density, min_energy);
-  if (xp->entropy_full < min_entropy) {
-    xp->entropy_full = min_entropy;
+  const float physical_density = p->rho * cosmo->a3_inv;
+  const float min_physical_energy = hydro_props->minimal_internal_energy;
+  const float min_physical_entropy =
+      gas_entropy_from_internal_energy(physical_density, min_physical_energy);
+  const float min_comoving_entropy = min_physical_entropy; /* A' = A */
+  if (xp->entropy_full < min_comoving_entropy) {
+    xp->entropy_full = min_comoving_entropy;
     p->entropy_dt = 0.f;
   }
 
@@ -584,20 +698,21 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props) {
 
-  /* We read u in the entropy field. We now get (comoving) S from (physical) u
-   * and (physical) rho. Note that comoving S == physical S */
+  /* We read u in the entropy field. We now get (comoving) A from (physical) u
+   * and (physical) rho. Note that comoving A (A') == physical A */
   xp->entropy_full =
       gas_entropy_from_internal_energy(p->rho * cosmo->a3_inv, p->entropy);
   p->entropy = xp->entropy_full;
 
   /* Apply the minimal energy limit */
-  const float density = p->rho * cosmo->a3_inv;
-  const float min_energy = hydro_props->minimal_internal_energy;
-  const float min_entropy =
-      gas_entropy_from_internal_energy(density, min_energy);
-  if (xp->entropy_full < min_entropy) {
-    xp->entropy_full = min_entropy;
-    p->entropy = min_entropy;
+  const float physical_density = p->rho * cosmo->a3_inv;
+  const float min_physical_energy = hydro_props->minimal_internal_energy;
+  const float min_physical_entropy =
+      gas_entropy_from_internal_energy(physical_density, min_physical_energy);
+  const float min_comoving_entropy = min_physical_entropy; /* A' = A */
+  if (xp->entropy_full < min_comoving_entropy) {
+    xp->entropy_full = min_comoving_entropy;
+    p->entropy = min_comoving_entropy;
     p->entropy_dt = 0.f;
   }
 
diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h
index b2d77a261e91a4f8460189cfce99951a820ef8f1..746fd4778563aeaab43bafcc7904683ed5b6811c 100644
--- a/src/hydro/Gadget2/hydro_iact.h
+++ b/src/hydro/Gadget2/hydro_iact.h
@@ -493,12 +493,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
 
   /* Signal velocity */
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
 
   /* Now construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
-                     (balsara_i + balsara_j) / rho_ij;
+  const float visc = -0.25f * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij;
 
   /* Now, convolve with the kernel */
   const float visc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
@@ -526,8 +525,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   pj->force.v_sig = max(pj->force.v_sig, v_sig);
 
   /* Change in entropy */
-  pi->entropy_dt += mj * visc_term * dvdr;
-  pj->entropy_dt += mi * visc_term * dvdr;
+  pi->entropy_dt += mj * visc_term * dvdr_Hubble;
+  pj->entropy_dt += mi * visc_term * dvdr_Hubble;
 
 #ifdef DEBUG_INTERACTIONS_SPH
   /* Update ngb counters */
@@ -616,12 +615,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
 
   /* Signal velocity */
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
 
   /* Now construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
-                     (balsara_i + balsara_j) / rho_ij;
+  const float visc = -0.25f * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij;
 
   /* Now, convolve with the kernel */
   const float visc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
@@ -643,7 +641,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   pi->force.v_sig = max(pi->force.v_sig, v_sig);
 
   /* Change in entropy */
-  pi->entropy_dt += mj * visc_term * dvdr;
+  pi->entropy_dt += mj * visc_term * dvdr_Hubble;
 
 #ifdef DEBUG_INTERACTIONS_SPH
   /* Update ngb counters */
@@ -654,8 +652,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
 }
 
 #ifdef WITH_VECTORIZATION
-static const vector const_viscosity_alpha_fac =
-    FILL_VEC(-0.25f * const_viscosity_alpha);
 
 /**
  * @brief Force interaction computed using 1 vector
@@ -741,13 +737,14 @@ runner_iact_nonsym_1_vec_force(
                     vec_mul(ri.v, omega_ij.v)); /* This is 0 or negative */
 
   /* Compute signal velocity */
-  v_sig.v = vec_fnma(vec_set1(3.f), mu_ij.v, vec_add(ci.v, cj.v));
+  v_sig.v =
+      vec_fnma(vec_set1(const_viscosity_beta), mu_ij.v, vec_add(ci.v, cj.v));
 
   /* Now construct the full viscosity term */
   rho_ij.v = vec_mul(vec_set1(0.5f), vec_add(pirho.v, pjrho.v));
-  visc.v = vec_div(vec_mul(const_viscosity_alpha_fac.v,
-                           vec_mul(v_sig.v, vec_mul(mu_ij.v, balsara.v))),
-                   rho_ij.v);
+  visc.v = vec_div(
+      vec_mul(vec_set1(-0.25f), vec_mul(v_sig.v, vec_mul(mu_ij.v, balsara.v))),
+      rho_ij.v);
 
   /* Now, convolve with the kernel */
   visc_term.v =
@@ -772,7 +769,7 @@ runner_iact_nonsym_1_vec_force(
       vec_div(vec_mul(mj.v, vec_mul(dvdr.v, vec_mul(ri.v, wi_dr.v))), pjrho.v);
 
   /* Change in entropy */
-  entropy_dt.v = vec_mul(mj.v, vec_mul(visc_term.v, dvdr.v));
+  entropy_dt.v = vec_mul(mj.v, vec_mul(visc_term.v, dvdr_Hubble.v));
 
   /* Store the forces back on the particles. */
   a_hydro_xSum->v = vec_mask_sub(a_hydro_xSum->v, piax.v, mask);
@@ -927,18 +924,20 @@ runner_iact_nonsym_2_vec_force(
       v_fac_mu.v, vec_mul(ri_2.v, omega_ij_2.v)); /* This is 0 or negative */
 
   /* Compute signal velocity */
-  v_sig.v = vec_fnma(vec_set1(3.f), mu_ij.v, vec_add(ci.v, cj.v));
-  v_sig_2.v = vec_fnma(vec_set1(3.f), mu_ij_2.v, vec_add(ci.v, cj_2.v));
+  v_sig.v =
+      vec_fnma(vec_set1(const_viscosity_beta), mu_ij.v, vec_add(ci.v, cj.v));
+  v_sig_2.v = vec_fnma(vec_set1(const_viscosity_beta), mu_ij_2.v,
+                       vec_add(ci.v, cj_2.v));
 
   /* Now construct the full viscosity term */
   rho_ij.v = vec_mul(vec_set1(0.5f), vec_add(pirho.v, pjrho.v));
   rho_ij_2.v = vec_mul(vec_set1(0.5f), vec_add(pirho.v, pjrho_2.v));
 
-  visc.v = vec_div(vec_mul(const_viscosity_alpha_fac.v,
-                           vec_mul(v_sig.v, vec_mul(mu_ij.v, balsara.v))),
-                   rho_ij.v);
+  visc.v = vec_div(
+      vec_mul(vec_set1(-0.25f), vec_mul(v_sig.v, vec_mul(mu_ij.v, balsara.v))),
+      rho_ij.v);
   visc_2.v =
-      vec_div(vec_mul(const_viscosity_alpha_fac.v,
+      vec_div(vec_mul(vec_set1(-0.25f),
                       vec_mul(v_sig_2.v, vec_mul(mu_ij_2.v, balsara_2.v))),
               rho_ij_2.v);
 
@@ -982,8 +981,8 @@ runner_iact_nonsym_2_vec_force(
               pjrho_2.v);
 
   /* Change in entropy */
-  entropy_dt.v = vec_mul(mj.v, vec_mul(visc_term.v, dvdr.v));
-  entropy_dt_2.v = vec_mul(mj_2.v, vec_mul(visc_term_2.v, dvdr_2.v));
+  entropy_dt.v = vec_mul(mj.v, vec_mul(visc_term.v, dvdr_Hubble.v));
+  entropy_dt_2.v = vec_mul(mj_2.v, vec_mul(visc_term_2.v, dvdr_Hubble_2.v));
 
   /* Store the forces back on the particles. */
   if (mask_cond) {
diff --git a/src/hydro/Gadget2/hydro_io.h b/src/hydro/Gadget2/hydro_io.h
index 3f2af41dc7f0cc8f60992a15a0f09f3c90f764fe..ec7d34f7ad8697f1d639ea4951011ddb06ec8833 100644
--- a/src/hydro/Gadget2/hydro_io.h
+++ b/src/hydro/Gadget2/hydro_io.h
@@ -59,7 +59,7 @@ INLINE static void hydro_read_particles(struct part* parts,
 INLINE static void convert_part_u(const struct engine* e, const struct part* p,
                                   const struct xpart* xp, float* ret) {
 
-  ret[0] = hydro_get_comoving_internal_energy(p);
+  ret[0] = hydro_get_comoving_internal_energy(p, xp);
 }
 
 INLINE static void convert_part_P(const struct engine* e, const struct part* p,
@@ -132,6 +132,7 @@ INLINE static void convert_part_potential(const struct engine* e,
  * @brief Specifies which particle fields to write to a dataset
  *
  * @param parts The particle array.
+ * @param xparts The extended particle data array.
  * @param list The list of i/o properties to write.
  * @param num_fields The number of i/o fields to write.
  */
@@ -199,8 +200,6 @@ INLINE static void hydro_write_flavour(hid_t h_grpsph) {
   io_write_attribute_s(
       h_grpsph, "Viscosity Model",
       "as in Springel (2005), i.e. Monaghan (1992) with Balsara (1995) switch");
-  io_write_attribute_f(h_grpsph, "Viscosity alpha", const_viscosity_alpha);
-  io_write_attribute_f(h_grpsph, "Viscosity beta", 3.f);
 }
 
 /**
diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h
index 90f73571701b37b3377601655330d8d25f862a05..bcb40243362dc57d47a3832c1d9330cb68d93fb8 100644
--- a/src/hydro/Gadget2/hydro_part.h
+++ b/src/hydro/Gadget2/hydro_part.h
@@ -33,6 +33,7 @@
 
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "logger.h"
 
 /* Extra particle data not needed during the SPH loops over neighbours. */
 struct xpart {
@@ -55,6 +56,11 @@ struct xpart {
   /* Additional data used to record cooling information */
   struct cooling_xpart_data cooling_data;
 
+#ifdef WITH_LOGGER
+  /* Additional data for the particle logger */
+  struct logger_part_data logger_data;
+#endif
+
 } SWIFT_STRUCT_ALIGN;
 
 /* Data of a single particle. */
diff --git a/src/hydro/GizmoMFM/hydro.h b/src/hydro/GizmoMFM/hydro.h
index d6b44e19899fdb74ee32f699665ec6e706d718b1..8e466daabb59482a1c2ebbaf80af30c64c4abdfe 100644
--- a/src/hydro/GizmoMFM/hydro.h
+++ b/src/hydro/GizmoMFM/hydro.h
@@ -451,12 +451,14 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient(
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  * @param cosmo The current cosmological model.
+ * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part* restrict p, struct xpart* restrict xp,
-    const struct cosmology* cosmo, const float dt_alpha) {
+    const struct cosmology* cosmo, const struct hydro_props* hydro_props,
+    const float dt_alpha) {
 
   /* Initialise values that are used in the force loop */
   p->flux.momentum[0] = 0.0f;
@@ -730,6 +732,33 @@ hydro_get_physical_internal_energy(const struct part* restrict p,
          hydro_get_comoving_internal_energy(p);
 }
 
+/**
+ * @brief Returns the comoving internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_internal_energy(const struct part* restrict p) {
+
+  return hydro_get_comoving_internal_energy(p);
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_internal_energy(const struct part* restrict p,
+                                           const struct cosmology* cosmo) {
+
+  return hydro_get_comoving_internal_energy(p) *
+         cosmo->a_factor_internal_energy;
+}
+
 /**
  * @brief Returns the comoving entropy of a particle
  *
@@ -759,6 +788,21 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
   return hydro_get_comoving_entropy(p);
 }
 
+/**
+ * @brief Returns the physical internal energy of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_entropy(const struct part* restrict p,
+                                   const struct cosmology* cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return hydro_get_comoving_entropy(p);
+}
+
 /**
  * @brief Returns the sound speed of a particle
  *
diff --git a/src/hydro/GizmoMFM/hydro_iact.h b/src/hydro/GizmoMFM/hydro_iact.h
index 5bed20d7f894a76d5fe3642c7438dc03195e43d6..38a97cbea39c1ed5c6926c911941e655e52362aa 100644
--- a/src/hydro/GizmoMFM/hydro_iact.h
+++ b/src/hydro/GizmoMFM/hydro_iact.h
@@ -267,8 +267,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   const float dvdotdx = min(dvdr, 0.0f);
 
   /* Get the signal velocity */
-  /* the magical factor 3 also appears in Gadget2 */
-  vmax -= 3.0f * dvdotdx * r_inv;
+  vmax -= const_viscosity_beta * dvdotdx * r_inv;
 
   /* Store the signal velocity */
   pi->timestepvars.vmax = max(pi->timestepvars.vmax, vmax);
diff --git a/src/hydro/GizmoMFV/hydro.h b/src/hydro/GizmoMFV/hydro.h
index 24456b7bdf65acb516630d587e1319e58c96e2fe..98a70aefed098243bbf2dfe08e752ee48a838d3e 100644
--- a/src/hydro/GizmoMFV/hydro.h
+++ b/src/hydro/GizmoMFV/hydro.h
@@ -476,12 +476,14 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient(
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  * @param cosmo The current cosmological model.
+ * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part* restrict p, struct xpart* restrict xp,
-    const struct cosmology* cosmo, const float dt_alpha) {
+    const struct cosmology* cosmo, const struct hydro_props* hydro_props,
+    const float dt_alpha) {
 
   /* Initialise values that are used in the force loop */
   p->gravity.mflux[0] = 0.0f;
@@ -816,6 +818,19 @@ hydro_get_physical_internal_energy(const struct part* restrict p,
          hydro_get_comoving_internal_energy(p);
 }
 
+/**
+ * @brief Returns the physical internal energy of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_internal_energy(const struct part* restrict p,
+                                           const struct cosmology* cosmo) {
+
+  return hydro_get_physical_internal_energy(p, cosmo);
+}
+
 /**
  * @brief Returns the comoving entropy of a particle
  *
@@ -845,6 +860,21 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
   return hydro_get_comoving_entropy(p);
 }
 
+/**
+ * @brief Returns the physical internal energy of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_entropy(const struct part* restrict p,
+                                   const struct cosmology* cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return hydro_get_comoving_entropy(p);
+}
+
 /**
  * @brief Returns the sound speed of a particle
  *
diff --git a/src/hydro/GizmoMFV/hydro_iact.h b/src/hydro/GizmoMFV/hydro_iact.h
index c766ce3cc9048f8da8b3438c3c27e6998dd5df7e..2f73e67ea2fdcecc527de8b1af0d15731f967b9b 100644
--- a/src/hydro/GizmoMFV/hydro_iact.h
+++ b/src/hydro/GizmoMFV/hydro_iact.h
@@ -271,8 +271,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   dvdotdx = min(dvdotdx, 0.f);
 
   /* Get the signal velocity */
-  /* the magical factor 3 also appears in Gadget2 */
-  vmax -= 3.f * dvdotdx * r_inv;
+  vmax -= const_viscosity_beta * dvdotdx * r_inv;
 
   /* Store the signal velocity */
   pi->timestepvars.vmax = max(pi->timestepvars.vmax, vmax);
diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h
index cf629814817064c109f7f016f81b6c621b087436..93a2b3ec8ed4ecb6dcc73314233217d37141ba46 100644
--- a/src/hydro/Minimal/hydro.h
+++ b/src/hydro/Minimal/hydro.h
@@ -44,35 +44,58 @@
 #include "minmax.h"
 
 /**
- * @brief Returns the comoving internal energy of a particle
- *
- * For implementations where the main thermodynamic variable
- * is not internal energy, this function computes the internal
- * energy from the thermodynamic variable.
+ * @brief Returns the comoving internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * @param p The particle of interest
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float
-hydro_get_comoving_internal_energy(const struct part *restrict p) {
+hydro_get_comoving_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp) {
 
-  return p->u;
+  return xp->u_full;
 }
 
 /**
- * @brief Returns the physical internal energy of a particle
- *
- * For implementations where the main thermodynamic variable
- * is not internal energy, this function computes the internal
- * energy from the thermodynamic variable and converts it to
- * physical coordinates.
+ * @brief Returns the physical internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float
 hydro_get_physical_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp,
                                    const struct cosmology *cosmo) {
 
+  return xp->u_full * cosmo->a_factor_internal_energy;
+}
+
+/**
+ * @brief Returns the comoving internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_internal_energy(const struct part *restrict p) {
+
+  return p->u;
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_internal_energy(const struct part *restrict p,
+                                           const struct cosmology *cosmo) {
+
   return p->u * cosmo->a_factor_internal_energy;
 }
 
@@ -106,33 +129,57 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
 }
 
 /**
- * @brief Returns the comoving entropy of a particle
+ * @brief Returns the comoving entropy of a particle at the last
+ * time the particle was kicked.
  *
- * For implementations where the main thermodynamic variable
- * is not entropy, this function computes the entropy from
- * the thermodynamic variable.
- *
- * @param p The particle of interest
+ * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
-    const struct part *restrict p) {
+    const struct part *restrict p, const struct xpart *restrict xp) {
 
-  return gas_entropy_from_internal_energy(p->rho, p->u);
+  return gas_entropy_from_internal_energy(p->rho, xp->u_full);
 }
 
 /**
- * @brief Returns the physical entropy of a particle
+ * @brief Returns the physical entropy of a particle at the last
+ * time the particle was kicked.
  *
- * For implementations where the main thermodynamic variable
- * is not entropy, this function computes the entropy from
- * the thermodynamic variable and converts it to
- * physical coordinates.
- *
- * @param p The particle of interest
+ * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
-    const struct part *restrict p, const struct cosmology *cosmo) {
+    const struct part *restrict p, const struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return gas_entropy_from_internal_energy(p->rho, xp->u_full);
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_entropy(const struct part *restrict p) {
+
+  return gas_entropy_from_internal_energy(p->rho, p->u);
+}
+
+/**
+ * @brief Returns the physical entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_entropy(const struct part *restrict p,
+                                   const struct cosmology *cosmo) {
 
   /* Note: no cosmological conversion required here with our choice of
    * coordinates. */
@@ -231,14 +278,14 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
 }
 
 /**
- * @brief Returns the time derivative of internal energy of a particle
+ * @brief Returns the time derivative of co-moving internal energy of a particle
  *
  * We assume a constant density.
  *
  * @param p The particle of interest
  */
-__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
-    const struct part *restrict p) {
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy_dt(const struct part *restrict p) {
 
   return p->u_dt;
 }
@@ -248,14 +295,48 @@ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
  *
  * We assume a constant density.
  *
+ * @param p The particle of interest
+ * @param cosmo Cosmology data structure
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy_dt(const struct part *restrict p,
+                                      const struct cosmology *cosmo) {
+
+  return p->u_dt * cosmo->a_factor_internal_energy;
+}
+
+/**
+ * @brief Sets the time derivative of the co-moving internal energy of a
+ * particle
+ *
+ * We assume a constant density for the conversion to entropy.
+ *
  * @param p The particle of interest.
  * @param du_dt The new time derivative of the internal energy.
  */
-__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt(
-    struct part *restrict p, float du_dt) {
+__attribute__((always_inline)) INLINE static void
+hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) {
 
   p->u_dt = du_dt;
 }
+
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest.
+ * @param cosmo Cosmology data structure
+ * @param du_dt The new time derivative of the internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_physical_internal_energy_dt(struct part *restrict p,
+                                      const struct cosmology *cosmo,
+                                      float du_dt) {
+
+  p->u_dt = du_dt * cosmo->a_factor_internal_energy;
+}
+
 /**
  * @brief Computes the hydro time-step of a given particle
  *
@@ -308,6 +389,10 @@ __attribute__((always_inline)) INLINE static void hydro_init_part(
   p->density.wcount_dh = 0.f;
   p->rho = 0.f;
   p->density.rho_dh = 0.f;
+  p->density.div_v = 0.f;
+  p->density.rot_v[0] = 0.f;
+  p->density.rot_v[1] = 0.f;
+  p->density.rot_v[2] = 0.f;
 }
 
 /**
@@ -343,6 +428,17 @@ __attribute__((always_inline)) INLINE static void hydro_end_density(
   p->density.rho_dh *= h_inv_dim_plus_one;
   p->density.wcount *= h_inv_dim;
   p->density.wcount_dh *= h_inv_dim_plus_one;
+
+  const float rho_inv = 1.f / p->rho;
+  const float a_inv2 = cosmo->a2_inv;
+
+  /* Finish calculation of the (physical) velocity curl components */
+  p->density.rot_v[0] *= h_inv_dim_plus_one * a_inv2 * rho_inv;
+  p->density.rot_v[1] *= h_inv_dim_plus_one * a_inv2 * rho_inv;
+  p->density.rot_v[2] *= h_inv_dim_plus_one * a_inv2 * rho_inv;
+
+  /* Finish calculation of the (physical) velocity divergence */
+  p->density.div_v *= h_inv_dim_plus_one * a_inv2 * rho_inv;
 }
 
 /**
@@ -370,6 +466,10 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
   p->density.wcount = kernel_root * h_inv_dim;
   p->density.rho_dh = 0.f;
   p->density.wcount_dh = 0.f;
+  p->density.div_v = 0.f;
+  p->density.rot_v[0] = 0.f;
+  p->density.rot_v[1] = 0.f;
+  p->density.rot_v[2] = 0.f;
 }
 
 /**
@@ -385,12 +485,28 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  * @param cosmo The current cosmological model.
+ * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
-    const struct cosmology *cosmo, const float dt_alpha) {
+    const struct cosmology *cosmo, const struct hydro_props *hydro_props,
+    const float dt_alpha) {
+
+  const float fac_Balsara_eps = cosmo->a_factor_Balsara_eps;
+
+  /* Inverse of the smoothing length */
+  const float h_inv = 1.f / p->h;
+
+  /* Compute the norm of the curl */
+  const float curl_v = sqrtf(p->density.rot_v[0] * p->density.rot_v[0] +
+                             p->density.rot_v[1] * p->density.rot_v[1] +
+                             p->density.rot_v[2] * p->density.rot_v[2]);
+
+  /* Compute the norm of div v including the Hubble flow term */
+  const float div_physical_v = p->density.div_v + 3.f * cosmo->H;
+  const float abs_div_physical_v = fabsf(div_physical_v);
 
   /* Compute the pressure */
   const float pressure = gas_pressure_from_internal_energy(p->rho, p->u);
@@ -403,10 +519,18 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
   const float grad_h_term =
       1.f / (1.f + hydro_dimension_inv * p->h * p->density.rho_dh * rho_inv);
 
+  /* Compute the Balsara switch */
+  /* Pre-multiply in the AV factor; hydro_props are not passed to the iact
+   * functions */
+  const float balsara = hydro_props->viscosity.alpha * abs_div_physical_v /
+                        (abs_div_physical_v + curl_v +
+                         0.0001f * fac_Balsara_eps * soundspeed * h_inv);
+
   /* Update variables. */
   p->force.f = grad_h_term;
   p->force.pressure = pressure;
   p->force.soundspeed = soundspeed;
+  p->force.balsara = balsara;
 }
 
 /**
@@ -542,10 +666,10 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
   xp->u_full += p->u_dt * dt_therm;
 
   /* Apply the minimal energy limit */
-  const float min_energy =
+  const float min_comoving_energy =
       hydro_props->minimal_internal_energy / cosmo->a_factor_internal_energy;
-  if (xp->u_full < min_energy) {
-    xp->u_full = min_energy;
+  if (xp->u_full < min_comoving_energy) {
+    xp->u_full = min_comoving_energy;
     p->u_dt = 0.f;
   }
 
@@ -584,11 +708,11 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
   xp->u_full = p->u;
 
   /* Apply the minimal energy limit */
-  const float min_energy =
+  const float min_comoving_energy =
       hydro_props->minimal_internal_energy / cosmo->a_factor_internal_energy;
-  if (xp->u_full < min_energy) {
-    xp->u_full = min_energy;
-    p->u = min_energy;
+  if (xp->u_full < min_comoving_energy) {
+    xp->u_full = min_comoving_energy;
+    p->u = min_comoving_energy;
     p->u_dt = 0.f;
   }
 
diff --git a/src/hydro/Minimal/hydro_iact.h b/src/hydro/Minimal/hydro_iact.h
index 0193886f57413540d110c513ae3119ba1315f384..e060cb3562f1b319c64d6f6523b18858662312e7 100644
--- a/src/hydro/Minimal/hydro_iact.h
+++ b/src/hydro/Minimal/hydro_iact.h
@@ -53,6 +53,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
 
   float wi, wj, wi_dx, wj_dx;
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (pi->time_bin == time_bin_inhibited)
+    error("Inhibited pi in interaction function!");
+  if (pj->time_bin == time_bin_inhibited)
+    error("Inhibited pj in interaction function!");
+#endif
+
   /* Get r. */
   const float r_inv = 1.0f / sqrtf(r2);
   const float r = r2 * r_inv;
@@ -80,6 +87,33 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
   pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx);
   pj->density.wcount += wj;
   pj->density.wcount_dh -= (hydro_dimension * wj + uj * wj_dx);
+
+  /* Compute dv dot r */
+  float dv[3], curlvr[3];
+
+  const float faci = mj * wi_dx * r_inv;
+  const float facj = mi * wj_dx * r_inv;
+
+  dv[0] = pi->v[0] - pj->v[0];
+  dv[1] = pi->v[1] - pj->v[1];
+  dv[2] = pi->v[2] - pj->v[2];
+  const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2];
+
+  pi->density.div_v -= faci * dvdr;
+  pj->density.div_v -= facj * dvdr;
+
+  /* Compute dv cross r */
+  curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1];
+  curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2];
+  curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0];
+
+  pi->density.rot_v[0] += faci * curlvr[0];
+  pi->density.rot_v[1] += faci * curlvr[1];
+  pi->density.rot_v[2] += faci * curlvr[2];
+
+  pj->density.rot_v[0] += facj * curlvr[0];
+  pj->density.rot_v[1] += facj * curlvr[1];
+  pj->density.rot_v[2] += facj * curlvr[2];
 }
 
 /**
@@ -100,6 +134,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
 
   float wi, wi_dx;
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (pi->time_bin == time_bin_inhibited)
+    error("Inhibited pi in interaction function!");
+  if (pj->time_bin == time_bin_inhibited)
+    error("Inhibited pj in interaction function!");
+#endif
+
   /* Get the masses. */
   const float mj = pj->mass;
 
@@ -115,6 +156,27 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
   pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx);
   pi->density.wcount += wi;
   pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx);
+
+  /* Compute dv dot r */
+  float dv[3], curlvr[3];
+
+  const float faci = mj * wi_dx * r_inv;
+
+  dv[0] = pi->v[0] - pj->v[0];
+  dv[1] = pi->v[1] - pj->v[1];
+  dv[2] = pi->v[2] - pj->v[2];
+  const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2];
+
+  pi->density.div_v -= faci * dvdr;
+
+  /* Compute dv cross r */
+  curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1];
+  curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2];
+  curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0];
+
+  pi->density.rot_v[0] += faci * curlvr[0];
+  pi->density.rot_v[1] += faci * curlvr[1];
+  pi->density.rot_v[2] += faci * curlvr[2];
 }
 
 /**
@@ -133,6 +195,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
     float r2, const float *dx, float hi, float hj, struct part *restrict pi,
     struct part *restrict pj, float a, float H) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (pi->time_bin == time_bin_inhibited)
+    error("Inhibited pi in interaction function!");
+  if (pj->time_bin == time_bin_inhibited)
+    error("Inhibited pj in interaction function!");
+#endif
+
   /* Cosmological factors entering the EoMs */
   const float fac_mu = pow_three_gamma_minus_five_over_two(a);
   const float a2_Hubble = a * a * H;
@@ -184,11 +253,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   /* Compute sound speeds and signal velocity */
   const float ci = pi->force.soundspeed;
   const float cj = pj->force.soundspeed;
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
+
+  /* Grab balsara switches */
+  const float balsara_i = pi->force.balsara;
+  const float balsara_j = pj->force.balsara;
 
   /* Construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-  const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij;
+  const float visc = -0.25f * v_sig * (balsara_i + balsara_j) * mu_ij / rho_ij;
 
   /* Convolve with the kernel */
   const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
@@ -214,7 +287,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   const float sph_du_term_j = P_over_rho2_j * dvdr * r_inv * wj_dr;
 
   /* Viscosity term */
-  const float visc_du_term = 0.5f * visc_acc_term * dvdr;
+  const float visc_du_term = 0.5f * visc_acc_term * dvdr_Hubble;
 
   /* Assemble the energy equation term */
   const float du_dt_i = sph_du_term_i + visc_du_term;
@@ -249,6 +322,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
     float r2, const float *dx, float hi, float hj, struct part *restrict pi,
     const struct part *restrict pj, float a, float H) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (pi->time_bin == time_bin_inhibited)
+    error("Inhibited pi in interaction function!");
+  if (pj->time_bin == time_bin_inhibited)
+    error("Inhibited pj in interaction function!");
+#endif
+
   /* Cosmological factors entering the EoMs */
   const float fac_mu = pow_three_gamma_minus_five_over_two(a);
   const float a2_Hubble = a * a * H;
@@ -300,11 +380,15 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   /* Compute sound speeds and signal velocity */
   const float ci = pi->force.soundspeed;
   const float cj = pj->force.soundspeed;
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
+
+  /* Grab balsara switches */
+  const float balsara_i = pi->force.balsara;
+  const float balsara_j = pj->force.balsara;
 
   /* Construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-  const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij;
+  const float visc = -0.25f * v_sig * (balsara_i + balsara_j) * mu_ij / rho_ij;
 
   /* Convolve with the kernel */
   const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
@@ -325,7 +409,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   const float sph_du_term_i = P_over_rho2_i * dvdr * r_inv * wi_dr;
 
   /* Viscosity term */
-  const float visc_du_term = 0.5f * visc_acc_term * dvdr;
+  const float visc_du_term = 0.5f * visc_acc_term * dvdr_Hubble;
 
   /* Assemble the energy equation term */
   const float du_dt_i = sph_du_term_i + visc_du_term;
diff --git a/src/hydro/Minimal/hydro_io.h b/src/hydro/Minimal/hydro_io.h
index 879255640fc1a1d6a06a666c80d3860c9c31ab64..1146aa9347d443833cd481103da6f6c57d21fcbf 100644
--- a/src/hydro/Minimal/hydro_io.h
+++ b/src/hydro/Minimal/hydro_io.h
@@ -73,7 +73,7 @@ INLINE static void hydro_read_particles(struct part* parts,
 INLINE static void convert_S(const struct engine* e, const struct part* p,
                              const struct xpart* xp, float* ret) {
 
-  ret[0] = hydro_get_comoving_entropy(p);
+  ret[0] = hydro_get_comoving_entropy(p, xp);
 }
 
 INLINE static void convert_P(const struct engine* e, const struct part* p,
diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h
index c33f1b9a214cf9839f1acb965b686d4a4962865c..1d14a94f2d91bf259df54c875a32bf3072ad33b6 100644
--- a/src/hydro/Minimal/hydro_part.h
+++ b/src/hydro/Minimal/hydro_part.h
@@ -124,6 +124,12 @@ struct part {
       /*! Derivative of density with respect to h */
       float rho_dh;
 
+      /*! Velocity divergence */
+      float div_v;
+
+      /*! Velocity curl */
+      float rot_v[3];
+
     } density;
 
     /**
@@ -150,6 +156,9 @@ struct part {
       /*! Time derivative of smoothing length  */
       float h_dt;
 
+      /*! Balsara switch */
+      float balsara;
+
     } force;
   };
 
diff --git a/src/hydro/Planetary/hydro.h b/src/hydro/Planetary/hydro.h
index 57025b17e106ae4b71f150d2c2d319e92752ec9e..dee65a15758043d2cf526ea889b993c694d5dab4 100644
--- a/src/hydro/Planetary/hydro.h
+++ b/src/hydro/Planetary/hydro.h
@@ -26,8 +26,8 @@
  * equations) with multiple materials.
  *
  * The thermal variable is the internal energy (u). Simple constant
- * viscosity term without switches is implemented. No thermal conduction
- * term is implemented.
+ * viscosity term with the Balsara (1995) switch (optional).
+ * No thermal conduction term is implemented.
  *
  * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
  * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational
@@ -45,28 +45,32 @@
 #include "minmax.h"
 
 /*
- * Note: Define PLANETARY_SPH_BALSARA to use the Balsara (1995) switch for
- * the artificial viscosity, instead of the default Monaghan (1992).
- * i.e. compile with:  make CFLAGS=-DPLANETARY_SPH_BALSARA  to use.
+ * Note: Define PLANETARY_SPH_NO_BALSARA to disable the Balsara (1995) switch
+ * for the artificial viscosity and use the vanilla Monaghan (1992) instead.
+ * i.e. compile with:  make CFLAGS=-DPLANETARY_SPH_NO_BALSARA
  */
 
 /**
- * @brief Returns the comoving internal energy of a particle
+ * @brief Returns the comoving internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * For implementations where the main thermodynamic variable
  * is not internal energy, this function computes the internal
  * energy from the thermodynamic variable.
  *
  * @param p The particle of interest
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float
-hydro_get_comoving_internal_energy(const struct part *restrict p) {
+hydro_get_comoving_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp) {
 
-  return p->u;
+  return xp->u_full;
 }
 
 /**
- * @brief Returns the physical internal energy of a particle
+ * @brief Returns the physical internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * For implementations where the main thermodynamic variable
  * is not internal energy, this function computes the internal
@@ -74,12 +78,40 @@ hydro_get_comoving_internal_energy(const struct part *restrict p) {
  * physical coordinates.
  *
  * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float
 hydro_get_physical_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp,
                                    const struct cosmology *cosmo) {
 
+  return xp->u_full * cosmo->a_factor_internal_energy;
+}
+
+/**
+ * @brief Returns the comoving internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_internal_energy(const struct part *restrict p) {
+
+  return p->u;
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_internal_energy(const struct part *restrict p,
+                                           const struct cosmology *cosmo) {
+
   return p->u * cosmo->a_factor_internal_energy;
 }
 
@@ -120,11 +152,12 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
  * the thermodynamic variable.
  *
  * @param p The particle of interest
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
-    const struct part *restrict p) {
+    const struct part *restrict p, const struct xpart *restrict xp) {
 
-  return gas_entropy_from_internal_energy(p->rho, p->u, p->mat_id);
+  return gas_entropy_from_internal_energy(p->rho, xp->u_full, p->mat_id);
 }
 
 /**
@@ -136,10 +169,40 @@ __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
  * physical coordinates.
  *
  * @param p The particle of interest
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
-    const struct part *restrict p, const struct cosmology *cosmo) {
+    const struct part *restrict p, const struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return gas_entropy_from_internal_energy(p->rho, xp->u_full, p->mat_id);
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_entropy(const struct part *restrict p) {
+
+  return gas_entropy_from_internal_energy(p->rho, p->u, p->mat_id);
+}
+
+/**
+ * @brief Returns the physical entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_entropy(const struct part *restrict p,
+                                   const struct cosmology *cosmo) {
 
   /* Note: no cosmological conversion required here with our choice of
    * coordinates. */
@@ -244,12 +307,27 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
  *
  * @param p The particle of interest
  */
-__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
-    const struct part *restrict p) {
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy_dt(const struct part *restrict p) {
 
   return p->u_dt;
 }
 
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest
+ * @param cosmo Cosmology data structure
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy_dt(const struct part *restrict p,
+                                      const struct cosmology *cosmo) {
+
+  return p->u_dt * cosmo->a_factor_internal_energy;
+}
+
 /**
  * @brief Returns the time derivative of internal energy of a particle
  *
@@ -258,12 +336,29 @@ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
  * @param p The particle of interest.
  * @param du_dt The new time derivative of the internal energy.
  */
-__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt(
-    struct part *restrict p, float du_dt) {
+__attribute__((always_inline)) INLINE static void
+hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) {
 
   p->u_dt = du_dt;
 }
 
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest.
+ * @param cosmo Cosmology data structure
+ * @param du_dt The new time derivative of the internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_physical_internal_energy_dt(struct part *restrict p,
+                                      const struct cosmology *cosmo,
+                                      float du_dt) {
+
+  p->u_dt = du_dt * cosmo->a_factor_internal_energy;
+}
+
 /**
  * @brief Computes the hydro time-step of a given particle
  *
@@ -393,14 +488,15 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  * @param cosmo The current cosmological model.
+ * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
-    const struct cosmology *cosmo, const float dt_alpha) {
+    const struct cosmology *cosmo, const struct hydro_props *hydro_props,
+    const float dt_alpha) {
 
-#ifdef PLANETARY_SPH_BALSARA
   const float fac_mu = cosmo->a_factor_mu;
 
   /* Compute the norm of the curl */
@@ -410,7 +506,6 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
 
   /* Compute the norm of div v */
   const float abs_div_v = fabsf(p->density.div_v);
-#endif  // PLANETARY_SPH_BALSARA
 
   /* Compute the pressure */
   const float pressure =
@@ -432,20 +527,20 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     grad_h_term = 0.f;
   }
 
-#ifdef PLANETARY_SPH_BALSARA
-  /* Compute the Balsara switch */
+    /* Compute the Balsara switch */
+#ifdef PLANETARY_SPH_NO_BALSARA
+  const float balsara = hydro_props->viscosity.alpha;
+#else
   const float balsara =
-      abs_div_v / (abs_div_v + curl_v + 0.0001f * fac_mu * soundspeed / p->h);
-#endif  // PLANETARY_SPH_BALSARA
+      hydro_props->viscosity.alpha * abs_div_v /
+      (abs_div_v + curl_v + 0.0001f * fac_mu * soundspeed / p->h);
+#endif
 
   /* Update variables. */
   p->force.f = grad_h_term;
   p->force.pressure = pressure;
   p->force.soundspeed = soundspeed;
-
-#ifdef PLANETARY_SPH_BALSARA
   p->force.balsara = balsara;
-#endif  // PLANETARY_SPH_BALSARA
 }
 
 /**
diff --git a/src/hydro/Planetary/hydro_iact.h b/src/hydro/Planetary/hydro_iact.h
index bf96034696806e3adff1d8ba7f385af65461b9ea..19ee002b85c1b0bc8ed621a029059cd02c5e670f 100644
--- a/src/hydro/Planetary/hydro_iact.h
+++ b/src/hydro/Planetary/hydro_iact.h
@@ -25,8 +25,8 @@
  * @brief Minimal conservative implementation of SPH (Neighbour loop equations)
  *
  * The thermal variable is the internal energy (u). Simple constant
- * viscosity term without switches is implemented. No thermal conduction
- * term is implemented.
+ * viscosity term with the Balsara (1995) switch (optional).
+ * No thermal conduction term is implemented.
  *
  * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
  * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational
@@ -176,11 +176,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
                      (pi->v[1] - pj->v[1]) * dx[1] +
                      (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2;
 
-#ifdef PLANETARY_SPH_BALSARA
   /* Balsara term */
   const float balsara_i = pi->force.balsara;
   const float balsara_j = pj->force.balsara;
-#endif  // PLANETARY_SPH_BALSARA
 
   /* Are the particles moving towards each other? */
   const float omega_ij = min(dvdr, 0.f);
@@ -189,16 +187,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   /* Compute sound speeds and signal velocity */
   const float ci = pi->force.soundspeed;
   const float cj = pj->force.soundspeed;
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
 
   /* Now construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-#ifdef PLANETARY_SPH_BALSARA
-  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
-                     (balsara_i + balsara_j) / rho_ij;
-#else
-  const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij;
-#endif  // PLANETARY_SPH_BALSARA
+  const float visc = -0.25f * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij;
 
   /* Convolve with the kernel */
   const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
@@ -300,11 +293,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
                      (pi->v[1] - pj->v[1]) * dx[1] +
                      (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2;
 
-#ifdef PLANETARY_SPH_BALSARA
   /* Balsara term */
   const float balsara_i = pi->force.balsara;
   const float balsara_j = pj->force.balsara;
-#endif  // PLANETARY_SPH_BALSARA
 
   /* Are the particles moving towards each other? */
   const float omega_ij = min(dvdr, 0.f);
@@ -315,16 +306,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   const float cj = pj->force.soundspeed;
 
   /* Signal velocity */
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
 
   /* Construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-#ifdef PLANETARY_SPH_BALSARA
-  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
-                     (balsara_i + balsara_j) / rho_ij;
-#else
-  const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij;
-#endif  // PLANETARY_SPH_BALSARA
+  const float visc = -0.25f * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij;
 
   /* Convolve with the kernel */
   const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
diff --git a/src/hydro/Planetary/hydro_io.h b/src/hydro/Planetary/hydro_io.h
index afb37d884494fd02e30c143194804a2b49a77be0..1b84f8d6db295694846ffd26a422ce158aad0c60 100644
--- a/src/hydro/Planetary/hydro_io.h
+++ b/src/hydro/Planetary/hydro_io.h
@@ -25,8 +25,8 @@
  * @brief Minimal conservative implementation of SPH (i/o routines)
  *
  * The thermal variable is the internal energy (u). Simple constant
- * viscosity term without switches is implemented. No thermal conduction
- * term is implemented.
+ * viscosity term with the Balsara (1995) switch (optional).
+ * No thermal conduction term is implemented.
  *
  * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
  * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of
@@ -76,7 +76,7 @@ INLINE static void hydro_read_particles(struct part* parts,
 INLINE static void convert_S(const struct engine* e, const struct part* p,
                              const struct xpart* xp, float* ret) {
 
-  ret[0] = hydro_get_comoving_entropy(p);
+  ret[0] = hydro_get_comoving_entropy(p, xp);
 }
 
 INLINE static void convert_P(const struct engine* e, const struct part* p,
@@ -197,14 +197,14 @@ INLINE static void hydro_write_flavour(hid_t h_grpsph) {
   /* Viscosity and thermal conduction */
   /* Nothing in this minimal model... */
   io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment");
-#ifdef PLANETARY_SPH_BALSARA
+#ifdef PLANETARY_SPH_NO_BALSARA
+  io_write_attribute_s(h_grpsph, "Viscosity Model",
+                       "Minimal treatment as in Monaghan (1992)");
+#else
   io_write_attribute_s(
       h_grpsph, "Viscosity Model",
       "as in Springel (2005), i.e. Monaghan (1992) with Balsara (1995) switch");
-#else
-  io_write_attribute_s(h_grpsph, "Viscosity Model",
-                       "Minimal treatment as in Monaghan (1992)");
-#endif  // PLANETARY_SPH_BALSARA
+#endif
 
   /* Time integration properties */
   io_write_attribute_f(h_grpsph, "Maximal Delta u change over dt",
diff --git a/src/hydro/Planetary/hydro_part.h b/src/hydro/Planetary/hydro_part.h
index 7d1fc8f6729992bfdf2eeaba6e33cc9a7b071655..4087cef62e873231a556f82869a7f6d848c8d72c 100644
--- a/src/hydro/Planetary/hydro_part.h
+++ b/src/hydro/Planetary/hydro_part.h
@@ -25,8 +25,8 @@
  * @brief Minimal conservative implementation of SPH (Particle definition)
  *
  * The thermal variable is the internal energy (u). Simple constant
- * viscosity term without switches is implemented. No thermal conduction
- * term is implemented.
+ * viscosity term with the Balsara (1995) switch (optional).
+ * No thermal conduction term is implemented.
  *
  * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
  * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational
@@ -126,13 +126,11 @@ struct part {
       /*! Derivative of density with respect to h */
       float rho_dh;
 
-#ifdef PLANETARY_SPH_BALSARA
       /*! Velocity divergence. */
       float div_v;
 
       /*! Velocity curl. */
       float rot_v[3];
-#endif  // PLANETARY_SPH_BALSARA
 
     } density;
 
@@ -160,10 +158,8 @@ struct part {
       /*! Time derivative of smoothing length  */
       float h_dt;
 
-#ifdef PLANETARY_SPH_BALSARA
       /*! Balsara switch */
       float balsara;
-#endif  // PLANETARY_SPH_BALSARA
 
     } force;
   };
diff --git a/src/hydro/PressureEnergy/hydro.h b/src/hydro/PressureEnergy/hydro.h
index 57be32072ad0cb8ba5187db9ff78115ce9fb5217..3274c1b1b58adf0fa6a4c94132fa5a87186e59ce 100644
--- a/src/hydro/PressureEnergy/hydro.h
+++ b/src/hydro/PressureEnergy/hydro.h
@@ -49,22 +49,26 @@
 #include <float.h>
 
 /**
- * @brief Returns the comoving internal energy of a particle
+ * @brief Returns the comoving internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * For implementations where the main thermodynamic variable
  * is not internal energy, this function computes the internal
  * energy from the thermodynamic variable.
  *
  * @param p The particle of interest
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float
-hydro_get_comoving_internal_energy(const struct part *restrict p) {
+hydro_get_comoving_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp) {
 
-  return p->u;
+  return xp->u_full;
 }
 
 /**
- * @brief Returns the physical internal energy of a particle
+ * @brief Returns the physical internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * For implementations where the main thermodynamic variable
  * is not internal energy, this function computes the internal
@@ -72,12 +76,40 @@ hydro_get_comoving_internal_energy(const struct part *restrict p) {
  * physical coordinates.
  *
  * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float
 hydro_get_physical_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp,
                                    const struct cosmology *cosmo) {
 
+  return xp->u_full * cosmo->a_factor_internal_energy;
+}
+
+/**
+ * @brief Returns the comoving internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_internal_energy(const struct part *restrict p) {
+
+  return p->u;
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_internal_energy(const struct part *restrict p,
+                                           const struct cosmology *cosmo) {
+
   return p->u * cosmo->a_factor_internal_energy;
 }
 
@@ -110,33 +142,66 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
 }
 
 /**
- * @brief Returns the comoving entropy of a particle
+ * @brief Returns the comoving entropy of a particle at the last
+ * time the particle was kicked.
  *
  * For implementations where the main thermodynamic variable
  * is not entropy, this function computes the entropy from
  * the thermodynamic variable.
  *
  * @param p The particle of interest
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
-    const struct part *restrict p) {
+    const struct part *restrict p, const struct xpart *restrict xp) {
 
-  return gas_entropy_from_internal_energy(p->rho, p->u);
+  return gas_entropy_from_internal_energy(p->rho, xp->u_full);
 }
 
 /**
- * @brief Returns the physical entropy of a particle
+ * @brief Returns the physical entropy of a particle at the last
+ * time the particle was kicked.
  *
  * For implementations where the main thermodynamic variable
  * is not entropy, this function computes the entropy from
  * the thermodynamic variable and converts it to
  * physical coordinates.
  *
- * @param p The particle of interest
+ * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
-    const struct part *restrict p, const struct cosmology *cosmo) {
+    const struct part *restrict p, const struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return gas_entropy_from_internal_energy(p->rho, xp->u_full);
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_entropy(const struct part *restrict p) {
+
+  return gas_entropy_from_internal_energy(p->rho, p->u);
+}
+
+/**
+ * @brief Returns the physical entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_entropy(const struct part *restrict p,
+                                   const struct cosmology *cosmo) {
 
   /* Note: no cosmological conversion required here with our choice of
    * coordinates. */
@@ -245,12 +310,27 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
  *
  * @param p The particle of interest
  */
-__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
-    const struct part *restrict p) {
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy_dt(const struct part *restrict p) {
 
   return p->u_dt;
 }
 
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest
+ * @param cosmo Cosmology data structure
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy_dt(const struct part *restrict p,
+                                      const struct cosmology *cosmo) {
+
+  return p->u_dt * cosmo->a_factor_internal_energy;
+}
+
 /**
  * @brief Sets the time derivative of internal energy of a particle
  *
@@ -259,12 +339,29 @@ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
  * @param p The particle of interest.
  * @param du_dt The new time derivative of the internal energy.
  */
-__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt(
-    struct part *restrict p, float du_dt) {
+__attribute__((always_inline)) INLINE static void
+hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) {
 
   p->u_dt = du_dt;
 }
 
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest.
+ * @param cosmo Cosmology data structure
+ * @param du_dt The new time derivative of the internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_physical_internal_energy_dt(struct part *restrict p,
+                                      const struct cosmology *cosmo,
+                                      float du_dt) {
+
+  p->u_dt = du_dt * cosmo->a_factor_internal_energy;
+}
+
 /**
  * @brief Computes the hydro time-step of a given particle
  *
@@ -427,12 +524,14 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  * @param cosmo The current cosmological model.
+ * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
-    const struct cosmology *cosmo, const float dt_alpha) {
+    const struct cosmology *cosmo, const struct hydro_props *hydro_props,
+    const float dt_alpha) {
 
   const float fac_mu = cosmo->a_factor_mu;
 
@@ -449,7 +548,8 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
 
   /* Compute the Balsara switch */
   const float balsara =
-      abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed * fac_mu / p->h);
+      hydro_props->viscosity.alpha * abs_div_v /
+      (abs_div_v + curl_v + 0.0001f * soundspeed * fac_mu / p->h);
 
   /* Compute the "grad h" term */
   const float common_factor = p->h / (hydro_dimension * p->density.wcount);
diff --git a/src/hydro/PressureEnergy/hydro_iact.h b/src/hydro/PressureEnergy/hydro_iact.h
index e23b06247e255bee088bd920dee46919443abd96..2ed7fe8cb8112c42de933a2ca315966e67108f0a 100644
--- a/src/hydro/PressureEnergy/hydro_iact.h
+++ b/src/hydro/PressureEnergy/hydro_iact.h
@@ -244,7 +244,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   /* Compute sound speeds and signal velocity */
   const float ci = pi->force.soundspeed;
   const float cj = pj->force.soundspeed;
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
 
   /* Balsara term */
   const float balsara_i = pi->force.balsara;
@@ -252,8 +252,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
 
   /* Construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
-                     (balsara_i + balsara_j) / rho_ij;
+  const float visc = -0.25f * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij;
 
   /* Convolve with the kernel */
   const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
@@ -372,7 +371,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   /* Compute sound speeds and signal velocity */
   const float ci = pi->force.soundspeed;
   const float cj = pj->force.soundspeed;
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
 
   /* Balsara term */
   const float balsara_i = pi->force.balsara;
@@ -380,8 +379,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
 
   /* Construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
-                     (balsara_i + balsara_j) / rho_ij;
+  const float visc = -0.25f * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij;
 
   /* Convolve with the kernel */
   const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
diff --git a/src/hydro/PressureEnergy/hydro_io.h b/src/hydro/PressureEnergy/hydro_io.h
index a69bfdac425ba671dbadc237fe6647bbee0072b9..06762c6124c2c726c4e687980455ab956a5fa79e 100644
--- a/src/hydro/PressureEnergy/hydro_io.h
+++ b/src/hydro/PressureEnergy/hydro_io.h
@@ -68,16 +68,10 @@ INLINE static void hydro_read_particles(struct part* parts,
                                 UNIT_CONV_DENSITY, parts, rho);
 }
 
-INLINE static void convert_u(const struct engine* e, const struct part* p,
-                             const struct xpart* xp, float* ret) {
-
-  ret[0] = hydro_get_comoving_internal_energy(p);
-}
-
 INLINE static void convert_S(const struct engine* e, const struct part* p,
                              const struct xpart* xp, float* ret) {
 
-  ret[0] = hydro_get_comoving_entropy(p);
+  ret[0] = hydro_get_comoving_entropy(p, xp);
 }
 
 INLINE static void convert_P(const struct engine* e, const struct part* p,
@@ -169,9 +163,8 @@ INLINE static void hydro_write_particles(const struct part* parts,
       io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass);
   list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH,
                                  parts, h);
-  list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1,
-                                              UNIT_CONV_ENERGY_PER_UNIT_MASS,
-                                              parts, xparts, convert_u);
+  list[4] = io_make_output_field("InternalEnergy", FLOAT, 1,
+                                 UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u);
   list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
                                  UNIT_CONV_NO_UNITS, parts, id);
   list[6] =
diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h
index a5c0c62c385a2304adb27ccbad4b7f6769d8b5d0..b16d24cfcee9407c8213b1e17465005884da6617 100644
--- a/src/hydro/PressureEntropy/hydro.h
+++ b/src/hydro/PressureEntropy/hydro.h
@@ -42,26 +42,58 @@
 #include "minmax.h"
 
 /**
- * @brief Returns the comoving internal energy of a particle
+ * @brief Returns the comoving internal energy of a particle at the last
+ * time the particle was kicked.
  *
  * @param p The particle of interest
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float
-hydro_get_comoving_internal_energy(const struct part *restrict p) {
+hydro_get_comoving_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp) {
 
-  return gas_internal_energy_from_entropy(p->rho_bar, p->entropy);
+  return gas_internal_energy_from_entropy(p->rho_bar, xp->entropy_full);
 }
 
 /**
- * @brief Returns the physical internal energy of a particle
+ * @brief Returns the physical internal energy of a particle at the last
+ * time the particle was kicked.
  *
- * @param p The particle of interest
+ * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float
 hydro_get_physical_internal_energy(const struct part *restrict p,
+                                   const struct xpart *restrict xp,
                                    const struct cosmology *cosmo) {
 
+  return gas_internal_energy_from_entropy(p->rho_bar * cosmo->a3_inv,
+                                          xp->entropy_full);
+}
+/**
+ * @brief Returns the comoving internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_internal_energy(const struct part *restrict p) {
+
+  return gas_internal_energy_from_entropy(p->rho_bar, p->entropy);
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_internal_energy(const struct part *restrict p,
+                                           const struct cosmology *cosmo) {
+
   return gas_internal_energy_from_entropy(p->rho_bar * cosmo->a3_inv,
                                           p->entropy);
 }
@@ -89,24 +121,57 @@ __attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
 }
 
 /**
- * @brief Returns the comoving entropy of a particle
+ * @brief Returns the comoving entropy of a particle at the last
+ * time the particle was kicked.
  *
- * @param p The particle of interest
+ * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
-    const struct part *restrict p) {
+    const struct part *restrict p, const struct xpart *restrict xp) {
 
-  return p->entropy;
+  return xp->entropy_full;
 }
 
 /**
- * @brief Returns the physical entropy of a particle
+ * @brief Returns the physical entropy of a particl at the last
+ * time the particle was kicked.
  *
  * @param p The particle of interest.
+ * @param xp The extended data of the particle of interest.
  * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
-    const struct part *restrict p, const struct cosmology *cosmo) {
+    const struct part *restrict p, const struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return xp->entropy_full;
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_comoving_entropy(const struct part *restrict p) {
+
+  return p->entropy;
+}
+
+/**
+ * @brief Returns the physical entropy of a particle drifted to the
+ * current time.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_drifted_physical_entropy(const struct part *restrict p,
+                                   const struct cosmology *cosmo) {
 
   /* Note: no cosmological conversion required here with our choice of
    * coordinates. */
@@ -210,12 +275,28 @@ __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
  *
  * @param p The particle of interest
  */
-__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
-    const struct part *restrict p) {
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy_dt(const struct part *restrict p) {
 
   return gas_internal_energy_from_entropy(p->rho_bar, p->entropy_dt);
 }
 
+/**
+ * @brief Returns the time derivative of physical internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy_dt(const struct part *restrict p,
+                                      const struct cosmology *cosmo) {
+
+  return gas_internal_energy_from_entropy(p->rho_bar * cosmo->a3_inv,
+                                          p->entropy_dt);
+}
+
 /**
  * @brief Returns the time derivative of internal energy of a particle
  *
@@ -224,12 +305,29 @@ __attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
  * @param p The particle of interest.
  * @param du_dt The new time derivative of the internal energy.
  */
-__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt(
-    struct part *restrict p, float du_dt) {
+__attribute__((always_inline)) INLINE static void
+hydro_set_comoving_internal_energy_dt(struct part *restrict p, float du_dt) {
 
   p->entropy_dt = gas_entropy_from_internal_energy(p->rho_bar, du_dt);
 }
 
+/**
+ * @brief Sets the time derivative of the physical internal energy of a particle
+ *
+ * We assume a constant density for the conversion to entropy.
+ *
+ * @param p The particle of interest.
+ * @param cosmo Cosmology data structure
+ * @param du_dt The time derivative of the internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_physical_internal_energy_dt(struct part *restrict p,
+                                      const struct cosmology *restrict cosmo,
+                                      float du_dt) {
+  p->entropy_dt =
+      gas_entropy_from_internal_energy(p->rho_bar * cosmo->a3_inv, du_dt);
+}
+
 /**
  * @brief Computes the hydro time-step of a given particle
  *
@@ -380,12 +478,14 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  * @param cosmo The current cosmological model.
+ * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
-    const struct cosmology *cosmo, const float dt_alpha) {
+    const struct cosmology *cosmo, const struct hydro_props *hydro_props,
+    const float dt_alpha) {
 
   const float fac_mu = cosmo->a_factor_mu;
 
@@ -405,7 +505,8 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
 
   /* Compute the Balsara switch */
   const float balsara =
-      abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed * fac_mu / p->h);
+      hydro_props->viscosity.alpha * abs_div_v /
+      (abs_div_v + curl_v + 0.0001f * soundspeed * fac_mu / p->h);
 
   /* Divide the pressure by the density squared to get the SPH term */
   const float rho_bar_inv = 1.f / p->rho_bar;
diff --git a/src/hydro/PressureEntropy/hydro_iact.h b/src/hydro/PressureEntropy/hydro_iact.h
index b8f8c1983a3b1fb67781f7228194deb770273988..a018b39a99be5ed691485d93bd8dfd1735378bda 100644
--- a/src/hydro/PressureEntropy/hydro_iact.h
+++ b/src/hydro/PressureEntropy/hydro_iact.h
@@ -259,12 +259,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
 
   /* Signal velocity */
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
 
   /* Now construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
-                     (balsara_i + balsara_j) / rho_ij;
+  const float visc = -0.25f * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij;
 
   /* Now, convolve with the kernel */
   const float visc_term = 0.5f * visc * (wi_dr + wj_dr);
@@ -373,12 +372,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
 
   /* Signal velocity */
-  const float v_sig = ci + cj - 3.f * mu_ij;
+  const float v_sig = ci + cj - const_viscosity_beta * mu_ij;
 
   /* Now construct the full viscosity term */
   const float rho_ij = 0.5f * (rhoi + rhoj);
-  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
-                     (balsara_i + balsara_j) / rho_ij;
+  const float visc = -0.25f * v_sig * mu_ij * (balsara_i + balsara_j) / rho_ij;
 
   /* Now, convolve with the kernel */
   const float visc_term = 0.5f * visc * (wi_dr + wj_dr);
diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h
index 8c11bf6e334e18b10217e90f6573a42e40880955..e9397bf6108b8bc16658157e424055274f05f23c 100644
--- a/src/hydro/PressureEntropy/hydro_io.h
+++ b/src/hydro/PressureEntropy/hydro_io.h
@@ -71,7 +71,7 @@ INLINE static void hydro_read_particles(struct part* parts,
 INLINE static void convert_u(const struct engine* e, const struct part* p,
                              const struct xpart* xp, float* ret) {
 
-  ret[0] = hydro_get_comoving_internal_energy(p);
+  ret[0] = hydro_get_comoving_internal_energy(p, xp);
 }
 
 INLINE static void convert_P(const struct engine* e, const struct part* p,
@@ -194,8 +194,6 @@ INLINE static void hydro_write_flavour(hid_t h_grpsph) {
   io_write_attribute_s(
       h_grpsph, "Viscosity Model",
       "as in Springel (2005), i.e. Monaghan (1992) with Balsara (1995) switch");
-  io_write_attribute_f(h_grpsph, "Viscosity alpha", const_viscosity_alpha);
-  io_write_attribute_f(h_grpsph, "Viscosity beta", 3.f);
 
   /* Time integration properties */
   io_write_attribute_f(h_grpsph, "Maximal Delta u change over dt",
diff --git a/src/hydro/Shadowswift/hydro.h b/src/hydro/Shadowswift/hydro.h
index 0715223850023be90294e5ccaa43c197c8eab2d4..7e38aa6b57f383564e96d9fea24730926c0ac70b 100644
--- a/src/hydro/Shadowswift/hydro.h
+++ b/src/hydro/Shadowswift/hydro.h
@@ -295,12 +295,14 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  * @param cosmo The current cosmological model.
+ * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part* restrict p, struct xpart* restrict xp,
-    const struct cosmology* cosmo, const float dt_alpha) {
+    const struct cosmology* cosmo, const struct hydro_props* hydro_props,
+    const float dt_alpha) {
 
   /* Initialize time step criterion variables */
   p->timestepvars.vmax = 0.0f;
diff --git a/src/hydro_properties.c b/src/hydro_properties.c
index 260bbdd79c8a9ca380d93142a073674e2f9a489a..2b1cd42055c66768e943241c75298e53e0bf75a8 100644
--- a/src/hydro_properties.c
+++ b/src/hydro_properties.c
@@ -40,6 +40,13 @@
 #define hydro_props_default_init_temp 0.f
 #define hydro_props_default_min_temp 0.f
 #define hydro_props_default_H_ionization_temperature 1e4
+#define hydro_props_default_viscosity_alpha 0.8f
+#define hydro_props_default_viscosity_alpha_min \
+  0.1f /* Values taken from (Price,2004), not used in legacy gadget mode */
+#define hydro_props_default_viscosity_alpha_max \
+  2.0f /* Values taken from (Price,2004), not used in legacy gadget mode */
+#define hydro_props_default_viscosity_length \
+  0.1f /* Values taken from (Price,2004), not used in legacy gadget mode */
 
 /**
  * @brief Initialize the global properties of the hydro scheme.
@@ -112,6 +119,21 @@ void hydro_props_init(struct hydro_props *p,
   p->hydrogen_mass_fraction = parser_get_opt_param_double(
       params, "SPH:H_mass_fraction", default_H_fraction);
 
+  /* Read the artificial viscosity parameters from the file, if they exist */
+  p->viscosity.alpha = parser_get_opt_param_float(
+      params, "SPH:viscosity_alpha", hydro_props_default_viscosity_alpha);
+
+  p->viscosity.alpha_max =
+      parser_get_opt_param_float(params, "SPH:viscosity_alpha_max",
+                                 hydro_props_default_viscosity_alpha_max);
+
+  p->viscosity.alpha_min =
+      parser_get_opt_param_float(params, "SPH:viscosity_alpha_min",
+                                 hydro_props_default_viscosity_alpha_min);
+
+  p->viscosity.length = parser_get_opt_param_float(
+      params, "SPH:viscosity_length", hydro_props_default_viscosity_length);
+
   /* Compute the initial energy (Note the temp. read is in internal units) */
   /* u_init = k_B T_init / (mu m_p (gamma - 1)) */
   double u_init = phys_const->const_boltzmann_k / phys_const->const_proton_mass;
@@ -164,6 +186,12 @@ void hydro_props_print(const struct hydro_props *p) {
 
   message("Hydrodynamic integration: CFL parameter: %.4f.", p->CFL_condition);
 
+  message(
+      "Artificial viscosity parameters set to alpha: %.3f, max: %.3f, "
+      "min: %.3f, length: %.3f.",
+      p->viscosity.alpha, p->viscosity.alpha_max, p->viscosity.alpha_min,
+      p->viscosity.length);
+
   message(
       "Hydrodynamic integration: Max change of volume: %.2f "
       "(max|dlog(h)/dt|=%f).",
@@ -183,13 +211,14 @@ void hydro_props_print(const struct hydro_props *p) {
     message("Minimal gas temperature set to %f", p->minimal_temperature);
 
     // Matthieu: Temporary location for this i/o business.
+
 #ifdef PLANETARY_SPH
-#ifdef PLANETARY_SPH_BALSARA
-  message("Planetary SPH: Balsara switch enabled");
+#ifdef PLANETARY_SPH_NO_BALSARA
+  message("Planetary SPH: Balsara switch DISABLED");
 #else
-  message("Planetary SPH: Balsara switch disabled");
-#endif  // PLANETARY_SPH_BALSARA
-#endif  // PLANETARY_SPH
+  message("Planetary SPH: Balsara switch ENABLED");
+#endif
+#endif
 }
 
 #if defined(HAVE_HDF5)
@@ -220,10 +249,54 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) {
                        p->hydrogen_mass_fraction);
   io_write_attribute_f(h_grpsph, "Hydrogen ionization transition temperature",
                        p->hydrogen_ionization_temperature);
-  io_write_attribute_f(h_grpsph, "Alpha viscosity", const_viscosity_alpha);
+  io_write_attribute_f(h_grpsph, "Alpha viscosity", p->viscosity.alpha);
+  io_write_attribute_f(h_grpsph, "Alpha viscosity (max)",
+                       p->viscosity.alpha_max);
+  io_write_attribute_f(h_grpsph, "Alpha viscosity (min)",
+                       p->viscosity.alpha_min);
+  io_write_attribute_f(h_grpsph, "Viscosity decay length", p->viscosity.length);
+  io_write_attribute_f(h_grpsph, "Beta viscosity", const_viscosity_beta);
 }
 #endif
 
+/**
+ * @brief Initialises a hydro_props struct with somewhat useful values for
+ *        the automated test suite. This is not intended for production use,
+ *        but rather to fill for the purposes of mocking.
+ *
+ * @param p the struct
+ */
+void hydro_props_init_no_hydro(struct hydro_props *p) {
+  p->eta_neighbours = 1.2348;
+  p->h_tolerance = hydro_props_default_h_tolerance;
+  p->target_neighbours = pow_dimension(p->eta_neighbours) * kernel_norm;
+  const float delta_eta = p->eta_neighbours * (1.f + p->h_tolerance);
+  p->delta_neighbours =
+      (pow_dimension(delta_eta) - pow_dimension(p->eta_neighbours)) *
+      kernel_norm;
+  p->h_max = hydro_props_default_h_max;
+  p->max_smoothing_iterations = hydro_props_default_max_iterations;
+  p->CFL_condition = 0.1;
+  p->log_max_h_change = logf(powf(1.4, hydro_dimension_inv));
+
+  /* These values are inconsistent and in a production run would probably lead
+     to a crash. Again, this function is intended for mocking use in unit tests
+     and is _not_ to be used otherwise! */
+  p->minimal_temperature = hydro_props_default_min_temp;
+  p->minimal_internal_energy = 0.f;
+  p->initial_temperature = hydro_props_default_init_temp;
+  p->initial_internal_energy = 0.f;
+
+  p->hydrogen_mass_fraction = 0.755;
+  p->hydrogen_ionization_temperature =
+      hydro_props_default_H_ionization_temperature;
+
+  p->viscosity.alpha = hydro_props_default_viscosity_alpha;
+  p->viscosity.alpha_max = hydro_props_default_viscosity_alpha_max;
+  p->viscosity.alpha_min = hydro_props_default_viscosity_alpha_min;
+  p->viscosity.length = hydro_props_default_viscosity_length;
+}
+
 /**
  * @brief Write a hydro_props struct to the given FILE as a stream of bytes.
  *
diff --git a/src/hydro_properties.h b/src/hydro_properties.h
index cb3fba9724b6c2e01a616505345a99c2259a67a2..b45b93192e7db7b1bdca49557f8563322f09aae9 100644
--- a/src/hydro_properties.h
+++ b/src/hydro_properties.h
@@ -69,20 +69,36 @@ struct hydro_props {
   /*! Minimal temperature allowed */
   float minimal_temperature;
 
-  /*! Minimal internal energy per unit mass */
+  /*! Minimal physical internal energy per unit mass */
   float minimal_internal_energy;
 
   /*! Initial temperature */
   float initial_temperature;
 
-  /*! Initial internal energy per unit mass */
+  /*! Initial physical internal energy per unit mass */
   float initial_internal_energy;
 
-  /*! Primoridal hydrogen mass fraction for initial energy conversion */
+  /*! Primordial hydrogen mass fraction for initial energy conversion */
   float hydrogen_mass_fraction;
 
   /*! Temperature of the neutral to ionized transition of Hydrogen */
   float hydrogen_ionization_temperature;
+
+  /*! Artificial viscosity parameters */
+  struct {
+    /*! For the fixed, simple case. Also used to set the initial AV
+        coefficient for variable schemes. */
+    float alpha;
+
+    /*! Artificial viscosity (max) for the variable case (e.g. M&M) */
+    float alpha_max;
+
+    /*! Artificial viscosity (min) for the variable case (e.g. M&M) */
+    float alpha_min;
+
+    /*! The decay length of the artificial viscosity (used in M&M, etc.) */
+    float length;
+  } viscosity;
 };
 
 void hydro_props_print(const struct hydro_props *p);
@@ -99,4 +115,7 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p);
 void hydro_props_struct_dump(const struct hydro_props *p, FILE *stream);
 void hydro_props_struct_restore(const struct hydro_props *p, FILE *stream);
 
+/* Setup for tests */
+void hydro_props_init_no_hydro(struct hydro_props *p);
+
 #endif /* SWIFT_HYDRO_PROPERTIES */
diff --git a/src/kick.h b/src/kick.h
index 8c1c55519c311888ea1271f2285841731d7407ba..e85c9de40d2084304bde108e6f5fa9c776fd3e8f 100644
--- a/src/kick.h
+++ b/src/kick.h
@@ -45,8 +45,8 @@ __attribute__((always_inline)) INLINE static void kick_gpart(
   if (gp->ti_kick != ti_start)
     error(
         "g-particle has not been kicked to the current time gp->ti_kick=%lld, "
-        "ti_start=%lld, ti_end=%lld",
-        gp->ti_kick, ti_start, ti_end);
+        "ti_start=%lld, ti_end=%lld id=%lld",
+        gp->ti_kick, ti_start, ti_end, gp->id_or_neg_offset);
 
   gp->ti_kick = ti_end;
 #endif
@@ -85,8 +85,8 @@ __attribute__((always_inline)) INLINE static void kick_part(
   if (p->ti_kick != ti_start)
     error(
         "particle has not been kicked to the current time p->ti_kick=%lld, "
-        "ti_start=%lld, ti_end=%lld",
-        p->ti_kick, ti_start, ti_end);
+        "ti_start=%lld, ti_end=%lld id=%lld",
+        p->ti_kick, ti_start, ti_end, p->id);
 
   p->ti_kick = ti_end;
 #endif
@@ -133,8 +133,8 @@ __attribute__((always_inline)) INLINE static void kick_spart(
   if (sp->ti_kick != ti_start)
     error(
         "s-particle has not been kicked to the current time sp->ti_kick=%lld, "
-        "ti_start=%lld, ti_end=%lld",
-        sp->ti_kick, ti_start, ti_end);
+        "ti_start=%lld, ti_end=%lld id=%lld",
+        sp->ti_kick, ti_start, ti_end, sp->id);
 
   sp->ti_kick = ti_end;
 #endif
diff --git a/src/logger.c b/src/logger.c
index 5fd4145aa1b042ed806dd3fe5487d094600b66c4..2f4b0593dac039db96375afcee258a25dd871549 100644
--- a/src/logger.c
+++ b/src/logger.c
@@ -21,8 +21,10 @@
 #include "../config.h"
 
 #ifdef HAVE_POSIX_FALLOCATE /* Are we on a sensible platform? */
+#ifdef WITH_LOGGER
 
 /* Some standard headers. */
+#include <hdf5.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -33,8 +35,110 @@
 /* Local headers. */
 #include "atomic.h"
 #include "dump.h"
+#include "engine.h"
 #include "error.h"
 #include "part.h"
+#include "units.h"
+
+/* header constants
+ * Thoses are definitions from the format and therefore should not be changed!
+ * Size in bytes
+ */
+/* size of a mask */
+#define logger_mask_size 1
+
+/* size of an offset */
+#define logger_offset_size 7
+
+/* size of the version information */
+#define logger_version_size 20
+
+/* size of the size information */
+#define logger_header_number_size 2
+
+char logger_version[logger_version_size] = "0.1";
+
+const unsigned int logger_datatype_size[logger_data_count] = {
+    sizeof(int),  sizeof(float),     sizeof(double),
+    sizeof(char), sizeof(long long), 1,
+};
+
+/**
+ * @brief Write the header of a chunk (offset + mask).
+ *
+ * This is maybe broken for big(?) endian.
+ *
+ * @param buff The writing buffer
+ * @param mask The mask to write
+ * @param offset The old offset
+ * @param offset_new The new offset
+ *
+ * @return updated buff
+ */
+char *logger_write_chunk_header(char *buff, const unsigned int *mask,
+                                const size_t *offset, const size_t offset_new) {
+  /* write mask */
+  memcpy(buff, mask, logger_mask_size);
+  buff += logger_mask_size;
+
+  /* write offset */
+  size_t diff_offset = offset_new - *offset;
+  memcpy(buff, &diff_offset, logger_offset_size);
+  buff += logger_offset_size;
+
+  return buff;
+}
+
+/**
+ * @brief Write to the dump
+ *
+ * @param d #dump file
+ * @param offset (return) offset of the data
+ * @param size number of bytes to write
+ * @param p pointer to the data
+ */
+void logger_write_data(struct dump *d, size_t *offset, size_t size,
+                       const void *p) {
+  /* get buffer */
+  char *buff = dump_get(d, size, offset);
+
+  /* write data to the buffer */
+  memcpy(buff, p, size);
+}
+
+/**
+ * @brief Write a parameter to the file
+ *
+ * TODO Make it thread safe or remove it.
+ *
+ * write data in the following order: name, data type, data.
+ * It should be used only for the file header.
+ *
+ * @param d #dump file
+ * @param params #logger_parameters file format informations
+ * @param offset (return) offset of the next chunk
+ * @param p pointer to the data
+ * @param name Label of the parameter (should be smaller than log->name)
+ * @param data_type #logger_datatype to write
+ */
+void logger_write_general_data(struct dump *d,
+                               const struct logger_parameters *params,
+                               size_t *offset, const void *p, char *name,
+                               size_t data_type) {
+  /* write name */
+  logger_write_data(d, offset, params->label_size, name);
+
+  /* write data type */
+  logger_write_data(d, offset, params->data_type_size, &data_type);
+
+  /* write value */
+  if (data_type >= logger_data_count) error("Not implemented");
+  size_t size = logger_datatype_size[data_type];
+
+  logger_write_data(d, offset, size, p);
+
+  *offset += size;
+}
 
 /**
  * @brief Compute the size of a message given its mask.
@@ -43,10 +147,10 @@
  *
  * @return The size of the logger message in bytes.
  */
-int logger_size(unsigned int mask) {
+int logger_compute_chunk_size(unsigned int mask) {
 
   /* Start with 8 bytes for the header. */
-  int size = 8;
+  int size = logger_mask_size + logger_offset_size;
 
   /* Is this a particle or a timestep? */
   if (mask & logger_mask_timestamp) {
@@ -57,6 +161,7 @@ int logger_size(unsigned int mask) {
 
     /* A timestamp consists of an unsigned long long int. */
     size += sizeof(unsigned long long int);
+    size += sizeof(double);
 
   } else {
 
@@ -88,33 +193,65 @@ int logger_size(unsigned int mask) {
   return size;
 }
 
+/**
+ * @brief log all particles in the engine.
+ *
+ * @param log The #logger
+ * @param e The #engine
+ */
+void logger_log_all(struct logger *log, const struct engine *e) {
+
+  /* Ensure that enough space is available */
+  logger_ensure_size(log, e->total_nr_parts, e->total_nr_gparts, 0);
+#ifdef SWIFT_DEBUG_CHECKS
+  message("Need to implement stars");
+#endif
+
+  /* some constants */
+  const struct space *s = e->s;
+  const unsigned int mask = logger_mask_x | logger_mask_v | logger_mask_a |
+                            logger_mask_u | logger_mask_h | logger_mask_rho |
+                            logger_mask_consts;
+
+  /* loop over all parts */
+  for (long long i = 0; i < e->total_nr_parts; i++) {
+    logger_log_part(log, &s->parts[i], mask,
+                    &s->xparts[i].logger_data.last_offset);
+    s->xparts[i].logger_data.steps_since_last_output = 0;
+  }
+
+  /* loop over all gparts */
+  if (e->total_nr_gparts > 0) error("Not implemented");
+
+  /* loop over all sparts */
+  // TODO
+}
+
 /**
  * @brief Dump a #part to the log.
  *
+ * @param log The #logger
  * @param p The #part to dump.
  * @param mask The mask of the data to dump.
- * @param offset Pointer to the offset of the previous log of this particle.
- * @param dump The #dump in which to log the particle data.
+ * @param offset Pointer to the offset of the previous log of this particle;
+ * (return) offset of this log.
  */
-void logger_log_part(struct part *p, unsigned int mask, size_t *offset,
-                     struct dump *dump) {
+void logger_log_part(struct logger *log, const struct part *p,
+                     unsigned int mask, size_t *offset) {
 
   /* Make sure we're not writing a timestamp. */
   if (mask & logger_mask_timestamp)
     error("You should not log particles as timestamps.");
 
   /* Start by computing the size of the message. */
-  const int size = logger_size(mask);
+  const int size = logger_compute_chunk_size(mask);
 
   /* Allocate a chunk of memory in the dump of the right size. */
   size_t offset_new;
-  char *buff = (char *)dump_get(dump, size, &offset_new);
+  char *buff = (char *)dump_get(log->dump, size, &offset_new);
 
   /* Write the header. */
-  uint64_t temp = (((uint64_t)(offset_new - *offset)) & 0xffffffffffffffULL) |
-                  ((uint64_t)mask << 56);
-  memcpy(buff, &temp, 8);
-  buff += 8;
+  buff = logger_write_chunk_header(buff, &mask, offset, offset_new);
 
   /* Particle position as three doubles. */
   if (mask & logger_mask_x) {
@@ -155,7 +292,7 @@ void logger_log_part(struct part *p, unsigned int mask, size_t *offset,
   }
 
   /* Particle constants, which is a bit more complicated. */
-  if (mask & logger_mask_rho) {
+  if (mask & logger_mask_consts) {
     memcpy(buff, &p->mass, sizeof(float));
     buff += sizeof(float);
     memcpy(buff, &p->id, sizeof(long long));
@@ -171,13 +308,14 @@ void logger_log_part(struct part *p, unsigned int mask, size_t *offset,
 /**
  * @brief Dump a #gpart to the log.
  *
+ * @param log The #logger
  * @param p The #gpart to dump.
  * @param mask The mask of the data to dump.
- * @param offset Pointer to the offset of the previous log of this particle.
- * @param dump The #dump in which to log the particle data.
+ * @param offset Pointer to the offset of the previous log of this particle;
+ * (return) offset of this log.
  */
-void logger_log_gpart(struct gpart *p, unsigned int mask, size_t *offset,
-                      struct dump *dump) {
+void logger_log_gpart(struct logger *log, const struct gpart *p,
+                      unsigned int mask, size_t *offset) {
 
   /* Make sure we're not writing a timestamp. */
   if (mask & logger_mask_timestamp)
@@ -188,17 +326,14 @@ void logger_log_gpart(struct gpart *p, unsigned int mask, size_t *offset,
     error("Can't log SPH quantities for gparts.");
 
   /* Start by computing the size of the message. */
-  const int size = logger_size(mask);
+  const int size = logger_compute_chunk_size(mask);
 
   /* Allocate a chunk of memory in the dump of the right size. */
   size_t offset_new;
-  char *buff = (char *)dump_get(dump, size, &offset_new);
+  char *buff = (char *)dump_get(log->dump, size, &offset_new);
 
   /* Write the header. */
-  uint64_t temp = (((uint64_t)(offset_new - *offset)) & 0xffffffffffffffULL) |
-                  ((uint64_t)mask << 56);
-  memcpy(buff, &temp, 8);
-  buff += 8;
+  buff = logger_write_chunk_header(buff, &mask, offset, offset_new);
 
   /* Particle position as three doubles. */
   if (mask & logger_mask_x) {
@@ -219,7 +354,7 @@ void logger_log_gpart(struct gpart *p, unsigned int mask, size_t *offset,
   }
 
   /* Particle constants, which is a bit more complicated. */
-  if (mask & logger_mask_rho) {
+  if (mask & logger_mask_consts) {
     memcpy(buff, &p->mass, sizeof(float));
     buff += sizeof(float);
     memcpy(buff, &p->id_or_neg_offset, sizeof(long long));
@@ -230,29 +365,327 @@ void logger_log_gpart(struct gpart *p, unsigned int mask, size_t *offset,
   *offset = offset_new;
 }
 
-void logger_log_timestamp(unsigned long long int timestamp, size_t *offset,
-                          struct dump *dump) {
+/**
+ * @brief write a timestamp
+ *
+ * @param log The #logger
+ * @param timestamp time to write
+ * @param time time or scale factor
+ * @param offset Pointer to the offset of the previous log of this particle;
+ * (return) offset of this log.
+ */
+void logger_log_timestamp(struct logger *log, integertime_t timestamp,
+                          double time, size_t *offset) {
+  struct dump *dump = log->dump;
 
   /* Start by computing the size of the message. */
-  const int size = logger_size(logger_mask_timestamp);
+  const int size = logger_compute_chunk_size(logger_mask_timestamp);
 
   /* Allocate a chunk of memory in the dump of the right size. */
   size_t offset_new;
   char *buff = (char *)dump_get(dump, size, &offset_new);
 
   /* Write the header. */
-  uint64_t temp = (((uint64_t)(offset_new - *offset)) & 0xffffffffffffffULL) |
-                  ((uint64_t)logger_mask_timestamp << 56);
-  memcpy(buff, &temp, 8);
-  buff += 8;
+  unsigned int mask = logger_mask_timestamp;
+  buff = logger_write_chunk_header(buff, &mask, offset, offset_new);
 
   /* Store the timestamp. */
-  memcpy(buff, &timestamp, sizeof(unsigned long long int));
+  memcpy(buff, &timestamp, sizeof(integertime_t));
+  buff += sizeof(integertime_t);
+
+  /* Store the time */
+  memcpy(buff, &time, sizeof(double));
 
   /* Update the log message offset. */
   *offset = offset_new;
 }
 
+/**
+ * @brief Ensure that the buffer is large enough for a step.
+ *
+ * Check if logger parameters are large enough to write all particles
+ * and ensure that enough space is available in the buffer.
+ *
+ * @param log The #logger
+ * @param total_nr_parts total number of part
+ * @param total_nr_gparts total number of gpart
+ * @param total_nr_sparts total number of spart
+ */
+void logger_ensure_size(struct logger *log, size_t total_nr_parts,
+                        size_t total_nr_gparts, size_t total_nr_sparts) {
+
+  struct logger_parameters *log_params = log->params;
+
+  /* count part memory */
+  size_t limit = log_params->total_size;
+
+  limit *= total_nr_parts;
+
+  /* count gpart memory */
+  if (total_nr_gparts > 0) error("Not implemented");
+
+  /* count spart memory */
+  if (total_nr_sparts > 0) error("Not implemented");
+
+  /* ensure enough space in dump */
+  dump_ensure(log->dump, limit, log->buffer_scale * limit);
+}
+
+/**
+ * @brief intialize the logger structure
+ *
+ * @param log The #logger
+ * @param params The #swift_params
+ */
+void logger_init(struct logger *log, struct swift_params *params) {
+  /* read parameters */
+  log->delta_step = parser_get_param_int(params, "Logger:delta_step");
+  size_t buffer_size =
+      parser_get_opt_param_float(params, "Logger:initial_buffer_size", 0.5) *
+      1e9;
+  log->buffer_scale =
+      parser_get_opt_param_float(params, "Logger:buffer_scale", 10);
+  parser_get_param_string(params, "Logger:basename", log->base_name);
+
+  /* set initial value of parameters */
+  log->timestamp_offset = 0;
+
+  /* generate dump filename */
+  char logger_name_file[PARSER_MAX_LINE_SIZE];
+  strcpy(logger_name_file, log->base_name);
+  strcat(logger_name_file, ".dump");
+
+  /* init parameters */
+  log->params =
+      (struct logger_parameters *)malloc(sizeof(struct logger_parameters));
+  logger_parameters_init(log->params);
+
+  /* init dump */
+  log->dump = malloc(sizeof(struct dump));
+  struct dump *dump_file = log->dump;
+
+  dump_init(dump_file, logger_name_file, buffer_size);
+}
+
+/**
+ * @brief Close dump file and desallocate memory
+ *
+ * @param log The #logger
+ */
+void logger_clean(struct logger *log) {
+  dump_close(log->dump);
+  logger_parameters_clean(log->params);
+  free(log->params);
+}
+
+/**
+ * @brief Write a file header to a logger file
+ *
+ * @param log The #logger
+ * @param dump The #dump in which to log the particle data.
+ *
+ */
+void logger_write_file_header(struct logger *log, const struct engine *e) {
+
+  /* get required variables */
+  const struct logger_parameters log_params = *log->params;
+  struct dump *dump = log->dump;
+
+  size_t file_offset = dump->file_offset;
+
+  if (file_offset != 0)
+    error(
+        "The logger is not empty."
+        "This function should be called before writing anything in the logger");
+
+  /* Write version information */
+  logger_write_data(dump, &file_offset, logger_version_size, &logger_version);
+
+  /* write number of bytes used for the offsets */
+  logger_write_data(dump, &file_offset, logger_header_number_size,
+                    &log_params.offset_size);
+
+  /* write offset direction */
+  int reversed = 0;
+  logger_write_data(dump, &file_offset, logger_datatype_size[logger_data_bool],
+                    &reversed);
+
+  /* placeholder to write the offset of the first log here */
+  char *skip_header = dump_get(dump, log_params.offset_size, &file_offset);
+
+  /* write number of bytes used for names */
+  logger_write_data(dump, &file_offset, logger_header_number_size,
+                    &log_params.label_size);
+
+  /* write number of bytes used for numbers */
+  logger_write_data(dump, &file_offset, logger_header_number_size,
+                    &log_params.number_size);
+
+  /* write number of bytes used for masks */
+  logger_write_data(dump, &file_offset, logger_header_number_size,
+                    &log_params.mask_size);
+
+  /* write number of masks */
+  logger_write_data(dump, &file_offset, log_params.number_size,
+                    &log_params.number_mask);
+
+  /* write masks */
+  // loop over all mask type
+  for (size_t i = 0; i < log_params.number_mask; i++) {
+    // mask name
+    size_t j = i * log_params.label_size;
+    logger_write_data(dump, &file_offset, log_params.label_size,
+                      &log_params.masks_name[j]);
+
+    // mask
+    logger_write_data(dump, &file_offset, log_params.mask_size,
+                      &log_params.masks[i]);
+
+    // mask size
+    logger_write_data(dump, &file_offset, log_params.number_size,
+                      &log_params.masks_data_size[i]);
+  }
+
+  /* write mask data */
+  // TODO
+  /* loop over each mask and each data in this mask */
+  /* write number of bytes for each field */
+  /* write data type (float, double, ...) */
+  /* write data name (mass, id, ...) */
+
+  /* Write data */
+  char *name = malloc(sizeof(char) * log_params.label_size);
+  strcpy(name, "time_base");
+  logger_write_general_data(dump, &log_params, &file_offset, &e->time_base,
+                            name, logger_data_double);
+
+  /* last step: write first offset */
+  memcpy(skip_header, &file_offset, log_params.offset_size);
+
+  /* free memory */
+  free(name);
+}
+
+/**
+ * @brief initialize the #logger_parameters with the format informations
+ *
+ * @param log_params #logger_parameters to initialize
+ */
+void logger_parameters_init(struct logger_parameters *log_params) {
+  /* set parameters */
+  log_params->label_size = 20;
+  log_params->offset_size = 7;
+  log_params->mask_size = 1;
+  log_params->number_size = 1;
+  log_params->data_type_size = 1;
+
+  log_params->number_mask = 8;
+
+  /* set masks array */
+  log_params->masks = malloc(sizeof(size_t) * log_params->number_mask);
+  log_params->masks[0] = logger_mask_x;
+  log_params->masks[1] = logger_mask_v;
+  log_params->masks[2] = logger_mask_a;
+  log_params->masks[3] = logger_mask_u;
+  log_params->masks[4] = logger_mask_h;
+  log_params->masks[5] = logger_mask_rho;
+  log_params->masks[6] = logger_mask_consts;
+  log_params->masks[7] = logger_mask_timestamp;
+
+  /* set the mask names */
+  size_t block_size = log_params->label_size * log_params->number_mask;
+  log_params->masks_name = malloc(block_size);
+  char *cur_name = log_params->masks_name;
+
+  char tmp[log_params->label_size];
+  strcpy(tmp, "position");
+  memcpy(cur_name, &tmp, log_params->label_size);
+  cur_name += log_params->label_size;
+
+  strcpy(tmp, "velocity");
+  memcpy(cur_name, &tmp, log_params->label_size);
+  cur_name += log_params->label_size;
+
+  strcpy(tmp, "acceleration");
+  memcpy(cur_name, &tmp, log_params->label_size);
+  cur_name += log_params->label_size;
+
+  strcpy(tmp, "entropy");
+  memcpy(cur_name, &tmp, log_params->label_size);
+  cur_name += log_params->label_size;
+
+  strcpy(tmp, "cutoff radius");
+  memcpy(cur_name, &tmp, log_params->label_size);
+  cur_name += log_params->label_size;
+
+  strcpy(tmp, "density");
+  memcpy(cur_name, &tmp, log_params->label_size);
+  cur_name += log_params->label_size;
+
+  strcpy(tmp, "consts");
+  memcpy(cur_name, &tmp, log_params->label_size);
+  cur_name += log_params->label_size;
+
+  strcpy(tmp, "timestamp");
+  memcpy(cur_name, &tmp, log_params->label_size);
+  cur_name += log_params->label_size;
+
+  /* set the data size */
+  log_params->masks_data_size =
+      malloc(sizeof(size_t) * log_params->number_mask);
+  log_params->masks_data_size[0] = 3 * sizeof(double);
+  log_params->masks_data_size[1] = 3 * sizeof(float);
+  log_params->masks_data_size[2] = 3 * sizeof(float);
+  log_params->masks_data_size[3] = sizeof(float);
+  log_params->masks_data_size[4] = sizeof(float);
+  log_params->masks_data_size[5] = sizeof(float);
+  log_params->masks_data_size[6] = sizeof(float) + sizeof(long long);
+  log_params->masks_data_size[7] = sizeof(integertime_t) + sizeof(double);
+
+  /* Compute the size of a chunk if all the mask are activated */
+  log_params->total_size = logger_offset_size + logger_mask_size;
+
+  for (size_t i = 0; i < log_params->number_mask; i++) {
+    if (log_params->masks[i] != logger_mask_timestamp)
+      log_params->total_size += log_params->masks_data_size[i];
+  }
+
+  // todo masks_type
+}
+
+/**
+ * @brief Clean the #logger_parameters
+ *
+ * @param log_params The #logger_parameters
+ */
+void logger_parameters_clean(struct logger_parameters *log_params) {
+  free(log_params->masks);
+  free(log_params->masks_name);
+  free(log_params->masks_data_size);
+}
+
+/**
+ * @brief read chunk header
+ *
+ * @param buff The reading buffer
+ * @param mask The mask to read
+ * @param offset (return) the offset pointed by this chunk (absolute)
+ * @param offset_cur The current chunk offset
+ *
+ * @return Number of bytes read
+ */
+__attribute__((always_inline)) INLINE static int logger_read_chunk_header(
+    const char *buff, unsigned int *mask, size_t *offset, size_t cur_offset) {
+  memcpy(mask, buff, logger_mask_size);
+  buff += logger_mask_size;
+
+  *offset = 0;
+  memcpy(offset, buff, logger_offset_size);
+  *offset = cur_offset - *offset;
+
+  return logger_mask_size + logger_offset_size;
+}
+
 /**
  * @brief Read a logger message and store the data in a #part.
  *
@@ -269,11 +702,9 @@ int logger_read_part(struct part *p, size_t *offset, const char *buff) {
   buff = &buff[*offset];
 
   /* Start by reading the logger mask for this entry. */
-  uint64_t temp;
-  memcpy(&temp, buff, 8);
-  const int mask = temp >> 56;
-  *offset -= temp & 0xffffffffffffffULL;
-  buff += 8;
+  const size_t cur_offset = *offset;
+  unsigned int mask = 0;
+  buff += logger_read_chunk_header(buff, &mask, offset, cur_offset);
 
   /* We are only interested in particle data. */
   if (mask & logger_mask_timestamp)
@@ -347,11 +778,9 @@ int logger_read_gpart(struct gpart *p, size_t *offset, const char *buff) {
   buff = &buff[*offset];
 
   /* Start by reading the logger mask for this entry. */
-  uint64_t temp;
-  memcpy(&temp, buff, 8);
-  const int mask = temp >> 56;
-  *offset -= temp & 0xffffffffffffffULL;
-  buff += 8;
+  const size_t cur_offset = *offset;
+  unsigned int mask = 0;
+  buff += logger_read_chunk_header(buff, &mask, offset, cur_offset);
 
   /* We are only interested in particle data. */
   if (mask & logger_mask_timestamp)
@@ -401,18 +830,16 @@ int logger_read_gpart(struct gpart *p, size_t *offset, const char *buff) {
  *
  * @return The mask containing the values read.
  */
-int logger_read_timestamp(unsigned long long int *t, size_t *offset,
-                          const char *buff) {
+int logger_read_timestamp(unsigned long long int *t, double *time,
+                          size_t *offset, const char *buff) {
 
   /* Jump to the offset. */
   buff = &buff[*offset];
 
   /* Start by reading the logger mask for this entry. */
-  uint64_t temp;
-  memcpy(&temp, buff, 8);
-  const int mask = temp >> 56;
-  *offset -= temp & 0xffffffffffffffULL;
-  buff += 8;
+  const size_t cur_offset = *offset;
+  unsigned int mask = 0;
+  buff += logger_read_chunk_header(buff, &mask, offset, cur_offset);
 
   /* We are only interested in timestamps. */
   if (!(mask & logger_mask_timestamp))
@@ -424,9 +851,15 @@ int logger_read_timestamp(unsigned long long int *t, size_t *offset,
 
   /* Copy the timestamp value from the buffer. */
   memcpy(t, buff, sizeof(unsigned long long int));
+  buff += sizeof(unsigned long long int);
+
+  /* Copy the timestamp value from the buffer. */
+  memcpy(time, buff, sizeof(double));
 
   /* Finally, return the mask of the values we just read. */
   return mask;
 }
 
+#endif /* WITH_LOGGER */
+
 #endif /* HAVE_POSIX_FALLOCATE */
diff --git a/src/logger.h b/src/logger.h
index 596c0903750404d0934e0d3843a5461523700e9e..3ac5291eaab1f0e4fc05640cc23e1705a7178c9a 100644
--- a/src/logger.h
+++ b/src/logger.h
@@ -19,11 +19,20 @@
 #ifndef SWIFT_LOGGER_H
 #define SWIFT_LOGGER_H
 
+#ifdef WITH_LOGGER
+
 /* Includes. */
-#include "part.h"
+#include "common_io.h"
+#include "inline.h"
+#include "timeline.h"
+#include "units.h"
 
 /* Forward declaration */
 struct dump;
+struct gpart;
+struct part;
+/* TODO remove dependency */
+struct engine;
 
 /**
  * Logger entries contain messages representing the particle data at a given
@@ -59,31 +68,147 @@ struct dump;
  * The offset refers to the relative location of the previous message for the
  * same particle or for the previous timestamp (if mask bit 7 is set). I.e.
  * the previous log entry will be at the address of the current mask byte minus
- * the unsigned value stored in the offset. An offset of zero indicates that
- * this is the first message for the given particle/timestamp.
+ * the unsigned value stored in the offset. An offset equal to the chunk offset
+ * indicated that this is the first message for the given particle/timestamp.
  */
 
 /* Some constants. */
-#define logger_mask_x 1
-#define logger_mask_v 2
-#define logger_mask_a 4
-#define logger_mask_u 8
-#define logger_mask_h 16
-#define logger_mask_rho 32
-#define logger_mask_consts 64
-#define logger_mask_timestamp 128
+enum logger_masks {
+  logger_mask_x = (1 << 0),
+  logger_mask_v = (1 << 1),
+  logger_mask_a = (1 << 2),
+  logger_mask_u = (1 << 3),
+  logger_mask_h = (1 << 4),
+  logger_mask_rho = (1 << 5),
+  logger_mask_consts = (1 << 6),
+  logger_mask_timestamp = (1 << 7),
+};
+
+/* Size of the strings. */
+#define logger_string_length 200
+
+/* parameters of the logger */
+struct logger_parameters {
+  /* size of a label in bytes */
+  size_t label_size;
+
+  /* size of an offset in bytes */
+  size_t offset_size;
+
+  /* size of a mask in bytes */
+  size_t mask_size;
+
+  /* size of a number in bytes */
+  size_t number_size;
+
+  /* size of a data type in bytes */
+  size_t data_type_size;
+
+  /* number of different mask */
+  size_t number_mask;
+
+  /* value of each masks */
+  size_t *masks;
+
+  /* data size of each mask */
+  size_t *masks_data_size;
+
+  /* label of each mask */
+  char *masks_name;
+
+  /* Size of a chunk if every mask are activated */
+  size_t total_size;
+};
+
+/* structure containing global data */
+struct logger {
+  /* Number of particle steps between dumping a chunk of data */
+  short int delta_step;
+
+  /* Logger basename */
+  char base_name[logger_string_length];
+
+  /* File name of the dump file */
+  struct dump *dump;
+
+  /* timestamp offset for logger*/
+  size_t timestamp_offset;
+
+  /* scaling factor when buffer is too small */
+  float buffer_scale;
+
+  /* logger parameters */
+  struct logger_parameters *params;
+
+} SWIFT_STRUCT_ALIGN;
+
+/* required structure for each particle type */
+struct logger_part_data {
+  /* Number of particle updates since last output */
+  int steps_since_last_output;
+
+  /* offset of last particle log entry */
+  size_t last_offset;
+};
+
+enum logger_datatype {
+  logger_data_int,
+  logger_data_float,
+  logger_data_double,
+  logger_data_char,
+  logger_data_longlong,
+  logger_data_bool,
+  logger_data_count /* should be last */
+};
+
+extern const unsigned int logger_datatype_size[];
 
 /* Function prototypes. */
-int logger_size(unsigned int mask);
-void logger_log_part(struct part *p, unsigned int mask, size_t *offset,
-                     struct dump *dump);
-void logger_log_gpart(struct gpart *p, unsigned int mask, size_t *offset,
-                      struct dump *dump);
-void logger_log_timestamp(unsigned long long int t, size_t *offset,
-                          struct dump *dump);
+int logger_compute_chunk_size(unsigned int mask);
+void logger_log_all(struct logger *log, const struct engine *e);
+void logger_log_part(struct logger *log, const struct part *p,
+                     unsigned int mask, size_t *offset);
+void logger_log_gpart(struct logger *log, const struct gpart *p,
+                      unsigned int mask, size_t *offset);
+void logger_init(struct logger *log, struct swift_params *params);
+void logger_clean(struct logger *log);
+void logger_log_timestamp(struct logger *log, integertime_t t, double time,
+                          size_t *offset);
+void logger_ensure_size(struct logger *log, size_t total_nr_parts,
+                        size_t total_nr_gparts, size_t total_nr_sparts);
+void logger_write_file_header(struct logger *log, const struct engine *e);
+
 int logger_read_part(struct part *p, size_t *offset, const char *buff);
 int logger_read_gpart(struct gpart *p, size_t *offset, const char *buff);
-int logger_read_timestamp(unsigned long long int *t, size_t *offset,
-                          const char *buff);
+int logger_read_timestamp(unsigned long long int *t, double *time,
+                          size_t *offset, const char *buff);
+
+void logger_parameters_init(struct logger_parameters *log_params);
+void logger_parameters_clean(struct logger_parameters *log_params);
+
+/**
+ * @brief Initialize the logger data for a particle.
+ *
+ * @param logger The #logger_part_data.
+ */
+INLINE static void logger_part_data_init(struct logger_part_data *logger) {
+  logger->last_offset = 0;
+  logger->steps_since_last_output = SHRT_MAX;
+}
+
+/**
+ * @brief Should this particle write its data now ?
+ *
+ * @param xp The #xpart.
+ * @param e The #engine containing information about the current time.
+ * @return 1 if the #part should write, 0 otherwise.
+ */
+__attribute__((always_inline)) INLINE static int logger_should_write(
+    const struct logger_part_data *logger_data, const struct logger *log) {
+
+  return (logger_data->steps_since_last_output > log->delta_step);
+}
+
+#endif /* WITH_LOGGER */
 
 #endif /* SWIFT_LOGGER_H */
diff --git a/src/logger_io.c b/src/logger_io.c
new file mode 100644
index 0000000000000000000000000000000000000000..a0a5ba1db85aa4eb96ee140966a47393ba5a3b68
--- /dev/null
+++ b/src/logger_io.c
@@ -0,0 +1,299 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
+ *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+#ifdef WITH_LOGGER
+
+/* Some standard headers. */
+#include <hdf5.h>
+#include <math.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* This object's header. */
+#include "logger_io.h"
+
+/* Local includes. */
+#include "chemistry_io.h"
+#include "common_io.h"
+#include "cooling.h"
+#include "dimension.h"
+#include "engine.h"
+#include "error.h"
+#include "gravity_io.h"
+#include "gravity_properties.h"
+#include "hydro_io.h"
+#include "hydro_properties.h"
+#include "io_properties.h"
+#include "kernel_hydro.h"
+#include "parallel_io.h"
+#include "part.h"
+#include "serial_io.h"
+#include "single_io.h"
+#include "stars_io.h"
+#include "units.h"
+#include "xmf.h"
+
+/**
+ * @brief Writes an HDF5 index file
+ *
+ * @param e The engine containing all the system.
+ * @param baseName The common part of the snapshot file name.
+ * @param internal_units The #unit_system used internally
+ * @param snapshot_units The #unit_system used in the snapshots
+ *
+ * Creates an HDF5 output file and writes the offset and id of particles
+ * contained in the engine. If such a file already exists, it is erased and
+ * replaced by the new one.
+ *
+ * Calls #error() if an error occurs.
+ *
+ */
+void write_index_single(struct engine* e, const char* baseName,
+                        const struct unit_system* internal_units,
+                        const struct unit_system* snapshot_units) {
+
+  hid_t h_file = 0, h_grp = 0;
+  const size_t Ngas = e->s->nr_parts;
+  const size_t Nstars = e->s->nr_sparts;
+  const size_t Ntot = e->s->nr_gparts;
+  int periodic = e->s->periodic;
+  int numFiles = 1;
+  struct part* parts = e->s->parts;
+  struct xpart* xparts = e->s->xparts;
+  // struct gpart* gparts = e->s->gparts;
+  struct gpart* dmparts = NULL;
+  // struct spart* sparts = e->s->sparts;
+  static int outputCount = 0;
+
+  struct logger* log = e->logger;
+
+  /* Number of unassociated gparts */
+  const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
+
+  long long N_total[swift_type_count] = {Ngas, Ndm, 0, 0, Nstars, 0};
+
+  /* File name */
+  char fileName[FILENAME_BUFFER_SIZE];
+  snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName,
+           outputCount);
+
+  /* Open file */
+  /* message("Opening file '%s'.", fileName); */
+  h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+  if (h_file < 0) {
+    error("Error while opening file '%s'.", fileName);
+  }
+
+  /* Open header to write simulation properties */
+  /* message("Writing runtime parameters..."); */
+  h_grp =
+      H5Gcreate(h_file, "/RuntimePars", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+  if (h_grp < 0) error("Error while creating runtime parameters group\n");
+
+  /* Write the relevant information */
+  io_write_attribute(h_grp, "PeriodicBoundariesOn", INT, &periodic, 1);
+
+  /* Close runtime parameters */
+  H5Gclose(h_grp);
+
+  /* Open header to write simulation properties */
+  /* message("Writing file header..."); */
+  h_grp = H5Gcreate(h_file, "/Header", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+  if (h_grp < 0) error("Error while creating file header\n");
+
+  /* Print the relevant information and print status */
+  io_write_attribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3);
+  double dblTime = e->time;
+  io_write_attribute(h_grp, "Time", DOUBLE, &dblTime, 1);
+  io_write_attribute(h_grp, "Time Offset", UINT, &log->timestamp_offset, 1);
+  int dimension = (int)hydro_dimension;
+  io_write_attribute(h_grp, "Dimension", INT, &dimension, 1);
+
+  /* GADGET-2 legacy values */
+  /* Number of particles of each type */
+  unsigned int numParticles[swift_type_count] = {0};
+  unsigned int numParticlesHighWord[swift_type_count] = {0};
+  for (int ptype = 0; ptype < swift_type_count; ++ptype) {
+    numParticles[ptype] = (unsigned int)N_total[ptype];
+    numParticlesHighWord[ptype] = (unsigned int)(N_total[ptype] >> 32);
+  }
+  io_write_attribute(h_grp, "NumPart_ThisFile", LONGLONG, N_total,
+                     swift_type_count);
+  io_write_attribute(h_grp, "NumPart_Total", UINT, numParticles,
+                     swift_type_count);
+  io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT,
+                     numParticlesHighWord, swift_type_count);
+  double MassTable[swift_type_count] = {0};
+  io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count);
+  unsigned int flagEntropy[swift_type_count] = {0};
+  flagEntropy[0] = writeEntropyFlag();
+  io_write_attribute(h_grp, "Flag_Entropy_ICs", UINT, flagEntropy,
+                     swift_type_count);
+  io_write_attribute(h_grp, "NumFilesPerSnapshot", INT, &numFiles, 1);
+
+  /* Close header */
+  H5Gclose(h_grp);
+
+  /* Print the code version */
+  io_write_code_description(h_file);
+
+  /* Print the SPH parameters */
+  if (e->policy & engine_policy_hydro) {
+    h_grp = H5Gcreate(h_file, "/HydroScheme", H5P_DEFAULT, H5P_DEFAULT,
+                      H5P_DEFAULT);
+    if (h_grp < 0) error("Error while creating SPH group");
+    hydro_props_print_snapshot(h_grp, e->hydro_properties);
+    hydro_write_flavour(h_grp);
+    H5Gclose(h_grp);
+  }
+
+  /* Print the gravity parameters */
+  if (e->policy & engine_policy_self_gravity) {
+    h_grp = H5Gcreate(h_file, "/GravityScheme", H5P_DEFAULT, H5P_DEFAULT,
+                      H5P_DEFAULT);
+    if (h_grp < 0) error("Error while creating gravity group");
+    gravity_props_print_snapshot(h_grp, e->gravity_properties);
+    H5Gclose(h_grp);
+  }
+
+  /* Print the runtime parameters */
+  h_grp =
+      H5Gcreate(h_file, "/Parameters", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+  if (h_grp < 0) error("Error while creating parameters group");
+  parser_write_params_to_hdf5(e->parameter_file, h_grp, 1);
+  H5Gclose(h_grp);
+
+  /* Print the runtime unused parameters */
+  h_grp = H5Gcreate(h_file, "/UnusedParameters", H5P_DEFAULT, H5P_DEFAULT,
+                    H5P_DEFAULT);
+  if (h_grp < 0) error("Error while creating parameters group");
+  parser_write_params_to_hdf5(e->parameter_file, h_grp, 0);
+  H5Gclose(h_grp);
+
+  /* Print the system of Units used in the spashot */
+  io_write_unit_system(h_file, snapshot_units, "Units");
+
+  /* Print the system of Units used internally */
+  io_write_unit_system(h_file, internal_units, "InternalCodeUnits");
+
+  /* Tell the user if a conversion will be needed */
+  if (e->verbose) {
+    if (units_are_equal(snapshot_units, internal_units)) {
+
+      message("Snapshot and internal units match. No conversion needed.");
+
+    } else {
+
+      message("Conversion needed from:");
+      message("(Snapshot) Unit system: U_M =      %e g.",
+              snapshot_units->UnitMass_in_cgs);
+      message("(Snapshot) Unit system: U_L =      %e cm.",
+              snapshot_units->UnitLength_in_cgs);
+      message("(Snapshot) Unit system: U_t =      %e s.",
+              snapshot_units->UnitTime_in_cgs);
+      message("(Snapshot) Unit system: U_I =      %e A.",
+              snapshot_units->UnitCurrent_in_cgs);
+      message("(Snapshot) Unit system: U_T =      %e K.",
+              snapshot_units->UnitTemperature_in_cgs);
+      message("to:");
+      message("(internal) Unit system: U_M = %e g.",
+              internal_units->UnitMass_in_cgs);
+      message("(internal) Unit system: U_L = %e cm.",
+              internal_units->UnitLength_in_cgs);
+      message("(internal) Unit system: U_t = %e s.",
+              internal_units->UnitTime_in_cgs);
+      message("(internal) Unit system: U_I = %e A.",
+              internal_units->UnitCurrent_in_cgs);
+      message("(internal) Unit system: U_T = %e K.",
+              internal_units->UnitTemperature_in_cgs);
+    }
+  }
+
+  /* Loop over all particle types */
+  for (int ptype = 0; ptype < swift_type_count; ptype++) {
+
+    /* Don't do anything if no particle of this kind */
+    if (numParticles[ptype] == 0) continue;
+
+    /* Open the particle group in the file */
+    char partTypeGroupName[PARTICLE_GROUP_BUFFER_SIZE];
+    snprintf(partTypeGroupName, PARTICLE_GROUP_BUFFER_SIZE, "/PartType%d",
+             ptype);
+    h_grp = H5Gcreate(h_file, partTypeGroupName, H5P_DEFAULT, H5P_DEFAULT,
+                      H5P_DEFAULT);
+    if (h_grp < 0) {
+      error("Error while creating particle group.\n");
+    }
+
+    int num_fields = 0;
+    struct io_props list[100];
+    size_t N = 0;
+
+    /* Write particle fields from the particle structure */
+    switch (ptype) {
+
+      case swift_type_gas:
+        N = Ngas;
+        hydro_write_index(parts, xparts, list, &num_fields);
+        break;
+
+      case swift_type_dark_matter:
+        error("TODO");
+        break;
+
+      case swift_type_stars:
+        N = Nstars;
+        error("TODO");
+        // star_write_index(sparts, list, &num_fields);
+        break;
+
+      default:
+        error("Particle Type %d not yet supported. Aborting", ptype);
+    }
+
+    /* Write everything */
+    for (int i = 0; i < num_fields; ++i)
+      writeArray(e, h_grp, fileName, NULL, partTypeGroupName, list[i], N,
+                 internal_units, snapshot_units);
+
+    /* Free temporary array */
+    if (dmparts) {
+      free(dmparts);
+      dmparts = NULL;
+    }
+
+    /* Close particle group */
+    H5Gclose(h_grp);
+  }
+
+  /* message("Done writing particles..."); */
+
+  /* Close file */
+  H5Fclose(h_file);
+
+  ++outputCount;
+}
+
+#endif /* HAVE_HDF5 */
diff --git a/src/logger_io.h b/src/logger_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..f5b1274fb7b957d5b48bc8425bf784c586ac6a08
--- /dev/null
+++ b/src/logger_io.h
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2012 Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_LOGGER_IO_H
+#define SWIFT_LOGGER_IO_H
+
+/* Config parameters. */
+#include "../config.h"
+
+#ifdef WITH_LOGGER
+
+/* Includes. */
+#include "engine.h"
+#include "io_properties.h"
+#include "part.h"
+#include "units.h"
+
+void write_index_single(struct engine* e, const char* baseName,
+                        const struct unit_system* internal_units,
+                        const struct unit_system* snapshot_units);
+
+/**
+ * @brief Specifies which particle fields to write to a dataset
+ *
+ * @param parts The particle array.
+ * @param list The list of i/o properties to write.
+ * @param num_fields The number of i/o fields to write.
+ *
+ * In this version, we only want the ids and the offset.
+ */
+__attribute__((always_inline)) INLINE static void hydro_write_index(
+    const struct part* parts, const struct xpart* xparts, struct io_props* list,
+    int* num_fields) {
+
+  *num_fields = 2;
+
+  /* List what we want to write */
+  list[0] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
+                                 UNIT_CONV_NO_UNITS, parts, id);
+
+  list[1] = io_make_output_field("Offset", ULONGLONG, 1, UNIT_CONV_NO_UNITS,
+                                 xparts, logger_data.last_offset);
+}
+#endif
+
+#endif /* SWIFT_LOGGER_IO_H */
diff --git a/src/map.c b/src/map.c
index b0a3117388cb77f4311cf7ee3c5d62a0937da655..68c3618fcdb10a618a97e5d1a2565d58db677cdb 100644
--- a/src/map.c
+++ b/src/map.c
@@ -73,9 +73,9 @@ void map_cells_plot(struct cell *c, void *data) {
     printf("%.16e %.16e %.16e\n\n\n", l[0] + h[0], l[1] + h[1], l[2]);
 
     if (!c->split) {
-      for (int k = 0; k < c->count; k++)
-        printf("0 0 0 %.16e %.16e %.16e\n", c->parts[k].x[0], c->parts[k].x[1],
-               c->parts[k].x[2]);
+      for (int k = 0; k < c->hydro.count; k++)
+        printf("0 0 0 %.16e %.16e %.16e\n", c->hydro.parts[k].x[0],
+               c->hydro.parts[k].x[1], c->hydro.parts[k].x[2]);
       printf("\n\n");
     }
     /* else
@@ -102,11 +102,11 @@ void map_check(struct part *p, struct cell *c, void *data) {
 void map_cellcheck(struct cell *c, void *data) {
 
   int *count = (int *)data;
-  atomic_add(count, c->count);
+  atomic_add(count, c->hydro.count);
 
   /* Loop over all parts and check if they are in the cell. */
-  for (int k = 0; k < c->count; k++) {
-    struct part *p = &c->parts[k];
+  for (int k = 0; k < c->hydro.count; k++) {
+    struct part *p = &c->hydro.parts[k];
     if (p->x[0] < c->loc[0] || p->x[1] < c->loc[1] || p->x[2] < c->loc[2] ||
         p->x[0] > c->loc[0] + c->width[0] ||
         p->x[1] > c->loc[1] + c->width[1] ||
@@ -122,8 +122,8 @@ void map_cellcheck(struct cell *c, void *data) {
   }
 
   /* Loop over all gparts and check if they are in the cell. */
-  for (int k = 0; k < c->gcount; k++) {
-    struct gpart *p = &c->gparts[k];
+  for (int k = 0; k < c->grav.count; k++) {
+    struct gpart *p = &c->grav.parts[k];
     if (p->x[0] < c->loc[0] || p->x[1] < c->loc[1] || p->x[2] < c->loc[2] ||
         p->x[0] > c->loc[0] + c->width[0] ||
         p->x[1] > c->loc[1] + c->width[1] ||
@@ -191,6 +191,13 @@ void map_h_max(struct part *p, struct cell *c, void *data) {
   if (p->h > (*p2)->h) *p2 = p;
 }
 
+void map_stars_h_max(struct spart *p, struct cell *c, void *data) {
+
+  struct spart **p2 = (struct spart **)data;
+
+  if (p->h > (*p2)->h) *p2 = p;
+}
+
 /**
  * @brief Mapping function for neighbour count.
  */
diff --git a/src/map.h b/src/map.h
index 950a5fd96ebdc7177b41912b1565163f33de8701..6ad05e30df0644e1ee37b1b912bc11681ccf837c 100644
--- a/src/map.h
+++ b/src/map.h
@@ -34,6 +34,7 @@ void map_wcount_min(struct part *p, struct cell *c, void *data);
 void map_wcount_max(struct part *p, struct cell *c, void *data);
 void map_h_min(struct part *p, struct cell *c, void *data);
 void map_h_max(struct part *p, struct cell *c, void *data);
+void map_stars_h_max(struct spart *p, struct cell *c, void *data);
 void map_icount(struct part *p, struct cell *c, void *data);
 void map_dump(struct part *p, struct cell *c, void *data);
 
diff --git a/src/mesh_gravity.c b/src/mesh_gravity.c
index 2f2f1628e7077ba301322c7af6b858e979b313ad..e7005b083c94e20f5218923e443f71464ab383e1 100644
--- a/src/mesh_gravity.c
+++ b/src/mesh_gravity.c
@@ -185,8 +185,8 @@ INLINE static void gpart_to_mesh_CIC(const struct gpart* gp, double* rho, int N,
  */
 void cell_gpart_to_mesh_CIC(const struct cell* c, double* rho, int N,
                             double fac, const double dim[3]) {
-  const int gcount = c->gcount;
-  const struct gpart* gparts = c->gparts;
+  const int gcount = c->grav.count;
+  const struct gpart* gparts = c->grav.parts;
 
   /* Assign all the gpart of that cell to the mesh */
   for (int i = 0; i < gcount; ++i)
diff --git a/src/multipole.c b/src/multipole.c
index bd5c6d6546fa0546108dcd53d7fe4060293c37a7..a77e6fce297802fb4118b7ac3d4c6a9bf4ecfd22 100644
--- a/src/multipole.c
+++ b/src/multipole.c
@@ -20,3 +20,70 @@
 
 /* Config parameters. */
 #include "../config.h"
+
+/* This object's header. */
+#include "multipole.h"
+
+/* MPI headers. */
+#ifdef WITH_MPI
+#include <mpi.h>
+#endif
+
+#ifdef WITH_MPI
+
+/* MPI data type for the multipole transfer and reduction */
+MPI_Datatype multipole_mpi_type;
+MPI_Op multipole_mpi_reduce_op;
+
+/**
+ * @brief Apply a bit-by-bit XOR operattion on #gravity_tensors (i.e. does
+ * a^=b).
+ *
+ * @param a The #gravity_tensors to add to.
+ * @param b The #gravity_tensors to add.
+ */
+void gravity_binary_xor(struct gravity_tensors *a,
+                        const struct gravity_tensors *b) {
+
+  char *aa = (char *)a;
+  const char *bb = (const char *)b;
+
+  for (size_t i = 0; i < sizeof(struct gravity_tensors); ++i) {
+    aa[i] ^= bb[i];
+  }
+}
+
+/**
+ * @brief MPI reduction function for the #gravity_tensors.
+ *
+ * @param invec Array of #gravity_tensors to read.
+ * @param inoutvec Array of #gravity_tensors to read and do the reduction into.
+ * @param len The length of the array.
+ * @param datatype The MPI type this function acts upon (unused).
+ */
+void gravity_tensors_mpi_reduce(void *invec, void *inoutvec, int *len,
+                                MPI_Datatype *datatype) {
+
+  for (int i = 0; i < *len; ++i) {
+    gravity_binary_xor(&((struct gravity_tensors *)inoutvec)[i],
+                       &((const struct gravity_tensors *)invec)[i]);
+  }
+}
+
+void multipole_create_mpi_types(void) {
+
+  /* Create the datatype for multipoles */
+  /* We just consider each structure to be a byte field disregarding their */
+  /* detailed content */
+  if (MPI_Type_contiguous(
+          sizeof(struct gravity_tensors) / sizeof(unsigned char), MPI_BYTE,
+          &multipole_mpi_type) != MPI_SUCCESS ||
+      MPI_Type_commit(&multipole_mpi_type) != MPI_SUCCESS) {
+    error("Failed to create MPI type for multipole.");
+  }
+
+  /* And the reduction operator */
+  MPI_Op_create(gravity_tensors_mpi_reduce, 1, &multipole_mpi_reduce_op);
+}
+
+#endif
diff --git a/src/multipole.h b/src/multipole.h
index 41839b932716c5631bfe5354190c40eb6ab96a3f..8139dc0548bb94b108d6e32da4b19808998f48d3 100644
--- a/src/multipole.h
+++ b/src/multipole.h
@@ -207,6 +207,13 @@ struct gravity_tensors {
   };
 } SWIFT_STRUCT_ALIGN;
 
+#ifdef WITH_MPI
+/* MPI datatypes for transfers */
+extern MPI_Datatype multipole_mpi_type;
+extern MPI_Op multipole_mpi_reduce_op;
+void multipole_create_mpi_types(void);
+#endif
+
 /**
  * @brief Reset the data of a #multipole.
  *
@@ -1028,6 +1035,11 @@ INLINE static void gravity_P2M(struct gravity_tensors *multi,
   for (int k = 0; k < gcount; k++) {
     const double m = gparts[k].mass;
 
+#ifdef SWIFT_DEBUG_CHECKS
+    if (gparts[k].time_bin == time_bin_inhibited)
+      error("Inhibited particle in P2M. Should have been removed earlier.");
+#endif
+
     mass += m;
     com[0] += gparts[k].x[0] * m;
     com[1] += gparts[k].x[1] * m;
diff --git a/src/outputlist.c b/src/outputlist.c
index fd33370ca45f25c17ecd2cc8df622138842507f3..2ab904d4fd0b7008b324f3c37a5cab6c6b337520 100644
--- a/src/outputlist.c
+++ b/src/outputlist.c
@@ -112,6 +112,9 @@ void output_list_read_file(struct output_list *outputlist, const char *filename,
     ind += 1;
   }
 
+  /* Cleanup */
+  free(line);
+
   if (ind != outputlist->size)
     error("Did not read the correct number of output times.");
 
@@ -266,8 +269,12 @@ void output_list_print(const struct output_list *outputlist) {
 /**
  * @brief Clean an #output_list
  */
-void output_list_clean(struct output_list *outputlist) {
-  free(outputlist->times);
+void output_list_clean(struct output_list **outputlist) {
+  if (*outputlist) {
+    free((*outputlist)->times);
+    free(*outputlist);
+    *outputlist = NULL;
+  }
 }
 
 /**
diff --git a/src/outputlist.h b/src/outputlist.h
index 6045d75ea29f0aab44252835147502f3df0de20c..b7b12ca32f469c70f716553b30a15f48198f8e5e 100644
--- a/src/outputlist.h
+++ b/src/outputlist.h
@@ -58,7 +58,7 @@ void output_list_read_next_time(struct output_list *t, const struct engine *e,
 void output_list_init(struct output_list **list, const struct engine *e,
                       const char *name, double *delta_time, double *time_first);
 void output_list_print(const struct output_list *outputlist);
-void output_list_clean(struct output_list *outputlist);
+void output_list_clean(struct output_list **outputlist);
 void output_list_struct_dump(struct output_list *list, FILE *stream);
 void output_list_struct_restore(struct output_list *list, FILE *stream);
 
diff --git a/src/parallel_io.c b/src/parallel_io.c
index fa1b58d4dc5f2dd8d330c42df95e3e67de8ea2b1..510b637c67ffa2a6e9cb8edd6c4871fc1960dc95 100644
--- a/src/parallel_io.c
+++ b/src/parallel_io.c
@@ -138,7 +138,37 @@ void readArray_chunk(hid_t h_data, hid_t h_plist_id,
       for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= factor;
     } else {
       float* temp_f = (float*)temp;
-      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= factor;
+
+#ifdef SWIFT_DEBUG_CHECKS
+      float maximum = 0.;
+      float minimum = FLT_MAX;
+#endif
+
+      /* Loop that converts the Units */
+      for (size_t i = 0; i < num_elements; ++i) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+        /* Find the absolute minimum and maximum values */
+        const float abstemp_f = fabsf(temp_f[i]);
+        if (abstemp_f != 0.f) {
+          maximum = max(maximum, abstemp_f);
+          minimum = min(minimum, abstemp_f);
+        }
+#endif
+
+        /* Convert the float units */
+        temp_f[i] *= factor;
+      }
+
+#ifdef SWIFT_DEBUG_CHECKS
+      /* The two possible errors: larger than float or smaller
+       * than float precission. */
+      if (factor * maximum > FLT_MAX) {
+        error("Unit conversion results in numbers larger than floats");
+      } else if (factor * minimum < FLT_MIN) {
+        error("Numbers smaller than float precision");
+      }
+#endif
     }
   }
 
@@ -401,8 +431,9 @@ void prepareArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile,
   io_write_attribute_s(h_data, "Conversion factor", buffer);
 
   /* Add a line to the XMF */
-  xmf_write_line(xmfFile, fileName, partTypeGroupName, props.name, N_total,
-                 props.dimension, props.type);
+  if (xmfFile != NULL)
+    xmf_write_line(xmfFile, fileName, partTypeGroupName, props.name, N_total,
+                   props.dimension, props.type);
 
   /* Close everything */
   H5Pclose(h_plist_id);
@@ -618,7 +649,6 @@ void writeArray(struct engine* e, hid_t grp, char* fileName,
  * @param Ngas (output) The number of particles read from the file.
  * @param Ngparts (output) The number of particles read from the file.
  * @param Nstars (output) The number of particles read from the file.
- * @param periodic (output) 1 if the volume is periodic, 0 if not.
  * @param flag_entropy (output) 1 if the ICs contained Entropy in the
  * InternalEnergy field
  * @param with_hydro Are we running with hydro ?
@@ -640,11 +670,11 @@ void writeArray(struct engine* e, hid_t grp, char* fileName,
 void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
                       double dim[3], struct part** parts, struct gpart** gparts,
                       struct spart** sparts, size_t* Ngas, size_t* Ngparts,
-                      size_t* Nstars, int* periodic, int* flag_entropy,
-                      int with_hydro, int with_gravity, int with_stars,
-                      int cleanup_h, int cleanup_sqrt_a, double h, double a,
-                      int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
-                      int n_threads, int dry_run) {
+                      size_t* Nstars, int* flag_entropy, int with_hydro,
+                      int with_gravity, int with_stars, int cleanup_h,
+                      int cleanup_sqrt_a, double h, double a, int mpi_rank,
+                      int mpi_size, MPI_Comm comm, MPI_Info info, int n_threads,
+                      int dry_run) {
 
   hid_t h_file = 0, h_grp = 0;
   /* GADGET has only cubic boxes (in cosmological mode) */
@@ -664,17 +694,6 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
   h_file = H5Fopen(fileName, H5F_ACC_RDONLY, h_plist_id);
   if (h_file < 0) error("Error while opening file '%s'.", fileName);
 
-  /* Open header to read simulation properties */
-  /* message("Reading runtime parameters..."); */
-  h_grp = H5Gopen(h_file, "/RuntimePars", H5P_DEFAULT);
-  if (h_grp < 0) error("Error while opening runtime parameters\n");
-
-  /* Read the relevant information */
-  io_read_attribute(h_grp, "PeriodicBoundariesOn", INT, periodic);
-
-  /* Close runtime parameters */
-  H5Gclose(h_grp);
-
   /* Open header to read simulation properties */
   /* message("Reading file header..."); */
   h_grp = H5Gopen(h_file, "/Header", H5P_DEFAULT);
@@ -1020,7 +1039,7 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
   h_grp = H5Gcreate(h_file, "/SubgridScheme", H5P_DEFAULT, H5P_DEFAULT,
                     H5P_DEFAULT);
   if (h_grp < 0) error("Error while creating subgrid group");
-  cooling_write_flavour(h_grp);
+  cooling_write_flavour(h_grp, e->cooling_func);
   chemistry_write_flavour(h_grp);
   H5Gclose(h_grp);
 
@@ -1171,22 +1190,30 @@ void write_output_parallel(struct engine* e, const char* baseName,
                            int mpi_rank, int mpi_size, MPI_Comm comm,
                            MPI_Info info) {
 
-  const size_t Ngas = e->s->nr_parts;
-  const size_t Nstars = e->s->nr_sparts;
-  const size_t Ntot = e->s->nr_gparts;
   const struct part* parts = e->s->parts;
   const struct xpart* xparts = e->s->xparts;
   const struct gpart* gparts = e->s->gparts;
-  struct gpart* dmparts = NULL;
   const struct spart* sparts = e->s->sparts;
-  const struct cooling_function_data* cooling = e->cooling_func;
   struct swift_params* params = e->parameter_file;
 
-  /* Number of unassociated gparts */
-  const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
+  /* Number of particles currently in the arrays */
+  const size_t Ntot = e->s->nr_gparts;
+  const size_t Ngas = e->s->nr_parts;
+  const size_t Nstars = e->s->nr_sparts;
+  // const size_t Nbaryons = Ngas + Nstars;
+  // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0;
+
+  /* Number of particles that we will write */
+  const size_t Ntot_written = e->s->nr_gparts - e->s->nr_inhibited_sparts;
+  const size_t Ngas_written = e->s->nr_parts - e->s->nr_inhibited_parts;
+  const size_t Nstars_written = e->s->nr_sparts - e->s->nr_inhibited_gparts;
+  const size_t Nbaryons_written = Ngas_written + Nstars_written;
+  const size_t Ndm_written =
+      Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0;
 
   /* Compute offset in the file and total number of particles */
-  size_t N[swift_type_count] = {Ngas, Ndm, 0, 0, Nstars, 0};
+  size_t N[swift_type_count] = {
+      Ngas_written, Ndm_written, 0, 0, Nstars_written, 0};
   long long N_total[swift_type_count] = {0};
   long long offset[swift_type_count] = {0};
   MPI_Exscan(&N, &offset, swift_type_count, MPI_LONG_LONG_INT, MPI_SUM, comm);
@@ -1260,8 +1287,8 @@ void write_output_parallel(struct engine* e, const char* baseName,
 #if H5_VERSION_GE(1, 10, 0)
   h_err = H5Pset_all_coll_metadata_ops(plist_id, 1);
   if (h_err < 0) error("Error setting collective meta-data on all ops");
-  h_err = H5Pset_coll_metadata_write(plist_id, 1);
-  if (h_err < 0) error("Error setting collective meta-data writes");
+    // h_err = H5Pset_coll_metadata_write(plist_id, 1);
+    // if (h_err < 0) error("Error setting collective meta-data writes");
 #endif
 
 #ifdef IO_SPEED_MEASUREMENT
@@ -1337,38 +1364,98 @@ void write_output_parallel(struct engine* e, const char* baseName,
     struct io_props list[100];
     size_t Nparticles = 0;
 
+    struct part* parts_written = NULL;
+    struct xpart* xparts_written = NULL;
+    struct gpart* gparts_written = NULL;
+    struct spart* sparts_written = NULL;
+
     /* Write particle fields from the particle structure */
     switch (ptype) {
 
-      case swift_type_gas:
-        Nparticles = Ngas;
-        hydro_write_particles(parts, xparts, list, &num_fields);
-        num_fields += chemistry_write_particles(parts, list + num_fields);
-        num_fields +=
-            cooling_write_particles(xparts, list + num_fields, cooling);
-        break;
+      case swift_type_gas: {
+        if (Ngas == Ngas_written) {
+
+          /* No inhibted particles: easy case */
+          Nparticles = Ngas;
+          hydro_write_particles(parts, xparts, list, &num_fields);
+          num_fields += chemistry_write_particles(parts, list + num_fields);
+          num_fields += cooling_write_particles(xparts, list + num_fields,
+                                                e->cooling_func);
+        } else {
+
+          /* Ok, we need to fish out the particles we want */
+          Nparticles = Ngas_written;
+
+          /* Allocate temporary arrays */
+          if (posix_memalign((void**)&parts_written, part_align,
+                             Ngas_written * sizeof(struct part)) != 0)
+            error("Error while allocating temporart memory for parts");
+          if (posix_memalign((void**)&xparts_written, xpart_align,
+                             Ngas_written * sizeof(struct xpart)) != 0)
+            error("Error while allocating temporart memory for xparts");
+
+          /* Collect the particles we want to write */
+          io_collect_parts_to_write(parts, xparts, parts_written,
+                                    xparts_written, Ngas, Ngas_written);
+
+          /* Select the fields to write */
+          hydro_write_particles(parts_written, xparts_written, list,
+                                &num_fields);
+          num_fields +=
+              chemistry_write_particles(parts_written, list + num_fields);
+          num_fields += cooling_write_particles(
+              xparts_written, list + num_fields, e->cooling_func);
+        }
+      } break;
 
-      case swift_type_dark_matter:
-        /* Allocate temporary array */
-        if (posix_memalign((void**)&dmparts, gpart_align,
-                           Ndm * sizeof(struct gpart)) != 0)
-          error(
-              "Error while allocating temporart memory for "
-              "DM particles");
-        bzero(dmparts, Ndm * sizeof(struct gpart));
-
-        /* Collect the DM particles from gpart */
-        io_collect_dm_gparts(gparts, Ntot, dmparts, Ndm);
-
-        /* Write DM particles */
-        Nparticles = Ndm;
-        darkmatter_write_particles(dmparts, list, &num_fields);
-        break;
+      case swift_type_dark_matter: {
+        if (Ntot == Ndm_written) {
 
-      case swift_type_stars:
-        Nparticles = Nstars;
-        stars_write_particles(sparts, list, &num_fields);
-        break;
+          /* This is a DM-only run without inhibited particles */
+          Nparticles = Ntot;
+          darkmatter_write_particles(gparts, list, &num_fields);
+        } else {
+
+          /* Ok, we need to fish out the particles we want */
+          Nparticles = Ndm_written;
+
+          /* Allocate temporary array */
+          if (posix_memalign((void**)&gparts_written, gpart_align,
+                             Ndm_written * sizeof(struct gpart)) != 0)
+            error("Error while allocating temporart memory for gparts");
+
+          /* Collect the non-inhibited DM particles from gpart */
+          io_collect_gparts_to_write(gparts, gparts_written, Ntot, Ndm_written);
+
+          /* Write DM particles */
+          darkmatter_write_particles(gparts_written, list, &num_fields);
+        }
+      } break;
+
+      case swift_type_stars: {
+        if (Nstars == Nstars_written) {
+
+          /* No inhibted particles: easy case */
+          Nparticles = Nstars;
+          stars_write_particles(sparts, list, &num_fields);
+        } else {
+
+          /* Ok, we need to fish out the particles we want */
+          Nparticles = Nstars_written;
+
+          /* Allocate temporary arrays */
+          if (posix_memalign((void**)&sparts_written, spart_align,
+                             Nstars_written * sizeof(struct spart)) != 0)
+            error("Error while allocating temporart memory for sparts");
+
+          /* Collect the particles we want to write */
+          io_collect_sparts_to_write(sparts, sparts_written, Nstars,
+                                     Nstars_written);
+
+          /* Select the fields to write */
+          stars_write_particles(sparts, list, &num_fields);
+        }
+      } break;
 
       default:
         error("Particle Type %d not yet supported. Aborting", ptype);
@@ -1390,10 +1477,10 @@ void write_output_parallel(struct engine* e, const char* baseName,
     }
 
     /* Free temporary array */
-    if (dmparts) {
-      free(dmparts);
-      dmparts = 0;
-    }
+    if (parts_written) free(parts_written);
+    if (xparts_written) free(xparts_written);
+    if (gparts_written) free(gparts_written);
+    if (sparts_written) free(sparts_written);
 
 #ifdef IO_SPEED_MEASUREMENT
     MPI_Barrier(MPI_COMM_WORLD);
diff --git a/src/parallel_io.h b/src/parallel_io.h
index 668b6f83443fe4c39ddf3269c8d2236e72588e32..9cd775347f0d5fbb3bc1b17664e0d5dba734d795 100644
--- a/src/parallel_io.h
+++ b/src/parallel_io.h
@@ -25,22 +25,21 @@
 #if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5)
 
 /* MPI headers. */
-#ifdef WITH_MPI
 #include <mpi.h>
-#endif
 
 /* Includes. */
 #include "engine.h"
+#include "io_properties.h"
 #include "part.h"
 #include "units.h"
 
 void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
                       double dim[3], struct part** parts, struct gpart** gparts,
                       struct spart** sparts, size_t* Ngas, size_t* Ngparts,
-                      size_t* Nsparts, int* periodic, int* flag_entropy,
-                      int with_hydro, int with_gravity, int with_stars,
-                      int cleanup_h, int cleanup_sqrt_a, double h, double a,
-                      int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
+                      size_t* Nsparts, int* flag_entropy, int with_hydro,
+                      int with_gravity, int with_stars, int cleanup_h,
+                      int cleanup_sqrt_a, double h, double a, int mpi_rank,
+                      int mpi_size, MPI_Comm comm, MPI_Info info,
                       int nr_threads, int dry_run);
 
 void write_output_parallel(struct engine* e, const char* baseName,
@@ -48,6 +47,13 @@ void write_output_parallel(struct engine* e, const char* baseName,
                            const struct unit_system* snapshot_units,
                            int mpi_rank, int mpi_size, MPI_Comm comm,
                            MPI_Info info);
+
+void writeArray(struct engine* e, hid_t grp, char* fileName,
+                char* partTypeGroupName, struct io_props props, size_t N,
+                long long N_total, int mpi_rank, long long offset,
+                const struct unit_system* internal_units,
+                const struct unit_system* snapshot_units);
+
 #endif
 
 #endif /* SWIFT_PARALLEL_IO_H */
diff --git a/src/parser.c b/src/parser.c
index f3e5ef00f96f0f7b55daff0ff32077e9373c4a2f..57592d57abb78100d113b91710af68f7b1c3e32d 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -35,6 +35,7 @@
 #include "error.h"
 #include "restart.h"
 #include "tools.h"
+#include "version.h"
 
 #define PARSER_COMMENT_STRING "#"
 #define PARSER_COMMENT_CHAR '#'
@@ -1158,7 +1159,13 @@ void parser_write_params_to_file(const struct swift_params *params,
   char *token;
 
   /* Start of file identifier in YAML. */
-  fprintf(file, "%s\n", PARSER_START_OF_FILE);
+  fprintf(file, "%s\n\n", PARSER_START_OF_FILE);
+
+  fprintf(file, "# SWIFT used parameter file\n");
+  fprintf(file, "# Code version: %s\n", package_version());
+  fprintf(file, "# git revision: %s\n", git_revision());
+  fprintf(file, "# git branch: %s\n", git_branch());
+  fprintf(file, "# git date: %s\n", git_date());
 
   /* Flags to track which parameters are written. */
   int *written = (int *)calloc(params->paramCount, sizeof(int));
diff --git a/src/part.c b/src/part.c
index db221dbd4bf9ff2b829b02fbae673aa1c65f339e..3a626e652cf28f0376cadc1d9a40ab85b752e6c1 100644
--- a/src/part.c
+++ b/src/part.c
@@ -26,8 +26,10 @@
 #endif
 
 /* This object's header. */
-#include "error.h"
 #include "multipole.h"
+
+/* Local headers */
+#include "error.h"
 #include "part.h"
 
 /**
@@ -133,6 +135,8 @@ void part_verify_links(struct part *parts, struct gpart *gparts,
                        struct spart *sparts, size_t nr_parts, size_t nr_gparts,
                        size_t nr_sparts, int verbose) {
 
+  ticks tic = getticks();
+
   for (size_t k = 0; k < nr_gparts; ++k) {
 
     /* We have a DM particle */
@@ -246,6 +250,9 @@ void part_verify_links(struct part *parts, struct gpart *gparts,
   }
 
   if (verbose) message("All links OK");
+  if (verbose)
+    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
+            clocks_getunit());
 }
 
 #ifdef WITH_MPI
@@ -254,7 +261,6 @@ MPI_Datatype part_mpi_type;
 MPI_Datatype xpart_mpi_type;
 MPI_Datatype gpart_mpi_type;
 MPI_Datatype spart_mpi_type;
-MPI_Datatype multipole_mpi_type;
 
 /**
  * @brief Registers MPI particle types.
@@ -287,11 +293,5 @@ void part_create_mpi_types(void) {
       MPI_Type_commit(&spart_mpi_type) != MPI_SUCCESS) {
     error("Failed to create MPI type for sparts.");
   }
-  if (MPI_Type_contiguous(
-          sizeof(struct gravity_tensors) / sizeof(unsigned char), MPI_BYTE,
-          &multipole_mpi_type) != MPI_SUCCESS ||
-      MPI_Type_commit(&multipole_mpi_type) != MPI_SUCCESS) {
-    error("Failed to create MPI type for multipole.");
-  }
 }
 #endif
diff --git a/src/part.h b/src/part.h
index 1608467bf73693e88cb7d300bea6125d7e542e52..41c0a8867ec513a507adcf5ae97268175c4b4b69 100644
--- a/src/part.h
+++ b/src/part.h
@@ -109,7 +109,6 @@ extern MPI_Datatype part_mpi_type;
 extern MPI_Datatype xpart_mpi_type;
 extern MPI_Datatype gpart_mpi_type;
 extern MPI_Datatype spart_mpi_type;
-extern MPI_Datatype multipole_mpi_type;
 
 void part_create_mpi_types(void);
 #endif
diff --git a/src/partition.c b/src/partition.c
index 6a88731d518ddd702d4f3b116b5108ffd8d61c3c..d3b7f7d285904c38c6424656ee27f3fbd0e1f1a2 100644
--- a/src/partition.c
+++ b/src/partition.c
@@ -1197,8 +1197,8 @@ static void repart_edge_metis(int vweights, int eweights, int timebins,
                * we cut for that. Note that weight is added to the local and
                * remote cells, as we want to keep both away from any cuts, this
                * can overflow int, so take care. */
-              int dti = num_time_bins - get_time_bin(ci->ti_hydro_end_min);
-              int dtj = num_time_bins - get_time_bin(cj->ti_hydro_end_min);
+              int dti = num_time_bins - get_time_bin(ci->hydro.ti_end_min);
+              int dtj = num_time_bins - get_time_bin(cj->hydro.ti_end_min);
               double dt = (double)(1 << dti) + (double)(1 << dtj);
               weights_e[ik] += dt;
               weights_e[jk] += dt;
diff --git a/src/physical_constants_cgs.h b/src/physical_constants_cgs.h
index 59e36e288e70dabecbdccd86891853b1daa0c5bd..fecd1f894af5df5db54b37883ba07e470b956bdf 100644
--- a/src/physical_constants_cgs.h
+++ b/src/physical_constants_cgs.h
@@ -95,7 +95,7 @@ const double const_earth_mass_cgs = 5.9724e27;
 /*! Temperature of the CMB at present day [K] */
 const double const_T_CMB_0_cgs = 2.7255;
 
-/*! Primordial Helium fraction */
+/*! Primordial Helium fraction [-] */
 const double const_primordial_He_fraction_cgs = 0.245;
 
 #endif /* SWIFT_PHYSICAL_CONSTANTS_CGS_H */
diff --git a/src/potential.h b/src/potential.h
index 814b83c69180631db21e392704c0279808a6f03e..59567fe92296068f838c39a3eb5ff55c14005d48 100644
--- a/src/potential.h
+++ b/src/potential.h
@@ -34,6 +34,10 @@
 #include "./potential/point_mass/potential.h"
 #elif defined(EXTERNAL_POTENTIAL_ISOTHERMAL)
 #include "./potential/isothermal/potential.h"
+#elif defined(EXTERNAL_POTENTIAL_HERNQUIST)
+#include "./potential/hernquist/potential.h"
+#elif defined(EXTERNAL_POTENTIAL_NFW)
+#include "./potential/nfw/potential.h"
 #elif defined(EXTERNAL_POTENTIAL_DISC_PATCH)
 #include "./potential/disc_patch/potential.h"
 #elif defined(EXTERNAL_POTENTIAL_SINE_WAVE)
diff --git a/src/potential/hernquist/potential.h b/src/potential/hernquist/potential.h
new file mode 100644
index 0000000000000000000000000000000000000000..d0ec339d91376d25e0e5a2106826d07deca7115b
--- /dev/null
+++ b/src/potential/hernquist/potential.h
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_POTENTIAL_HERNQUIST_H
+#define SWIFT_POTENTIAL_HERNQUIST_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local includes. */
+#include "error.h"
+#include "parser.h"
+#include "part.h"
+#include "physical_constants.h"
+#include "space.h"
+#include "units.h"
+
+/**
+ * @brief External Potential Properties - Hernquist potential
+ */
+struct external_potential {
+
+  /*! Position of the centre of potential */
+  double x[3];
+
+  /*! Mass of the halo */
+  double mass;
+
+  /*! Scale length (often as a, to prevent confusion with the cosmological
+   * scale-factor we use al) */
+  double al;
+
+  /*! Square of the softening length. Acceleration tends to zero within this
+   * distance from the origin */
+  double epsilon2;
+
+  /* Minimum timestep of the potential given by the timestep multiple
+   * times the orbital time at the softening length */
+  double mintime;
+
+  /*! Time-step condition pre-factor, is multiplied times the circular orbital
+   * time to get the time steps */
+  double timestep_mult;
+};
+
+/**
+ * @brief Computes the time-step in a Hernquist potential based on a
+ *        fraction of the circular orbital time
+ *
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static float external_gravity_timestep(
+    double time, const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const,
+    const struct gpart* restrict g) {
+
+  const float G_newton = phys_const->const_newton_G;
+
+  /* Calculate the relative potential with respect to the centre of the
+   * potential */
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  /* calculate the radius  */
+  const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2);
+  const float sqrtgm_inv = 1.f / sqrtf(G_newton * potential->mass);
+
+  /* Calculate the circular orbital period */
+  const float period = 2.f * M_PI * sqrtf(r) * potential->al *
+                       (1 + r / potential->al) * sqrtgm_inv;
+
+  /* Time-step as a fraction of the cirecular orbital time */
+  const float time_step = potential->timestep_mult * period;
+
+  return max(time_step, potential->mintime);
+}
+
+/**
+ * @brief Computes the gravitational acceleration from an Hernquist potential.
+ *
+ * Note that the accelerations are multiplied by Newton's G constant
+ * later on.
+ *
+ * a_x = - GM / (a+r)^2 * x/r
+ * a_y = - GM / (a+r)^2 * y/r
+ * a_z = - GM / (a+r)^2 * z/r
+ *
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static void external_gravity_acceleration(
+    double time, const struct external_potential* potential,
+    const struct phys_const* const phys_const, struct gpart* g) {
+
+  /* Determine the position relative to the centre of the potential */
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  /* Calculate the acceleration */
+  const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2);
+  const float r_plus_a_inv = 1.f / (r + potential->al);
+  const float r_plus_a_inv2 = r_plus_a_inv * r_plus_a_inv;
+  const float term = -potential->mass * r_plus_a_inv2 / r;
+
+  g->a_grav[0] += term * dx;
+  g->a_grav[1] += term * dy;
+  g->a_grav[2] += term * dz;
+}
+
+/**
+ * @brief Computes the gravitational potential energy of a particle in an
+ * Hernquist potential.
+ *
+ * phi = - GM/(r+a)
+ *
+ * @param time The current time (unused here).
+ * @param potential The #external_potential used in the run.
+ * @param phys_const Physical constants in internal units.
+ * @param g Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+external_gravity_get_potential_energy(
+    double time, const struct external_potential* potential,
+    const struct phys_const* const phys_const, const struct gpart* g) {
+
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+  const float r = sqrtf(dx * dx + dy * dy + dz * dz);
+  const float r_plus_alinv = 1.f / (r + potential->al);
+  return -phys_const->const_newton_G * potential->mass * r_plus_alinv;
+}
+
+/**
+ * @brief Initialises the external potential properties in the internal system
+ * of units.
+ *
+ * @param parameter_file The parsed parameter file
+ * @param phys_const Physical constants in internal units
+ * @param us The current internal system of units
+ * @param potential The external potential properties to initialize
+ */
+static INLINE void potential_init_backend(
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
+
+  /* Read in the position of the centre of potential */
+  parser_get_param_double_array(parameter_file, "HernquistPotential:position",
+                                3, potential->x);
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "HernquistPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
+
+  /* Read the other parameters of the model */
+  potential->mass =
+      parser_get_param_double(parameter_file, "HernquistPotential:mass");
+  potential->al =
+      parser_get_param_double(parameter_file, "HernquistPotential:scalelength");
+  potential->timestep_mult = parser_get_param_float(
+      parameter_file, "HernquistPotential:timestep_mult");
+  const float epsilon =
+      parser_get_param_double(parameter_file, "HernquistPotential:epsilon");
+  potential->epsilon2 = epsilon * epsilon;
+
+  /* Compute the minimal time-step. */
+  /* This is the circular orbital time at the softened radius */
+  const float sqrtgm = sqrtf(phys_const->const_newton_G * potential->mass);
+  potential->mintime = 2.f * sqrtf(epsilon) * potential->al * M_PI *
+                       (1 + epsilon / potential->al) / sqrtgm *
+                       potential->timestep_mult;
+}
+
+/**
+ * @brief prints the properties of the external potential to stdout.
+ *
+ * @param  potential the external potential properties.
+ */
+static inline void potential_print_backend(
+    const struct external_potential* potential) {
+
+  message(
+      "external potential is 'hernquist' with properties are (x,y,z) = (%e, "
+      "%e, %e), mass = %e "
+      "scale length = %e , minimum time = %e "
+      "timestep multiplier = %e",
+      potential->x[0], potential->x[1], potential->x[2], potential->mass,
+      potential->al, potential->mintime, potential->timestep_mult);
+}
+
+#endif /* SWIFT_POTENTIAL_HERNQUIST_H */
diff --git a/src/potential/isothermal/potential.h b/src/potential/isothermal/potential.h
index b5f8d7c39738bfe1895c73e6e59ae1279c0f74fa..160372210e41036f2737c10a4aa3d2ddac1077f2 100644
--- a/src/potential/isothermal/potential.h
+++ b/src/potential/isothermal/potential.h
@@ -148,7 +148,7 @@ external_gravity_get_potential_energy(
   const float dy = g->x[1] - potential->x[1];
   const float dz = g->x[2] - potential->x[2];
 
-  return -0.5f * potential->vrot * potential->vrot *
+  return 0.5f * potential->vrot * potential->vrot *
          logf(dx * dx + dy * dy + dz * dz + potential->epsilon2);
 }
 
@@ -166,11 +166,19 @@ static INLINE void potential_init_backend(
     const struct unit_system* us, const struct space* s,
     struct external_potential* potential) {
 
+  /* Read in the position of the centre of potential */
   parser_get_param_double_array(parameter_file, "IsothermalPotential:position",
                                 3, potential->x);
-  potential->x[0] += s->dim[0] / 2.;
-  potential->x[1] += s->dim[1] / 2.;
-  potential->x[2] += s->dim[2] / 2.;
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "IsothermalPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
 
   potential->vrot =
       parser_get_param_double(parameter_file, "IsothermalPotential:vrot");
diff --git a/src/potential/nfw/potential.h b/src/potential/nfw/potential.h
new file mode 100644
index 0000000000000000000000000000000000000000..28bafd439a36a41f2feecdc7169f8628fbed47f4
--- /dev/null
+++ b/src/potential/nfw/potential.h
@@ -0,0 +1,260 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018   Ashley Kelly ()
+ *                      Folkert Nobels (nobels@strw.leidenuniv.nl)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_POTENTIAL_NFW_H
+#define SWIFT_POTENTIAL_NFW_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <float.h>
+#include <math.h>
+
+/* Local includes. */
+#include "error.h"
+#include "parser.h"
+#include "part.h"
+#include "physical_constants.h"
+#include "space.h"
+#include "units.h"
+
+/**
+ * @brief External Potential Properties - NFW Potential
+                rho(r) = rho_0 / ( (r/R_s)*(1+r/R_s)^2 )
+
+        We however parameterise this in terms of c and virial_mass
+ */
+struct external_potential {
+
+  /*! Position of the centre of potential */
+  double x[3];
+
+  /*! The scale radius of the NFW potential */
+  double r_s;
+
+  /*! The pre-factor \f$ 4 \pi G \rho_0 \r_s^3 \f$ */
+  double pre_factor;
+
+  /*! The critical density of the universe */
+  double rho_c;
+
+  /*! The concentration parameter */
+  double c_200;
+
+  /*! The virial mass */
+  double M_200;
+
+  /*! Time-step condition pre_factor, this factor is used to multiply times the
+   * orbital time, so in the case of 0.01 we take 1% of the orbital time as
+   * the time integration steps */
+  double timestep_mult;
+
+  /*! Minimum time step based on the orbital time at the softening times
+   * the timestep_mult */
+  double mintime;
+
+  /*! Common log term \f$ \ln(1+c_{200}) - \frac{c_{200}}{1 + c_{200}} \f$ */
+  double log_c200_term;
+
+  /*! Softening length */
+  double eps;
+};
+
+/**
+ * @brief Computes the time-step due to the acceleration from the NFW potential
+ *        as a fraction (timestep_mult) of the circular orbital time of that
+ *        particle.
+ *
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static float external_gravity_timestep(
+    double time, const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const,
+    const struct gpart* restrict g) {
+
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  const float r =
+      sqrtf(dx * dx + dy * dy + dz * dz + potential->eps * potential->eps);
+
+  const float mr = potential->M_200 *
+                   (logf(1.f + r / potential->r_s) - r / (r + potential->r_s)) /
+                   potential->log_c200_term;
+
+  const float period =
+      2 * M_PI * r * sqrtf(r / (phys_const->const_newton_G * mr));
+
+  /* Time-step as a fraction of the circular period */
+  const float time_step = potential->timestep_mult * period;
+
+  return max(time_step, potential->mintime);
+}
+
+/**
+ * @brief Computes the gravitational acceleration from an NFW Halo potential.
+ *
+ * Note that the accelerations are multiplied by Newton's G constant
+ * later on.
+ *
+ * a_x = 4 pi \rho_0 r_s^3 ( 1/((r+rs)*r^2) - log(1+r/rs)/r^3) * x
+ * a_y = 4 pi \rho_0 r_s^3 ( 1/((r+rs)*r^2) - log(1+r/rs)/r^3) * y
+ * a_z = 4 pi \rho_0 r_s^3 ( 1/((r+rs)*r^2) - log(1+r/rs)/r^3) * z
+ *
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static void external_gravity_acceleration(
+    double time, const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const, struct gpart* restrict g) {
+
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  const float r =
+      sqrtf(dx * dx + dy * dy + dz * dz + potential->eps * potential->eps);
+  const float term1 = potential->pre_factor;
+  const float term2 = (1.0f / ((r + potential->r_s) * r * r) -
+                       logf(1.0f + r / potential->r_s) / (r * r * r));
+
+  g->a_grav[0] += term1 * term2 * dx;
+  g->a_grav[1] += term1 * term2 * dy;
+  g->a_grav[2] += term1 * term2 * dz;
+}
+
+/**
+ * @brief Computes the gravitational potential energy of a particle in an
+ * NFW potential.
+ *
+ * phi = -4 * pi * G * rho_0 * r_s^3 * ln(1+r/r_s)
+ *
+ * @param time The current time (unused here).
+ * @param potential The #external_potential used in the run.
+ * @param phys_const Physical constants in internal units.
+ * @param g Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+external_gravity_get_potential_energy(
+    double time, const struct external_potential* potential,
+    const struct phys_const* const phys_const, const struct gpart* g) {
+
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  const float r =
+      sqrtf(dx * dx + dy * dy + dz * dz + potential->eps * potential->eps);
+  const float term1 = -potential->pre_factor / r;
+  const float term2 = logf(1.0f + r / potential->r_s);
+
+  return term1 * term2;
+}
+
+/**
+ * @brief Initialises the external potential properties in the internal system
+ * of units.
+ *
+ * @param parameter_file The parsed parameter file
+ * @param phys_const Physical constants in internal units
+ * @param us The current internal system of units
+ * @param potential The external potential properties to initialize
+ */
+static INLINE void potential_init_backend(
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
+
+  /* Read in the position of the centre of potential */
+  parser_get_param_double_array(parameter_file, "NFWPotential:position", 3,
+                                potential->x);
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "NFWPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
+
+  /* Read the other parameters of the model */
+  potential->timestep_mult =
+      parser_get_param_double(parameter_file, "NFWPotential:timestep_mult");
+  potential->c_200 =
+      parser_get_param_double(parameter_file, "NFWPotential:concentration");
+  potential->M_200 =
+      parser_get_param_double(parameter_file, "NFWPotential:M_200");
+  potential->rho_c =
+      parser_get_param_double(parameter_file, "NFWPotential:critical_density");
+  potential->eps = 0.05;
+
+  /* Compute R_200 */
+  const double R_200 =
+      cbrtf(3.0 * potential->M_200 / (4. * M_PI * 200.0 * potential->rho_c));
+
+  /* NFW scale-radius */
+  potential->r_s = R_200 / potential->c_200;
+  const double r_s3 = potential->r_s * potential->r_s * potential->r_s;
+
+  /* Log(c_200) term appearing in many expressions */
+  potential->log_c200_term =
+      log(1. + potential->c_200) - potential->c_200 / (1. + potential->c_200);
+
+  const double rho_0 =
+      potential->M_200 / (4.f * M_PI * r_s3 * potential->log_c200_term);
+
+  /* Pre-factor for the accelerations (note G is multiplied in later on) */
+  potential->pre_factor = 4.0f * M_PI * rho_0 * r_s3;
+
+  /* Compute the orbital time at the softening radius */
+  const double sqrtgm = sqrt(phys_const->const_newton_G * potential->M_200);
+  const double epslnthing = log(1.f + potential->eps / potential->r_s) -
+                            potential->eps / (potential->eps + potential->r_s);
+
+  potential->mintime = 2. * M_PI * potential->eps * sqrtf(potential->eps) *
+                       sqrtf(potential->log_c200_term / epslnthing) / sqrtgm *
+                       potential->timestep_mult;
+}
+
+/**
+ * @brief Prints the properties of the external potential to stdout.
+ *
+ * @param  potential The external potential properties.
+ */
+static INLINE void potential_print_backend(
+    const struct external_potential* potential) {
+
+  message(
+      "External potential is 'NFW' with properties are (x,y,z) = (%e, "
+      "%e, %e), scale radius = %e "
+      "timestep multiplier = %e, mintime = %e",
+      potential->x[0], potential->x[1], potential->x[2], potential->r_s,
+      potential->timestep_mult, potential->mintime);
+}
+
+#endif /* SWIFT_POTENTIAL_NFW_H */
diff --git a/src/potential/point_mass/potential.h b/src/potential/point_mass/potential.h
index f9d56a1ff165f2331c91ea828b5ffe0e0db76c2f..5ae03f8637708d75800a6a7fb283b98bdb42cec2 100644
--- a/src/potential/point_mass/potential.h
+++ b/src/potential/point_mass/potential.h
@@ -137,7 +137,7 @@ external_gravity_get_potential_energy(
   const float dx = g->x[0] - potential->x[0];
   const float dy = g->x[1] - potential->x[1];
   const float dz = g->x[2] - potential->x[2];
-  const float rinv = 1. / sqrtf(dx * dx + dy * dy + dz * dz);
+  const float rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz);
   return -phys_const->const_newton_G * potential->mass * rinv;
 }
 
@@ -156,8 +156,21 @@ static INLINE void potential_init_backend(
     const struct unit_system* us, const struct space* s,
     struct external_potential* potential) {
 
+  /* Read in the position of the centre of potential */
   parser_get_param_double_array(parameter_file, "PointMassPotential:position",
                                 3, potential->x);
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "PointMassPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
+
+  /* Read the other parameters of the model */
   potential->mass =
       parser_get_param_double(parameter_file, "PointMassPotential:mass");
   potential->timestep_mult = parser_get_param_float(
diff --git a/src/potential/point_mass_softened/potential.h b/src/potential/point_mass_softened/potential.h
index 0e35e7bb9870c7954b47316a3cc30bb68cde5fc4..050bc1a00c98da4c350e59cf1ef8ef855094e552 100644
--- a/src/potential/point_mass_softened/potential.h
+++ b/src/potential/point_mass_softened/potential.h
@@ -183,8 +183,21 @@ static INLINE void potential_init_backend(
     const struct unit_system* us, const struct space* s,
     struct external_potential* potential) {
 
+  /* Read in the position of the centre of potential */
   parser_get_param_double_array(parameter_file, "PointMassPotential:position",
                                 3, potential->x);
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "PointMassPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
+
+  /* Read the other parameters of the model */
   potential->mass =
       parser_get_param_double(parameter_file, "PointMassPotential:mass");
   potential->timestep_mult = parser_get_param_float(
diff --git a/src/proxy.c b/src/proxy.c
index 80b33a776c1b2833e711f3bbd3be32474cf5cf73..325ed78644b07a497374e40bfc8518edcb018593 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -71,8 +71,8 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies,
   int offset_out[s->nr_cells];
   for (int k = 0; k < s->nr_cells; k++) {
     offset_out[k] = count_out;
-    if (s->cells_top[k].sendto) {
-      count_out += s->cells_top[k].pcell_size;
+    if (s->cells_top[k].mpi.sendto) {
+      count_out += s->cells_top[k].mpi.pcell_size;
     }
   }
 
@@ -82,7 +82,7 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies,
   for (int k = 0; k < num_proxies; k++) {
     for (int j = 0; j < proxies[k].nr_cells_in; j++) {
       offset_in[proxies[k].cells_in[j] - s->cells_top] = count_in;
-      count_in += proxies[k].cells_in[j]->pcell_size;
+      count_in += proxies[k].cells_in[j]->mpi.pcell_size;
     }
   }
 
@@ -97,7 +97,7 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies,
 
   /* Pack the local tags. */
   for (int k = 0; k < s->nr_cells; k++) {
-    if (s->cells_top[k].sendto) {
+    if (s->cells_top[k].mpi.sendto) {
       cell_pack_tags(&s->cells_top[k], &tags_out[offset_out[k]]);
     }
   }
@@ -129,8 +129,8 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies,
       const int cid = proxies[k].cells_in[j] - s->cells_top;
       cids_in[recv_rid] = cid;
       int err = MPI_Irecv(
-          &tags_in[offset_in[cid]], proxies[k].cells_in[j]->pcell_size, MPI_INT,
-          proxies[k].nodeID, cid, MPI_COMM_WORLD, &reqs_in[recv_rid]);
+          &tags_in[offset_in[cid]], proxies[k].cells_in[j]->mpi.pcell_size,
+          MPI_INT, proxies[k].nodeID, cid, MPI_COMM_WORLD, &reqs_in[recv_rid]);
       if (err != MPI_SUCCESS) mpi_error(err, "Failed to irecv tags.");
       recv_rid += 1;
     }
@@ -138,7 +138,7 @@ void proxy_tags_exchange(struct proxy *proxies, int num_proxies,
       const int cid = proxies[k].cells_out[j] - s->cells_top;
       cids_out[send_rid] = cid;
       int err = MPI_Isend(
-          &tags_out[offset_out[cid]], proxies[k].cells_out[j]->pcell_size,
+          &tags_out[offset_out[cid]], proxies[k].cells_out[j]->mpi.pcell_size,
           MPI_INT, proxies[k].nodeID, cid, MPI_COMM_WORLD, &reqs_out[send_rid]);
       if (err != MPI_SUCCESS) mpi_error(err, "Failed to isend tags.");
       send_rid += 1;
@@ -193,7 +193,7 @@ void proxy_cells_exchange_first(struct proxy *p) {
   /* Get the number of pcells we will need to send. */
   p->size_pcells_out = 0;
   for (int k = 0; k < p->nr_cells_out; k++)
-    p->size_pcells_out += p->cells_out[k]->pcell_size;
+    p->size_pcells_out += p->cells_out[k]->mpi.pcell_size;
 
   /* Send the number of pcells. */
   int err = MPI_Isend(&p->size_pcells_out, 1, MPI_INT, p->nodeID,
@@ -209,9 +209,9 @@ void proxy_cells_exchange_first(struct proxy *p) {
                      sizeof(struct pcell) * p->size_pcells_out) != 0)
     error("Failed to allocate pcell_out buffer.");
   for (int ind = 0, k = 0; k < p->nr_cells_out; k++) {
-    memcpy(&p->pcells_out[ind], p->cells_out[k]->pcell,
-           sizeof(struct pcell) * p->cells_out[k]->pcell_size);
-    ind += p->cells_out[k]->pcell_size;
+    memcpy(&p->pcells_out[ind], p->cells_out[k]->mpi.pcell,
+           sizeof(struct pcell) * p->cells_out[k]->mpi.pcell_size);
+    ind += p->cells_out[k]->mpi.pcell_size;
   }
 
   /* Send the pcell buffer. */
@@ -298,9 +298,9 @@ void proxy_cells_exchange(struct proxy *proxies, int num_proxies,
   int offset[s->nr_cells];
   for (int k = 0; k < s->nr_cells; k++) {
     offset[k] = count_out;
-    if (s->cells_top[k].sendto)
+    if (s->cells_top[k].mpi.sendto)
       count_out +=
-          (s->cells_top[k].pcell_size = cell_getsize(&s->cells_top[k]));
+          (s->cells_top[k].mpi.pcell_size = cell_getsize(&s->cells_top[k]));
   }
 
   if (s->e->verbose)
@@ -317,9 +317,9 @@ void proxy_cells_exchange(struct proxy *proxies, int num_proxies,
 
   /* Pack the cells. */
   for (int k = 0; k < s->nr_cells; k++)
-    if (s->cells_top[k].sendto) {
+    if (s->cells_top[k].mpi.sendto) {
       cell_pack(&s->cells_top[k], &pcells[offset[k]], with_gravity);
-      s->cells_top[k].pcell = &pcells[offset[k]];
+      s->cells_top[k].mpi.pcell = &pcells[offset[k]];
     }
 
   if (s->e->verbose)
diff --git a/src/restart.c b/src/restart.c
index c412c8477d9f93e7c085e13c9e3fe72cd0cab9df..54a098413d7a393ac88a7ef5d7300d912c99f845 100644
--- a/src/restart.c
+++ b/src/restart.c
@@ -334,3 +334,17 @@ void restart_remove_previous(const char *filename) {
     }
   }
 }
+
+/**
+ * @brief Run a given command, usually to resubmit a job.
+ *
+ * No check is done on the command being run.
+ *
+ * @param command The command to run in the system's shell.
+ */
+void restart_resubmit(const char *command) {
+
+  /* Let's trust the user's command... */
+  const int result = system(command);
+  if (result != 0) message("Command returned error code %d", result);
+}
diff --git a/src/restart.h b/src/restart.h
index 49d127492255364cbf0f48653c560494e83a2920..b9380201659dacf05fcedad8c9fcb29e7bd89be2 100644
--- a/src/restart.h
+++ b/src/restart.h
@@ -41,4 +41,6 @@ int restart_stop_now(const char *dir, int cleanup);
 void restart_save_previous(const char *filename);
 void restart_remove_previous(const char *filename);
 
+void restart_resubmit(const char *command);
+
 #endif /* SWIFT_RESTART_H */
diff --git a/src/runner.c b/src/runner.c
index f88a91e790fac997bcc09d7323a50bc13a63e4bf..cf5492dad4702d3090393809e02c526b45c293eb 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -53,6 +53,7 @@
 #include "hydro.h"
 #include "hydro_properties.h"
 #include "kick.h"
+#include "logger.h"
 #include "minmax.h"
 #include "runner_doiact_vec.h"
 #include "scheduler.h"
@@ -107,7 +108,7 @@
  * @param timer 1 if the time is to be recorded.
  */
 void runner_do_sourceterms(struct runner *r, struct cell *c, int timer) {
-  const int count = c->count;
+  const int count = c->hydro.count;
   const double cell_min[3] = {c->loc[0], c->loc[1], c->loc[2]};
   const double cell_width[3] = {c->width[0], c->width[1], c->width[2]};
   struct sourceterms *sourceterms = r->e->sourceterms;
@@ -145,7 +146,7 @@ void runner_do_sourceterms(struct runner *r, struct cell *c, int timer) {
  */
 void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
 
-  struct spart *restrict sparts = c->sparts;
+  struct spart *restrict sparts = c->stars.parts;
   const struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
   const struct stars_props *stars_properties = e->stars_properties;
@@ -168,9 +169,9 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
 
     /* Init the list of active particles that have to be updated. */
     int *sid = NULL;
-    if ((sid = (int *)malloc(sizeof(int) * c->scount)) == NULL)
+    if ((sid = (int *)malloc(sizeof(int) * c->stars.count)) == NULL)
       error("Can't allocate memory for sid.");
-    for (int k = 0; k < c->scount; k++)
+    for (int k = 0; k < c->stars.count; k++)
       if (spart_is_active(&sparts[k], e)) {
         sid[scount] = k;
         ++scount;
@@ -283,7 +284,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
         for (struct cell *finger = c; finger != NULL; finger = finger->parent) {
 
           /* Run through this cell's density interactions. */
-          for (struct link *l = finger->density; l != NULL; l = l->next) {
+          for (struct link *l = finger->stars.density; l != NULL; l = l->next) {
 
 #ifdef SWIFT_DEBUG_CHECKS
             if (l->t->ti_run < r->e->ti_current)
@@ -348,8 +349,8 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
  */
 void runner_do_grav_external(struct runner *r, struct cell *c, int timer) {
 
-  struct gpart *restrict gparts = c->gparts;
-  const int gcount = c->gcount;
+  struct gpart *restrict gparts = c->grav.parts;
+  const int gcount = c->grav.count;
   const struct engine *e = r->e;
   const struct external_potential *potential = e->external_potential;
   const struct phys_const *constants = e->physical_constants;
@@ -391,8 +392,8 @@ void runner_do_grav_external(struct runner *r, struct cell *c, int timer) {
  */
 void runner_do_grav_mesh(struct runner *r, struct cell *c, int timer) {
 
-  struct gpart *restrict gparts = c->gparts;
-  const int gcount = c->gcount;
+  struct gpart *restrict gparts = c->grav.parts;
+  const int gcount = c->grav.count;
   const struct engine *e = r->e;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -433,11 +434,12 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) {
   const struct cooling_function_data *cooling_func = e->cooling_func;
   const struct phys_const *constants = e->physical_constants;
   const struct unit_system *us = e->internal_units;
+  const struct hydro_props *hydro_props = e->hydro_properties;
   const double time_base = e->time_base;
   const integertime_t ti_current = e->ti_current;
-  struct part *restrict parts = c->parts;
-  struct xpart *restrict xparts = c->xparts;
-  const int count = c->count;
+  struct part *restrict parts = c->hydro.parts;
+  struct xpart *restrict xparts = c->hydro.xparts;
+  const int count = c->hydro.count;
 
   TIMER_TIC;
 
@@ -459,19 +461,25 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) {
 
       if (part_is_active(p, e)) {
 
-        double dt_cool;
+        double dt_cool, dt_therm;
         if (with_cosmology) {
           const integertime_t ti_step = get_integer_timestep(p->time_bin);
           const integertime_t ti_begin =
-              get_integer_time_begin(ti_current + 1, p->time_bin);
+              get_integer_time_begin(ti_current - 1, p->time_bin);
+
           dt_cool =
               cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step);
+          dt_therm = cosmology_get_therm_kick_factor(e->cosmology, ti_begin,
+                                                     ti_begin + ti_step);
+
         } else {
           dt_cool = get_timestep(p->time_bin, time_base);
+          dt_therm = get_timestep(p->time_bin, time_base);
         }
 
         /* Let's cool ! */
-        cooling_cool_part(constants, us, cosmo, cooling_func, p, xp, dt_cool);
+        cooling_cool_part(constants, us, cosmo, hydro_props, cooling_func, p,
+                          xp, dt_cool, dt_therm);
       }
     }
   }
@@ -479,6 +487,52 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) {
   if (timer) TIMER_TOC(timer_do_cooling);
 }
 
+/**
+ *
+ */
+void runner_do_star_formation(struct runner *r, struct cell *c, int timer) {
+
+  const struct engine *e = r->e;
+  const struct cosmology *cosmo = e->cosmology;
+  const int count = c->hydro.count;
+  struct part *restrict parts = c->hydro.parts;
+  struct xpart *restrict xparts = c->hydro.xparts;
+
+  TIMER_TIC;
+
+  /* Anything to do here? */
+  if (!cell_is_active_hydro(c, e)) return;
+
+  /* Recurse? */
+  if (c->split) {
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL) runner_do_star_formation(r, c->progeny[k], 0);
+  } else {
+
+    /* Loop over the gas particles in this cell. */
+    for (int k = 0; k < count; k++) {
+
+      /* Get a handle on the part. */
+      struct part *restrict p = &parts[k];
+      struct xpart *restrict xp = &xparts[k];
+
+      if (part_is_active(p, e)) {
+
+        const float rho = hydro_get_physical_density(p, cosmo);
+
+        // MATTHIEU: Temporary star-formation law
+        // Do not use this at home.
+        if (rho > 1.5e7 && e->step > 2) {
+          message("Removing particle id=%lld rho=%e", p->id, rho);
+          cell_convert_part_to_gpart(e, c, p, xp);
+        }
+      }
+    }
+  }
+
+  if (timer) TIMER_TOC(timer_do_star_formation);
+}
+
 /**
  * @brief Sort the entries in ascending order using QuickSort.
  *
@@ -568,11 +622,11 @@ void runner_do_sort_ascending(struct entry *sort, int N) {
 void runner_check_sorts(struct cell *c, int flags) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (flags & ~c->sorted) error("Inconsistent sort flags (downward)!");
+  if (flags & ~c->hydro.sorted) error("Inconsistent sort flags (downward)!");
   if (c->split)
     for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL && c->progeny[k]->count > 0)
-        runner_check_sorts(c->progeny[k], c->sorted);
+      if (c->progeny[k] != NULL && c->progeny[k]->hydro.count > 0)
+        runner_check_sorts(c->progeny[k], c->hydro.sorted);
 #else
   error("Calling debugging code without debugging flag activated.");
 #endif
@@ -593,21 +647,21 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
                     int clock) {
 
   struct entry *fingers[8];
-  const int count = c->count;
-  const struct part *parts = c->parts;
-  struct xpart *xparts = c->xparts;
+  const int count = c->hydro.count;
+  const struct part *parts = c->hydro.parts;
+  struct xpart *xparts = c->hydro.xparts;
   float buff[8];
 
   TIMER_TIC;
 
   /* We need to do the local sorts plus whatever was requested further up. */
-  flags |= c->do_sort;
+  flags |= c->hydro.do_sort;
   if (cleanup) {
-    c->sorted = 0;
+    c->hydro.sorted = 0;
   } else {
-    flags &= ~c->sorted;
+    flags &= ~c->hydro.sorted;
   }
-  if (flags == 0 && !c->do_sub_sort) return;
+  if (flags == 0 && !c->hydro.do_sub_sort) return;
 
   /* Check that the particles have been moved to the current time */
   if (flags && !cell_are_part_drifted(c, r->e))
@@ -615,24 +669,25 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Make sure the sort flags are consistent (downward). */
-  runner_check_sorts(c, c->sorted);
+  runner_check_sorts(c, c->hydro.sorted);
 
   /* Make sure the sort flags are consistent (upard). */
   for (struct cell *finger = c->parent; finger != NULL;
        finger = finger->parent) {
-    if (finger->sorted & ~c->sorted) error("Inconsistent sort flags (upward).");
+    if (finger->hydro.sorted & ~c->hydro.sorted)
+      error("Inconsistent sort flags (upward).");
   }
 
   /* Update the sort timer which represents the last time the sorts
      were re-set. */
-  if (c->sorted == 0) c->ti_sort = r->e->ti_current;
+  if (c->hydro.sorted == 0) c->hydro.ti_sort = r->e->ti_current;
 #endif
 
   /* start by allocating the entry arrays in the requested dimensions. */
   for (int j = 0; j < 13; j++) {
-    if ((flags & (1 << j)) && c->sort[j] == NULL) {
-      if ((c->sort[j] = (struct entry *)malloc(sizeof(struct entry) *
-                                               (count + 1))) == NULL)
+    if ((flags & (1 << j)) && c->hydro.sort[j] == NULL) {
+      if ((c->hydro.sort[j] = (struct entry *)malloc(sizeof(struct entry) *
+                                                     (count + 1))) == NULL)
         error("Failed to allocate sort memory.");
     }
   }
@@ -644,18 +699,19 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
     float dx_max_sort = 0.0f;
     float dx_max_sort_old = 0.0f;
     for (int k = 0; k < 8; k++) {
-      if (c->progeny[k] != NULL && c->progeny[k]->count > 0) {
+      if (c->progeny[k] != NULL && c->progeny[k]->hydro.count > 0) {
         /* Only propagate cleanup if the progeny is stale. */
         runner_do_sort(r, c->progeny[k], flags,
-                       cleanup && (c->progeny[k]->dx_max_sort >
+                       cleanup && (c->progeny[k]->hydro.dx_max_sort >
                                    space_maxreldx * c->progeny[k]->dmin),
                        0);
-        dx_max_sort = max(dx_max_sort, c->progeny[k]->dx_max_sort);
-        dx_max_sort_old = max(dx_max_sort_old, c->progeny[k]->dx_max_sort_old);
+        dx_max_sort = max(dx_max_sort, c->progeny[k]->hydro.dx_max_sort);
+        dx_max_sort_old =
+            max(dx_max_sort_old, c->progeny[k]->hydro.dx_max_sort_old);
       }
     }
-    c->dx_max_sort = dx_max_sort;
-    c->dx_max_sort_old = dx_max_sort_old;
+    c->hydro.dx_max_sort = dx_max_sort;
+    c->hydro.dx_max_sort_old = dx_max_sort_old;
 
     /* Loop over the 13 different sort arrays. */
     for (int j = 0; j < 13; j++) {
@@ -668,7 +724,7 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
       off[0] = 0;
       for (int k = 1; k < 8; k++)
         if (c->progeny[k - 1] != NULL)
-          off[k] = off[k - 1] + c->progeny[k - 1]->count;
+          off[k] = off[k - 1] + c->progeny[k - 1]->hydro.count;
         else
           off[k] = off[k - 1];
 
@@ -676,8 +732,8 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
       int inds[8];
       for (int k = 0; k < 8; k++) {
         inds[k] = k;
-        if (c->progeny[k] != NULL && c->progeny[k]->count > 0) {
-          fingers[k] = c->progeny[k]->sort[j];
+        if (c->progeny[k] != NULL && c->progeny[k]->hydro.count > 0) {
+          fingers[k] = c->progeny[k]->hydro.sort[j];
           buff[k] = fingers[k]->d;
           off[k] = off[k];
         } else
@@ -694,7 +750,7 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
           }
 
       /* For each entry in the new sort list. */
-      struct entry *finger = c->sort[j];
+      struct entry *finger = c->hydro.sort[j];
       for (int ind = 0; ind < count; ind++) {
 
         /* Copy the minimum into the new sort array. */
@@ -715,11 +771,11 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
       } /* Merge. */
 
       /* Add a sentinel. */
-      c->sort[j][count].d = FLT_MAX;
-      c->sort[j][count].i = 0;
+      c->hydro.sort[j][count].d = FLT_MAX;
+      c->hydro.sort[j][count].i = 0;
 
       /* Mark as sorted. */
-      atomic_or(&c->sorted, 1 << j);
+      atomic_or(&c->hydro.sorted, 1 << j);
 
     } /* loop over sort arrays. */
 
@@ -729,7 +785,7 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
   else {
 
     /* Reset the sort distance */
-    if (c->sorted == 0) {
+    if (c->hydro.sorted == 0) {
 #ifdef SWIFT_DEBUG_CHECKS
       if (xparts != NULL && c->nodeID != engine_rank)
         error("Have non-NULL xparts in foreign cell");
@@ -743,8 +799,8 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
           xparts[k].x_diff_sort[2] = 0.0f;
         }
       }
-      c->dx_max_sort_old = 0.f;
-      c->dx_max_sort = 0.f;
+      c->hydro.dx_max_sort_old = 0.f;
+      c->hydro.dx_max_sort = 0.f;
     }
 
     /* Fill the sort array. */
@@ -752,20 +808,20 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
       const double px[3] = {parts[k].x[0], parts[k].x[1], parts[k].x[2]};
       for (int j = 0; j < 13; j++)
         if (flags & (1 << j)) {
-          c->sort[j][k].i = k;
-          c->sort[j][k].d = px[0] * runner_shift[j][0] +
-                            px[1] * runner_shift[j][1] +
-                            px[2] * runner_shift[j][2];
+          c->hydro.sort[j][k].i = k;
+          c->hydro.sort[j][k].d = px[0] * runner_shift[j][0] +
+                                  px[1] * runner_shift[j][1] +
+                                  px[2] * runner_shift[j][2];
         }
     }
 
     /* Add the sentinel and sort. */
     for (int j = 0; j < 13; j++)
       if (flags & (1 << j)) {
-        c->sort[j][count].d = FLT_MAX;
-        c->sort[j][count].i = 0;
-        runner_do_sort_ascending(c->sort[j], count);
-        atomic_or(&c->sorted, 1 << j);
+        c->hydro.sort[j][count].d = FLT_MAX;
+        c->hydro.sort[j][count].i = 0;
+        runner_do_sort_ascending(c->hydro.sort[j], count);
+        atomic_or(&c->hydro.sorted, 1 << j);
       }
   }
 
@@ -773,7 +829,7 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
   /* Verify the sorting. */
   for (int j = 0; j < 13; j++) {
     if (!(flags & (1 << j))) continue;
-    struct entry *finger = c->sort[j];
+    struct entry *finger = c->hydro.sort[j];
     for (int k = 1; k < count; k++) {
       if (finger[k].d < finger[k - 1].d)
         error("Sorting failed, ascending array.");
@@ -787,14 +843,15 @@ void runner_do_sort(struct runner *r, struct cell *c, int flags, int cleanup,
   /* Make sure the sort flags are consistent (upward). */
   for (struct cell *finger = c->parent; finger != NULL;
        finger = finger->parent) {
-    if (finger->sorted & ~c->sorted) error("Inconsistent sort flags.");
+    if (finger->hydro.sorted & ~c->hydro.sorted)
+      error("Inconsistent sort flags.");
   }
 #endif
 
   /* Clear the cell's sort flags. */
-  c->do_sort = 0;
-  c->do_sub_sort = 0;
-  c->requires_sorts = 0;
+  c->hydro.do_sort = 0;
+  c->hydro.do_sub_sort = 0;
+  c->hydro.requires_sorts = 0;
 
   if (clock) TIMER_TOC(timer_dosort);
 }
@@ -821,7 +878,7 @@ void runner_do_init_grav(struct runner *r, struct cell *c, int timer) {
   if (!cell_is_active_gravity(c, e)) return;
 
   /* Reset the gravity acceleration tensors */
-  gravity_field_tensors_init(&c->multipole->pot, e->ti_current);
+  gravity_field_tensors_init(&c->grav.multipole->pot, e->ti_current);
 
   /* Recurse? */
   if (c->split) {
@@ -845,14 +902,15 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) {
 
 #ifdef EXTRA_HYDRO_LOOP
 
-  struct part *restrict parts = c->parts;
-  struct xpart *restrict xparts = c->xparts;
-  const int count = c->count;
+  struct part *restrict parts = c->hydro.parts;
+  struct xpart *restrict xparts = c->hydro.xparts;
+  const int count = c->hydro.count;
   const struct engine *e = r->e;
   const integertime_t ti_end = e->ti_current;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const double time_base = e->time_base;
   const struct cosmology *cosmo = e->cosmology;
+  const struct hydro_props *hydro_props = e->hydro_properties;
 
   TIMER_TIC;
 
@@ -891,7 +949,7 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) {
         }
 
         /* Compute variables required for the force loop */
-        hydro_prepare_force(p, xp, cosmo, dt_alpha);
+        hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha);
 
         /* The particle force values are now set.  Do _NOT_
            try to read any particle density variables! */
@@ -919,8 +977,8 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) {
  */
 void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
-  struct part *restrict parts = c->parts;
-  struct xpart *restrict xparts = c->xparts;
+  struct part *restrict parts = c->hydro.parts;
+  struct xpart *restrict xparts = c->hydro.xparts;
   const struct engine *e = r->e;
   const struct space *s = e->s;
   const struct hydro_space *hs = &s->hs;
@@ -946,9 +1004,9 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
     /* Init the list of active particles that have to be updated. */
     int *pid = NULL;
-    if ((pid = (int *)malloc(sizeof(int) * c->count)) == NULL)
+    if ((pid = (int *)malloc(sizeof(int) * c->hydro.count)) == NULL)
       error("Can't allocate memory for pid.");
-    for (int k = 0; k < c->count; k++)
+    for (int k = 0; k < c->hydro.count; k++)
       if (part_is_active(&parts[k], e)) {
         pid[count] = k;
         ++count;
@@ -1024,6 +1082,8 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
             hydro_reset_gradient(p);
 
 #else
+            const struct hydro_props *hydro_props = e->hydro_properties;
+
             /* Calculate the time-step for passing to hydro_prepare_force, used
              * for the evolution of alpha factors (i.e. those involved in the
              * artificial viscosity and thermal conduction terms) */
@@ -1043,7 +1103,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
             /* As of here, particle force variables will be set. */
 
             /* Compute variables required for the force loop */
-            hydro_prepare_force(p, xp, cosmo, dt_alpha);
+            hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha);
 
             /* The particle force values are now set.  Do _NOT_
                try to read any particle density variables! */
@@ -1053,6 +1113,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
 #endif /* EXTRA_HYDRO_LOOP */
 
+            /* Ok, we are done with this particle */
             continue;
           }
 
@@ -1122,6 +1183,8 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
         hydro_reset_gradient(p);
 
 #else
+        const struct hydro_props *hydro_props = e->hydro_properties;
+
         /* Calculate the time-step for passing to hydro_prepare_force, used for
          * the evolution of alpha factors (i.e. those involved in the artificial
          * viscosity and thermal conduction terms) */
@@ -1140,7 +1203,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
         /* As of here, particle force variables will be set. */
 
         /* Compute variables required for the force loop */
-        hydro_prepare_force(p, xp, cosmo, dt_alpha);
+        hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha);
 
         /* The particle force values are now set.  Do _NOT_
            try to read any particle density variables! */
@@ -1162,7 +1225,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
         for (struct cell *finger = c; finger != NULL; finger = finger->parent) {
 
           /* Run through this cell's density interactions. */
-          for (struct link *l = finger->density; l != NULL; l = l->next) {
+          for (struct link *l = finger->hydro.density; l != NULL; l = l->next) {
 
 #ifdef SWIFT_DEBUG_CHECKS
             if (l->t->ti_run < r->e->ti_current)
@@ -1226,7 +1289,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 static void runner_do_unskip_hydro(struct cell *c, struct engine *e) {
 
   /* Ignore empty cells. */
-  if (c->count == 0) return;
+  if (c->hydro.count == 0) return;
 
   /* Skip inactive cells. */
   if (!cell_is_active_hydro(c, e)) return;
@@ -1255,7 +1318,7 @@ static void runner_do_unskip_hydro(struct cell *c, struct engine *e) {
 static void runner_do_unskip_stars(struct cell *c, struct engine *e) {
 
   /* Ignore empty cells. */
-  if (c->scount == 0) return;
+  if (c->stars.count == 0) return;
 
   /* Skip inactive cells. */
   if (!cell_is_active_stars(c, e)) return;
@@ -1284,7 +1347,7 @@ static void runner_do_unskip_stars(struct cell *c, struct engine *e) {
 static void runner_do_unskip_gravity(struct cell *c, struct engine *e) {
 
   /* Ignore empty cells. */
-  if (c->gcount == 0) return;
+  if (c->grav.count == 0) return;
 
   /* Skip inactive cells. */
   if (!cell_is_active_gravity(c, e)) return;
@@ -1379,13 +1442,13 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) {
   const struct cosmology *cosmo = e->cosmology;
   const struct hydro_props *hydro_props = e->hydro_properties;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
-  struct part *restrict parts = c->parts;
-  struct xpart *restrict xparts = c->xparts;
-  struct gpart *restrict gparts = c->gparts;
-  struct spart *restrict sparts = c->sparts;
-  const int count = c->count;
-  const int gcount = c->gcount;
-  const int scount = c->scount;
+  struct part *restrict parts = c->hydro.parts;
+  struct xpart *restrict xparts = c->hydro.xparts;
+  struct gpart *restrict gparts = c->grav.parts;
+  struct spart *restrict sparts = c->stars.parts;
+  const int count = c->hydro.count;
+  const int gcount = c->grav.count;
+  const int scount = c->stars.count;
   const integertime_t ti_current = e->ti_current;
   const double time_base = e->time_base;
 
@@ -1553,13 +1616,13 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) {
   const struct cosmology *cosmo = e->cosmology;
   const struct hydro_props *hydro_props = e->hydro_properties;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
-  const int count = c->count;
-  const int gcount = c->gcount;
-  const int scount = c->scount;
-  struct part *restrict parts = c->parts;
-  struct xpart *restrict xparts = c->xparts;
-  struct gpart *restrict gparts = c->gparts;
-  struct spart *restrict sparts = c->sparts;
+  const int count = c->hydro.count;
+  const int gcount = c->grav.count;
+  const int scount = c->stars.count;
+  struct part *restrict parts = c->hydro.parts;
+  struct xpart *restrict xparts = c->hydro.xparts;
+  struct gpart *restrict gparts = c->grav.parts;
+  struct spart *restrict sparts = c->stars.parts;
   const integertime_t ti_current = e->ti_current;
   const double time_base = e->time_base;
 
@@ -1727,25 +1790,26 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
 
   const struct engine *e = r->e;
   const integertime_t ti_current = e->ti_current;
-  const int count = c->count;
-  const int gcount = c->gcount;
-  const int scount = c->scount;
-  struct part *restrict parts = c->parts;
-  struct xpart *restrict xparts = c->xparts;
-  struct gpart *restrict gparts = c->gparts;
-  struct spart *restrict sparts = c->sparts;
+  const int count = c->hydro.count;
+  const int gcount = c->grav.count;
+  const int scount = c->stars.count;
+  struct part *restrict parts = c->hydro.parts;
+  struct xpart *restrict xparts = c->hydro.xparts;
+  struct gpart *restrict gparts = c->grav.parts;
+  struct spart *restrict sparts = c->stars.parts;
 
   TIMER_TIC;
 
   /* Anything to do here? */
   if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e)) {
-    c->updated = 0;
-    c->g_updated = 0;
-    c->s_updated = 0;
+    c->hydro.updated = 0;
+    c->grav.updated = 0;
+    c->stars.updated = 0;
     return;
   }
 
   int updated = 0, g_updated = 0, s_updated = 0;
+  int inhibited = 0, g_inhibited = 0, s_inhibited = 0;
   integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0,
                 ti_hydro_beg_max = 0;
   integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0,
@@ -1806,6 +1870,9 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
 
       else { /* part is inactive */
 
+        /* Count the number of inhibited particles */
+        if (part_is_inhibited(p, e)) inhibited++;
+
         const integertime_t ti_end =
             get_integer_time_end(ti_current, p->time_bin);
 
@@ -1872,6 +1939,9 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
 
         } else { /* gpart is inactive */
 
+          /* Count the number of inhibited particles */
+          if (gpart_is_inhibited(gp, e)) g_inhibited++;
+
           const integertime_t ti_end =
               get_integer_time_end(ti_current, gp->time_bin);
 
@@ -1923,7 +1993,11 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
         /* What is the next starting point for this cell ? */
         ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max);
 
-      } else { /* stars particle is inactive */
+        /* star particle is inactive but not inhibited */
+      } else {
+
+        /* Count the number of inhibited particles */
+        if (spart_is_inhibited(sp, e)) ++s_inhibited;
 
         const integertime_t ti_end =
             get_integer_time_end(ti_current, sp->time_bin);
@@ -1950,35 +2024,41 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
         runner_do_timestep(r, cp, 0);
 
         /* And aggregate */
-        updated += cp->updated;
-        g_updated += cp->g_updated;
-        s_updated += cp->s_updated;
-        ti_hydro_end_min = min(cp->ti_hydro_end_min, ti_hydro_end_min);
-        ti_hydro_end_max = max(cp->ti_hydro_end_max, ti_hydro_end_max);
-        ti_hydro_beg_max = max(cp->ti_hydro_beg_max, ti_hydro_beg_max);
-        ti_gravity_end_min = min(cp->ti_gravity_end_min, ti_gravity_end_min);
-        ti_gravity_end_max = max(cp->ti_gravity_end_max, ti_gravity_end_max);
-        ti_gravity_beg_max = max(cp->ti_gravity_beg_max, ti_gravity_beg_max);
+        updated += cp->hydro.updated;
+        g_updated += cp->grav.updated;
+        s_updated += cp->stars.updated;
+        inhibited += cp->hydro.inhibited;
+        g_inhibited += cp->grav.inhibited;
+        s_inhibited += cp->stars.inhibited;
+        ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min);
+        ti_hydro_end_max = max(cp->hydro.ti_end_max, ti_hydro_end_max);
+        ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max);
+        ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min);
+        ti_gravity_end_max = max(cp->grav.ti_end_max, ti_gravity_end_max);
+        ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max);
       }
   }
 
   /* Store the values. */
-  c->updated = updated;
-  c->g_updated = g_updated;
-  c->s_updated = s_updated;
-  c->ti_hydro_end_min = ti_hydro_end_min;
-  c->ti_hydro_end_max = ti_hydro_end_max;
-  c->ti_hydro_beg_max = ti_hydro_beg_max;
-  c->ti_gravity_end_min = ti_gravity_end_min;
-  c->ti_gravity_end_max = ti_gravity_end_max;
-  c->ti_gravity_beg_max = ti_gravity_beg_max;
+  c->hydro.updated = updated;
+  c->grav.updated = g_updated;
+  c->stars.updated = s_updated;
+  c->hydro.inhibited = inhibited;
+  c->grav.inhibited = g_inhibited;
+  c->stars.inhibited = s_inhibited;
+  c->hydro.ti_end_min = ti_hydro_end_min;
+  c->hydro.ti_end_max = ti_hydro_end_max;
+  c->hydro.ti_beg_max = ti_hydro_beg_max;
+  c->grav.ti_end_min = ti_gravity_end_min;
+  c->grav.ti_end_max = ti_gravity_end_max;
+  c->grav.ti_beg_max = ti_gravity_beg_max;
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_hydro_end_min == e->ti_current &&
-      c->ti_hydro_end_min < max_nr_timesteps)
+  if (c->hydro.ti_end_min == e->ti_current &&
+      c->hydro.ti_end_min < max_nr_timesteps)
     error("End of next hydro step is current time!");
-  if (c->ti_gravity_end_min == e->ti_current &&
-      c->ti_gravity_end_min < max_nr_timesteps)
+  if (c->grav.ti_end_min == e->ti_current &&
+      c->grav.ti_end_min < max_nr_timesteps)
     error("End of next gravity step is current time!");
 #endif
 
@@ -1998,12 +2078,12 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
   const struct engine *e = r->e;
   const struct space *s = e->s;
   const struct cosmology *cosmo = e->cosmology;
-  const int count = c->count;
-  const int gcount = c->gcount;
-  const int scount = c->scount;
-  struct part *restrict parts = c->parts;
-  struct gpart *restrict gparts = c->gparts;
-  struct spart *restrict sparts = c->sparts;
+  const int count = c->hydro.count;
+  const int gcount = c->grav.count;
+  const int scount = c->stars.count;
+  struct part *restrict parts = c->hydro.parts;
+  struct gpart *restrict gparts = c->grav.parts;
+  struct spart *restrict sparts = c->stars.parts;
   const int periodic = s->periodic;
   const float G_newton = e->physical_constants->const_newton_G;
 
@@ -2089,7 +2169,8 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
 
           /* Check that this gpart has interacted with all the other
            * particles (via direct or multipoles) in the box */
-          if (gp->num_interacted != e->total_nr_gparts) {
+          if (gp->num_interacted !=
+              e->total_nr_gparts - e->count_inhibited_gparts) {
 
             /* Get the ID of the gpart */
             long long my_id = 0;
@@ -2106,9 +2187,9 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
                 "g-particle (id=%lld, type=%s) did not interact "
                 "gravitationally with all other gparts "
                 "gp->num_interacted=%lld, total_gparts=%lld (local "
-                "num_gparts=%zd)",
+                "num_gparts=%zd inhibited_gparts=%lld)",
                 my_id, part_type_names[gp->type], gp->num_interacted,
-                e->total_nr_gparts, e->s->nr_gparts);
+                e->total_nr_gparts, e->s->nr_gparts, e->count_inhibited_gparts);
           }
         }
 #endif
@@ -2144,8 +2225,8 @@ void runner_do_recv_part(struct runner *r, struct cell *c, int clear_sorts,
 
 #ifdef WITH_MPI
 
-  const struct part *restrict parts = c->parts;
-  const size_t nr_parts = c->count;
+  const struct part *restrict parts = c->hydro.parts;
+  const size_t nr_parts = c->hydro.count;
   const integertime_t ti_current = r->e->ti_current;
 
   TIMER_TIC;
@@ -2161,7 +2242,7 @@ void runner_do_recv_part(struct runner *r, struct cell *c, int clear_sorts,
 #endif
 
   /* Clear this cell's sorted mask. */
-  if (clear_sorts) c->sorted = 0;
+  if (clear_sorts) c->hydro.sorted = 0;
 
   /* If this cell is a leaf, collect the particle data. */
   if (!c->split) {
@@ -2182,13 +2263,13 @@ void runner_do_recv_part(struct runner *r, struct cell *c, int clear_sorts,
   /* Otherwise, recurse and collect. */
   else {
     for (int k = 0; k < 8; k++) {
-      if (c->progeny[k] != NULL && c->progeny[k]->count > 0) {
+      if (c->progeny[k] != NULL && c->progeny[k]->hydro.count > 0) {
         runner_do_recv_part(r, c->progeny[k], clear_sorts, 0);
         ti_hydro_end_min =
-            min(ti_hydro_end_min, c->progeny[k]->ti_hydro_end_min);
+            min(ti_hydro_end_min, c->progeny[k]->hydro.ti_end_min);
         ti_hydro_end_max =
-            max(ti_hydro_end_max, c->progeny[k]->ti_hydro_end_max);
-        h_max = max(h_max, c->progeny[k]->h_max);
+            max(ti_hydro_end_max, c->progeny[k]->hydro.ti_end_max);
+        h_max = max(h_max, c->progeny[k]->hydro.h_max);
       }
     }
   }
@@ -2202,10 +2283,10 @@ void runner_do_recv_part(struct runner *r, struct cell *c, int clear_sorts,
 #endif
 
   /* ... and store. */
-  // c->ti_hydro_end_min = ti_hydro_end_min;
-  // c->ti_hydro_end_max = ti_hydro_end_max;
-  c->ti_old_part = ti_current;
-  c->h_max = h_max;
+  // c->hydro.ti_end_min = ti_hydro_end_min;
+  // c->hydro.ti_end_max = ti_hydro_end_max;
+  c->hydro.ti_old_part = ti_current;
+  c->hydro.h_max = h_max;
 
   if (timer) TIMER_TOC(timer_dorecv_part);
 
@@ -2225,8 +2306,8 @@ void runner_do_recv_gpart(struct runner *r, struct cell *c, int timer) {
 
 #ifdef WITH_MPI
 
-  const struct gpart *restrict gparts = c->gparts;
-  const size_t nr_gparts = c->gcount;
+  const struct gpart *restrict gparts = c->grav.parts;
+  const size_t nr_gparts = c->grav.count;
   const integertime_t ti_current = r->e->ti_current;
 
   TIMER_TIC;
@@ -2258,12 +2339,12 @@ void runner_do_recv_gpart(struct runner *r, struct cell *c, int timer) {
   /* Otherwise, recurse and collect. */
   else {
     for (int k = 0; k < 8; k++) {
-      if (c->progeny[k] != NULL && c->progeny[k]->gcount > 0) {
+      if (c->progeny[k] != NULL && c->progeny[k]->grav.count > 0) {
         runner_do_recv_gpart(r, c->progeny[k], 0);
         ti_gravity_end_min =
-            min(ti_gravity_end_min, c->progeny[k]->ti_gravity_end_min);
+            min(ti_gravity_end_min, c->progeny[k]->grav.ti_end_min);
         ti_gravity_end_max =
-            max(ti_gravity_end_max, c->progeny[k]->ti_gravity_end_max);
+            max(ti_gravity_end_max, c->progeny[k]->grav.ti_end_max);
       }
     }
   }
@@ -2277,9 +2358,9 @@ void runner_do_recv_gpart(struct runner *r, struct cell *c, int timer) {
 #endif
 
   /* ... and store. */
-  // c->ti_gravity_end_min = ti_gravity_end_min;
-  // c->ti_gravity_end_max = ti_gravity_end_max;
-  c->ti_old_gpart = ti_current;
+  // c->grav.ti_end_min = ti_gravity_end_min;
+  // c->grav.ti_end_max = ti_gravity_end_max;
+  c->grav.ti_old_part = ti_current;
 
   if (timer) TIMER_TOC(timer_dorecv_gpart);
 
@@ -2299,10 +2380,12 @@ void runner_do_recv_spart(struct runner *r, struct cell *c, int timer) {
 
 #ifdef WITH_MPI
 
-  const struct spart *restrict sparts = c->sparts;
-  const size_t nr_sparts = c->scount;
+  const struct spart *restrict sparts = c->stars.parts;
+  const size_t nr_sparts = c->stars.count;
   const integertime_t ti_current = r->e->ti_current;
 
+  error("Need to add h_max computation");
+
   TIMER_TIC;
 
   integertime_t ti_gravity_end_min = max_nr_timesteps;
@@ -2332,12 +2415,12 @@ void runner_do_recv_spart(struct runner *r, struct cell *c, int timer) {
   /* Otherwise, recurse and collect. */
   else {
     for (int k = 0; k < 8; k++) {
-      if (c->progeny[k] != NULL && c->progeny[k]->scount > 0) {
+      if (c->progeny[k] != NULL && c->progeny[k]->stars.count > 0) {
         runner_do_recv_spart(r, c->progeny[k], 0);
         ti_gravity_end_min =
-            min(ti_gravity_end_min, c->progeny[k]->ti_gravity_end_min);
+            min(ti_gravity_end_min, c->progeny[k]->grav.ti_end_min);
         ti_gravity_end_max =
-            max(ti_gravity_end_max, c->progeny[k]->ti_gravity_end_max);
+            max(ti_gravity_end_max, c->progeny[k]->grav.ti_end_max);
       }
     }
   }
@@ -2351,9 +2434,9 @@ void runner_do_recv_spart(struct runner *r, struct cell *c, int timer) {
 #endif
 
   /* ... and store. */
-  // c->ti_gravity_end_min = ti_gravity_end_min;
-  // c->ti_gravity_end_max = ti_gravity_end_max;
-  c->ti_old_gpart = ti_current;
+  // c->grav.ti_end_min = ti_gravity_end_min;
+  // c->grav.ti_end_max = ti_gravity_end_max;
+  c->grav.ti_old_part = ti_current;
 
   if (timer) TIMER_TOC(timer_dorecv_spart);
 
@@ -2494,7 +2577,8 @@ void *runner_main(void *data) {
         case task_type_sort:
           /* Cleanup only if any of the indices went stale. */
           runner_do_sort(r, ci, t->flags,
-                         ci->dx_max_sort_old > space_maxreldx * ci->dmin, 1);
+                         ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin,
+                         1);
           /* Reset the sort flags as our work here is done. */
           t->flags = 0;
           break;
@@ -2527,6 +2611,9 @@ void *runner_main(void *data) {
         case task_type_end_force:
           runner_do_end_force(r, ci, 1);
           break;
+        case task_type_logger:
+          runner_do_logger(r, ci, 1);
+          break;
         case task_type_timestep:
           runner_do_timestep(r, ci, 1);
           break;
@@ -2573,6 +2660,9 @@ void *runner_main(void *data) {
         case task_type_cooling:
           runner_do_cooling(r, t->ci, 1);
           break;
+        case task_type_star_formation:
+          runner_do_star_formation(r, t->ci, 1);
+          break;
         case task_type_sourceterms:
           runner_do_sourceterms(r, t->ci, 1);
           break;
@@ -2602,3 +2692,70 @@ void *runner_main(void *data) {
   /* Be kind, rewind. */
   return NULL;
 }
+
+/**
+ * @brief Write the required particles through the logger.
+ *
+ * @param r The runner thread.
+ * @param c The cell.
+ * @param timer Are we timing this ?
+ */
+void runner_do_logger(struct runner *r, struct cell *c, int timer) {
+
+#ifdef WITH_LOGGER
+  TIMER_TIC;
+
+  const struct engine *e = r->e;
+  struct part *restrict parts = c->hydro.parts;
+  struct xpart *restrict xparts = c->hydro.xparts;
+  const int count = c->hydro.count;
+
+  /* Anything to do here? */
+  if (!cell_is_starting_hydro(c, e) && !cell_is_starting_gravity(c, e)) return;
+
+  /* Recurse? Avoid spending too much time in useless cells. */
+  if (c->split) {
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL) runner_do_logger(r, c->progeny[k], 0);
+  } else {
+
+    /* Loop over the parts in this cell. */
+    for (int k = 0; k < count; k++) {
+
+      /* Get a handle on the part. */
+      struct part *restrict p = &parts[k];
+      struct xpart *restrict xp = &xparts[k];
+
+      /* If particle needs to be log */
+      /* This is the same function than part_is_active, except for
+       * debugging checks */
+      if (part_is_starting(p, e)) {
+
+        if (logger_should_write(&xp->logger_data, e->logger)) {
+          /* Write particle */
+          /* Currently writing everything, should adapt it through time */
+          logger_log_part(e->logger, p,
+                          logger_mask_x | logger_mask_v | logger_mask_a |
+                              logger_mask_u | logger_mask_h | logger_mask_rho |
+                              logger_mask_consts,
+                          &xp->logger_data.last_offset);
+
+          /* Set counter back to zero */
+          xp->logger_data.steps_since_last_output = 0;
+        } else
+          /* Update counter */
+          xp->logger_data.steps_since_last_output += 1;
+      }
+    }
+  }
+
+  if (c->grav.count > 0) error("gparts not implemented");
+
+  if (c->stars.count > 0) error("sparts not implemented");
+
+  if (timer) TIMER_TOC(timer_logger);
+
+#else
+  error("Logger disabled, please enable it during configuration");
+#endif
+}
diff --git a/src/runner.h b/src/runner.h
index e33a3e380e6097a67258d116d617483caca35086..29c42da405255c217d43cc905e87dde9b69276f9 100644
--- a/src/runner.h
+++ b/src/runner.h
@@ -80,6 +80,7 @@ void runner_do_init(struct runner *r, struct cell *c, int timer);
 void runner_do_cooling(struct runner *r, struct cell *c, int timer);
 void runner_do_grav_external(struct runner *r, struct cell *c, int timer);
 void runner_do_grav_fft(struct runner *r, int timer);
+void runner_do_logger(struct runner *r, struct cell *c, int timer);
 void *runner_main(void *data);
 void runner_do_unskip_mapper(void *map_data, int num_elements,
                              void *extra_data);
diff --git a/src/runner_doiact.h b/src/runner_doiact.h
index 942455194da947c3d3ef170f674d9d7b816f9381..53cf51ed400f82d0e195e38dd08fcc5af16f1ad7 100644
--- a/src/runner_doiact.h
+++ b/src/runner_doiact.h
@@ -145,10 +145,10 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci,
   /* Anything to do here? */
   if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
 
-  const int count_i = ci->count;
-  const int count_j = cj->count;
-  struct part *restrict parts_i = ci->parts;
-  struct part *restrict parts_j = cj->parts;
+  const int count_i = ci->hydro.count;
+  const int count_j = cj->hydro.count;
+  struct part *restrict parts_i = ci->hydro.parts;
+  struct part *restrict parts_j = cj->hydro.parts;
 
   /* Cosmological terms */
   const float a = cosmo->a;
@@ -169,6 +169,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci,
     /* Get a hold of the ith part in ci. */
     struct part *restrict pi = &parts_i[pid];
     const int pi_active = part_is_active(pi, e);
+    const int pi_inhibited = part_is_inhibited(pi, e);
     const float hi = pi->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])),
@@ -183,6 +184,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci,
       const float hj = pj->h;
       const float hjg2 = hj * hj * kernel_gamma2;
       const int pj_active = part_is_active(pj, e);
+      const int pj_inhibited = part_is_inhibited(pj, e);
 
       /* Compute the pairwise distance. */
       const float pjx[3] = {(float)(pj->x[0] - cj->loc[0]),
@@ -193,21 +195,21 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci,
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Check that particles have been drifted to the current time */
-      if (pi->ti_drift != e->ti_current)
+      if (pi->ti_drift != e->ti_current && !pi_inhibited)
         error("Particle pi not drifted to current time");
-      if (pj->ti_drift != e->ti_current)
+      if (pj->ti_drift != e->ti_current && !pj_inhibited)
         error("Particle pj not drifted to current time");
 #endif
 
       /* Hit or miss? */
-      if (r2 < hig2 && pi_active) {
+      if (r2 < hig2 && pi_active && !pj_inhibited) {
 
         IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
         runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H);
 #endif
       }
-      if (r2 < hjg2 && pj_active) {
+      if (r2 < hjg2 && pj_active && !pi_inhibited) {
 
         dx[0] = -dx[0];
         dx[1] = -dx[1];
@@ -245,10 +247,10 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci,
   /* Anything to do here? */
   if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
 
-  const int count_i = ci->count;
-  const int count_j = cj->count;
-  struct part *restrict parts_i = ci->parts;
-  struct part *restrict parts_j = cj->parts;
+  const int count_i = ci->hydro.count;
+  const int count_j = cj->hydro.count;
+  struct part *restrict parts_i = ci->hydro.parts;
+  struct part *restrict parts_j = cj->hydro.parts;
 
   /* Cosmological terms */
   const float a = cosmo->a;
@@ -269,6 +271,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci,
     /* Get a hold of the ith part in ci. */
     struct part *restrict pi = &parts_i[pid];
     const int pi_active = part_is_active(pi, e);
+    const int pi_inhibited = part_is_inhibited(pi, e);
     const float hi = pi->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])),
@@ -281,6 +284,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci,
       /* Get a pointer to the jth particle. */
       struct part *restrict pj = &parts_j[pjd];
       const int pj_active = part_is_active(pj, e);
+      const int pj_inhibited = part_is_inhibited(pj, e);
       const float hj = pj->h;
       const float hjg2 = hj * hj * kernel_gamma2;
 
@@ -293,28 +297,28 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci,
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Check that particles have been drifted to the current time */
-      if (pi->ti_drift != e->ti_current)
+      if (pi->ti_drift != e->ti_current && !pj_inhibited)
         error("Particle pi not drifted to current time");
-      if (pj->ti_drift != e->ti_current)
+      if (pj->ti_drift != e->ti_current && !pi_inhibited)
         error("Particle pj not drifted to current time");
 #endif
 
       /* Hit or miss? */
       if (r2 < hig2 || r2 < hjg2) {
 
-        if (pi_active && pj_active) {
+        if (pi_active && pj_active && !pi_inhibited && !pj_inhibited) {
 
           IACT(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
           runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H);
 #endif
-        } else if (pi_active) {
+        } else if (pi_active && !pj_inhibited) {
 
           IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
           runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H);
 #endif
-        } else if (pj_active) {
+        } else if (pj_active && !pi_inhibited) {
 
           dx[0] = -dx[0];
           dx[1] = -dx[1];
@@ -354,8 +358,8 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) {
   const float a = cosmo->a;
   const float H = cosmo->H;
 
-  const int count = c->count;
-  struct part *restrict parts = c->parts;
+  const int count = c->hydro.count;
+  struct part *restrict parts = c->hydro.parts;
 
   /* Loop over the parts in ci. */
   for (int pid = 0; pid < count; pid++) {
@@ -363,6 +367,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) {
     /* Get a hold of the ith part in ci. */
     struct part *restrict pi = &parts[pid];
     const int pi_active = part_is_active(pi, e);
+    const int pi_inhibited = part_is_inhibited(pi, e);
     const float hi = pi->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float pix[3] = {(float)(pi->x[0] - c->loc[0]),
@@ -377,6 +382,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) {
       const float hj = pj->h;
       const float hjg2 = hj * hj * kernel_gamma2;
       const int pj_active = part_is_active(pj, e);
+      const int pj_inhibited = part_is_inhibited(pj, e);
 
       /* Compute the pairwise distance. */
       const float pjx[3] = {(float)(pj->x[0] - c->loc[0]),
@@ -385,14 +391,14 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) {
       float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]};
       const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
-      const int doi = pi_active && (r2 < hig2);
-      const int doj = pj_active && (r2 < hjg2);
+      const int doi = pi_active && (r2 < hig2) && !pj_inhibited;
+      const int doj = pj_active && (r2 < hjg2) && !pi_inhibited;
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Check that particles have been drifted to the current time */
-      if (pi->ti_drift != e->ti_current)
+      if (pi->ti_drift != e->ti_current && !pi_inhibited)
         error("Particle pi not drifted to current time");
-      if (pj->ti_drift != e->ti_current)
+      if (pj->ti_drift != e->ti_current && !pj_inhibited)
         error("Particle pj not drifted to current time");
 #endif
 
@@ -448,8 +454,8 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) {
   const float a = cosmo->a;
   const float H = cosmo->H;
 
-  const int count = c->count;
-  struct part *restrict parts = c->parts;
+  const int count = c->hydro.count;
+  struct part *restrict parts = c->hydro.parts;
 
   /* Loop over the parts in ci. */
   for (int pid = 0; pid < count; pid++) {
@@ -457,6 +463,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) {
     /* Get a hold of the ith part in ci. */
     struct part *restrict pi = &parts[pid];
     const int pi_active = part_is_active(pi, e);
+    const int pi_inhibited = part_is_inhibited(pi, e);
     const float hi = pi->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float pix[3] = {(float)(pi->x[0] - c->loc[0]),
@@ -471,6 +478,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) {
       const float hj = pj->h;
       const float hjg2 = hj * hj * kernel_gamma2;
       const int pj_active = part_is_active(pj, e);
+      const int pj_inhibited = part_is_inhibited(pj, e);
 
       /* Compute the pairwise distance. */
       const float pjx[3] = {(float)(pj->x[0] - c->loc[0]),
@@ -479,14 +487,16 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) {
       float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]};
       const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
-      const int doi = pi_active && ((r2 < hig2) || (r2 < hjg2));
-      const int doj = pj_active && ((r2 < hig2) || (r2 < hjg2));
+      const int doi =
+          pi_active && ((r2 < hig2) || (r2 < hjg2)) && !pj_inhibited;
+      const int doj =
+          pj_active && ((r2 < hig2) || (r2 < hjg2)) && !pi_inhibited;
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Check that particles have been drifted to the current time */
-      if (pi->ti_drift != e->ti_current)
+      if (pi->ti_drift != e->ti_current && !pi_inhibited)
         error("Particle pi not drifted to current time");
-      if (pj->ti_drift != e->ti_current)
+      if (pj->ti_drift != e->ti_current && !pj_inhibited)
         error("Particle pj not drifted to current time");
 #endif
 
@@ -544,8 +554,8 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci,
 
   TIMER_TIC;
 
-  const int count_j = cj->count;
-  struct part *restrict parts_j = cj->parts;
+  const int count_j = cj->hydro.count;
+  struct part *restrict parts_j = cj->hydro.parts;
 
   /* Cosmological terms */
   const float a = cosmo->a;
@@ -571,6 +581,7 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci,
 
       /* Get a pointer to the jth particle. */
       struct part *restrict pj = &parts_j[pjd];
+      const int pj_inhibited = part_is_inhibited(pj, e);
 
       /* Compute the pairwise distance. */
       float r2 = 0.0f;
@@ -584,12 +595,12 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci,
       /* Check that particles have been drifted to the current time */
       if (pi->ti_drift != e->ti_current)
         error("Particle pi not drifted to current time");
-      if (pj->ti_drift != e->ti_current)
+      if (pj->ti_drift != e->ti_current && !pj_inhibited)
         error("Particle pj not drifted to current time");
 #endif
 
       /* Hit or miss? */
-      if (r2 < hig2) {
+      if (r2 < hig2 && !pj_inhibited) {
 
         IACT_NONSYM(r2, dx, hi, pj->h, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -626,16 +637,16 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci,
 
   TIMER_TIC;
 
-  const int count_j = cj->count;
-  struct part *restrict parts_j = cj->parts;
+  const int count_j = cj->hydro.count;
+  struct part *restrict parts_j = cj->hydro.parts;
 
   /* Cosmological terms */
   const float a = cosmo->a;
   const float H = cosmo->H;
 
   /* Pick-out the sorted lists. */
-  const struct entry *restrict sort_j = cj->sort[sid];
-  const float dxj = cj->dx_max_sort;
+  const struct entry *restrict sort_j = cj->hydro.sort[sid];
+  const float dxj = cj->hydro.dx_max_sort;
 
   /* Parts are on the left? */
   if (!flipped) {
@@ -658,6 +669,7 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci,
 
         /* Get a pointer to the jth particle. */
         struct part *restrict pj = &parts_j[sort_j[pjd].i];
+        const int pj_inhibited = part_is_inhibited(pj, e);
         const float hj = pj->h;
         const double pjx = pj->x[0];
         const double pjy = pj->x[1];
@@ -672,12 +684,12 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci,
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
-        if (pj->ti_drift != e->ti_current)
+        if (pj->ti_drift != e->ti_current && !pj_inhibited)
           error("Particle pj not drifted to current time");
 #endif
 
         /* Hit or miss? */
-        if (r2 < hig2) {
+        if (r2 < hig2 && !pj_inhibited) {
 
           IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -709,6 +721,7 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci,
 
         /* Get a pointer to the jth particle. */
         struct part *restrict pj = &parts_j[sort_j[pjd].i];
+        const int pj_inhibited = part_is_inhibited(pj, e);
         const float hj = pj->h;
         const double pjx = pj->x[0];
         const double pjy = pj->x[1];
@@ -723,12 +736,12 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci,
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
-        if (pj->ti_drift != e->ti_current)
+        if (pj->ti_drift != e->ti_current && !pj_inhibited)
           error("Particle pj not drifted to current time");
 #endif
 
         /* Hit or miss? */
-        if (r2 < hig2) {
+        if (r2 < hig2 && !pj_inhibited) {
 
           IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -782,8 +795,8 @@ void DOPAIR_SUBSET_BRANCH(struct runner *r, struct cell *restrict ci,
   sid = sortlistID[sid];
 
   /* Has the cell cj been sorted? */
-  if (!(cj->sorted & (1 << sid)) ||
-      cj->dx_max_sort_old > space_maxreldx * cj->dmin)
+  if (!(cj->hydro.sorted & (1 << sid)) ||
+      cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin)
     error("Interacting unsorted cells.");
 #endif
 
@@ -822,8 +835,8 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci,
   const float a = cosmo->a;
   const float H = cosmo->H;
 
-  const int count_i = ci->count;
-  struct part *restrict parts_j = ci->parts;
+  const int count_i = ci->hydro.count;
+  struct part *restrict parts_j = ci->hydro.parts;
 
   /* Loop over the parts in ci. */
   for (int pid = 0; pid < count; pid++) {
@@ -845,6 +858,7 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci,
 
       /* Get a pointer to the jth particle. */
       struct part *restrict pj = &parts_j[pjd];
+      const int pj_inhibited = part_is_inhibited(pj, e);
       const float hj = pj->h;
 
       /* Compute the pairwise distance. */
@@ -858,12 +872,12 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci,
       /* Check that particles have been drifted to the current time */
       if (pi->ti_drift != e->ti_current)
         error("Particle pi not drifted to current time");
-      if (pj->ti_drift != e->ti_current)
+      if (pj->ti_drift != e->ti_current && !pj_inhibited)
         error("Particle pj not drifted to current time");
 #endif
 
       /* Hit or miss? */
-      if (r2 > 0.f && r2 < hig2) {
+      if (r2 > 0.f && r2 < hig2 && !pj_inhibited) {
 
         IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -919,29 +933,32 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
   for (int k = 0; k < 3; k++) rshift += shift[k] * runner_shift[sid][k];
 
   /* Pick-out the sorted lists. */
-  const struct entry *restrict sort_i = ci->sort[sid];
-  const struct entry *restrict sort_j = cj->sort[sid];
+  const struct entry *restrict sort_i = ci->hydro.sort[sid];
+  const struct entry *restrict sort_j = cj->hydro.sort[sid];
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Some constants used to checks that the parts are in the right frame */
   const float shift_threshold_x =
-      2. * ci->width[0] + 2. * max(ci->dx_max_part, cj->dx_max_part);
+      2. * ci->width[0] +
+      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_y =
-      2. * ci->width[1] + 2. * max(ci->dx_max_part, cj->dx_max_part);
+      2. * ci->width[1] +
+      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_z =
-      2. * ci->width[2] + 2. * max(ci->dx_max_part, cj->dx_max_part);
+      2. * ci->width[2] +
+      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
 #endif /* SWIFT_DEBUG_CHECKS */
 
   /* Get some other useful values. */
-  const double hi_max = ci->h_max * kernel_gamma - rshift;
-  const double hj_max = cj->h_max * kernel_gamma;
-  const int count_i = ci->count;
-  const int count_j = cj->count;
-  struct part *restrict parts_i = ci->parts;
-  struct part *restrict parts_j = cj->parts;
+  const double hi_max = ci->hydro.h_max * kernel_gamma - rshift;
+  const double hj_max = cj->hydro.h_max * kernel_gamma;
+  const int count_i = ci->hydro.count;
+  const int count_j = cj->hydro.count;
+  struct part *restrict parts_i = ci->hydro.parts;
+  struct part *restrict parts_j = cj->hydro.parts;
   const double di_max = sort_i[count_i - 1].d - rshift;
   const double dj_min = sort_j[0].d;
-  const float dx_max = (ci->dx_max_sort + cj->dx_max_sort);
+  const float dx_max = (ci->hydro.dx_max_sort + cj->hydro.dx_max_sort);
 
   /* Cosmological terms */
   const float a = cosmo->a;
@@ -975,6 +992,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
         /* Recover pj */
         struct part *pj = &parts_j[sort_j[pjd].i];
+        const int pj_inhibited = part_is_inhibited(pj, e);
         const float hj = pj->h;
         const float pjx = pj->x[0] - cj->loc[0];
         const float pjy = pj->x[1] - cj->loc[1];
@@ -1014,12 +1032,12 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
-        if (pj->ti_drift != e->ti_current)
+        if (pj->ti_drift != e->ti_current && !pj_inhibited)
           error("Particle pj not drifted to current time");
 #endif
 
         /* Hit or miss? */
-        if (r2 < hig2) {
+        if (r2 < hig2 && !pj_inhibited) {
 
           IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -1058,6 +1076,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
         /* Recover pi */
         struct part *pi = &parts_i[sort_i[pid].i];
+        const int pi_inhibited = part_is_inhibited(pi, e);
         const float hi = pi->h;
         const float pix = pi->x[0] - (cj->loc[0] + shift[0]);
         const float piy = pi->x[1] - (cj->loc[1] + shift[1]);
@@ -1095,14 +1114,14 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               pjz, ci->width[2]);
 
         /* Check that particles have been drifted to the current time */
-        if (pi->ti_drift != e->ti_current)
+        if (pi->ti_drift != e->ti_current && !pi_inhibited)
           error("Particle pi not drifted to current time");
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
 #endif
 
         /* Hit or miss? */
-        if (r2 < hjg2) {
+        if (r2 < hjg2 && !pi_inhibited) {
 
           IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -1141,49 +1160,55 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
   const int sid = space_getsid(e->s, &ci, &cj, shift);
 
   /* Have the cells been sorted? */
-  if (!(ci->sorted & (1 << sid)) ||
-      ci->dx_max_sort_old > space_maxreldx * ci->dmin)
+  if (!(ci->hydro.sorted & (1 << sid)) ||
+      ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin)
     error("Interacting unsorted cells.");
-  if (!(cj->sorted & (1 << sid)) ||
-      cj->dx_max_sort_old > space_maxreldx * cj->dmin)
+  if (!(cj->hydro.sorted & (1 << sid)) ||
+      cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin)
     error("Interacting unsorted cells.");
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Pick-out the sorted lists. */
-  const struct entry *restrict sort_i = ci->sort[sid];
-  const struct entry *restrict sort_j = cj->sort[sid];
+  const struct entry *restrict sort_i = ci->hydro.sort[sid];
+  const struct entry *restrict sort_j = cj->hydro.sort[sid];
 
   /* Check that the dx_max_sort values in the cell are indeed an upper
      bound on particle movement. */
-  for (int pid = 0; pid < ci->count; pid++) {
-    const struct part *p = &ci->parts[sort_i[pid].i];
+  for (int pid = 0; pid < ci->hydro.count; pid++) {
+    const struct part *p = &ci->hydro.parts[sort_i[pid].i];
+    if (part_is_inhibited(p, e)) continue;
+
     const float d = p->x[0] * runner_shift[sid][0] +
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
-    if (fabsf(d - sort_i[pid].d) - ci->dx_max_sort >
-            1.0e-4 * max(fabsf(d), ci->dx_max_sort_old) &&
-        fabsf(d - sort_i[pid].d) - ci->dx_max_sort > ci->width[0] * 1.0e-10)
+    if (fabsf(d - sort_i[pid].d) - ci->hydro.dx_max_sort >
+            1.0e-4 * max(fabsf(d), ci->hydro.dx_max_sort_old) &&
+        fabsf(d - sort_i[pid].d) - ci->hydro.dx_max_sort >
+            ci->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell ci. ci->nodeID=%d "
-          "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->dx_max_sort=%e "
-          "ci->dx_max_sort_old=%e",
-          ci->nodeID, cj->nodeID, d, sort_i[pid].d, ci->dx_max_sort,
-          ci->dx_max_sort_old);
+          "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->hydro.dx_max_sort=%e "
+          "ci->hydro.dx_max_sort_old=%e",
+          ci->nodeID, cj->nodeID, d, sort_i[pid].d, ci->hydro.dx_max_sort,
+          ci->hydro.dx_max_sort_old);
   }
-  for (int pjd = 0; pjd < cj->count; pjd++) {
-    const struct part *p = &cj->parts[sort_j[pjd].i];
+  for (int pjd = 0; pjd < cj->hydro.count; pjd++) {
+    const struct part *p = &cj->hydro.parts[sort_j[pjd].i];
+    if (part_is_inhibited(p, e)) continue;
+
     const float d = p->x[0] * runner_shift[sid][0] +
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
-    if ((fabsf(d - sort_j[pjd].d) - cj->dx_max_sort) >
-            1.0e-4 * max(fabsf(d), cj->dx_max_sort_old) &&
-        (fabsf(d - sort_j[pjd].d) - cj->dx_max_sort) > cj->width[0] * 1.0e-10)
+    if ((fabsf(d - sort_j[pjd].d) - cj->hydro.dx_max_sort) >
+            1.0e-4 * max(fabsf(d), cj->hydro.dx_max_sort_old) &&
+        (fabsf(d - sort_j[pjd].d) - cj->hydro.dx_max_sort) >
+            cj->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell cj. cj->nodeID=%d "
-          "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->dx_max_sort=%e "
-          "cj->dx_max_sort_old=%e",
-          cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->dx_max_sort,
-          cj->dx_max_sort_old);
+          "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->hydro.dx_max_sort=%e "
+          "cj->hydro.dx_max_sort_old=%e",
+          cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->hydro.dx_max_sort,
+          cj->hydro.dx_max_sort_old);
   }
 #endif /* SWIFT_DEBUG_CHECKS */
 
@@ -1222,33 +1247,36 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
   for (int k = 0; k < 3; k++) rshift += shift[k] * runner_shift[sid][k];
 
   /* Pick-out the sorted lists. */
-  struct entry *restrict sort_i = ci->sort[sid];
-  struct entry *restrict sort_j = cj->sort[sid];
+  struct entry *restrict sort_i = ci->hydro.sort[sid];
+  struct entry *restrict sort_j = cj->hydro.sort[sid];
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Some constants used to checks that the parts are in the right frame */
   const float shift_threshold_x =
-      2. * ci->width[0] + 2. * max(ci->dx_max_part, cj->dx_max_part);
+      2. * ci->width[0] +
+      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_y =
-      2. * ci->width[1] + 2. * max(ci->dx_max_part, cj->dx_max_part);
+      2. * ci->width[1] +
+      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_z =
-      2. * ci->width[2] + 2. * max(ci->dx_max_part, cj->dx_max_part);
+      2. * ci->width[2] +
+      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
 #endif /* SWIFT_DEBUG_CHECKS */
 
   /* Get some other useful values. */
-  const double hi_max = ci->h_max;
-  const double hj_max = cj->h_max;
-  const int count_i = ci->count;
-  const int count_j = cj->count;
-  struct part *restrict parts_i = ci->parts;
-  struct part *restrict parts_j = cj->parts;
+  const double hi_max = ci->hydro.h_max;
+  const double hj_max = cj->hydro.h_max;
+  const int count_i = ci->hydro.count;
+  const int count_j = cj->hydro.count;
+  struct part *restrict parts_i = ci->hydro.parts;
+  struct part *restrict parts_j = cj->hydro.parts;
 
   /* Cosmological terms */
   const float a = cosmo->a;
   const float H = cosmo->H;
 
   /* Maximal displacement since last rebuild */
-  const double dx_max = (ci->dx_max_sort + cj->dx_max_sort);
+  const double dx_max = (ci->hydro.dx_max_sort + cj->hydro.dx_max_sort);
 
   /* Position on the axis of the particles closest to the interface */
   const double di_max = sort_i[count_i - 1].d;
@@ -1307,6 +1335,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
     /* Get a hold of the ith part in ci. */
     struct part *pi = &parts_i[sort_i[pid].i];
+    const int pi_inhibited = part_is_inhibited(pi, e);
     const float hi = pi->h;
 
     /* Is there anything we need to interact with (for this specific hi) ? */
@@ -1337,7 +1366,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
         const float pjz = pj->x[2] - shift_j[2];
 
         /* Compute the pairwise distance. */
-        float dx[3] = {pjx - pix, pjy - piy, pjz - piz};
+        const float dx[3] = {pjx - pix, pjy - piy, pjz - piz};
         const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -1368,7 +1397,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               pjz, ci->width[2]);
 
         /* Check that particles have been drifted to the current time */
-        if (pi->ti_drift != e->ti_current)
+        if (pi->ti_drift != e->ti_current && !pi_inhibited)
           error("Particle pi not drifted to current time");
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
@@ -1376,7 +1405,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
         /* Hit or miss?
            (note that we will do the other condition in the reverse loop) */
-        if (r2 < hig2) {
+        if (r2 < hig2 && !pi_inhibited) {
           IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
           runner_iact_nonsym_chemistry(r2, dx, hj, hi, pj, pi, a, H);
@@ -1392,6 +1421,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
         /* Recover pj */
         struct part *pj = &parts_j[sort_j[pjd].i];
+        const int pj_inhibited = part_is_inhibited(pj, e);
         const float hj = pj->h;
 
         /* Get the position of pj in the right frame */
@@ -1400,7 +1430,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
         const float pjz = pj->x[2] - shift_j[2];
 
         /* Compute the pairwise distance. */
-        float dx[3] = {pix - pjx, piy - pjy, piz - pjz};
+        const float dx[3] = {pix - pjx, piy - pjy, piz - pjz};
         const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -1431,14 +1461,14 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               pjz, ci->width[2]);
 
         /* Check that particles have been drifted to the current time */
-        if (pi->ti_drift != e->ti_current)
+        if (pi->ti_drift != e->ti_current && !pi_inhibited)
           error("Particle pi not drifted to current time");
-        if (pj->ti_drift != e->ti_current)
+        if (pj->ti_drift != e->ti_current && !pj_inhibited)
           error("Particle pj not drifted to current time");
 #endif
         /* Hit or miss?
            (note that we will do the other condition in the reverse loop) */
-        if (r2 < hig2) {
+        if (r2 < hig2 && !pj_inhibited) {
 
           /* Does pj need to be updated too? */
           if (part_is_active(pj, e)) {
@@ -1466,6 +1496,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
     /* Get a hold of the jth part in cj. */
     struct part *pj = &parts_j[sort_j[pjd].i];
+    const int pj_inhibited = part_is_inhibited(pj, e);
     const float hj = pj->h;
 
     /* Is there anything we need to interact with (for this specific hj) ? */
@@ -1497,7 +1528,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
         const float piz = pi->x[2] - shift_i[2];
 
         /* Compute the pairwise distance. */
-        float dx[3] = {pix - pjx, piy - pjy, piz - pjz};
+        const float dx[3] = {pix - pjx, piy - pjy, piz - pjz};
         const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -1530,13 +1561,13 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
-        if (pj->ti_drift != e->ti_current)
+        if (pj->ti_drift != e->ti_current && !pj_inhibited)
           error("Particle pj not drifted to current time");
 #endif
 
         /* Hit or miss?
            (note that we must avoid the r2 < hig2 cases we already processed) */
-        if (r2 < hjg2 && r2 >= hig2) {
+        if (r2 < hjg2 && r2 >= hig2 && !pj_inhibited) {
           IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
           runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H);
@@ -1553,6 +1584,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
         /* Recover pi */
         struct part *pi = &parts_i[sort_i[pid].i];
+        const int pi_inhibited = part_is_inhibited(pi, e);
         const float hi = pi->h;
         const float hig2 = hi * hi * kernel_gamma2;
 
@@ -1562,7 +1594,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
         const float piz = pi->x[2] - shift_i[2];
 
         /* Compute the pairwise distance. */
-        float dx[3] = {pjx - pix, pjy - piy, pjz - piz};
+        const float dx[3] = {pjx - pix, pjy - piy, pjz - piz};
         const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -1593,15 +1625,15 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               pjz, ci->width[2]);
 
         /* Check that particles have been drifted to the current time */
-        if (pi->ti_drift != e->ti_current)
+        if (pi->ti_drift != e->ti_current && !pi_inhibited)
           error("Particle pi not drifted to current time");
-        if (pj->ti_drift != e->ti_current)
+        if (pj->ti_drift != e->ti_current && !pj_inhibited)
           error("Particle pj not drifted to current time");
 #endif
 
         /* Hit or miss?
            (note that we must avoid the r2 < hig2 cases we already processed) */
-        if (r2 < hjg2 && r2 >= hig2) {
+        if (r2 < hjg2 && r2 >= hig2 && !pi_inhibited) {
 
           /* Does pi need to be updated too? */
           if (part_is_active(pi, e)) {
@@ -1654,49 +1686,55 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
   const int sid = space_getsid(e->s, &ci, &cj, shift);
 
   /* Have the cells been sorted? */
-  if (!(ci->sorted & (1 << sid)) ||
-      ci->dx_max_sort_old > space_maxreldx * ci->dmin)
+  if (!(ci->hydro.sorted & (1 << sid)) ||
+      ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin)
     error("Interacting unsorted cells.");
-  if (!(cj->sorted & (1 << sid)) ||
-      cj->dx_max_sort_old > space_maxreldx * cj->dmin)
+  if (!(cj->hydro.sorted & (1 << sid)) ||
+      cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin)
     error("Interacting unsorted cells.");
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Pick-out the sorted lists. */
-  const struct entry *restrict sort_i = ci->sort[sid];
-  const struct entry *restrict sort_j = cj->sort[sid];
+  const struct entry *restrict sort_i = ci->hydro.sort[sid];
+  const struct entry *restrict sort_j = cj->hydro.sort[sid];
 
   /* Check that the dx_max_sort values in the cell are indeed an upper
      bound on particle movement. */
-  for (int pid = 0; pid < ci->count; pid++) {
-    const struct part *p = &ci->parts[sort_i[pid].i];
+  for (int pid = 0; pid < ci->hydro.count; pid++) {
+    const struct part *p = &ci->hydro.parts[sort_i[pid].i];
+    if (part_is_inhibited(p, e)) continue;
+
     const float d = p->x[0] * runner_shift[sid][0] +
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
-    if (fabsf(d - sort_i[pid].d) - ci->dx_max_sort >
-            1.0e-4 * max(fabsf(d), ci->dx_max_sort_old) &&
-        fabsf(d - sort_i[pid].d) - ci->dx_max_sort > ci->width[0] * 1.0e-10)
+    if (fabsf(d - sort_i[pid].d) - ci->hydro.dx_max_sort >
+            1.0e-4 * max(fabsf(d), ci->hydro.dx_max_sort_old) &&
+        fabsf(d - sort_i[pid].d) - ci->hydro.dx_max_sort >
+            ci->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell ci. ci->nodeID=%d "
-          "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->dx_max_sort=%e "
-          "ci->dx_max_sort_old=%e",
-          ci->nodeID, cj->nodeID, d, sort_i[pid].d, ci->dx_max_sort,
-          ci->dx_max_sort_old);
+          "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->hydro.dx_max_sort=%e "
+          "ci->hydro.dx_max_sort_old=%e",
+          ci->nodeID, cj->nodeID, d, sort_i[pid].d, ci->hydro.dx_max_sort,
+          ci->hydro.dx_max_sort_old);
   }
-  for (int pjd = 0; pjd < cj->count; pjd++) {
-    const struct part *p = &cj->parts[sort_j[pjd].i];
+  for (int pjd = 0; pjd < cj->hydro.count; pjd++) {
+    const struct part *p = &cj->hydro.parts[sort_j[pjd].i];
+    if (part_is_inhibited(p, e)) continue;
+
     const float d = p->x[0] * runner_shift[sid][0] +
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
-    if (fabsf(d - sort_j[pjd].d) - cj->dx_max_sort >
-            1.0e-4 * max(fabsf(d), cj->dx_max_sort_old) &&
-        fabsf(d - sort_j[pjd].d) - cj->dx_max_sort > cj->width[0] * 1.0e-10)
+    if (fabsf(d - sort_j[pjd].d) - cj->hydro.dx_max_sort >
+            1.0e-4 * max(fabsf(d), cj->hydro.dx_max_sort_old) &&
+        fabsf(d - sort_j[pjd].d) - cj->hydro.dx_max_sort >
+            cj->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell cj. cj->nodeID=%d "
-          "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->dx_max_sort=%e "
-          "cj->dx_max_sort_old=%e",
-          cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->dx_max_sort,
-          cj->dx_max_sort_old);
+          "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->hydro.dx_max_sort=%e "
+          "cj->hydro.dx_max_sort_old=%e",
+          cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->hydro.dx_max_sort,
+          cj->hydro.dx_max_sort_old);
   }
 #endif /* SWIFT_DEBUG_CHECKS */
 
@@ -1726,8 +1764,8 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
 
   TIMER_TIC;
 
-  struct part *restrict parts = c->parts;
-  const int count = c->count;
+  struct part *restrict parts = c->hydro.parts;
+  const int count = c->hydro.count;
 
   /* Set up indt. */
   int *indt = NULL;
@@ -1750,6 +1788,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
 
     /* Get a pointer to the ith particle. */
     struct part *restrict pi = &parts[pid];
+    const int pi_inhibited = part_is_inhibited(pi, e);
 
     /* Get the particle position and radius. */
     double pix[3];
@@ -1769,7 +1808,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
 
 #ifdef SWIFT_DEBUG_CHECKS
         /* Check that particles have been drifted to the current time */
-        if (pi->ti_drift != e->ti_current)
+        if (pi->ti_drift != e->ti_current && !pi_inhibited)
           error("Particle pi not drifted to current time");
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
@@ -1784,7 +1823,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
         }
 
         /* Hit or miss? */
-        if (r2 < hj * hj * kernel_gamma2) {
+        if (r2 < hj * hj * kernel_gamma2 && !pi_inhibited) {
 
           IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -1805,6 +1844,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
 
         /* Get a pointer to the jth particle. */
         struct part *restrict pj = &parts[pjd];
+        const int pj_inhibited = part_is_inhibited(pj, e);
         const float hj = pj->h;
 
         /* Compute the pairwise distance. */
@@ -1817,29 +1857,34 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
         const int doj =
             (part_is_active(pj, e)) && (r2 < hj * hj * kernel_gamma2);
 
+        const int doi = (r2 < hig2);
+
 #ifdef SWIFT_DEBUG_CHECKS
         /* Check that particles have been drifted to the current time */
-        if (pi->ti_drift != e->ti_current)
+        if (pi->ti_drift != e->ti_current && !pi_inhibited)
           error("Particle pi not drifted to current time");
-        if (pj->ti_drift != e->ti_current)
+        if (pj->ti_drift != e->ti_current && !pj_inhibited)
           error("Particle pj not drifted to current time");
 #endif
 
         /* Hit or miss? */
-        if (r2 < hig2 || doj) {
+        if (doi || doj) {
 
           /* Which parts need to be updated? */
-          if (r2 < hig2 && doj) {
+          if (doi && doj) {
+
             IACT(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
             runner_iact_chemistry(r2, dx, hi, hj, pi, pj, a, H);
 #endif
-          } else if (!doj) {
+          } else if (doi && !pj_inhibited) {
+
             IACT_NONSYM(r2, dx, hi, hj, pi, pj, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
             runner_iact_nonsym_chemistry(r2, dx, hi, hj, pi, pj, a, H);
 #endif
-          } else {
+          } else if (doj && !pi_inhibited) {
+
             dx[0] = -dx[0];
             dx[1] = -dx[1];
             dx[2] = -dx[2];
@@ -1874,7 +1919,7 @@ void DOSELF1_BRANCH(struct runner *r, struct cell *c) {
   if (!cell_is_active_hydro(c, e)) return;
 
   /* Did we mess up the recursion? */
-  if (c->h_max_old * kernel_gamma > c->dmin)
+  if (c->hydro.h_max_old * kernel_gamma > c->dmin)
     error("Cell smaller than smoothing length");
 
   /* Check that cells are drifted. */
@@ -1903,8 +1948,8 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
 
   TIMER_TIC;
 
-  struct part *restrict parts = c->parts;
-  const int count = c->count;
+  struct part *restrict parts = c->hydro.parts;
+  const int count = c->hydro.count;
 
   /* Set up indt. */
   int *indt = NULL;
@@ -1927,6 +1972,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
 
     /* Get a pointer to the ith particle. */
     struct part *restrict pi = &parts[pid];
+    const int pi_inhibited = part_is_inhibited(pi, e);
 
     /* Get the particle position and radius. */
     double pix[3];
@@ -1954,14 +2000,14 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
 
 #ifdef SWIFT_DEBUG_CHECKS
         /* Check that particles have been drifted to the current time */
-        if (pi->ti_drift != e->ti_current)
+        if (pi->ti_drift != e->ti_current && !pi_inhibited)
           error("Particle pi not drifted to current time");
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
 #endif
 
         /* Hit or miss? */
-        if (r2 < hig2 || r2 < hj * hj * kernel_gamma2) {
+        if ((r2 < hig2 || r2 < hj * hj * kernel_gamma2) && !pi_inhibited) {
 
           IACT_NONSYM(r2, dx, hj, hi, pj, pi, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -1982,6 +2028,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
 
         /* Get a pointer to the jth particle. */
         struct part *restrict pj = &parts[pjd];
+        const int pj_inhibited = part_is_inhibited(pj, e);
         const float hj = pj->h;
 
         /* Compute the pairwise distance. */
@@ -1994,14 +2041,14 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
 
 #ifdef SWIFT_DEBUG_CHECKS
         /* Check that particles have been drifted to the current time */
-        if (pi->ti_drift != e->ti_current)
+        if (pi->ti_drift != e->ti_current && !pi_inhibited)
           error("Particle pi not drifted to current time");
-        if (pj->ti_drift != e->ti_current)
+        if (pj->ti_drift != e->ti_current && !pj_inhibited)
           error("Particle pj not drifted to current time");
 #endif
 
         /* Hit or miss? */
-        if (r2 < hig2 || r2 < hj * hj * kernel_gamma2) {
+        if ((r2 < hig2 || r2 < hj * hj * kernel_gamma2) && !pj_inhibited) {
 
           /* Does pj need to be updated too? */
           if (part_is_active(pj, e)) {
@@ -2041,7 +2088,7 @@ void DOSELF2_BRANCH(struct runner *r, struct cell *c) {
   if (!cell_is_active_hydro(c, e)) return;
 
   /* Did we mess up the recursion? */
-  if (c->h_max_old * kernel_gamma > c->dmin)
+  if (c->hydro.h_max_old * kernel_gamma > c->dmin)
     error("Cell smaller than smoothing length");
 
   /* Check that cells are drifted. */
@@ -2079,7 +2126,7 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid,
 
   /* Should we even bother? */
   if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
-  if (ci->count == 0 || cj->count == 0) return;
+  if (ci->hydro.count == 0 || cj->hydro.count == 0) return;
 
   /* Get the type of pair if not specified explicitly. */
   double shift[3];
@@ -2295,18 +2342,18 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid,
       error("Interacting undrifted cells.");
 
     /* Do any of the cells need to be sorted first? */
-    if (!(ci->sorted & (1 << sid)) ||
-        ci->dx_max_sort_old > ci->dmin * space_maxreldx)
+    if (!(ci->hydro.sorted & (1 << sid)) ||
+        ci->hydro.dx_max_sort_old > ci->dmin * space_maxreldx)
       error(
-          "Interacting unsorted cell. ci->dx_max_sort_old=%e ci->dmin=%e "
+          "Interacting unsorted cell. ci->hydro.dx_max_sort_old=%e ci->dmin=%e "
           "ci->sorted=%d sid=%d",
-          ci->dx_max_sort_old, ci->dmin, ci->sorted, sid);
-    if (!(cj->sorted & (1 << sid)) ||
-        cj->dx_max_sort_old > cj->dmin * space_maxreldx)
+          ci->hydro.dx_max_sort_old, ci->dmin, ci->hydro.sorted, sid);
+    if (!(cj->hydro.sorted & (1 << sid)) ||
+        cj->hydro.dx_max_sort_old > cj->dmin * space_maxreldx)
       error(
-          "Interacting unsorted cell. cj->dx_max_sort_old=%e cj->dmin=%e "
+          "Interacting unsorted cell. cj->hydro.dx_max_sort_old=%e cj->dmin=%e "
           "cj->sorted=%d sid=%d",
-          cj->dx_max_sort_old, cj->dmin, cj->sorted, sid);
+          cj->hydro.dx_max_sort_old, cj->dmin, cj->hydro.sorted, sid);
 
     /* Compute the interactions. */
     DOPAIR1_BRANCH(r, ci, cj);
@@ -2327,7 +2374,7 @@ void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer) {
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (ci->count == 0 || !cell_is_active_hydro(ci, r->e)) return;
+  if (ci->hydro.count == 0 || !cell_is_active_hydro(ci, r->e)) return;
 
   /* Recurse? */
   if (cell_can_recurse_in_self_hydro_task(ci)) {
@@ -2376,7 +2423,7 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid,
 
   /* Should we even bother? */
   if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
-  if (ci->count == 0 || cj->count == 0) return;
+  if (ci->hydro.count == 0 || cj->hydro.count == 0) return;
 
   /* Get the type of pair if not specified explicitly. */
   double shift[3];
@@ -2592,18 +2639,18 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid,
       error("Interacting undrifted cells.");
 
     /* Do any of the cells need to be sorted first? */
-    if (!(ci->sorted & (1 << sid)) ||
-        ci->dx_max_sort_old > ci->dmin * space_maxreldx)
+    if (!(ci->hydro.sorted & (1 << sid)) ||
+        ci->hydro.dx_max_sort_old > ci->dmin * space_maxreldx)
       error(
-          "Interacting unsorted cell. ci->dx_max_sort_old=%e ci->dmin=%e "
+          "Interacting unsorted cell. ci->hydro.dx_max_sort_old=%e ci->dmin=%e "
           "ci->sorted=%d sid=%d",
-          ci->dx_max_sort_old, ci->dmin, ci->sorted, sid);
-    if (!(cj->sorted & (1 << sid)) ||
-        cj->dx_max_sort_old > cj->dmin * space_maxreldx)
+          ci->hydro.dx_max_sort_old, ci->dmin, ci->hydro.sorted, sid);
+    if (!(cj->hydro.sorted & (1 << sid)) ||
+        cj->hydro.dx_max_sort_old > cj->dmin * space_maxreldx)
       error(
-          "Interacting unsorted cell. cj->dx_max_sort_old=%e cj->dmin=%e "
+          "Interacting unsorted cell. cj->hydro.dx_max_sort_old=%e cj->dmin=%e "
           "cj->sorted=%d sid=%d",
-          cj->dx_max_sort_old, cj->dmin, cj->sorted, sid);
+          cj->hydro.dx_max_sort_old, cj->dmin, cj->hydro.sorted, sid);
 
     /* Compute the interactions. */
     DOPAIR2_BRANCH(r, ci, cj);
@@ -2624,7 +2671,7 @@ void DOSUB_SELF2(struct runner *r, struct cell *ci, int gettimer) {
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (ci->count == 0 || !cell_is_active_hydro(ci, r->e)) return;
+  if (ci->hydro.count == 0 || !cell_is_active_hydro(ci, r->e)) return;
 
   /* Recurse? */
   if (cell_can_recurse_in_self_hydro_task(ci)) {
@@ -2659,15 +2706,16 @@ void DOSUB_SUBSET(struct runner *r, struct cell *ci, struct part *parts,
   if (!cell_is_active_hydro(ci, e) &&
       (cj == NULL || !cell_is_active_hydro(cj, e)))
     return;
-  if (ci->count == 0 || (cj != NULL && cj->count == 0)) return;
+  if (ci->hydro.count == 0 || (cj != NULL && cj->hydro.count == 0)) return;
 
   /* Find out in which sub-cell of ci the parts are. */
   struct cell *sub = NULL;
   if (ci->split) {
     for (int k = 0; k < 8; k++) {
       if (ci->progeny[k] != NULL) {
-        if (&parts[ind[0]] >= &ci->progeny[k]->parts[0] &&
-            &parts[ind[0]] < &ci->progeny[k]->parts[ci->progeny[k]->count]) {
+        if (&parts[ind[0]] >= &ci->progeny[k]->hydro.parts[0] &&
+            &parts[ind[0]] <
+                &ci->progeny[k]->hydro.parts[ci->progeny[k]->hydro.count]) {
           sub = ci->progeny[k];
           break;
         }
diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h
index 26e3cfdca0dc37449c90e1c85fa9fb0597ff68a8..1e15df821e7068458203b55a147694deee8510a9 100644
--- a/src/runner_doiact_grav.h
+++ b/src/runner_doiact_grav.h
@@ -48,8 +48,9 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
   TIMER_TIC;
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->ti_old_multipole != e->ti_current) error("c->multipole not drifted.");
-  if (c->multipole->pot.ti_init != e->ti_current)
+  if (c->grav.ti_old_multipole != e->ti_current)
+    error("c->multipole not drifted.");
+  if (c->grav.multipole->pot.ti_init != e->ti_current)
     error("c->field tensor not initialised");
 #endif
 
@@ -65,22 +66,22 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
       if (cp != NULL && cell_is_active_gravity(cp, e)) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-        if (cp->ti_old_multipole != e->ti_current)
+        if (cp->grav.ti_old_multipole != e->ti_current)
           error("cp->multipole not drifted.");
-        if (cp->multipole->pot.ti_init != e->ti_current)
+        if (cp->grav.multipole->pot.ti_init != e->ti_current)
           error("cp->field tensor not initialised");
 #endif
         /* If the tensor received any contribution, push it down */
-        if (c->multipole->pot.interacted) {
+        if (c->grav.multipole->pot.interacted) {
 
           struct grav_tensor shifted_tensor;
 
           /* Shift the field tensor */
-          gravity_L2L(&shifted_tensor, &c->multipole->pot, cp->multipole->CoM,
-                      c->multipole->CoM);
+          gravity_L2L(&shifted_tensor, &c->grav.multipole->pot,
+                      cp->grav.multipole->CoM, c->grav.multipole->CoM);
 
           /* Add it to this level's tensor */
-          gravity_field_tensors_add(&cp->multipole->pot, &shifted_tensor);
+          gravity_field_tensors_add(&cp->grav.multipole->pot, &shifted_tensor);
         }
 
         /* Recurse */
@@ -93,16 +94,16 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
     /* Leaf case */
 
     /* We can abort early if no interactions via multipole happened */
-    if (!c->multipole->pot.interacted) return;
+    if (!c->grav.multipole->pot.interacted) return;
 
     if (!cell_are_gpart_drifted(c, e)) error("Un-drifted gparts");
 
     /* Cell properties */
-    struct gpart *gparts = c->gparts;
-    const int gcount = c->gcount;
-    const struct grav_tensor *pot = &c->multipole->pot;
-    const double CoM[3] = {c->multipole->CoM[0], c->multipole->CoM[1],
-                           c->multipole->CoM[2]};
+    struct gpart *gparts = c->grav.parts;
+    const int gcount = c->grav.count;
+    const struct grav_tensor *pot = &c->grav.multipole->pot;
+    const double CoM[3] = {c->grav.multipole->CoM[0], c->grav.multipole->CoM[1],
+                           c->grav.multipole->CoM[2]};
 
     /* Apply accelerations to the particles */
     for (int i = 0; i < gcount; ++i) {
@@ -117,9 +118,12 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
         /* Check that particles have been drifted to the current time */
         if (gp->ti_drift != e->ti_current)
           error("gpart not drifted to current time");
-        if (c->multipole->pot.ti_init != e->ti_current)
+        if (c->grav.multipole->pot.ti_init != e->ti_current)
           error("c->field tensor not initialised");
 
+        /* Check that we are not updated an inhibited particle */
+        if (gpart_is_inhibited(gp, e)) error("Updating an inhibited particle!");
+
         /* Check that the particle was initialised */
         if (gp->initialised == 0)
           error("Adding forces to an un-initialised gpart.");
@@ -225,9 +229,19 @@ static INLINE void runner_dopair_grav_pp_full(
       /* Check that particles have been drifted to the current time */
       if (gparts_i[pid].ti_drift != e->ti_current)
         error("gpi not drifted to current time");
-      if (pjd < gcount_j && gparts_j[pjd].ti_drift != e->ti_current)
+      if (pjd < gcount_j && gparts_j[pjd].ti_drift != e->ti_current &&
+          !gpart_is_inhibited(&gparts_j[pjd], e))
         error("gpj not drifted to current time");
 
+      /* Check that we are not updated an inhibited particle */
+      if (gpart_is_inhibited(&gparts_i[pid], e))
+        error("Updating an inhibited particle!");
+
+      /* Check that the particle we interact with was not inhibited */
+      if (pjd < gcount_j && gpart_is_inhibited(&gparts_j[pjd], e) &&
+          mass_j != 0.f)
+        error("Inhibited particle used as gravity source.");
+
       /* Check that the particle was initialised */
       if (gparts_i[pid].initialised == 0)
         error("Adding forces to an un-initialised gpart.");
@@ -246,7 +260,8 @@ static INLINE void runner_dopair_grav_pp_full(
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Update the interaction counter if it's not a padded gpart */
-      if (pjd < gcount_j) gparts_i[pid].num_interacted++;
+      if (pjd < gcount_j && !gpart_is_inhibited(&gparts_j[pjd], e))
+        gparts_i[pid].num_interacted++;
 #endif
     }
 
@@ -355,9 +370,19 @@ static INLINE void runner_dopair_grav_pp_truncated(
       /* Check that particles have been drifted to the current time */
       if (gparts_i[pid].ti_drift != e->ti_current)
         error("gpi not drifted to current time");
-      if (pjd < gcount_j && gparts_j[pjd].ti_drift != e->ti_current)
+      if (pjd < gcount_j && gparts_j[pjd].ti_drift != e->ti_current &&
+          !gpart_is_inhibited(&gparts_j[pjd], e))
         error("gpj not drifted to current time");
 
+      /* Check that we are not updated an inhibited particle */
+      if (gpart_is_inhibited(&gparts_i[pid], e))
+        error("Updating an inhibited particle!");
+
+      /* Check that the particle we interact with was not inhibited */
+      if (pjd < gcount_j && gpart_is_inhibited(&gparts_j[pjd], e) &&
+          mass_j != 0.f)
+        error("Inhibited particle used as gravity source.");
+
       /* Check that the particle was initialised */
       if (gparts_i[pid].initialised == 0)
         error("Adding forces to an un-initialised gpart.");
@@ -376,7 +401,8 @@ static INLINE void runner_dopair_grav_pp_truncated(
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Update the interaction counter if it's not a padded gpart */
-      if (pjd < gcount_j) gparts_i[pid].num_interacted++;
+      if (pjd < gcount_j && !gpart_is_inhibited(&gparts_j[pjd], e))
+        gparts_i[pid].num_interacted++;
 #endif
     }
 
@@ -449,6 +475,10 @@ static INLINE void runner_dopair_grav_pm_full(
     if (gparts_i[pid].ti_drift != e->ti_current)
       error("gpi not drifted to current time");
 
+    /* Check that we are not updated an inhibited particle */
+    if (gpart_is_inhibited(&gparts_i[pid], e))
+      error("Updating an inhibited particle!");
+
     /* Check that the particle was initialised */
     if (gparts_i[pid].initialised == 0)
       error("Adding forces to an un-initialised gpart.");
@@ -479,7 +509,7 @@ static INLINE void runner_dopair_grav_pm_full(
     const float r2 = dx * dx + dy * dy + dz * dz;
 
 #ifdef SWIFT_DEBUG_CHECKS
-    const float r_max_j = cj->multipole->r_max;
+    const float r_max_j = cj->grav.multipole->r_max;
     const float r_max2 = r_max_j * r_max_j;
     const float theta_crit2 = e->gravity_properties->theta_crit2;
 
@@ -505,7 +535,7 @@ static INLINE void runner_dopair_grav_pm_full(
 #ifdef SWIFT_DEBUG_CHECKS
     /* Update the interaction counter */
     if (pid < gcount_i)
-      gparts_i[pid].num_interacted += cj->multipole->m_pole.num_gpart;
+      gparts_i[pid].num_interacted += cj->grav.multipole->m_pole.num_gpart;
 #endif
   }
 }
@@ -578,6 +608,10 @@ static INLINE void runner_dopair_grav_pm_truncated(
     if (gparts_i[pid].ti_drift != e->ti_current)
       error("gpi not drifted to current time");
 
+    /* Check that we are not updated an inhibited particle */
+    if (gpart_is_inhibited(&gparts_i[pid], e))
+      error("Updating an inhibited particle!");
+
     /* Check that the particle was initialised */
     if (gparts_i[pid].initialised == 0)
       error("Adding forces to an un-initialised gpart.");
@@ -606,7 +640,7 @@ static INLINE void runner_dopair_grav_pm_truncated(
     const float r2 = dx * dx + dy * dy + dz * dz;
 
 #ifdef SWIFT_DEBUG_CHECKS
-    const float r_max_j = cj->multipole->r_max;
+    const float r_max_j = cj->grav.multipole->r_max;
     const float r_max2 = r_max_j * r_max_j;
     const float theta_crit2 = e->gravity_properties->theta_crit2;
 
@@ -632,7 +666,7 @@ static INLINE void runner_dopair_grav_pm_truncated(
 #ifdef SWIFT_DEBUG_CHECKS
     /* Update the interaction counter */
     if (pid < gcount_i)
-      gparts_i[pid].num_interacted += cj->multipole->m_pole.num_gpart;
+      gparts_i[pid].num_interacted += cj->grav.multipole->m_pole.num_gpart;
 #endif
   }
 }
@@ -685,9 +719,9 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
   /* Let's start by checking things are drifted */
   if (!cell_are_gpart_drifted(ci, e)) error("Un-drifted gparts");
   if (!cell_are_gpart_drifted(cj, e)) error("Un-drifted gparts");
-  if (cj_active && ci->ti_old_multipole != e->ti_current)
+  if (cj_active && ci->grav.ti_old_multipole != e->ti_current)
     error("Un-drifted multipole");
-  if (ci_active && cj->ti_old_multipole != e->ti_current)
+  if (ci_active && cj->grav.ti_old_multipole != e->ti_current)
     error("Un-drifted multipole");
 
   /* Caches to play with */
@@ -699,24 +733,24 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
   const double shift_j[3] = {0., 0., 0.};
 
   /* Recover the multipole info and shift the CoM locations */
-  const float rmax_i = ci->multipole->r_max;
-  const float rmax_j = cj->multipole->r_max;
+  const float rmax_i = ci->grav.multipole->r_max;
+  const float rmax_j = cj->grav.multipole->r_max;
   const float rmax2_i = rmax_i * rmax_i;
   const float rmax2_j = rmax_j * rmax_j;
-  const struct multipole *multi_i = &ci->multipole->m_pole;
-  const struct multipole *multi_j = &cj->multipole->m_pole;
-  const float CoM_i[3] = {(float)(ci->multipole->CoM[0] - shift_i[0]),
-                          (float)(ci->multipole->CoM[1] - shift_i[1]),
-                          (float)(ci->multipole->CoM[2] - shift_i[2])};
-  const float CoM_j[3] = {(float)(cj->multipole->CoM[0] - shift_j[0]),
-                          (float)(cj->multipole->CoM[1] - shift_j[1]),
-                          (float)(cj->multipole->CoM[2] - shift_j[2])};
+  const struct multipole *multi_i = &ci->grav.multipole->m_pole;
+  const struct multipole *multi_j = &cj->grav.multipole->m_pole;
+  const float CoM_i[3] = {(float)(ci->grav.multipole->CoM[0] - shift_i[0]),
+                          (float)(ci->grav.multipole->CoM[1] - shift_i[1]),
+                          (float)(ci->grav.multipole->CoM[2] - shift_i[2])};
+  const float CoM_j[3] = {(float)(cj->grav.multipole->CoM[0] - shift_j[0]),
+                          (float)(cj->grav.multipole->CoM[1] - shift_j[1]),
+                          (float)(cj->grav.multipole->CoM[2] - shift_j[2])};
 
   /* Start by constructing particle caches */
 
   /* Computed the padded counts */
-  const int gcount_i = ci->gcount;
-  const int gcount_j = cj->gcount;
+  const int gcount_i = ci->grav.count;
+  const int gcount_j = cj->grav.count;
   const int gcount_padded_i = gcount_i - (gcount_i % VEC_SIZE) + VEC_SIZE;
   const int gcount_padded_j = gcount_j - (gcount_j % VEC_SIZE) + VEC_SIZE;
 
@@ -729,10 +763,10 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
 
   /* Fill the caches */
   gravity_cache_populate(e->max_active_bin, allow_mpole, periodic, dim,
-                         ci_cache, ci->gparts, gcount_i, gcount_padded_i,
+                         ci_cache, ci->grav.parts, gcount_i, gcount_padded_i,
                          shift_i, CoM_j, rmax2_j, ci, e->gravity_properties);
   gravity_cache_populate(e->max_active_bin, allow_mpole, periodic, dim,
-                         cj_cache, cj->gparts, gcount_j, gcount_padded_j,
+                         cj_cache, cj->grav.parts, gcount_j, gcount_padded_j,
                          shift_j, CoM_i, rmax2_i, cj, e->gravity_properties);
 
   /* Can we use the Newtonian version or do we need the truncated one ? */
@@ -745,25 +779,27 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
 
       /* First the P2P */
       runner_dopair_grav_pp_full(ci_cache, cj_cache, gcount_i, gcount_j,
-                                 gcount_padded_j, periodic, dim, e, ci->gparts,
-                                 cj->gparts);
+                                 gcount_padded_j, periodic, dim, e,
+                                 ci->grav.parts, cj->grav.parts);
 
       /* Then the M2P */
       if (allow_mpole)
         runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j,
-                                   periodic, dim, e, ci->gparts, gcount_i, cj);
+                                   periodic, dim, e, ci->grav.parts, gcount_i,
+                                   cj);
     }
     if (cj_active && symmetric) {
 
       /* First the P2P */
       runner_dopair_grav_pp_full(cj_cache, ci_cache, gcount_j, gcount_i,
-                                 gcount_padded_i, periodic, dim, e, cj->gparts,
-                                 ci->gparts);
+                                 gcount_padded_i, periodic, dim, e,
+                                 cj->grav.parts, ci->grav.parts);
 
       /* Then the M2P */
       if (allow_mpole)
         runner_dopair_grav_pm_full(cj_cache, gcount_padded_j, CoM_i, multi_i,
-                                   periodic, dim, e, cj->gparts, gcount_j, ci);
+                                   periodic, dim, e, cj->grav.parts, gcount_j,
+                                   ci);
     }
 
   } else { /* Periodic BC */
@@ -787,26 +823,26 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
         /* First the (truncated) P2P */
         runner_dopair_grav_pp_truncated(ci_cache, cj_cache, gcount_i, gcount_j,
                                         gcount_padded_j, dim, r_s_inv, e,
-                                        ci->gparts, cj->gparts);
+                                        ci->grav.parts, cj->grav.parts);
 
         /* Then the M2P */
         if (allow_mpole)
           runner_dopair_grav_pm_truncated(ci_cache, gcount_padded_i, CoM_j,
-                                          multi_j, dim, r_s_inv, e, ci->gparts,
-                                          gcount_i, cj);
+                                          multi_j, dim, r_s_inv, e,
+                                          ci->grav.parts, gcount_i, cj);
       }
       if (cj_active && symmetric) {
 
         /* First the (truncated) P2P */
         runner_dopair_grav_pp_truncated(cj_cache, ci_cache, gcount_j, gcount_i,
                                         gcount_padded_i, dim, r_s_inv, e,
-                                        cj->gparts, ci->gparts);
+                                        cj->grav.parts, ci->grav.parts);
 
         /* Then the M2P */
         if (allow_mpole)
           runner_dopair_grav_pm_truncated(cj_cache, gcount_padded_j, CoM_i,
-                                          multi_i, dim, r_s_inv, e, cj->gparts,
-                                          gcount_j, ci);
+                                          multi_i, dim, r_s_inv, e,
+                                          cj->grav.parts, gcount_j, ci);
       }
 
     } else {
@@ -819,12 +855,12 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
         /* First the (Newtonian) P2P */
         runner_dopair_grav_pp_full(ci_cache, cj_cache, gcount_i, gcount_j,
                                    gcount_padded_j, periodic, dim, e,
-                                   ci->gparts, cj->gparts);
+                                   ci->grav.parts, cj->grav.parts);
 
         /* Then the M2P */
         if (allow_mpole)
           runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j,
-                                     periodic, dim, e, ci->gparts, gcount_i,
+                                     periodic, dim, e, ci->grav.parts, gcount_i,
                                      cj);
       }
       if (cj_active && symmetric) {
@@ -832,21 +868,21 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
         /* First the (Newtonian) P2P */
         runner_dopair_grav_pp_full(cj_cache, ci_cache, gcount_j, gcount_i,
                                    gcount_padded_i, periodic, dim, e,
-                                   cj->gparts, ci->gparts);
+                                   cj->grav.parts, ci->grav.parts);
 
         /* Then the M2P */
         if (allow_mpole)
           runner_dopair_grav_pm_full(cj_cache, gcount_padded_j, CoM_i, multi_i,
-                                     periodic, dim, e, cj->gparts, gcount_j,
+                                     periodic, dim, e, cj->grav.parts, gcount_j,
                                      ci);
       }
     }
   }
 
   /* Write back to the particles */
-  if (ci_active) gravity_cache_write_back(ci_cache, ci->gparts, gcount_i);
+  if (ci_active) gravity_cache_write_back(ci_cache, ci->grav.parts, gcount_i);
   if (cj_active && symmetric)
-    gravity_cache_write_back(cj_cache, cj->gparts, gcount_j);
+    gravity_cache_write_back(cj_cache, cj->grav.parts, gcount_j);
 
   TIMER_TOC(timer_dopair_grav_pp);
 }
@@ -922,9 +958,18 @@ static INLINE void runner_doself_grav_pp_full(
       /* Check that particles have been drifted to the current time */
       if (gparts[pid].ti_drift != e->ti_current)
         error("gpi not drifted to current time");
-      if (pjd < gcount && gparts[pjd].ti_drift != e->ti_current)
+      if (pjd < gcount && gparts[pjd].ti_drift != e->ti_current &&
+          !gpart_is_inhibited(&gparts[pjd], e))
         error("gpj not drifted to current time");
 
+      /* Check that we are not updated an inhibited particle */
+      if (gpart_is_inhibited(&gparts[pid], e))
+        error("Updating an inhibited particle!");
+
+      /* Check that the particle we interact with was not inhibited */
+      if (pjd < gcount && gpart_is_inhibited(&gparts[pjd], e) && mass_j != 0.f)
+        error("Inhibited particle used as gravity source.");
+
       /* Check that the particle was initialised */
       if (gparts[pid].initialised == 0)
         error("Adding forces to an un-initialised gpart.");
@@ -943,7 +988,8 @@ static INLINE void runner_doself_grav_pp_full(
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Update the interaction counter if it's not a padded gpart */
-      if (pjd < gcount) gparts[pid].num_interacted++;
+      if (pjd < gcount && !gpart_is_inhibited(&gparts[pjd], e))
+        gparts[pid].num_interacted++;
 #endif
     }
 
@@ -1036,9 +1082,18 @@ static INLINE void runner_doself_grav_pp_truncated(
       /* Check that particles have been drifted to the current time */
       if (gparts[pid].ti_drift != e->ti_current)
         error("gpi not drifted to current time");
-      if (pjd < gcount && gparts[pjd].ti_drift != e->ti_current)
+      if (pjd < gcount && gparts[pjd].ti_drift != e->ti_current &&
+          !gpart_is_inhibited(&gparts[pjd], e))
         error("gpj not drifted to current time");
 
+      /* Check that we are not updated an inhibited particle */
+      if (gpart_is_inhibited(&gparts[pid], e))
+        error("Updating an inhibited particle!");
+
+      /* Check that the particle we interact with was not inhibited */
+      if (pjd < gcount && gpart_is_inhibited(&gparts[pjd], e) && mass_j != 0.f)
+        error("Inhibited particle used as gravity source.");
+
       /* Check that the particle was initialised */
       if (gparts[pid].initialised == 0)
         error("Adding forces to an un-initialised gpart.");
@@ -1057,7 +1112,8 @@ static INLINE void runner_doself_grav_pp_truncated(
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Update the interaction counter if it's not a padded gpart */
-      if (pjd < gcount) gparts[pid].num_interacted++;
+      if (pjd < gcount && !gpart_is_inhibited(&gparts[pjd], e))
+        gparts[pid].num_interacted++;
 #endif
     }
 
@@ -1094,7 +1150,7 @@ static INLINE void runner_doself_grav_pp(struct runner *r, struct cell *c) {
   TIMER_TIC;
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->gcount == 0) error("Doing self gravity on an empty cell !");
+  if (c->grav.count == 0) error("Doing self gravity on an empty cell !");
 #endif
 
   /* Anything to do here? */
@@ -1115,7 +1171,7 @@ static INLINE void runner_doself_grav_pp(struct runner *r, struct cell *c) {
                          c->loc[2] + 0.5 * c->width[2]};
 
   /* Computed the padded counts */
-  const int gcount = c->gcount;
+  const int gcount = c->grav.count;
   const int gcount_padded = gcount - (gcount % VEC_SIZE) + VEC_SIZE;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -1125,7 +1181,7 @@ static INLINE void runner_doself_grav_pp(struct runner *r, struct cell *c) {
 #endif
 
   /* Fill the cache */
-  gravity_cache_populate_no_mpole(e->max_active_bin, ci_cache, c->gparts,
+  gravity_cache_populate_no_mpole(e->max_active_bin, ci_cache, c->grav.parts,
                                   gcount, gcount_padded, loc, c,
                                   e->gravity_properties);
 
@@ -1133,29 +1189,31 @@ static INLINE void runner_doself_grav_pp(struct runner *r, struct cell *c) {
   if (!periodic) {
 
     /* Not periodic -> Can always use Newtonian potential */
-    runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e, c->gparts);
+    runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e,
+                               c->grav.parts);
 
   } else {
 
     /* Get the maximal distance between any two particles */
-    const double max_r = 2. * c->multipole->r_max;
+    const double max_r = 2. * c->grav.multipole->r_max;
 
     /* Do we need to use the truncated interactions ? */
     if (max_r > min_trunc) {
 
       /* Periodic but far-away cells must use the truncated potential */
       runner_doself_grav_pp_truncated(ci_cache, gcount, gcount_padded, r_s_inv,
-                                      e, c->gparts);
+                                      e, c->grav.parts);
 
     } else {
 
       /* Periodic but close-by cells can use the full Newtonian potential */
-      runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e, c->gparts);
+      runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e,
+                                 c->grav.parts);
     }
   }
 
   /* Write back to the particles */
-  gravity_cache_write_back(ci_cache, c->gparts, gcount);
+  gravity_cache_write_back(ci_cache, c->grav.parts, gcount);
 
   TIMER_TOC(timer_doself_grav_pp);
 }
@@ -1187,8 +1245,8 @@ static INLINE void runner_dopair_grav_mm_symmetric(struct runner *r,
     error("Invalid state in symmetric M-M calculation!");
 
   /* Short-cut to the multipole */
-  const struct multipole *multi_i = &ci->multipole->m_pole;
-  const struct multipole *multi_j = &cj->multipole->m_pole;
+  const struct multipole *multi_i = &ci->grav.multipole->m_pole;
+  const struct multipole *multi_j = &cj->grav.multipole->m_pole;
 
 #ifdef SWIFT_DEBUG_CHECKS
   if (ci == cj) error("Interacting a cell with itself using M2L");
@@ -1199,29 +1257,29 @@ static INLINE void runner_dopair_grav_mm_symmetric(struct runner *r,
   if (multi_j->num_gpart == 0)
     error("Multipole j does not seem to have been set.");
 
-  if (ci->multipole->pot.ti_init != e->ti_current)
+  if (ci->grav.multipole->pot.ti_init != e->ti_current)
     error("ci->grav tensor not initialised.");
 
-  if (ci->multipole->pot.ti_init != e->ti_current)
+  if (ci->grav.multipole->pot.ti_init != e->ti_current)
     error("cj->grav tensor not initialised.");
 
-  if (ci->ti_old_multipole != e->ti_current)
+  if (ci->grav.ti_old_multipole != e->ti_current)
     error(
-        "Undrifted multipole ci->ti_old_multipole=%lld ci->nodeID=%d "
+        "Undrifted multipole ci->grav.ti_old_multipole=%lld ci->nodeID=%d "
         "cj->nodeID=%d e->ti_current=%lld",
-        ci->ti_old_multipole, ci->nodeID, cj->nodeID, e->ti_current);
+        ci->grav.ti_old_multipole, ci->nodeID, cj->nodeID, e->ti_current);
 
-  if (cj->ti_old_multipole != e->ti_current)
+  if (cj->grav.ti_old_multipole != e->ti_current)
     error(
-        "Undrifted multipole cj->ti_old_multipole=%lld cj->nodeID=%d "
+        "Undrifted multipole cj->grav.ti_old_multipole=%lld cj->nodeID=%d "
         "ci->nodeID=%d e->ti_current=%lld",
-        cj->ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current);
+        cj->grav.ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current);
 #endif
 
   /* Let's interact at this level */
-  gravity_M2L_symmetric(&ci->multipole->pot, &cj->multipole->pot, multi_i,
-                        multi_j, ci->multipole->CoM, cj->multipole->CoM, props,
-                        periodic, dim, r_s_inv);
+  gravity_M2L_symmetric(&ci->grav.multipole->pot, &cj->grav.multipole->pot,
+                        multi_i, multi_j, ci->grav.multipole->CoM,
+                        cj->grav.multipole->CoM, props, periodic, dim, r_s_inv);
 
   TIMER_TOC(timer_dopair_grav_mm);
 }
@@ -1251,7 +1309,7 @@ static INLINE void runner_dopair_grav_mm_nonsym(
   if (!cell_is_active_gravity_mm(ci, e) || ci->nodeID != engine_rank) return;
 
   /* Short-cut to the multipole */
-  const struct multipole *multi_j = &cj->multipole->m_pole;
+  const struct multipole *multi_j = &cj->grav.multipole->m_pole;
 
 #ifdef SWIFT_DEBUG_CHECKS
   if (ci == cj) error("Interacting a cell with itself using M2L");
@@ -1259,19 +1317,19 @@ static INLINE void runner_dopair_grav_mm_nonsym(
   if (multi_j->num_gpart == 0)
     error("Multipole does not seem to have been set.");
 
-  if (ci->multipole->pot.ti_init != e->ti_current)
+  if (ci->grav.multipole->pot.ti_init != e->ti_current)
     error("ci->grav tensor not initialised.");
 
-  if (cj->ti_old_multipole != e->ti_current)
+  if (cj->grav.ti_old_multipole != e->ti_current)
     error(
-        "Undrifted multipole cj->ti_old_multipole=%lld cj->nodeID=%d "
+        "Undrifted multipole cj->grav.ti_old_multipole=%lld cj->nodeID=%d "
         "ci->nodeID=%d e->ti_current=%lld",
-        cj->ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current);
+        cj->grav.ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current);
 #endif
 
   /* Let's interact at this level */
-  gravity_M2L_nonsym(&ci->multipole->pot, multi_j, ci->multipole->CoM,
-                     cj->multipole->CoM, props, periodic, dim, r_s_inv);
+  gravity_M2L_nonsym(&ci->grav.multipole->pot, multi_j, ci->grav.multipole->CoM,
+                     cj->grav.multipole->CoM, props, periodic, dim, r_s_inv);
 
   TIMER_TOC(timer_dopair_grav_mm);
 }
@@ -1296,8 +1354,8 @@ static INLINE void runner_dopair_grav_mm(struct runner *r,
       cell_is_active_gravity_mm(cj, e) && (cj->nodeID == e->nodeID);
 
   /* Do we need drifting first? */
-  if (ci->ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e);
-  if (cj->ti_old_multipole < e->ti_current) cell_drift_multipole(cj, e);
+  if (ci->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e);
+  if (cj->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(cj, e);
 
   /* Interact! */
   if (do_i && do_j)
@@ -1335,7 +1393,7 @@ static INLINE void runner_dopair_grav_mm_progenies(struct runner *r,
           const int flag = i * 8 + j;
 
           /* Did we agree to use an M-M interaction here at the last rebuild? */
-          if (flags & (1LL << flag)) runner_dopair_grav_mm(r, cpi, cpj);
+          if (flags & (1ULL << flag)) runner_dopair_grav_mm(r, cpi, cpj);
         }
       }
     }
@@ -1356,14 +1414,14 @@ static INLINE void runner_dopair_recursive_grav_pm(struct runner *r,
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Early abort? */
-  if (ci->gcount == 0 || cj->gcount == 0)
+  if (ci->grav.count == 0 || cj->grav.count == 0)
     error("Doing pair gravity on an empty cell !");
 
   /* Sanity check */
   if (ci == cj) error("Pair interaction between a cell and itself.");
 
-  if (cj->ti_old_multipole != e->ti_current)
-    error("cj->multipole not drifted.");
+  if (cj->grav.ti_old_multipole != e->ti_current)
+    error("cj->grav.multipole not drifted.");
 #endif
 
   /* Can we recurse further? */
@@ -1384,7 +1442,7 @@ static INLINE void runner_dopair_recursive_grav_pm(struct runner *r,
     struct gravity_cache *const ci_cache = &r->ci_gravity_cache;
 
     /* Computed the padded counts */
-    const int gcount_i = ci->gcount;
+    const int gcount_i = ci->grav.count;
     const int gcount_padded_i = gcount_i - (gcount_i % VEC_SIZE) + VEC_SIZE;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -1394,32 +1452,33 @@ static INLINE void runner_dopair_recursive_grav_pm(struct runner *r,
 #endif
 
     /* Recover the multipole info and the CoM locations */
-    const struct multipole *multi_j = &cj->multipole->m_pole;
-    const float r_max = cj->multipole->r_max;
-    const float CoM_j[3] = {(float)(cj->multipole->CoM[0]),
-                            (float)(cj->multipole->CoM[1]),
-                            (float)(cj->multipole->CoM[2])};
+    const struct multipole *multi_j = &cj->grav.multipole->m_pole;
+    const float r_max = cj->grav.multipole->r_max;
+    const float CoM_j[3] = {(float)(cj->grav.multipole->CoM[0]),
+                            (float)(cj->grav.multipole->CoM[1]),
+                            (float)(cj->grav.multipole->CoM[2])};
 
     /* Fill the cache */
     gravity_cache_populate_all_mpole(
-        e->max_active_bin, periodic, dim, ci_cache, ci->gparts, gcount_i,
+        e->max_active_bin, periodic, dim, ci_cache, ci->grav.parts, gcount_i,
         gcount_padded_i, ci, CoM_j, r_max * r_max, e->gravity_properties);
 
     /* Can we use the Newtonian version or do we need the truncated one ? */
     if (!periodic) {
 
       runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j,
-                                 periodic, dim, e, ci->gparts, gcount_i, cj);
+                                 periodic, dim, e, ci->grav.parts, gcount_i,
+                                 cj);
 
     } else {
 
       runner_dopair_grav_pm_truncated(ci_cache, gcount_padded_i, CoM_j, multi_j,
-                                      dim, r_s_inv, e, ci->gparts, gcount_i,
+                                      dim, r_s_inv, e, ci->grav.parts, gcount_i,
                                       cj);
     }
 
     /* Write back to the particles */
-    gravity_cache_write_back(ci_cache, ci->gparts, gcount_i);
+    gravity_cache_write_back(ci_cache, ci->grav.parts, gcount_i);
   }
 }
 
@@ -1457,8 +1516,8 @@ static INLINE void runner_dopair_recursive_grav(struct runner *r,
 
 #ifdef SWIFT_DEBUG_CHECKS
 
-  const int gcount_i = ci->gcount;
-  const int gcount_j = cj->gcount;
+  const int gcount_i = ci->grav.count;
+  const int gcount_j = cj->grav.count;
 
   /* Early abort? */
   if (gcount_i == 0 || gcount_j == 0)
@@ -1467,17 +1526,19 @@ static INLINE void runner_dopair_recursive_grav(struct runner *r,
   /* Sanity check */
   if (ci == cj) error("Pair interaction between a cell and itself.");
 
-  if (cell_is_active_gravity(ci, e) && ci->ti_old_multipole != e->ti_current)
-    error("ci->multipole not drifted.");
-  if (cell_is_active_gravity(cj, e) && cj->ti_old_multipole != e->ti_current)
-    error("cj->multipole not drifted.");
+  if (cell_is_active_gravity(ci, e) &&
+      ci->grav.ti_old_multipole != e->ti_current)
+    error("ci->grav.multipole not drifted.");
+  if (cell_is_active_gravity(cj, e) &&
+      cj->grav.ti_old_multipole != e->ti_current)
+    error("cj->grav.multipole not drifted.");
 #endif
 
   TIMER_TIC;
 
   /* Recover the multipole information */
-  struct gravity_tensors *const multi_i = ci->multipole;
-  struct gravity_tensors *const multi_j = cj->multipole;
+  struct gravity_tensors *const multi_i = ci->grav.multipole;
+  struct gravity_tensors *const multi_j = cj->grav.multipole;
 
   /* Get the distance between the CoMs */
   double dx = multi_i->CoM[0] - multi_j->CoM[0];
@@ -1599,7 +1660,7 @@ static INLINE void runner_doself_recursive_grav(struct runner *r,
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Early abort? */
-  if (c->gcount == 0) error("Doing self gravity on an empty cell !");
+  if (c->grav.count == 0) error("Doing self gravity on an empty cell !");
 #endif
 
   TIMER_TIC;
@@ -1657,7 +1718,8 @@ static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci,
 
   /* Recover the list of top-level cells */
   struct cell *cells = e->s->cells_top;
-  const int nr_cells = e->s->nr_cells;
+  int *cells_with_particles = e->s->cells_with_particles_top;
+  const int nr_cells_with_particles = e->s->nr_cells_with_particles;
 
   /* Anything to do here? */
   if (!cell_is_active_gravity(ci, e)) return;
@@ -1666,28 +1728,28 @@ static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci,
     error("Non-local cell in long-range gravity task!");
 
   /* Check multipole has been drifted */
-  if (ci->ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e);
+  if (ci->grav.ti_old_multipole < e->ti_current) cell_drift_multipole(ci, e);
 
   /* Get this cell's multipole information */
-  struct gravity_tensors *const multi_i = ci->multipole;
+  struct gravity_tensors *const multi_i = ci->grav.multipole;
 
   /* Find this cell's top-level (great-)parent */
   struct cell *top = ci;
   while (top->parent != NULL) top = top->parent;
 
   /* Recover the top-level multipole (for distance checks) */
-  struct gravity_tensors *const multi_top = top->multipole;
+  struct gravity_tensors *const multi_top = top->grav.multipole;
   const double CoM_rebuild_top[3] = {multi_top->CoM_rebuild[0],
                                      multi_top->CoM_rebuild[1],
                                      multi_top->CoM_rebuild[2]};
 
   /* Loop over all the top-level cells and go for a M-M interaction if
    * well-separated */
-  for (int n = 0; n < nr_cells; ++n) {
+  for (int n = 0; n < nr_cells_with_particles; ++n) {
 
     /* Handle on the top-level cell and it's gravity business*/
-    struct cell *cj = &cells[n];
-    const struct gravity_tensors *const multi_j = cj->multipole;
+    const struct cell *cj = &cells[cells_with_particles[n]];
+    const struct gravity_tensors *const multi_j = cj->grav.multipole;
 
     /* Avoid self contributions */
     if (top == cj) continue;
diff --git a/src/runner_doiact_stars.h b/src/runner_doiact_stars.h
index 7941fdc926e83e5a1806afa70fde9ad7e6c4a502..e696e4fd10853536008d9d9fafc90e6475fd291a 100644
--- a/src/runner_doiact_stars.h
+++ b/src/runner_doiact_stars.h
@@ -40,10 +40,10 @@ void runner_doself_stars_density(struct runner *r, struct cell *c, int timer) {
   const float a = cosmo->a;
   const float H = cosmo->H;
 
-  const int scount = c->scount;
-  const int count = c->count;
-  struct spart *restrict sparts = c->sparts;
-  struct part *restrict parts = c->parts;
+  const int scount = c->stars.count;
+  const int count = c->hydro.count;
+  struct spart *restrict sparts = c->stars.parts;
+  struct part *restrict parts = c->hydro.parts;
 
   /* Loop over the sparts in ci. */
   for (int sid = 0; sid < scount; sid++) {
@@ -101,10 +101,10 @@ void runner_dosubpair_stars_density(struct runner *r, struct cell *restrict ci,
   /* Anything to do here? */
   if (!cell_is_active_stars(ci, e) && !cell_is_active_stars(cj, e)) return;
 
-  const int scount_i = ci->scount;
-  const int count_j = cj->count;
-  struct spart *restrict sparts_i = ci->sparts;
-  struct part *restrict parts_j = cj->parts;
+  const int scount_i = ci->stars.count;
+  const int count_j = cj->hydro.count;
+  struct spart *restrict sparts_i = ci->stars.parts;
+  struct part *restrict parts_j = cj->hydro.parts;
 
   /* Cosmological terms */
   const float a = cosmo->a;
@@ -194,8 +194,8 @@ void runner_dopair_subset_stars_density(struct runner *r,
 
   TIMER_TIC;
 
-  const int count_j = cj->count;
-  struct part *restrict parts_j = cj->parts;
+  const int count_j = cj->hydro.count;
+  struct part *restrict parts_j = cj->hydro.parts;
 
   /* Cosmological terms */
   const float a = cosmo->a;
@@ -269,8 +269,8 @@ void runner_doself_subset_stars_density(struct runner *r,
   const float a = cosmo->a;
   const float H = cosmo->H;
 
-  const int count_i = ci->count;
-  struct part *restrict parts_j = ci->parts;
+  const int count_i = ci->hydro.count;
+  struct part *restrict parts_j = ci->hydro.parts;
 
   /* Loop over the parts in ci. */
   for (int spid = 0; spid < scount; spid++) {
@@ -382,15 +382,16 @@ void runner_dosub_subset_stars_density(struct runner *r, struct cell *ci,
   if (!cell_is_active_stars(ci, e) &&
       (cj == NULL || !cell_is_active_stars(cj, e)))
     return;
-  if (ci->scount == 0 || (cj != NULL && cj->scount == 0)) return;
+  if (ci->stars.count == 0 || (cj != NULL && cj->stars.count == 0)) return;
 
   /* Find out in which sub-cell of ci the parts are. */
   struct cell *sub = NULL;
   if (ci->split) {
     for (int k = 0; k < 8; k++) {
       if (ci->progeny[k] != NULL) {
-        if (&sparts[ind[0]] >= &ci->progeny[k]->sparts[0] &&
-            &sparts[ind[0]] < &ci->progeny[k]->sparts[ci->progeny[k]->scount]) {
+        if (&sparts[ind[0]] >= &ci->progeny[k]->stars.parts[0] &&
+            &sparts[ind[0]] <
+                &ci->progeny[k]->stars.parts[ci->progeny[k]->stars.count]) {
           sub = ci->progeny[k];
           break;
         }
@@ -962,7 +963,7 @@ void runner_doself_branch_stars_density(struct runner *r, struct cell *c) {
   if (!cell_is_active_stars(c, e)) return;
 
   /* Did we mess up the recursion? */
-  if (c->h_max_old * kernel_gamma > c->dmin)
+  if (c->stars.h_max_old * kernel_gamma > c->dmin)
     error("Cell smaller than smoothing length");
 
   runner_doself_stars_density(r, c, 1);
@@ -994,49 +995,51 @@ void runner_dopair_branch_stars_density(struct runner *r, struct cell *ci,
   const int sid = space_getsid(e->s, &ci, &cj, shift);
 
   /* Have the cells been sorted? */
-  if (!(ci->sorted & (1 << sid)) ||
-      ci->dx_max_sort_old > space_maxreldx * ci->dmin)
+  if (!(ci->hydro.sorted & (1 << sid)) ||
+      ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin)
     error("Interacting unsorted cells.");
-  if (!(cj->sorted & (1 << sid)) ||
-      cj->dx_max_sort_old > space_maxreldx * cj->dmin)
+  if (!(cj->hydro.sorted & (1 << sid)) ||
+      cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin)
     error("Interacting unsorted cells.");
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Pick-out the sorted lists. */
-  const struct entry *restrict sort_i = ci->sort[sid];
-  const struct entry *restrict sort_j = cj->sort[sid];
+  const struct entry *restrict sort_i = ci->hydro.sort[sid];
+  const struct entry *restrict sort_j = cj->hydro.sort[sid];
 
   /* Check that the dx_max_sort values in the cell are indeed an upper
      bound on particle movement. */
-  for (int pid = 0; pid < ci->count; pid++) {
-    const struct part *p = &ci->parts[sort_i[pid].i];
+  for (int pid = 0; pid < ci->hydro.count; pid++) {
+    const struct part *p = &ci->hydro.parts[sort_i[pid].i];
     const float d = p->x[0] * runner_shift[sid][0] +
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
-    if (fabsf(d - sort_i[pid].d) - ci->dx_max_sort >
-            1.0e-4 * max(fabsf(d), ci->dx_max_sort_old) &&
-        fabsf(d - sort_i[pid].d) - ci->dx_max_sort > ci->width[0] * 1.0e-10)
+    if (fabsf(d - sort_i[pid].d) - ci->hydro.dx_max_sort >
+            1.0e-4 * max(fabsf(d), ci->hydro.dx_max_sort_old) &&
+        fabsf(d - sort_i[pid].d) - ci->hydro.dx_max_sort >
+            ci->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell ci. ci->nodeID=%d "
-          "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->dx_max_sort=%e "
-          "ci->dx_max_sort_old=%e",
-          ci->nodeID, cj->nodeID, d, sort_i[pid].d, ci->dx_max_sort,
-          ci->dx_max_sort_old);
+          "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->hydro.dx_max_sort=%e "
+          "ci->hydro.dx_max_sort_old=%e",
+          ci->nodeID, cj->nodeID, d, sort_i[pid].d, ci->hydro.dx_max_sort,
+          ci->hydro.dx_max_sort_old);
   }
-  for (int pjd = 0; pjd < cj->count; pjd++) {
-    const struct part *p = &cj->parts[sort_j[pjd].i];
+  for (int pjd = 0; pjd < cj->hydro.count; pjd++) {
+    const struct part *p = &cj->hydro.parts[sort_j[pjd].i];
     const float d = p->x[0] * runner_shift[sid][0] +
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
-    if ((fabsf(d - sort_j[pjd].d) - cj->dx_max_sort) >
-            1.0e-4 * max(fabsf(d), cj->dx_max_sort_old) &&
-        (fabsf(d - sort_j[pjd].d) - cj->dx_max_sort) > cj->width[0] * 1.0e-10)
+    if ((fabsf(d - sort_j[pjd].d) - cj->hydro.dx_max_sort) >
+            1.0e-4 * max(fabsf(d), cj->hydro.dx_max_sort_old) &&
+        (fabsf(d - sort_j[pjd].d) - cj->hydro.dx_max_sort) >
+            cj->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell cj. cj->nodeID=%d "
-          "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->dx_max_sort=%e "
-          "cj->dx_max_sort_old=%e",
-          cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->dx_max_sort,
-          cj->dx_max_sort_old);
+          "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->hydro.dx_max_sort=%e "
+          "cj->hydro.dx_max_sort_old=%e",
+          cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->hydro.dx_max_sort,
+          cj->hydro.dx_max_sort_old);
   }
 #endif /* SWIFT_DEBUG_CHECKS */
 
@@ -1065,7 +1068,7 @@ void runner_dosub_pair_stars_density(struct runner *r, struct cell *ci,
 
   /* Should we even bother? */
   if (!cell_is_active_stars(ci, e) && !cell_is_active_stars(cj, e)) return;
-  if (ci->scount == 0 || cj->scount == 0) return;
+  if (ci->stars.count == 0 || cj->stars.count == 0) return;
 
   /* Get the type of pair if not specified explicitly. */
   double shift[3];
@@ -1357,11 +1360,11 @@ void runner_dosub_pair_stars_density(struct runner *r, struct cell *ci,
       error("Interacting undrifted cells.");
 
     /* Do any of the cells need to be sorted first? */
-    if (!(ci->sorted & (1 << sid)) ||
-        ci->dx_max_sort_old > ci->dmin * space_maxreldx)
+    if (!(ci->hydro.sorted & (1 << sid)) ||
+        ci->hydro.dx_max_sort_old > ci->dmin * space_maxreldx)
       error("Interacting unsorted cell.");
-    if (!(cj->sorted & (1 << sid)) ||
-        cj->dx_max_sort_old > cj->dmin * space_maxreldx)
+    if (!(cj->hydro.sorted & (1 << sid)) ||
+        cj->hydro.dx_max_sort_old > cj->dmin * space_maxreldx)
       error("Interacting unsorted cell.");
 
     /* Compute the interactions. */
@@ -1384,7 +1387,7 @@ void runner_dosub_self_stars_density(struct runner *r, struct cell *ci,
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (ci->scount == 0 || !cell_is_active_stars(ci, r->e)) return;
+  if (ci->stars.count == 0 || !cell_is_active_stars(ci, r->e)) return;
 
   /* Recurse? */
   if (cell_can_recurse_in_self_stars_task(ci)) {
diff --git a/src/runner_doiact_vec.c b/src/runner_doiact_vec.c
index 2e86280d64491ee1750f41c2cd22ab01c08e30b8..c74fa7c8f53576f2e80578488fdf3378c59c0400 100644
--- a/src/runner_doiact_vec.c
+++ b/src/runner_doiact_vec.c
@@ -270,10 +270,10 @@ __attribute__((always_inline)) INLINE static void populate_max_index_density(
     int *max_index_i, int *max_index_j, int *init_pi, int *init_pj,
     const timebin_t max_active_bin, const int active_ci, const int active_cj) {
 
-  const struct part *restrict parts_i = ci->parts;
-  const struct part *restrict parts_j = cj->parts;
+  const struct part *restrict parts_i = ci->hydro.parts;
+  const struct part *restrict parts_j = cj->hydro.parts;
 
-  int first_pi = 0, last_pj = cj->count - 1;
+  int first_pi = 0, last_pj = cj->hydro.count - 1;
   int temp, active_id;
 
   /* Only populate max_index array for local actve cells. */
@@ -281,7 +281,7 @@ __attribute__((always_inline)) INLINE static void populate_max_index_density(
 
     /* Find the leftmost active particle in cell i that interacts with any
      * particle in cell j. */
-    first_pi = ci->count;
+    first_pi = ci->hydro.count;
     active_id = first_pi - 1;
     while (first_pi > 0 && sort_i[first_pi - 1].d + dx_max + hi_max > dj_min) {
       first_pi--;
@@ -295,7 +295,7 @@ __attribute__((always_inline)) INLINE static void populate_max_index_density(
 
     /* Find the maximum index into cell j for each particle in range in cell i.
      */
-    if (first_pi < ci->count) {
+    if (first_pi < ci->hydro.count) {
 
       /* Start from the first particle in cell j. */
       temp = 0;
@@ -305,33 +305,33 @@ __attribute__((always_inline)) INLINE static void populate_max_index_density(
           sort_i[first_pi].d + pi->h * kernel_gamma + dx_max - rshift;
 
       /* Loop through particles in cell j until they are not in range of pi.
-       * Make sure that temp stays between 0 and cj->count - 1.*/
-      while (temp < cj->count - 1 && first_di > sort_j[temp].d) temp++;
+       * Make sure that temp stays between 0 and cj->hydro.count - 1.*/
+      while (temp < cj->hydro.count - 1 && first_di > sort_j[temp].d) temp++;
 
       max_index_i[first_pi] = temp;
 
       /* Populate max_index_i for remaining particles that are within range. */
-      for (int i = first_pi + 1; i < ci->count; i++) {
+      for (int i = first_pi + 1; i < ci->hydro.count; i++) {
         temp = max_index_i[i - 1];
         pi = &parts_i[sort_i[i].i];
 
         const float di = sort_i[i].d + pi->h * kernel_gamma + dx_max - rshift;
 
-        /* Make sure that temp stays between 0 and cj->count - 1.*/
-        while (temp < cj->count - 1 && di > sort_j[temp].d) temp++;
+        /* Make sure that temp stays between 0 and cj->hydro.count - 1.*/
+        while (temp < cj->hydro.count - 1 && di > sort_j[temp].d) temp++;
 
         max_index_i[i] = temp;
       }
     } else {
       /* Make sure that max index is set to first particle in cj.*/
-      max_index_i[ci->count - 1] = 0;
+      max_index_i[ci->hydro.count - 1] = 0;
     }
   } else {
     /* Make sure that foreign cells are only read into the cache if the local
      * cell requires it.
      * Also ensure that it does not require any particles from cj. */
-    first_pi = ci->count - 1;
-    max_index_i[ci->count - 1] = 0;
+    first_pi = ci->hydro.count - 1;
+    max_index_i[ci->hydro.count - 1] = 0;
   }
 
   /* Only populate max_index array for local actve cells. */
@@ -340,7 +340,7 @@ __attribute__((always_inline)) INLINE static void populate_max_index_density(
      * particle in cell i. */
     last_pj = -1;
     active_id = last_pj;
-    while (last_pj < cj->count &&
+    while (last_pj < cj->hydro.count &&
            sort_j[last_pj + 1].d - hj_max - dx_max < di_max) {
       last_pj++;
       /* Store the index of the particle if it is active. */
@@ -356,7 +356,7 @@ __attribute__((always_inline)) INLINE static void populate_max_index_density(
     if (last_pj >= 0) {
 
       /* Start from the last particle in cell i. */
-      temp = ci->count - 1;
+      temp = ci->hydro.count - 1;
 
       const struct part *pj = &parts_j[sort_j[last_pj].i];
       const float last_dj =
@@ -379,14 +379,14 @@ __attribute__((always_inline)) INLINE static void populate_max_index_density(
       }
     } else {
       /* Make sure that max index is set to last particle in ci.*/
-      max_index_j[0] = ci->count - 1;
+      max_index_j[0] = ci->hydro.count - 1;
     }
   } else {
     /* Make sure that foreign cells are only read into the cache if the local
      * cell requires it.
      * Also ensure that it does not require any particles from ci. */
     last_pj = 0;
-    max_index_j[0] = ci->count - 1;
+    max_index_j[0] = ci->hydro.count - 1;
   }
 
   *init_pi = first_pi;
@@ -430,10 +430,10 @@ __attribute__((always_inline)) INLINE static void populate_max_index_force(
     int *init_pj, const timebin_t max_active_bin, const int active_ci,
     const int active_cj) {
 
-  const struct part *restrict parts_i = ci->parts;
-  const struct part *restrict parts_j = cj->parts;
+  const struct part *restrict parts_i = ci->hydro.parts;
+  const struct part *restrict parts_j = cj->hydro.parts;
 
-  int first_pi = 0, last_pj = cj->count - 1;
+  int first_pi = 0, last_pj = cj->hydro.count - 1;
   int temp, active_id;
 
   /* Only populate max_index array for local actve cells. */
@@ -441,7 +441,7 @@ __attribute__((always_inline)) INLINE static void populate_max_index_force(
 
     /* Find the leftmost active particle in cell i that interacts with any
      * particle in cell j. */
-    first_pi = ci->count;
+    first_pi = ci->hydro.count;
     active_id = first_pi - 1;
     while (first_pi > 0 && sort_i[first_pi - 1].d + dx_max + h_max > dj_min) {
       first_pi--;
@@ -455,7 +455,7 @@ __attribute__((always_inline)) INLINE static void populate_max_index_force(
 
     /* Find the maximum index into cell j for each particle in range in cell i.
      */
-    if (first_pi < ci->count) {
+    if (first_pi < ci->hydro.count) {
 
       /* Start from the first particle in cell j. */
       temp = 0;
@@ -466,34 +466,34 @@ __attribute__((always_inline)) INLINE static void populate_max_index_force(
                              rshift;
 
       /* Loop through particles in cell j until they are not in range of pi.
-       * Make sure that temp stays between 0 and cj->count - 1.*/
-      while (temp < cj->count - 1 && first_di > sort_j[temp].d) temp++;
+       * Make sure that temp stays between 0 and cj->hydro.count - 1.*/
+      while (temp < cj->hydro.count - 1 && first_di > sort_j[temp].d) temp++;
 
       max_index_i[first_pi] = temp;
 
       /* Populate max_index_i for remaining particles that are within range. */
-      for (int i = first_pi + 1; i < ci->count; i++) {
+      for (int i = first_pi + 1; i < ci->hydro.count; i++) {
         temp = max_index_i[i - 1];
         pi = &parts_i[sort_i[i].i];
 
         const float di = sort_i[i].d + max(pi->h, hj_max_raw) * kernel_gamma +
                          dx_max - rshift;
 
-        /* Make sure that temp stays between 0 and cj->count - 1.*/
-        while (temp < cj->count - 1 && di > sort_j[temp].d) temp++;
+        /* Make sure that temp stays between 0 and cj->hydro.count - 1.*/
+        while (temp < cj->hydro.count - 1 && di > sort_j[temp].d) temp++;
 
         max_index_i[i] = temp;
       }
     } else {
       /* Make sure that max index is set to first particle in cj.*/
-      max_index_i[ci->count - 1] = 0;
+      max_index_i[ci->hydro.count - 1] = 0;
     }
   } else {
     /* Make sure that foreign cells are only read into the cache if the local
      * cell requires it.
      * Also ensure that it does not require any particles from cj. */
-    first_pi = ci->count - 1;
-    max_index_i[ci->count - 1] = 0;
+    first_pi = ci->hydro.count - 1;
+    max_index_i[ci->hydro.count - 1] = 0;
   }
 
   /* Only populate max_index array for local actve cells. */
@@ -502,7 +502,7 @@ __attribute__((always_inline)) INLINE static void populate_max_index_force(
      * particle in cell i. */
     last_pj = -1;
     active_id = last_pj;
-    while (last_pj < cj->count &&
+    while (last_pj < cj->hydro.count &&
            sort_j[last_pj + 1].d - h_max - dx_max < di_max) {
       last_pj++;
       /* Store the index of the particle if it is active. */
@@ -518,7 +518,7 @@ __attribute__((always_inline)) INLINE static void populate_max_index_force(
     if (last_pj >= 0) {
 
       /* Start from the last particle in cell i. */
-      temp = ci->count - 1;
+      temp = ci->hydro.count - 1;
 
       const struct part *pj = &parts_j[sort_j[last_pj].i];
       const float last_dj = sort_j[last_pj].d - dx_max -
@@ -543,14 +543,14 @@ __attribute__((always_inline)) INLINE static void populate_max_index_force(
       }
     } else {
       /* Make sure that max index is set to last particle in ci.*/
-      max_index_j[0] = ci->count - 1;
+      max_index_j[0] = ci->hydro.count - 1;
     }
   } else {
     /* Make sure that foreign cells are only read into the cache if the local
      * cell requires it.
      * Also ensure that it does not require any particles from ci. */
     last_pj = 0;
-    max_index_j[0] = ci->count - 1;
+    max_index_j[0] = ci->hydro.count - 1;
   }
 
   *init_pi = first_pi;
@@ -655,8 +655,8 @@ void runner_doself1_density_vec(struct runner *r, struct cell *restrict c) {
   /* Get some local variables */
   const struct engine *e = r->e;
   const timebin_t max_active_bin = e->max_active_bin;
-  struct part *restrict parts = c->parts;
-  const int count = c->count;
+  struct part *restrict parts = c->hydro.parts;
+  const int count = c->hydro.count;
 
   TIMER_TIC;
 
@@ -888,7 +888,7 @@ void runner_doself_subset_density_vec(struct runner *r, struct cell *restrict c,
 
 #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH)
 
-  const int count = c->count;
+  const int count = c->hydro.count;
 
   TIMER_TIC;
 
@@ -1016,7 +1016,7 @@ void runner_doself_subset_density_vec(struct runner *r, struct cell *restrict c,
                             vec_is_mask_true(v_doi_mask2_self_check);
 
 #ifdef DEBUG_INTERACTIONS_SPH
-      struct part *restrict parts_i = c->parts;
+      struct part *restrict parts_i = c->hydro.parts;
       for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) {
         if (doi_mask & (1 << bit_index)) {
           if (pi->num_ngb_density < MAX_NUM_OF_NEIGHBOURS)
@@ -1113,8 +1113,8 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) {
   const struct engine *e = r->e;
   const struct cosmology *restrict cosmo = e->cosmology;
   const timebin_t max_active_bin = e->max_active_bin;
-  struct part *restrict parts = c->parts;
-  const int count = c->count;
+  struct part *restrict parts = c->hydro.parts;
+  const int count = c->hydro.count;
 
   TIMER_TIC;
 
@@ -1322,19 +1322,19 @@ void runner_dopair1_density_vec(struct runner *r, struct cell *ci,
   for (int k = 0; k < 3; k++) rshift += shift[k] * runner_shift[sid][k];
 
   /* Pick-out the sorted lists. */
-  const struct entry *restrict sort_i = ci->sort[sid];
-  const struct entry *restrict sort_j = cj->sort[sid];
+  const struct entry *restrict sort_i = ci->hydro.sort[sid];
+  const struct entry *restrict sort_j = cj->hydro.sort[sid];
 
   /* Get some other useful values. */
-  const int count_i = ci->count;
-  const int count_j = cj->count;
-  const double hi_max = ci->h_max * kernel_gamma - rshift;
-  const double hj_max = cj->h_max * kernel_gamma;
-  struct part *restrict parts_i = ci->parts;
-  struct part *restrict parts_j = cj->parts;
+  const int count_i = ci->hydro.count;
+  const int count_j = cj->hydro.count;
+  const double hi_max = ci->hydro.h_max * kernel_gamma - rshift;
+  const double hj_max = cj->hydro.h_max * kernel_gamma;
+  struct part *restrict parts_i = ci->hydro.parts;
+  struct part *restrict parts_j = cj->hydro.parts;
   const double di_max = sort_i[count_i - 1].d - rshift;
   const double dj_min = sort_j[0].d;
-  const float dx_max = (ci->dx_max_sort + cj->dx_max_sort);
+  const float dx_max = (ci->hydro.dx_max_sort + cj->hydro.dx_max_sort);
   const int active_ci = cell_is_active_hydro(ci, e) && ci_local;
   const int active_cj = cell_is_active_hydro(cj, e) && cj_local;
 
@@ -1693,11 +1693,11 @@ void runner_dopair_subset_density_vec(struct runner *r,
 
   TIMER_TIC;
 
-  const int count_j = cj->count;
+  const int count_j = cj->hydro.count;
 
   /* Pick-out the sorted lists. */
-  const struct entry *restrict sort_j = cj->sort[sid];
-  const float dxj = cj->dx_max_sort;
+  const struct entry *restrict sort_j = cj->hydro.sort[sid];
+  const float dxj = cj->hydro.dx_max_sort;
 
   /* Get both particle caches from the runner and re-allocate
    * them if they are not big enough for the cells. */
@@ -1806,7 +1806,7 @@ void runner_dopair_subset_density_vec(struct runner *r,
         vec_create_mask(v_doi_mask, vec_cmp_lt(v_r2.v, v_hig2.v));
 
 #ifdef DEBUG_INTERACTIONS_SPH
-        struct part *restrict parts_j = cj->parts;
+        struct part *restrict parts_j = cj->hydro.parts;
         for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) {
           if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) {
             if (pi->num_ngb_density < MAX_NUM_OF_NEIGHBOURS) {
@@ -1935,7 +1935,7 @@ void runner_dopair_subset_density_vec(struct runner *r,
         vec_create_mask(v_doi_mask, vec_cmp_lt(v_r2.v, v_hig2.v));
 
 #ifdef DEBUG_INTERACTIONS_SPH
-        struct part *restrict parts_j = cj->parts;
+        struct part *restrict parts_j = cj->hydro.parts;
         for (int bit_index = 0; bit_index < VEC_SIZE; bit_index++) {
           if (vec_is_mask_true(v_doi_mask) & (1 << bit_index)) {
             if (pi->num_ngb_density < MAX_NUM_OF_NEIGHBOURS) {
@@ -2007,21 +2007,21 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci,
   for (int k = 0; k < 3; k++) rshift += shift[k] * runner_shift[sid][k];
 
   /* Pick-out the sorted lists. */
-  const struct entry *restrict sort_i = ci->sort[sid];
-  const struct entry *restrict sort_j = cj->sort[sid];
+  const struct entry *restrict sort_i = ci->hydro.sort[sid];
+  const struct entry *restrict sort_j = cj->hydro.sort[sid];
 
   /* Get some other useful values. */
-  const int count_i = ci->count;
-  const int count_j = cj->count;
-  const double hi_max = ci->h_max * kernel_gamma;
-  const double hj_max = cj->h_max * kernel_gamma;
-  const double hi_max_raw = ci->h_max;
-  const double hj_max_raw = cj->h_max;
-  struct part *restrict parts_i = ci->parts;
-  struct part *restrict parts_j = cj->parts;
+  const int count_i = ci->hydro.count;
+  const int count_j = cj->hydro.count;
+  const double hi_max = ci->hydro.h_max * kernel_gamma;
+  const double hj_max = cj->hydro.h_max * kernel_gamma;
+  const double hi_max_raw = ci->hydro.h_max;
+  const double hj_max_raw = cj->hydro.h_max;
+  struct part *restrict parts_i = ci->hydro.parts;
+  struct part *restrict parts_j = cj->hydro.parts;
   const double di_max = sort_i[count_i - 1].d - rshift;
   const double dj_min = sort_j[0].d;
-  const float dx_max = (ci->dx_max_sort + cj->dx_max_sort);
+  const float dx_max = (ci->hydro.dx_max_sort + cj->hydro.dx_max_sort);
   const int active_ci = cell_is_active_hydro(ci, e) && ci_local;
   const int active_cj = cell_is_active_hydro(cj, e) && cj_local;
 
diff --git a/src/scheduler.c b/src/scheduler.c
index d582ee9ce17c397c8cfe65f55cf8671591af3ea3..28fb4146983ee40144ff30693453f7e3b27eac3b 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -380,10 +380,13 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose) {
     }
   }
 
-  /* Be clean */
+  /* Close the file */
   fprintf(f, "}");
   fclose(f);
+
+  /* Be clean */
   free(table);
+  free(count_rel);
 
   if (verbose)
     message("Printing task graph took %.3f %s.",
@@ -407,7 +410,7 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
 
     /* Non-splittable task? */
     if ((t->ci == NULL) || (t->type == task_type_pair && t->cj == NULL) ||
-        t->ci->count == 0 || (t->cj != NULL && t->cj->count == 0)) {
+        t->ci->hydro.count == 0 || (t->cj != NULL && t->cj->hydro.count == 0)) {
       t->type = task_type_none;
       t->subtype = task_subtype_none;
       t->cj = NULL;
@@ -431,7 +434,7 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
       if (cell_can_split_self_hydro_task(ci)) {
 
         /* Make a sub? */
-        if (scheduler_dosub && ci->count < space_subsize_self_hydro) {
+        if (scheduler_dosub && ci->hydro.count < space_subsize_self_hydro) {
 
           /* convert to a self-subtask. */
           t->type = task_type_sub_self;
@@ -447,7 +450,7 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
           while (ci->progeny[first_child] == NULL) first_child++;
           t->ci = ci->progeny[first_child];
           for (int k = first_child + 1; k < 8; k++)
-            if (ci->progeny[k] != NULL && ci->progeny[k]->count)
+            if (ci->progeny[k] != NULL && ci->progeny[k]->hydro.count)
               scheduler_splittask_hydro(
                   scheduler_addtask(s, task_type_self, t->subtype, 0, 0,
                                     ci->progeny[k], NULL),
@@ -455,9 +458,9 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
 
           /* Make a task for each pair of progeny */
           for (int j = 0; j < 8; j++)
-            if (ci->progeny[j] != NULL && ci->progeny[j]->count)
+            if (ci->progeny[j] != NULL && ci->progeny[j]->hydro.count)
               for (int k = j + 1; k < 8; k++)
-                if (ci->progeny[k] != NULL && ci->progeny[k]->count)
+                if (ci->progeny[k] != NULL && ci->progeny[k]->hydro.count)
                   scheduler_splittask_hydro(
                       scheduler_addtask(s, task_type_pair, t->subtype,
                                         sub_sid_flag[j][k], 0, ci->progeny[j],
@@ -492,7 +495,8 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
 
         /* Replace by a single sub-task? */
         if (scheduler_dosub && /* Use division to avoid integer overflow. */
-            ci->count * sid_scale[sid] < space_subsize_pair_hydro / cj->count &&
+            ci->hydro.count * sid_scale[sid] <
+                space_subsize_pair_hydro / cj->hydro.count &&
             !sort_is_corner(sid)) {
 
           /* Make this task a sub task. */
@@ -841,18 +845,18 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
 
         /* Otherwise, break it up if it is too large? */
       } else if (scheduler_doforcesplit && ci->split && cj->split &&
-                 (ci->count > space_maxsize / cj->count)) {
+                 (ci->hydro.count > space_maxsize / cj->hydro.count)) {
 
-        // message( "force splitting pair with %i and %i parts." , ci->count ,
-        // cj->count );
+        // message( "force splitting pair with %i and %i parts." ,
+        // ci->hydro.count , cj->hydro.count );
 
         /* Replace the current task. */
         t->type = task_type_none;
 
         for (int j = 0; j < 8; j++)
-          if (ci->progeny[j] != NULL && ci->progeny[j]->count)
+          if (ci->progeny[j] != NULL && ci->progeny[j]->hydro.count)
             for (int k = 0; k < 8; k++)
-              if (cj->progeny[k] != NULL && cj->progeny[k]->count) {
+              if (cj->progeny[k] != NULL && cj->progeny[k]->hydro.count) {
                 struct task *tl =
                     scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
                                       ci->progeny[j], cj->progeny[k]);
@@ -872,8 +876,6 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
  */
 static void scheduler_splittask_stars(struct task *t, struct scheduler *s) {
 
-  // LOIC: This is un-tested. Need to verify that it works.
-
   /* Iterate on this task until we're done with it. */
   int redo = 1;
   while (redo) {
@@ -883,7 +885,7 @@ static void scheduler_splittask_stars(struct task *t, struct scheduler *s) {
 
     /* Non-splittable task? */
     if ((t->ci == NULL) || (t->type == task_type_pair && t->cj == NULL) ||
-        t->ci->scount == 0 || (t->cj != NULL && t->cj->scount == 0)) {
+        t->ci->stars.count == 0 || (t->cj != NULL && t->cj->stars.count == 0)) {
       t->type = task_type_none;
       t->subtype = task_subtype_none;
       t->cj = NULL;
@@ -907,7 +909,7 @@ static void scheduler_splittask_stars(struct task *t, struct scheduler *s) {
       if (cell_can_split_self_stars_task(ci)) {
 
         /* Make a sub? */
-        if (scheduler_dosub && ci->scount < space_subsize_self_stars) {
+        if (scheduler_dosub && ci->stars.count < space_subsize_self_stars) {
 
           /* convert to a self-subtask. */
           t->type = task_type_sub_self;
@@ -923,7 +925,7 @@ static void scheduler_splittask_stars(struct task *t, struct scheduler *s) {
           while (ci->progeny[first_child] == NULL) first_child++;
           t->ci = ci->progeny[first_child];
           for (int k = first_child + 1; k < 8; k++)
-            if (ci->progeny[k] != NULL && ci->progeny[k]->scount)
+            if (ci->progeny[k] != NULL && ci->progeny[k]->stars.count)
               scheduler_splittask_stars(
                   scheduler_addtask(s, task_type_self, t->subtype, 0, 0,
                                     ci->progeny[k], NULL),
@@ -931,9 +933,9 @@ static void scheduler_splittask_stars(struct task *t, struct scheduler *s) {
 
           /* Make a task for each pair of progeny */
           for (int j = 0; j < 8; j++)
-            if (ci->progeny[j] != NULL && ci->progeny[j]->scount)
+            if (ci->progeny[j] != NULL && ci->progeny[j]->stars.count)
               for (int k = j + 1; k < 8; k++)
-                if (ci->progeny[k] != NULL && ci->progeny[k]->scount)
+                if (ci->progeny[k] != NULL && ci->progeny[k]->stars.count)
                   scheduler_splittask_stars(
                       scheduler_addtask(s, task_type_pair, t->subtype,
                                         sub_sid_flag[j][k], 0, ci->progeny[j],
@@ -968,8 +970,8 @@ static void scheduler_splittask_stars(struct task *t, struct scheduler *s) {
 
         /* Replace by a single sub-task? */
         if (scheduler_dosub && /* Use division to avoid integer overflow. */
-            ci->scount * sid_scale[sid] <
-                space_subsize_pair_stars / cj->scount &&
+            ci->stars.count * sid_scale[sid] <
+                space_subsize_pair_stars / cj->stars.count &&
             !sort_is_corner(sid)) {
 
           /* Make this task a sub task. */
@@ -1318,15 +1320,15 @@ static void scheduler_splittask_stars(struct task *t, struct scheduler *s) {
 
         /* Otherwise, break it up if it is too large? */
       } else if (scheduler_doforcesplit && ci->split && cj->split &&
-                 (ci->scount > space_maxsize / cj->scount)) {
+                 (ci->stars.count > space_maxsize / cj->stars.count)) {
 
         /* Replace the current task. */
         t->type = task_type_none;
 
         for (int j = 0; j < 8; j++)
-          if (ci->progeny[j] != NULL && ci->progeny[j]->scount)
+          if (ci->progeny[j] != NULL && ci->progeny[j]->stars.count)
             for (int k = 0; k < 8; k++)
-              if (cj->progeny[k] != NULL && cj->progeny[k]->scount) {
+              if (cj->progeny[k] != NULL && cj->progeny[k]->stars.count) {
                 struct task *tl =
                     scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
                                       ci->progeny[j], cj->progeny[k]);
@@ -1380,7 +1382,7 @@ static void scheduler_splittask_gravity(struct task *t, struct scheduler *s) {
       /* Should we split this task? */
       if (cell_can_split_self_gravity_task(ci)) {
 
-        if (scheduler_dosub && ci->gcount < space_subsize_self_grav) {
+        if (scheduler_dosub && ci->grav.count < space_subsize_self_grav) {
 
           /* Otherwise, split it. */
         } else {
@@ -1434,8 +1436,8 @@ static void scheduler_splittask_gravity(struct task *t, struct scheduler *s) {
       if (cell_can_split_pair_gravity_task(ci) &&
           cell_can_split_pair_gravity_task(cj)) {
 
-        const long long gcount_i = ci->gcount;
-        const long long gcount_j = cj->gcount;
+        const long long gcount_i = ci->grav.count;
+        const long long gcount_j = cj->grav.count;
 
         /* Replace by a single sub-task? */
         if (scheduler_dosub &&
@@ -1466,7 +1468,7 @@ static void scheduler_splittask_gravity(struct task *t, struct scheduler *s) {
                      * the information and operate according to the choices
                      * made here. */
                     const int flag = i * 8 + j;
-                    t->flags |= (1LL << flag);
+                    t->flags |= (1ULL << flag);
 
                   } else {
 
@@ -1820,35 +1822,44 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
     int partcost = 1;
 #endif
 
-    const float count_i = (t->ci != NULL) ? t->ci->count : 0.f;
-    const float count_j = (t->cj != NULL) ? t->cj->count : 0.f;
-    const float gcount_i = (t->ci != NULL) ? t->ci->gcount : 0.f;
-    const float gcount_j = (t->cj != NULL) ? t->cj->gcount : 0.f;
-    const float scount_i = (t->ci != NULL) ? t->ci->scount : 0.f;
+    const float count_i = (t->ci != NULL) ? t->ci->hydro.count : 0.f;
+    const float count_j = (t->cj != NULL) ? t->cj->hydro.count : 0.f;
+    const float gcount_i = (t->ci != NULL) ? t->ci->grav.count : 0.f;
+    const float gcount_j = (t->cj != NULL) ? t->cj->grav.count : 0.f;
+    const float scount_i = (t->ci != NULL) ? t->ci->stars.count : 0.f;
+    const float scount_j = (t->cj != NULL) ? t->cj->stars.count : 0.f;
 
     switch (t->type) {
       case task_type_sort:
         cost = wscale * intrinsics_popcount(t->flags) * count_i *
-               (sizeof(int) * 8 - intrinsics_clz(t->ci->count));
+               (sizeof(int) * 8 - intrinsics_clz(t->ci->hydro.count));
         break;
 
       case task_type_self:
-        // LOIC: Need to do something for stars here
         if (t->subtype == task_subtype_grav)
           cost = 1.f * (wscale * gcount_i) * gcount_i;
         else if (t->subtype == task_subtype_external_grav)
           cost = 1.f * wscale * gcount_i;
+        else if (t->subtype == task_subtype_stars_density)
+          cost = 1.f * wscale * scount_i * count_i;
         else
           cost = 1.f * (wscale * count_i) * count_i;
         break;
 
       case task_type_pair:
-        // LOIC: Need to do something for stars here
         if (t->subtype == task_subtype_grav) {
           if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID)
             cost = 3.f * (wscale * gcount_i) * gcount_j;
           else
             cost = 2.f * (wscale * gcount_i) * gcount_j;
+        } else if (t->subtype == task_subtype_stars_density) {
+          if (t->ci->nodeID != nodeID)
+            cost = 3.f * wscale * count_i * scount_j * sid_scale[t->flags];
+          else if (t->cj->nodeID != nodeID)
+            cost = 3.f * wscale * scount_i * count_j * sid_scale[t->flags];
+          else
+            cost = 2.f * wscale * (scount_i * count_j + scount_j * count_i) *
+                   sid_scale[t->flags];
         } else {
           if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID)
             cost = 3.f * (wscale * count_i) * count_j * sid_scale[t->flags];
@@ -1858,32 +1869,43 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
         break;
 
       case task_type_sub_pair:
-        // LOIC: Need to do something for stars here
-        if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) {
 #ifdef SWIFT_DEBUG_CHECKS
-          if (t->flags < 0) error("Negative flag value!");
+        if (t->flags < 0) error("Negative flag value!");
 #endif
-          cost = 3.f * (wscale * count_i) * count_j * sid_scale[t->flags];
+        if (t->subtype == task_subtype_stars_density) {
+          if (t->ci->nodeID != nodeID) {
+            cost = 3.f * (wscale * count_i) * scount_j * sid_scale[t->flags];
+          } else if (t->cj->nodeID != nodeID) {
+            cost = 3.f * (wscale * scount_i) * count_j * sid_scale[t->flags];
+          } else {
+            cost = 2.f * wscale * (scount_i * count_j + scount_j * count_i) *
+                   sid_scale[t->flags];
+          }
+
         } else {
-#ifdef SWIFT_DEBUG_CHECKS
-          if (t->flags < 0) error("Negative flag value!");
-#endif
-          cost = 2.f * (wscale * count_i) * count_j * sid_scale[t->flags];
+          if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) {
+            cost = 3.f * (wscale * count_i) * count_j * sid_scale[t->flags];
+          } else {
+            cost = 2.f * (wscale * count_i) * count_j * sid_scale[t->flags];
+          }
         }
         break;
 
       case task_type_sub_self:
-        // LOIC: Need to do something for stars here
-        cost = 1.f * (wscale * count_i) * count_i;
+        if (t->subtype == task_subtype_stars_density) {
+          cost = 1.f * (wscale * scount_i) * count_i;
+        } else {
+          cost = 1.f * (wscale * count_i) * count_i;
+        }
         break;
       case task_type_ghost:
-        if (t->ci == t->ci->super_hydro) cost = wscale * count_i;
+        if (t->ci == t->ci->hydro.super) cost = wscale * count_i;
         break;
       case task_type_extra_ghost:
-        if (t->ci == t->ci->super_hydro) cost = wscale * count_i;
+        if (t->ci == t->ci->hydro.super) cost = wscale * count_i;
         break;
       case task_type_stars_ghost:
-        if (t->ci == t->ci->super_hydro) cost = wscale * scount_i;
+        if (t->ci == t->ci->hydro.super) cost = wscale * scount_i;
         break;
       case task_type_drift_part:
         cost = wscale * count_i;
@@ -2086,21 +2108,22 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
       case task_type_sub_self:
         if (t->subtype == task_subtype_grav ||
             t->subtype == task_subtype_external_grav)
-          qid = t->ci->super_gravity->owner;
+          qid = t->ci->grav.super->owner;
         else
-          qid = t->ci->super_hydro->owner;
+          qid = t->ci->hydro.super->owner;
         break;
       case task_type_sort:
       case task_type_ghost:
       case task_type_drift_part:
-        qid = t->ci->super_hydro->owner;
+        qid = t->ci->hydro.super->owner;
         break;
       case task_type_drift_gpart:
-        qid = t->ci->super_gravity->owner;
+        qid = t->ci->grav.super->owner;
         break;
       case task_type_kick1:
       case task_type_kick2:
       case task_type_stars_ghost:
+      case task_type_logger:
       case task_type_timestep:
         qid = t->ci->super->owner;
         break;
@@ -2115,31 +2138,32 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
 #ifdef WITH_MPI
         if (t->subtype == task_subtype_tend) {
           t->buff = (struct pcell_step *)malloc(sizeof(struct pcell_step) *
-                                                t->ci->pcell_size);
-          err = MPI_Irecv(
-              t->buff, t->ci->pcell_size * sizeof(struct pcell_step), MPI_BYTE,
-              t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype], &t->req);
+                                                t->ci->mpi.pcell_size);
+          err = MPI_Irecv(t->buff,
+                          t->ci->mpi.pcell_size * sizeof(struct pcell_step),
+                          MPI_BYTE, t->ci->nodeID, t->flags,
+                          subtaskMPI_comms[t->subtype], &t->req);
         } else if (t->subtype == task_subtype_xv ||
                    t->subtype == task_subtype_rho ||
                    t->subtype == task_subtype_gradient) {
-          err = MPI_Irecv(t->ci->parts, t->ci->count, part_mpi_type,
+          err = MPI_Irecv(t->ci->hydro.parts, t->ci->hydro.count, part_mpi_type,
                           t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype],
                           &t->req);
           // message( "receiving %i parts with tag=%i from %i to %i." ,
-          //     t->ci->count , t->flags , t->ci->nodeID , s->nodeID );
+          //     t->ci->hydro.count , t->flags , t->ci->nodeID , s->nodeID );
           // fflush(stdout);
         } else if (t->subtype == task_subtype_gpart) {
-          err = MPI_Irecv(t->ci->gparts, t->ci->gcount, gpart_mpi_type,
+          err = MPI_Irecv(t->ci->grav.parts, t->ci->grav.count, gpart_mpi_type,
                           t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype],
                           &t->req);
         } else if (t->subtype == task_subtype_spart) {
-          err = MPI_Irecv(t->ci->sparts, t->ci->scount, spart_mpi_type,
-                          t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype],
-                          &t->req);
+          err = MPI_Irecv(t->ci->stars.parts, t->ci->stars.count,
+                          spart_mpi_type, t->ci->nodeID, t->flags,
+                          subtaskMPI_comms[t->subtype], &t->req);
         } else if (t->subtype == task_subtype_multipole) {
           t->buff = (struct gravity_tensors *)malloc(
-              sizeof(struct gravity_tensors) * t->ci->pcell_size);
-          err = MPI_Irecv(t->buff, t->ci->pcell_size, multipole_mpi_type,
+              sizeof(struct gravity_tensors) * t->ci->mpi.pcell_size);
+          err = MPI_Irecv(t->buff, t->ci->mpi.pcell_size, multipole_mpi_type,
                           t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype],
                           &t->req);
         } else {
@@ -2157,56 +2181,57 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
 #ifdef WITH_MPI
         if (t->subtype == task_subtype_tend) {
           t->buff = (struct pcell_step *)malloc(sizeof(struct pcell_step) *
-                                                t->ci->pcell_size);
+                                                t->ci->mpi.pcell_size);
           cell_pack_end_step(t->ci, (struct pcell_step *)t->buff);
-          if ((t->ci->pcell_size * sizeof(struct pcell_step)) >
+          if ((t->ci->mpi.pcell_size * sizeof(struct pcell_step)) >
               s->mpi_message_limit)
             err = MPI_Isend(t->buff,
-                            t->ci->pcell_size * sizeof(struct pcell_step),
+                            t->ci->mpi.pcell_size * sizeof(struct pcell_step),
                             MPI_BYTE, t->cj->nodeID, t->flags,
                             subtaskMPI_comms[t->subtype], &t->req);
           else
             err = MPI_Issend(t->buff,
-                             t->ci->pcell_size * sizeof(struct pcell_step),
+                             t->ci->mpi.pcell_size * sizeof(struct pcell_step),
                              MPI_BYTE, t->cj->nodeID, t->flags,
                              subtaskMPI_comms[t->subtype], &t->req);
         } else if (t->subtype == task_subtype_xv ||
                    t->subtype == task_subtype_rho ||
                    t->subtype == task_subtype_gradient) {
-          if ((t->ci->count * sizeof(struct part)) > s->mpi_message_limit)
-            err = MPI_Isend(t->ci->parts, t->ci->count, part_mpi_type,
-                            t->cj->nodeID, t->flags,
+          if ((t->ci->hydro.count * sizeof(struct part)) > s->mpi_message_limit)
+            err = MPI_Isend(t->ci->hydro.parts, t->ci->hydro.count,
+                            part_mpi_type, t->cj->nodeID, t->flags,
                             subtaskMPI_comms[t->subtype], &t->req);
           else
-            err = MPI_Issend(t->ci->parts, t->ci->count, part_mpi_type,
-                             t->cj->nodeID, t->flags,
+            err = MPI_Issend(t->ci->hydro.parts, t->ci->hydro.count,
+                             part_mpi_type, t->cj->nodeID, t->flags,
                              subtaskMPI_comms[t->subtype], &t->req);
           // message( "sending %i parts with tag=%i from %i to %i." ,
-          //     t->ci->count , t->flags , s->nodeID , t->cj->nodeID );
+          //     t->ci->hydro.count , t->flags , s->nodeID , t->cj->nodeID );
           // fflush(stdout);
         } else if (t->subtype == task_subtype_gpart) {
-          if ((t->ci->gcount * sizeof(struct gpart)) > s->mpi_message_limit)
-            err = MPI_Isend(t->ci->gparts, t->ci->gcount, gpart_mpi_type,
-                            t->cj->nodeID, t->flags,
+          if ((t->ci->grav.count * sizeof(struct gpart)) > s->mpi_message_limit)
+            err = MPI_Isend(t->ci->grav.parts, t->ci->grav.count,
+                            gpart_mpi_type, t->cj->nodeID, t->flags,
                             subtaskMPI_comms[t->subtype], &t->req);
           else
-            err = MPI_Issend(t->ci->gparts, t->ci->gcount, gpart_mpi_type,
-                             t->cj->nodeID, t->flags,
+            err = MPI_Issend(t->ci->grav.parts, t->ci->grav.count,
+                             gpart_mpi_type, t->cj->nodeID, t->flags,
                              subtaskMPI_comms[t->subtype], &t->req);
         } else if (t->subtype == task_subtype_spart) {
-          if ((t->ci->scount * sizeof(struct spart)) > s->mpi_message_limit)
-            err = MPI_Isend(t->ci->sparts, t->ci->scount, spart_mpi_type,
-                            t->cj->nodeID, t->flags,
+          if ((t->ci->stars.count * sizeof(struct spart)) >
+              s->mpi_message_limit)
+            err = MPI_Isend(t->ci->stars.parts, t->ci->stars.count,
+                            spart_mpi_type, t->cj->nodeID, t->flags,
                             subtaskMPI_comms[t->subtype], &t->req);
           else
-            err = MPI_Issend(t->ci->sparts, t->ci->scount, spart_mpi_type,
-                             t->cj->nodeID, t->flags,
+            err = MPI_Issend(t->ci->stars.parts, t->ci->stars.count,
+                             spart_mpi_type, t->cj->nodeID, t->flags,
                              subtaskMPI_comms[t->subtype], &t->req);
         } else if (t->subtype == task_subtype_multipole) {
           t->buff = (struct gravity_tensors *)malloc(
-              sizeof(struct gravity_tensors) * t->ci->pcell_size);
+              sizeof(struct gravity_tensors) * t->ci->mpi.pcell_size);
           cell_pack_multipoles(t->ci, (struct gravity_tensors *)t->buff);
-          err = MPI_Isend(t->buff, t->ci->pcell_size, multipole_mpi_type,
+          err = MPI_Isend(t->buff, t->ci->mpi.pcell_size, multipole_mpi_type,
                           t->cj->nodeID, t->flags, subtaskMPI_comms[t->subtype],
                           &t->req);
         } else {
@@ -2520,3 +2545,61 @@ void scheduler_free_tasks(struct scheduler *s) {
   }
   s->size = 0;
 }
+
+/**
+ * @brief write down each task level
+ */
+void scheduler_write_task_level(const struct scheduler *s) {
+  /* init */
+  const int max_depth = 30;
+  const struct task *tasks = s->tasks;
+  int nr_tasks = s->nr_tasks;
+
+  /* Init counter */
+  int size = task_type_count * task_subtype_count * max_depth;
+  int *count = (int *)malloc(size * sizeof(int));
+  if (count == NULL) error("Failed to allocate memory");
+
+  for (int i = 0; i < size; i++) count[i] = 0;
+
+  /* Count tasks */
+  for (int i = 0; i < nr_tasks; i++) {
+    const struct task *t = &tasks[i];
+    if (t->ci) {
+
+      if ((int)t->ci->depth >= max_depth)
+        error("Cell is too deep, you need to increase max_depth");
+
+      int ind = t->type * task_subtype_count * max_depth;
+      ind += t->subtype * max_depth;
+      ind += (int)t->ci->depth;
+
+      count[ind] += 1;
+    }
+  }
+
+  /* Open file */
+  char filename[200] = "task_level.txt";
+  FILE *f = fopen(filename, "w");
+  if (f == NULL) error("Error opening task level file.");
+
+  /* Print header */
+  fprintf(f, "# task_type, task_subtype, depth, count\n");
+
+  /* Print tasks level */
+  for (int i = 0; i < size; i++) {
+    if (count[i] == 0) continue;
+
+    int type = i / (task_subtype_count * max_depth);
+    int subtype = i - task_subtype_count * max_depth * type;
+    subtype /= max_depth;
+    int depth = i - task_subtype_count * max_depth * type;
+    depth -= subtype * max_depth;
+    fprintf(f, "%s %s %i %i\n", taskID_names[type], subtaskID_names[subtype],
+            depth, count[i]);
+  }
+
+  /* clean up */
+  fclose(f);
+  free(count);
+}
diff --git a/src/scheduler.h b/src/scheduler.h
index 1a75544de12b8402e553e3ae2b84e2d8a65c56e8..f1e130c6ce2a8538b0126e86ee0cbd79cf5a0e0d 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -173,5 +173,6 @@ void scheduler_print_tasks(const struct scheduler *s, const char *fileName);
 void scheduler_clean(struct scheduler *s);
 void scheduler_free_tasks(struct scheduler *s);
 void scheduler_write_dependencies(struct scheduler *s, int verbose);
+void scheduler_write_task_level(const struct scheduler *s);
 
 #endif /* SWIFT_SCHEDULER_H */
diff --git a/src/serial_io.c b/src/serial_io.c
index 9f874ca8c56c853816c0b52a719267481f443bc4..5e976335e241d07b2fc364e0d5fd2f6618765854 100644
--- a/src/serial_io.c
+++ b/src/serial_io.c
@@ -153,7 +153,37 @@ void readArray(hid_t grp, const struct io_props props, size_t N,
       for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= factor;
     } else {
       float* temp_f = temp;
-      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= factor;
+
+#ifdef SWIFT_DEBUG_CHECKS
+      float maximum = 0.f;
+      float minimum = FLT_MAX;
+#endif
+
+      /* Loop that converts the Units */
+      for (size_t i = 0; i < num_elements; ++i) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+        /* Find the absolute minimum and maximum values */
+        const float abstemp_f = fabsf(temp_f[i]);
+        if (abstemp_f != 0.f) {
+          maximum = max(maximum, abstemp_f);
+          minimum = min(minimum, abstemp_f);
+        }
+#endif
+
+        /* Convert the float units */
+        temp_f[i] *= factor;
+      }
+
+#ifdef SWIFT_DEBUG_CHECKS
+      /* The two possible errors: larger than float or smaller
+       * than float precision. */
+      if (factor * maximum > FLT_MAX) {
+        error("Unit conversion results in numbers larger than floats");
+      } else if (factor * minimum < FLT_MIN) {
+        error("Numbers smaller than float precision");
+      }
+#endif
     }
   }
 
@@ -270,8 +300,9 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName,
   if (h_data < 0) error("Error while creating dataspace '%s'.", props.name);
 
   /* Write XMF description for this data set */
-  xmf_write_line(xmfFile, fileName, partTypeGroupName, props.name, N_total,
-                 props.dimension, props.type);
+  if (xmfFile != NULL)
+    xmf_write_line(xmfFile, fileName, partTypeGroupName, props.name, N_total,
+                   props.dimension, props.type);
 
   /* Write unit conversion factors for this data set */
   char buffer[FIELD_BUFFER_SIZE];
@@ -398,7 +429,6 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
  * @param Ngas (output) The number of #part read from the file on that node.
  * @param Ngparts (output) The number of #gpart read from the file on that node.
  * @param Nstars (output) The number of #spart read from the file on that node.
- * @param periodic (output) 1 if the volume is periodic, 0 if not.
  * @param flag_entropy (output) 1 if the ICs contained Entropy in the
  * InternalEnergy field
  * @param with_hydro Are we reading gas particles ?
@@ -427,11 +457,11 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
 void read_ic_serial(char* fileName, const struct unit_system* internal_units,
                     double dim[3], struct part** parts, struct gpart** gparts,
                     struct spart** sparts, size_t* Ngas, size_t* Ngparts,
-                    size_t* Nstars, int* periodic, int* flag_entropy,
-                    int with_hydro, int with_gravity, int with_stars,
-                    int cleanup_h, int cleanup_sqrt_a, double h, double a,
-                    int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
-                    int n_threads, int dry_run) {
+                    size_t* Nstars, int* flag_entropy, int with_hydro,
+                    int with_gravity, int with_stars, int cleanup_h,
+                    int cleanup_sqrt_a, double h, double a, int mpi_rank,
+                    int mpi_size, MPI_Comm comm, MPI_Info info, int n_threads,
+                    int dry_run) {
 
   hid_t h_file = 0, h_grp = 0;
   /* GADGET has only cubic boxes (in cosmological mode) */
@@ -455,17 +485,6 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
     if (h_file < 0)
       error("Error while opening file '%s' for initial read.", fileName);
 
-    /* Open header to read simulation properties */
-    /* message("Reading runtime parameters..."); */
-    h_grp = H5Gopen(h_file, "/RuntimePars", H5P_DEFAULT);
-    if (h_grp < 0) error("Error while opening runtime parameters\n");
-
-    /* Read the relevant information */
-    io_read_attribute(h_grp, "PeriodicBoundariesOn", INT, periodic);
-
-    /* Close runtime parameters */
-    H5Gclose(h_grp);
-
     /* Open header to read simulation properties */
     /* message("Reading file header..."); */
     h_grp = H5Gopen(h_file, "/Header", H5P_DEFAULT);
@@ -560,7 +579,6 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
 
   /* Now need to broadcast that information to all ranks. */
   MPI_Bcast(flag_entropy, 1, MPI_INT, 0, comm);
-  MPI_Bcast(periodic, 1, MPI_INT, 0, comm);
   MPI_Bcast(&N_total, swift_type_count, MPI_LONG_LONG_INT, 0, comm);
   MPI_Bcast(dim, 3, MPI_DOUBLE, 0, comm);
   MPI_Bcast(ic_units, sizeof(struct unit_system), MPI_BYTE, 0, comm);
@@ -739,22 +757,29 @@ void write_output_serial(struct engine* e, const char* baseName,
                          int mpi_size, MPI_Comm comm, MPI_Info info) {
 
   hid_t h_file = 0, h_grp = 0;
-  const size_t Ngas = e->s->nr_parts;
-  const size_t Nstars = e->s->nr_sparts;
-  const size_t Ntot = e->s->nr_gparts;
   int periodic = e->s->periodic;
   int numFiles = 1;
   const struct part* parts = e->s->parts;
   const struct xpart* xparts = e->s->xparts;
   const struct gpart* gparts = e->s->gparts;
-  struct gpart* dmparts = NULL;
   const struct spart* sparts = e->s->sparts;
-  const struct cooling_function_data* cooling = e->cooling_func;
   struct swift_params* params = e->parameter_file;
   FILE* xmfFile = 0;
 
-  /* Number of unassociated gparts */
-  const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
+  /* Number of particles currently in the arrays */
+  const size_t Ntot = e->s->nr_gparts;
+  const size_t Ngas = e->s->nr_parts;
+  const size_t Nstars = e->s->nr_sparts;
+  // const size_t Nbaryons = Ngas + Nstars;
+  // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0;
+
+  /* Number of particles that we will write */
+  const size_t Ntot_written = e->s->nr_gparts - e->s->nr_inhibited_sparts;
+  const size_t Ngas_written = e->s->nr_parts - e->s->nr_inhibited_parts;
+  const size_t Nstars_written = e->s->nr_sparts - e->s->nr_inhibited_gparts;
+  const size_t Nbaryons_written = Ngas_written + Nstars_written;
+  const size_t Ndm_written =
+      Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0;
 
   /* File name */
   char fileName[FILENAME_BUFFER_SIZE];
@@ -766,7 +791,8 @@ void write_output_serial(struct engine* e, const char* baseName,
              e->snapshot_output_count);
 
   /* Compute offset in the file and total number of particles */
-  size_t N[swift_type_count] = {Ngas, Ndm, 0, 0, Nstars, 0};
+  size_t N[swift_type_count] = {
+      Ngas_written, Ndm_written, 0, 0, Nstars_written, 0};
   long long N_total[swift_type_count] = {0};
   long long offset[swift_type_count] = {0};
   MPI_Exscan(&N, &offset, swift_type_count, MPI_LONG_LONG_INT, MPI_SUM, comm);
@@ -870,7 +896,7 @@ void write_output_serial(struct engine* e, const char* baseName,
     h_grp = H5Gcreate(h_file, "/SubgridScheme", H5P_DEFAULT, H5P_DEFAULT,
                       H5P_DEFAULT);
     if (h_grp < 0) error("Error while creating subgrid group");
-    cooling_write_flavour(h_grp);
+    cooling_write_flavour(h_grp, e->cooling_func);
     chemistry_write_flavour(h_grp);
     H5Gclose(h_grp);
 
@@ -1012,36 +1038,99 @@ void write_output_serial(struct engine* e, const char* baseName,
         struct io_props list[100];
         size_t Nparticles = 0;
 
+        struct part* parts_written = NULL;
+        struct xpart* xparts_written = NULL;
+        struct gpart* gparts_written = NULL;
+        struct spart* sparts_written = NULL;
+
         /* Write particle fields from the particle structure */
         switch (ptype) {
 
-          case swift_type_gas:
-            Nparticles = Ngas;
-            hydro_write_particles(parts, xparts, list, &num_fields);
-            num_fields += chemistry_write_particles(parts, list + num_fields);
-            num_fields +=
-                cooling_write_particles(xparts, list + num_fields, cooling);
-            break;
+          case swift_type_gas: {
+            if (Ngas == Ngas_written) {
+
+              /* No inhibted particles: easy case */
+              Nparticles = Ngas;
+              hydro_write_particles(parts, xparts, list, &num_fields);
+              num_fields += chemistry_write_particles(parts, list + num_fields);
+              num_fields += cooling_write_particles(xparts, list + num_fields,
+                                                    e->cooling_func);
+            } else {
+
+              /* Ok, we need to fish out the particles we want */
+              Nparticles = Ngas_written;
+
+              /* Allocate temporary arrays */
+              if (posix_memalign((void**)&parts_written, part_align,
+                                 Ngas_written * sizeof(struct part)) != 0)
+                error("Error while allocating temporart memory for parts");
+              if (posix_memalign((void**)&xparts_written, xpart_align,
+                                 Ngas_written * sizeof(struct xpart)) != 0)
+                error("Error while allocating temporart memory for xparts");
+
+              /* Collect the particles we want to write */
+              io_collect_parts_to_write(parts, xparts, parts_written,
+                                        xparts_written, Ngas, Ngas_written);
+
+              /* Select the fields to write */
+              hydro_write_particles(parts_written, xparts_written, list,
+                                    &num_fields);
+              num_fields +=
+                  chemistry_write_particles(parts_written, list + num_fields);
+              num_fields += cooling_write_particles(
+                  xparts_written, list + num_fields, e->cooling_func);
+            }
+          } break;
 
-          case swift_type_dark_matter:
-            /* Allocate temporary array */
-            if (posix_memalign((void*)&dmparts, gpart_align,
-                               Ndm * sizeof(struct gpart)) != 0)
-              error("Error while allocating temporart memory for DM particles");
-            bzero(dmparts, Ndm * sizeof(struct gpart));
-
-            /* Collect the DM particles from gpart */
-            io_collect_dm_gparts(gparts, Ntot, dmparts, Ndm);
-
-            /* Write DM particles */
-            Nparticles = Ndm;
-            darkmatter_write_particles(dmparts, list, &num_fields);
-            break;
+          case swift_type_dark_matter: {
+            if (Ntot == Ndm_written) {
 
-          case swift_type_stars:
-            Nparticles = Nstars;
-            stars_write_particles(sparts, list, &num_fields);
-            break;
+              /* This is a DM-only run without inhibited particles */
+              Nparticles = Ntot;
+              darkmatter_write_particles(gparts, list, &num_fields);
+            } else {
+
+              /* Ok, we need to fish out the particles we want */
+              Nparticles = Ndm_written;
+
+              /* Allocate temporary array */
+              if (posix_memalign((void**)&gparts_written, gpart_align,
+                                 Ndm_written * sizeof(struct gpart)) != 0)
+                error("Error while allocating temporart memory for gparts");
+
+              /* Collect the non-inhibited DM particles from gpart */
+              io_collect_gparts_to_write(gparts, gparts_written, Ntot,
+                                         Ndm_written);
+
+              /* Write DM particles */
+              darkmatter_write_particles(gparts_written, list, &num_fields);
+            }
+          } break;
+
+          case swift_type_stars: {
+            if (Nstars == Nstars_written) {
+
+              /* No inhibted particles: easy case */
+              Nparticles = Nstars;
+              stars_write_particles(sparts, list, &num_fields);
+            } else {
+
+              /* Ok, we need to fish out the particles we want */
+              Nparticles = Nstars_written;
+
+              /* Allocate temporary arrays */
+              if (posix_memalign((void**)&sparts_written, spart_align,
+                                 Nstars_written * sizeof(struct spart)) != 0)
+                error("Error while allocating temporart memory for sparts");
+
+              /* Collect the particles we want to write */
+              io_collect_sparts_to_write(sparts, sparts_written, Nstars,
+                                         Nstars_written);
+
+              /* Select the fields to write */
+              stars_write_particles(sparts, list, &num_fields);
+            }
+          } break;
 
           default:
             error("Particle Type %d not yet supported. Aborting", ptype);
@@ -1063,10 +1152,10 @@ void write_output_serial(struct engine* e, const char* baseName,
         }
 
         /* Free temporary array */
-        if (dmparts) {
-          free(dmparts);
-          dmparts = 0;
-        }
+        if (parts_written) free(parts_written);
+        if (xparts_written) free(xparts_written);
+        if (gparts_written) free(gparts_written);
+        if (sparts_written) free(sparts_written);
 
         /* Close particle group */
         H5Gclose(h_grp);
diff --git a/src/serial_io.h b/src/serial_io.h
index 6644e34bb32bcbd63250f25502563155eda0a293..07df76fe869fa0612bba5cf953faadd8bc63f29e 100644
--- a/src/serial_io.h
+++ b/src/serial_io.h
@@ -31,23 +31,30 @@
 
 /* Includes. */
 #include "engine.h"
+#include "io_properties.h"
 #include "part.h"
 #include "units.h"
 
 void read_ic_serial(char* fileName, const struct unit_system* internal_units,
                     double dim[3], struct part** parts, struct gpart** gparts,
                     struct spart** sparts, size_t* Ngas, size_t* Ngparts,
-                    size_t* Nstars, int* periodic, int* flag_entropy,
-                    int with_hydro, int with_gravity, int with_stars,
-                    int cleanup_h, int cleanup_sqrt_a, double h, double a,
-                    int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
-                    int nr_threads, int dry_run);
+                    size_t* Nstars, int* flag_entropy, int with_hydro,
+                    int with_gravity, int with_stars, int cleanup_h,
+                    int cleanup_sqrt_a, double h, double a, int mpi_rank,
+                    int mpi_size, MPI_Comm comm, MPI_Info info, int nr_threads,
+                    int dry_run);
 
 void write_output_serial(struct engine* e, const char* baseName,
                          const struct unit_system* internal_units,
                          const struct unit_system* snapshot_units, int mpi_rank,
                          int mpi_size, MPI_Comm comm, MPI_Info info);
 
+void writeArray(const struct engine* e, hid_t grp, char* fileName,
+                FILE* xmfFile, char* partTypeGroupName,
+                const struct io_props props, size_t N, long long N_total,
+                int mpi_rank, long long offset,
+                const struct unit_system* internal_units,
+                const struct unit_system* snapshot_units);
 #endif
 
 #endif /* SWIFT_SERIAL_IO_H */
diff --git a/src/single_io.c b/src/single_io.c
index 33981b6cfa36bd01248b6ba87c28466cb41e0947..99f016809d11abc4f9f31695306850d82fd56c84 100644
--- a/src/single_io.c
+++ b/src/single_io.c
@@ -127,9 +127,40 @@ void readArray(hid_t h_grp, const struct io_props props, size_t N,
     if (io_is_double_precision(props.type)) {
       double* temp_d = (double*)temp;
       for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= unit_factor;
+
     } else {
       float* temp_f = (float*)temp;
-      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= unit_factor;
+
+#ifdef SWIFT_DEBUG_CHECKS
+      float maximum = 0.f;
+      float minimum = FLT_MAX;
+#endif
+
+      /* Loop that converts the Units */
+      for (size_t i = 0; i < num_elements; ++i) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+        /* Find the absolute minimum and maximum values */
+        const float abstemp_f = fabsf(temp_f[i]);
+        if (abstemp_f != 0.f) {
+          maximum = max(maximum, abstemp_f);
+          minimum = min(minimum, abstemp_f);
+        }
+#endif
+
+        /* Convert the float units */
+        temp_f[i] *= unit_factor;
+      }
+
+#ifdef SWIFT_DEBUG_CHECKS
+      /* The two possible errors: larger than float or smaller
+       * than float precision. */
+      if (unit_factor * maximum > FLT_MAX) {
+        error("Unit conversion results in numbers larger than floats");
+      } else if (unit_factor * minimum < FLT_MIN) {
+        error("Numbers smaller than float precision");
+      }
+#endif
     }
   }
 
@@ -281,8 +312,9 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
   if (h_err < 0) error("Error while writing data array '%s'.", props.name);
 
   /* Write XMF description for this data set */
-  xmf_write_line(xmfFile, fileName, partTypeGroupName, props.name, N,
-                 props.dimension, props.type);
+  if (xmfFile != NULL)
+    xmf_write_line(xmfFile, fileName, partTypeGroupName, props.name, N,
+                   props.dimension, props.type);
 
   /* Write unit conversion factors for this data set */
   char buffer[FIELD_BUFFER_SIZE];
@@ -314,7 +346,6 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
  * @param Ngas (output) number of Gas particles read.
  * @param Ngparts (output) The number of #gpart read.
  * @param Nstars (output) The number of #spart read.
- * @param periodic (output) 1 if the volume is periodic, 0 if not.
  * @param flag_entropy (output) 1 if the ICs contained Entropy in the
  * InternalEnergy field
  * @param with_hydro Are we reading gas particles ?
@@ -339,10 +370,10 @@ void read_ic_single(const char* fileName,
                     const struct unit_system* internal_units, double dim[3],
                     struct part** parts, struct gpart** gparts,
                     struct spart** sparts, size_t* Ngas, size_t* Ngparts,
-                    size_t* Nstars, int* periodic, int* flag_entropy,
-                    int with_hydro, int with_gravity, int with_stars,
-                    int cleanup_h, int cleanup_sqrt_a, double h, double a,
-                    int n_threads, int dry_run) {
+                    size_t* Nstars, int* flag_entropy, int with_hydro,
+                    int with_gravity, int with_stars, int cleanup_h,
+                    int cleanup_sqrt_a, double h, double a, int n_threads,
+                    int dry_run) {
 
   hid_t h_file = 0, h_grp = 0;
   /* GADGET has only cubic boxes (in cosmological mode) */
@@ -359,17 +390,6 @@ void read_ic_single(const char* fileName,
   h_file = H5Fopen(fileName, H5F_ACC_RDONLY, H5P_DEFAULT);
   if (h_file < 0) error("Error while opening file '%s'.", fileName);
 
-  /* Open header to read simulation properties */
-  /* message("Reading runtime parameters..."); */
-  h_grp = H5Gopen(h_file, "/RuntimePars", H5P_DEFAULT);
-  if (h_grp < 0) error("Error while opening runtime parameters\n");
-
-  /* Read the relevant information */
-  io_read_attribute(h_grp, "PeriodicBoundariesOn", INT, periodic);
-
-  /* Close runtime parameters */
-  H5Gclose(h_grp);
-
   /* Open header to read simulation properties */
   /* message("Reading file header..."); */
   h_grp = H5Gopen(h_file, "/Header", H5P_DEFAULT);
@@ -603,24 +623,36 @@ void write_output_single(struct engine* e, const char* baseName,
                          const struct unit_system* snapshot_units) {
 
   hid_t h_file = 0, h_grp = 0;
-  const size_t Ngas = e->s->nr_parts;
-  const size_t Nstars = e->s->nr_sparts;
-  const size_t Ntot = e->s->nr_gparts;
   int periodic = e->s->periodic;
   int numFiles = 1;
   const struct part* parts = e->s->parts;
   const struct xpart* xparts = e->s->xparts;
   const struct gpart* gparts = e->s->gparts;
-  struct gpart* dmparts = NULL;
   const struct spart* sparts = e->s->sparts;
-  const struct cooling_function_data* cooling = e->cooling_func;
   struct swift_params* params = e->parameter_file;
 
-  /* Number of unassociated gparts */
-  const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
-
-  long long N_total[swift_type_count] = {
-      (long long)Ngas, (long long)Ndm, 0, 0, (long long)Nstars, 0};
+  /* Number of particles currently in the arrays */
+  const size_t Ntot = e->s->nr_gparts;
+  const size_t Ngas = e->s->nr_parts;
+  const size_t Nstars = e->s->nr_sparts;
+  // const size_t Nbaryons = Ngas + Nstars;
+  // const size_t Ndm = Ntot > 0 ? Ntot - Nbaryons : 0;
+
+  /* Number of particles that we will write */
+  const size_t Ntot_written = e->s->nr_gparts - e->s->nr_inhibited_sparts;
+  const size_t Ngas_written = e->s->nr_parts - e->s->nr_inhibited_parts;
+  const size_t Nstars_written = e->s->nr_sparts - e->s->nr_inhibited_gparts;
+  const size_t Nbaryons_written = Ngas_written + Nstars_written;
+  const size_t Ndm_written =
+      Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0;
+
+  /* Format things in a Gadget-friendly array */
+  long long N_total[swift_type_count] = {(long long)Ngas_written,
+                                         (long long)Ndm_written,
+                                         0,
+                                         0,
+                                         (long long)Nstars_written,
+                                         0};
 
   /* File name */
   char fileName[FILENAME_BUFFER_SIZE];
@@ -720,7 +752,7 @@ void write_output_single(struct engine* e, const char* baseName,
   h_grp = H5Gcreate(h_file, "/SubgridScheme", H5P_DEFAULT, H5P_DEFAULT,
                     H5P_DEFAULT);
   if (h_grp < 0) error("Error while creating subgrid group");
-  cooling_write_flavour(h_grp);
+  cooling_write_flavour(h_grp, e->cooling_func);
   chemistry_write_flavour(h_grp);
   H5Gclose(h_grp);
 
@@ -828,36 +860,98 @@ void write_output_single(struct engine* e, const char* baseName,
     struct io_props list[100];
     size_t N = 0;
 
+    struct part* parts_written = NULL;
+    struct xpart* xparts_written = NULL;
+    struct gpart* gparts_written = NULL;
+    struct spart* sparts_written = NULL;
+
     /* Write particle fields from the particle structure */
     switch (ptype) {
 
-      case swift_type_gas:
-        N = Ngas;
-        hydro_write_particles(parts, xparts, list, &num_fields);
-        num_fields += chemistry_write_particles(parts, list + num_fields);
-        num_fields +=
-            cooling_write_particles(xparts, list + num_fields, cooling);
-        break;
+      case swift_type_gas: {
+        if (Ngas == Ngas_written) {
+
+          /* No inhibted particles: easy case */
+          N = Ngas;
+          hydro_write_particles(parts, xparts, list, &num_fields);
+          num_fields += chemistry_write_particles(parts, list + num_fields);
+          num_fields += cooling_write_particles(xparts, list + num_fields,
+                                                e->cooling_func);
+        } else {
+
+          /* Ok, we need to fish out the particles we want */
+          N = Ngas_written;
+
+          /* Allocate temporary arrays */
+          if (posix_memalign((void**)&parts_written, part_align,
+                             Ngas_written * sizeof(struct part)) != 0)
+            error("Error while allocating temporart memory for parts");
+          if (posix_memalign((void**)&xparts_written, xpart_align,
+                             Ngas_written * sizeof(struct xpart)) != 0)
+            error("Error while allocating temporart memory for xparts");
+
+          /* Collect the particles we want to write */
+          io_collect_parts_to_write(parts, xparts, parts_written,
+                                    xparts_written, Ngas, Ngas_written);
+
+          /* Select the fields to write */
+          hydro_write_particles(parts_written, xparts_written, list,
+                                &num_fields);
+          num_fields +=
+              chemistry_write_particles(parts_written, list + num_fields);
+          num_fields += cooling_write_particles(
+              xparts_written, list + num_fields, e->cooling_func);
+        }
+      } break;
 
-      case swift_type_dark_matter:
-        /* Allocate temporary array */
-        if (posix_memalign((void**)&dmparts, gpart_align,
-                           Ndm * sizeof(struct gpart)) != 0)
-          error("Error while allocating temporart memory for DM particles");
-        bzero(dmparts, Ndm * sizeof(struct gpart));
-
-        /* Collect the DM particles from gpart */
-        io_collect_dm_gparts(gparts, Ntot, dmparts, Ndm);
-
-        /* Write DM particles */
-        N = Ndm;
-        darkmatter_write_particles(dmparts, list, &num_fields);
-        break;
+      case swift_type_dark_matter: {
+        if (Ntot == Ndm_written) {
 
-      case swift_type_stars:
-        N = Nstars;
-        stars_write_particles(sparts, list, &num_fields);
-        break;
+          /* This is a DM-only run without inhibited particles */
+          N = Ntot;
+          darkmatter_write_particles(gparts, list, &num_fields);
+        } else {
+
+          /* Ok, we need to fish out the particles we want */
+          N = Ndm_written;
+
+          /* Allocate temporary array */
+          if (posix_memalign((void**)&gparts_written, gpart_align,
+                             Ndm_written * sizeof(struct gpart)) != 0)
+            error("Error while allocating temporart memory for gparts");
+
+          /* Collect the non-inhibited DM particles from gpart */
+          io_collect_gparts_to_write(gparts, gparts_written, Ntot, Ndm_written);
+
+          /* Write DM particles */
+          darkmatter_write_particles(gparts_written, list, &num_fields);
+        }
+      } break;
+
+      case swift_type_stars: {
+        if (Nstars == Nstars_written) {
+
+          /* No inhibted particles: easy case */
+          N = Nstars;
+          stars_write_particles(sparts, list, &num_fields);
+        } else {
+
+          /* Ok, we need to fish out the particles we want */
+          N = Nstars_written;
+
+          /* Allocate temporary arrays */
+          if (posix_memalign((void**)&sparts_written, spart_align,
+                             Nstars_written * sizeof(struct spart)) != 0)
+            error("Error while allocating temporart memory for sparts");
+
+          /* Collect the particles we want to write */
+          io_collect_sparts_to_write(sparts, sparts_written, Nstars,
+                                     Nstars_written);
+
+          /* Select the fields to write */
+          stars_write_particles(sparts, list, &num_fields);
+        }
+      } break;
 
       default:
         error("Particle Type %d not yet supported. Aborting", ptype);
@@ -877,11 +971,11 @@ void write_output_single(struct engine* e, const char* baseName,
                    internal_units, snapshot_units);
     }
 
-    /* Free temporary array */
-    if (dmparts) {
-      free(dmparts);
-      dmparts = NULL;
-    }
+    /* Free temporary arrays */
+    if (parts_written) free(parts_written);
+    if (xparts_written) free(xparts_written);
+    if (gparts_written) free(gparts_written);
+    if (sparts_written) free(sparts_written);
 
     /* Close particle group */
     H5Gclose(h_grp);
diff --git a/src/single_io.h b/src/single_io.h
index a0ce8370dfa1009f28e7c399b3f1db345c23de49..62285c3da210243e76347f33780146604673656f 100644
--- a/src/single_io.h
+++ b/src/single_io.h
@@ -26,6 +26,7 @@
 
 /* Includes. */
 #include "engine.h"
+#include "io_properties.h"
 #include "part.h"
 #include "units.h"
 
@@ -33,15 +34,21 @@ void read_ic_single(const char* fileName,
                     const struct unit_system* internal_units, double dim[3],
                     struct part** parts, struct gpart** gparts,
                     struct spart** sparts, size_t* Ngas, size_t* Ndm,
-                    size_t* Nstars, int* periodic, int* flag_entropy,
-                    int with_hydro, int with_gravity, int with_stars,
-                    int cleanup_h, int cleanup_sqrt_a, double h, double a,
-                    int nr_threads, int dry_run);
+                    size_t* Nstars, int* flag_entropy, int with_hydro,
+                    int with_gravity, int with_stars, int cleanup_h,
+                    int cleanup_sqrt_a, double h, double a, int nr_threads,
+                    int dry_run);
 
 void write_output_single(struct engine* e, const char* baseName,
                          const struct unit_system* internal_units,
                          const struct unit_system* snapshot_units);
 
+void writeArray(const struct engine* e, hid_t grp, char* fileName,
+                FILE* xmfFile, char* partTypeGroupName,
+                const struct io_props props, size_t N,
+                const struct unit_system* internal_units,
+                const struct unit_system* snapshot_units);
+
 #endif /* HAVE_HDF5 && !WITH_MPI */
 
 #endif /* SWIFT_SINGLE_IO_H */
diff --git a/src/space.c b/src/space.c
index 79097fbf22dcbe2477328d113592e86d26ed1f08..e11daf80c26e25e3b13b5fbdc33ea547fbb96839 100644
--- a/src/space.c
+++ b/src/space.c
@@ -102,9 +102,11 @@ struct parallel_sort {
  */
 struct index_data {
   struct space *s;
-  struct cell *cells;
   int *ind;
   int *cell_counts;
+  int count_inhibited_part;
+  int count_inhibited_gpart;
+  int count_inhibited_spart;
 };
 
 /**
@@ -135,15 +137,15 @@ void space_rebuild_recycle_rec(struct space *s, struct cell *c,
         *cell_rec_begin = c->progeny[k];
 
         if (s->gravity) {
-          c->progeny[k]->multipole->next = *multipole_rec_begin;
-          *multipole_rec_begin = c->progeny[k]->multipole;
+          c->progeny[k]->grav.multipole->next = *multipole_rec_begin;
+          *multipole_rec_begin = c->progeny[k]->grav.multipole;
         }
 
         if (*cell_rec_end == NULL) *cell_rec_end = *cell_rec_begin;
         if (s->gravity && *multipole_rec_end == NULL)
           *multipole_rec_end = *multipole_rec_begin;
 
-        c->progeny[k]->multipole = NULL;
+        c->progeny[k]->grav.multipole = NULL;
         c->progeny[k] = NULL;
       }
 }
@@ -164,79 +166,86 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     if (cell_rec_begin != NULL)
       space_recycle_list(s, cell_rec_begin, cell_rec_end, multipole_rec_begin,
                          multipole_rec_end);
-    c->sorts = NULL;
+    c->hydro.sorts = NULL;
     c->nr_tasks = 0;
-    c->nr_mm_tasks = 0;
-    c->density = NULL;
-    c->gradient = NULL;
-    c->force = NULL;
-    c->grav = NULL;
-    c->grav_mm = NULL;
-    c->dx_max_part = 0.0f;
-    c->dx_max_sort = 0.0f;
-    c->sorted = 0;
-    c->count = 0;
-    c->gcount = 0;
-    c->scount = 0;
-    c->init_grav = NULL;
-    c->init_grav_out = NULL;
-    c->extra_ghost = NULL;
-    c->ghost_in = NULL;
-    c->ghost_out = NULL;
-    c->ghost = NULL;
-    c->stars_ghost_in = NULL;
-    c->stars_ghost_out = NULL;
-    c->stars_ghost = NULL;
-    c->stars_density = NULL;
+    c->grav.nr_mm_tasks = 0;
+    c->hydro.density = NULL;
+    c->hydro.gradient = NULL;
+    c->hydro.force = NULL;
+    c->grav.grav = NULL;
+    c->grav.mm = NULL;
+    c->hydro.dx_max_part = 0.0f;
+    c->hydro.dx_max_sort = 0.0f;
+    c->stars.dx_max_part = 0.f;
+    c->hydro.sorted = 0;
+    c->hydro.count = 0;
+    c->hydro.updated = 0;
+    c->hydro.inhibited = 0;
+    c->grav.count = 0;
+    c->grav.updated = 0;
+    c->grav.inhibited = 0;
+    c->stars.count = 0;
+    c->stars.updated = 0;
+    c->stars.inhibited = 0;
+    c->grav.init = NULL;
+    c->grav.init_out = NULL;
+    c->hydro.extra_ghost = NULL;
+    c->hydro.ghost_in = NULL;
+    c->hydro.ghost_out = NULL;
+    c->hydro.ghost = NULL;
+    c->stars.ghost_in = NULL;
+    c->stars.ghost_out = NULL;
+    c->stars.ghost = NULL;
+    c->stars.density = NULL;
     c->kick1 = NULL;
     c->kick2 = NULL;
     c->timestep = NULL;
     c->end_force = NULL;
-    c->drift_part = NULL;
-    c->drift_gpart = NULL;
-    c->cooling = NULL;
+    c->hydro.drift = NULL;
+    c->grav.drift = NULL;
+    c->hydro.cooling = NULL;
     c->sourceterms = NULL;
-    c->grav_long_range = NULL;
-    c->grav_down_in = NULL;
-    c->grav_down = NULL;
-    c->grav_mesh = NULL;
+    c->grav.long_range = NULL;
+    c->grav.down_in = NULL;
+    c->grav.down = NULL;
+    c->grav.mesh = NULL;
     c->super = c;
-    c->super_hydro = c;
-    c->super_gravity = c;
-    c->parts = NULL;
-    c->xparts = NULL;
-    c->gparts = NULL;
-    c->sparts = NULL;
-    c->do_sub_sort = 0;
-    c->do_grav_sub_drift = 0;
-    c->do_sub_drift = 0;
-    c->ti_hydro_end_min = -1;
-    c->ti_hydro_end_max = -1;
-    c->ti_gravity_end_min = -1;
-    c->ti_gravity_end_max = -1;
+    c->hydro.super = c;
+    c->grav.super = c;
+    c->hydro.parts = NULL;
+    c->hydro.xparts = NULL;
+    c->grav.parts = NULL;
+    c->stars.parts = NULL;
+    c->hydro.do_sub_sort = 0;
+    c->grav.do_sub_drift = 0;
+    c->hydro.do_sub_drift = 0;
+    c->hydro.ti_end_min = -1;
+    c->hydro.ti_end_max = -1;
+    c->grav.ti_end_min = -1;
+    c->grav.ti_end_max = -1;
 #ifdef SWIFT_DEBUG_CHECKS
     c->cellID = 0;
 #endif
-    if (s->gravity) bzero(c->multipole, sizeof(struct gravity_tensors));
+    if (s->gravity) bzero(c->grav.multipole, sizeof(struct gravity_tensors));
     for (int i = 0; i < 13; i++)
-      if (c->sort[i] != NULL) {
-        free(c->sort[i]);
-        c->sort[i] = NULL;
+      if (c->hydro.sort[i] != NULL) {
+        free(c->hydro.sort[i]);
+        c->hydro.sort[i] = NULL;
       }
 #if WITH_MPI
-    c->tag = -1;
-
-    c->recv_xv = NULL;
-    c->recv_rho = NULL;
-    c->recv_gradient = NULL;
-    c->recv_grav = NULL;
-    c->recv_ti = NULL;
-
-    c->send_xv = NULL;
-    c->send_rho = NULL;
-    c->send_gradient = NULL;
-    c->send_grav = NULL;
-    c->send_ti = NULL;
+    c->mpi.tag = -1;
+
+    c->mpi.hydro.recv_xv = NULL;
+    c->mpi.hydro.recv_rho = NULL;
+    c->mpi.hydro.recv_gradient = NULL;
+    c->mpi.grav.recv = NULL;
+    c->mpi.recv_ti = NULL;
+
+    c->mpi.hydro.send_xv = NULL;
+    c->mpi.hydro.send_rho = NULL;
+    c->mpi.hydro.send_gradient = NULL;
+    c->mpi.grav.send = NULL;
+    c->mpi.send_ti = NULL;
 #endif
   }
 }
@@ -266,6 +275,7 @@ void space_free_cells(struct space *s) {
 void space_regrid(struct space *s, int verbose) {
 
   const size_t nr_parts = s->nr_parts;
+  const size_t nr_sparts = s->nr_sparts;
   const ticks tic = getticks();
   const integertime_t ti_current = (s->e != NULL) ? s->e->ti_current : 0;
 
@@ -273,24 +283,40 @@ void space_regrid(struct space *s, int verbose) {
   // tic = getticks();
   float h_max = s->cell_min / kernel_gamma / space_stretch;
   if (nr_parts > 0) {
-    if (s->local_cells_top != NULL) {
-      for (int k = 0; k < s->nr_local_cells; ++k) {
-        const struct cell *c = &s->cells_top[s->local_cells_top[k]];
-        if (c->h_max > h_max) {
-          h_max = s->cells_top[k].h_max;
+
+    /* Can we use the list of local non-empty top-level cells? */
+    if (s->local_cells_with_particles_top != NULL) {
+      for (int k = 0; k < s->nr_local_cells_with_particles; ++k) {
+        const struct cell *c =
+            &s->cells_top[s->local_cells_with_particles_top[k]];
+        if (c->hydro.h_max > h_max) {
+          h_max = c->hydro.h_max;
+        }
+        if (c->stars.h_max > h_max) {
+          h_max = c->stars.h_max;
         }
       }
+
+      /* Can we instead use all the top-level cells? */
     } else if (s->cells_top != NULL) {
       for (int k = 0; k < s->nr_cells; k++) {
         const struct cell *c = &s->cells_top[k];
-        if (c->nodeID == engine_rank && c->h_max > h_max) {
-          h_max = s->cells_top[k].h_max;
+        if (c->nodeID == engine_rank && c->hydro.h_max > h_max) {
+          h_max = c->hydro.h_max;
+        }
+        if (c->nodeID == engine_rank && c->stars.h_max > h_max) {
+          h_max = c->stars.h_max;
         }
       }
+
+      /* Last option: run through the particles */
     } else {
       for (size_t k = 0; k < nr_parts; k++) {
         if (s->parts[k].h > h_max) h_max = s->parts[k].h;
       }
+      for (size_t k = 0; k < nr_sparts; k++) {
+        if (s->sparts[k].h > h_max) h_max = s->sparts[k].h;
+      }
     }
   }
 
@@ -382,6 +408,8 @@ void space_regrid(struct space *s, int verbose) {
       space_free_cells(s);
       free(s->local_cells_with_tasks_top);
       free(s->local_cells_top);
+      free(s->cells_with_particles_top);
+      free(s->local_cells_with_particles_top);
       free(s->cells_top);
       free(s->multipoles_top);
     }
@@ -429,7 +457,7 @@ void space_regrid(struct space *s, int verbose) {
     /* Allocate the indices of local cells with tasks */
     if (posix_memalign((void **)&s->local_cells_with_tasks_top,
                        SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0)
-      error("Failed to allocate indices of local top-level cells.");
+      error("Failed to allocate indices of local top-level cells with tasks.");
     bzero(s->local_cells_with_tasks_top, s->nr_cells * sizeof(int));
     
     /* Allocate and initialise array of cell indices. */
@@ -439,15 +467,29 @@ void space_regrid(struct space *s, int verbose) {
     /* Set cell index into list of top-level cells. */
     for(int i = 0; i < s->nr_cells; i++) s->cell_index[i] = i;
 
+    /* Allocate the indices of cells with particles */
+    if (posix_memalign((void **)&s->cells_with_particles_top,
+                       SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0)
+      error("Failed to allocate indices of top-level cells with particles.");
+    bzero(s->cells_with_particles_top, s->nr_cells * sizeof(int));
+
+    /* Allocate the indices of local cells with particles */
+    if (posix_memalign((void **)&s->local_cells_with_particles_top,
+                       SWIFT_STRUCT_ALIGNMENT, s->nr_cells * sizeof(int)) != 0)
+      error(
+          "Failed to allocate indices of local top-level cells with "
+          "particles.");
+    bzero(s->local_cells_with_particles_top, s->nr_cells * sizeof(int));
+
     /* Set the cells' locks */
     for (int k = 0; k < s->nr_cells; k++) {
-      if (lock_init(&s->cells_top[k].lock) != 0)
+      if (lock_init(&s->cells_top[k].hydro.lock) != 0)
         error("Failed to init spinlock for hydro.");
-      if (lock_init(&s->cells_top[k].glock) != 0)
+      if (lock_init(&s->cells_top[k].grav.plock) != 0)
         error("Failed to init spinlock for gravity.");
-      if (lock_init(&s->cells_top[k].mlock) != 0)
+      if (lock_init(&s->cells_top[k].grav.mlock) != 0)
         error("Failed to init spinlock for multipoles.");
-      if (lock_init(&s->cells_top[k].slock) != 0)
+      if (lock_init(&s->cells_top[k].stars.lock) != 0)
         error("Failed to init spinlock for stars.");
     }
 
@@ -465,19 +507,20 @@ void space_regrid(struct space *s, int verbose) {
           c->width[2] = s->width[2];
           c->dmin = dmin;
           c->depth = 0;
-          c->count = 0;
-          c->gcount = 0;
-          c->scount = 0;
+          c->split = 0;
+          c->hydro.count = 0;
+          c->grav.count = 0;
+          c->stars.count = 0;
           c->super = c;
-          c->super_hydro = c;
-          c->super_gravity = c;
-          c->ti_old_part = ti_current;
-          c->ti_old_gpart = ti_current;
-          c->ti_old_multipole = ti_current;
+          c->hydro.super = c;
+          c->grav.super = c;
+          c->hydro.ti_old_part = ti_current;
+          c->grav.ti_old_part = ti_current;
+          c->grav.ti_old_multipole = ti_current;
 #ifdef WITH_MPI
-          c->tag = -1;
+          c->mpi.tag = -1;
 #endif  // WITH_MPI
-          if (s->gravity) c->multipole = &s->multipoles_top[cid];
+          if (s->gravity) c->grav.multipole = &s->multipoles_top[cid];
 #ifdef SWIFT_DEBUG_CHECKS
           c->cellID = -last_cell_id;
           last_cell_id++;
@@ -558,10 +601,10 @@ void space_regrid(struct space *s, int verbose) {
  * @brief Re-build the cells as well as the tasks.
  *
  * @param s The #space in which to update the cells.
+ * @param repartitioned Did we just repartition?
  * @param verbose Print messages to stdout or not
- *
  */
-void space_rebuild(struct space *s, int verbose) {
+void space_rebuild(struct space *s, int repartitioned, int verbose) {
 
   const ticks tic = getticks();
 
@@ -577,6 +620,9 @@ void space_rebuild(struct space *s, int verbose) {
   size_t nr_parts = s->nr_parts;
   size_t nr_gparts = s->nr_gparts;
   size_t nr_sparts = s->nr_sparts;
+  int count_inhibited_parts = 0;
+  int count_inhibited_gparts = 0;
+  int count_inhibited_sparts = 0;
   struct cell *restrict cells_top = s->cells_top;
   const integertime_t ti_current = (s->e != NULL) ? s->e->ti_current : 0;
 
@@ -590,7 +636,8 @@ void space_rebuild(struct space *s, int verbose) {
   if (cell_part_counts == NULL)
     error("Failed to allocate cell part count buffer.");
   if (s->size_parts > 0)
-    space_parts_get_cell_index(s, ind, cell_part_counts, cells_top, verbose);
+    space_parts_get_cell_index(s, ind, cell_part_counts, &count_inhibited_parts,
+                               verbose);
 
   /* Run through the gravity particles and get their cell index. */
   const size_t gind_size = s->size_gparts + 100;
@@ -600,7 +647,8 @@ void space_rebuild(struct space *s, int verbose) {
   if (cell_gpart_counts == NULL)
     error("Failed to allocate cell gpart count buffer.");
   if (s->size_gparts > 0)
-    space_gparts_get_cell_index(s, gind, cell_gpart_counts, cells_top, verbose);
+    space_gparts_get_cell_index(s, gind, cell_gpart_counts,
+                                &count_inhibited_gparts, verbose);
 
   /* Run through the star particles and get their cell index. */
   const size_t sind_size = s->size_sparts + 100;
@@ -610,137 +658,202 @@ void space_rebuild(struct space *s, int verbose) {
   if (cell_spart_counts == NULL)
     error("Failed to allocate cell gpart count buffer.");
   if (s->size_sparts > 0)
-    space_sparts_get_cell_index(s, sind, cell_spart_counts, cells_top, verbose);
+    space_sparts_get_cell_index(s, sind, cell_spart_counts,
+                                &count_inhibited_sparts, verbose);
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (repartitioned && count_inhibited_parts)
+    error("We just repartitioned but still found inhibited parts.");
+  if (repartitioned && count_inhibited_sparts)
+    error("We just repartitioned but still found inhibited sparts.");
+  if (repartitioned && count_inhibited_gparts)
+    error("We just repartitioned but still found inhibited gparts.");
+#endif
 
-#ifdef WITH_MPI
   const int local_nodeID = s->e->nodeID;
 
-  /* Move non-local parts to the end of the list. */
-  for (size_t k = 0; k < nr_parts;) {
-    if (cells_top[ind[k]].nodeID != local_nodeID) {
-      nr_parts -= 1;
-      /* Swap the particle */
-      memswap(&s->parts[k], &s->parts[nr_parts], sizeof(struct part));
-      /* Swap the link with the gpart */
-      if (s->parts[k].gpart != NULL) {
-        s->parts[k].gpart->id_or_neg_offset = -k;
-      }
-      if (s->parts[nr_parts].gpart != NULL) {
-        s->parts[nr_parts].gpart->id_or_neg_offset = -nr_parts;
+  /* Move non-local parts and inhibited parts to the end of the list. */
+  if (!repartitioned && (s->e->nr_nodes > 1 || count_inhibited_parts > 0)) {
+    for (size_t k = 0; k < nr_parts; /* void */) {
+
+      /* Inhibited particle or foreign particle */
+      if (ind[k] == -1 || cells_top[ind[k]].nodeID != local_nodeID) {
+
+        /* One fewer particle */
+        nr_parts -= 1;
+
+        /* Swap the particle */
+        memswap(&s->parts[k], &s->parts[nr_parts], sizeof(struct part));
+
+        /* Swap the link with the gpart */
+        if (s->parts[k].gpart != NULL) {
+          s->parts[k].gpart->id_or_neg_offset = -k;
+        }
+        if (s->parts[nr_parts].gpart != NULL) {
+          s->parts[nr_parts].gpart->id_or_neg_offset = -nr_parts;
+        }
+
+        /* Swap the xpart */
+        memswap(&s->xparts[k], &s->xparts[nr_parts], sizeof(struct xpart));
+        /* Swap the index */
+        memswap(&ind[k], &ind[nr_parts], sizeof(int));
+
+      } else {
+        /* Increment when not exchanging otherwise we need to retest "k".*/
+        k++;
       }
-      /* Swap the xpart */
-      memswap(&s->xparts[k], &s->xparts[nr_parts], sizeof(struct xpart));
-      /* Swap the index */
-      memswap(&ind[k], &ind[nr_parts], sizeof(int));
-    } else {
-      /* Increment when not exchanging otherwise we need to retest "k".*/
-      k++;
     }
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that all parts are in the correct places. */
+  int check_count_inhibited_part = 0;
   for (size_t k = 0; k < nr_parts; k++) {
-    if (cells_top[ind[k]].nodeID != local_nodeID) {
+    if (ind[k] == -1 || cells_top[ind[k]].nodeID != local_nodeID) {
       error("Failed to move all non-local parts to send list");
     }
   }
   for (size_t k = nr_parts; k < s->nr_parts; k++) {
-    if (cells_top[ind[k]].nodeID == local_nodeID) {
+    if (ind[k] != -1 && cells_top[ind[k]].nodeID == local_nodeID) {
       error("Failed to remove local parts from send list");
     }
+    if (ind[k] == -1) ++check_count_inhibited_part;
   }
-#endif
+  if (check_count_inhibited_part != count_inhibited_parts)
+    error("Counts of inhibited particles do not match!");
+#endif /* SWIFT_DEBUG_CHECKS */
 
-  /* Move non-local sparts to the end of the list. */
-  for (size_t k = 0; k < nr_sparts;) {
-    if (cells_top[sind[k]].nodeID != local_nodeID) {
-      nr_sparts -= 1;
-      /* Swap the particle */
-      memswap(&s->sparts[k], &s->sparts[nr_sparts], sizeof(struct spart));
-      /* Swap the link with the gpart */
-      if (s->sparts[k].gpart != NULL) {
-        s->sparts[k].gpart->id_or_neg_offset = -k;
-      }
-      if (s->sparts[nr_sparts].gpart != NULL) {
-        s->sparts[nr_sparts].gpart->id_or_neg_offset = -nr_sparts;
+  /* Move non-local sparts and inhibited sparts to the end of the list. */
+  if (!repartitioned && (s->e->nr_nodes > 1 || count_inhibited_sparts > 0)) {
+    for (size_t k = 0; k < nr_sparts; /* void */) {
+
+      /* Inhibited particle or foreign particle */
+      if (sind[k] == -1 || cells_top[sind[k]].nodeID != local_nodeID) {
+
+        /* One fewer particle */
+        nr_sparts -= 1;
+
+        /* Swap the particle */
+        memswap(&s->sparts[k], &s->sparts[nr_sparts], sizeof(struct spart));
+
+        /* Swap the link with the gpart */
+        if (s->sparts[k].gpart != NULL) {
+          s->sparts[k].gpart->id_or_neg_offset = -k;
+        }
+        if (s->sparts[nr_sparts].gpart != NULL) {
+          s->sparts[nr_sparts].gpart->id_or_neg_offset = -nr_sparts;
+        }
+
+        /* Swap the index */
+        memswap(&sind[k], &sind[nr_sparts], sizeof(int));
+
+      } else {
+        /* Increment when not exchanging otherwise we need to retest "k".*/
+        k++;
       }
-      /* Swap the index */
-      memswap(&sind[k], &sind[nr_sparts], sizeof(int));
-    } else {
-      /* Increment when not exchanging otherwise we need to retest "k".*/
-      k++;
     }
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
-  /* Check that all sparts are in the correct place (untested). */
+  /* Check that all sparts are in the correct place. */
+  int check_count_inhibited_spart = 0;
   for (size_t k = 0; k < nr_sparts; k++) {
-    if (cells_top[sind[k]].nodeID != local_nodeID) {
+    if (sind[k] == -1 || cells_top[sind[k]].nodeID != local_nodeID) {
       error("Failed to move all non-local sparts to send list");
     }
   }
   for (size_t k = nr_sparts; k < s->nr_sparts; k++) {
-    if (cells_top[sind[k]].nodeID == local_nodeID) {
+    if (sind[k] != -1 && cells_top[sind[k]].nodeID == local_nodeID) {
       error("Failed to remove local sparts from send list");
     }
+    if (sind[k] == -1) ++check_count_inhibited_spart;
   }
-#endif
+  if (check_count_inhibited_spart != count_inhibited_sparts)
+    error("Counts of inhibited s-particles do not match!");
+#endif /* SWIFT_DEBUG_CHECKS */
 
-  /* Move non-local gparts to the end of the list. */
-  for (size_t k = 0; k < nr_gparts;) {
-    if (cells_top[gind[k]].nodeID != local_nodeID) {
-      nr_gparts -= 1;
-      /* Swap the particle */
-      memswap(&s->gparts[k], &s->gparts[nr_gparts], sizeof(struct gpart));
-      /* Swap the link with part/spart */
-      if (s->gparts[k].type == swift_type_gas) {
-        s->parts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k];
-      } else if (s->gparts[k].type == swift_type_stars) {
-        s->sparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k];
-      }
-      if (s->gparts[nr_gparts].type == swift_type_gas) {
-        s->parts[-s->gparts[nr_gparts].id_or_neg_offset].gpart =
-            &s->gparts[nr_gparts];
-      } else if (s->gparts[nr_gparts].type == swift_type_stars) {
-        s->sparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart =
-            &s->gparts[nr_gparts];
+  /* Move non-local gparts and inhibited parts to the end of the list. */
+  if (!repartitioned && (s->e->nr_nodes > 1 || count_inhibited_gparts > 0)) {
+    for (size_t k = 0; k < nr_gparts; /* void */) {
+
+      /* Inhibited particle or foreign particle */
+      if (gind[k] == -1 || cells_top[gind[k]].nodeID != local_nodeID) {
+
+        /* One fewer particle */
+        nr_gparts -= 1;
+
+        /* Swap the particle */
+        memswap(&s->gparts[k], &s->gparts[nr_gparts], sizeof(struct gpart));
+
+        /* Swap the link with part/spart */
+        if (s->gparts[k].type == swift_type_gas) {
+          s->parts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k];
+        } else if (s->gparts[k].type == swift_type_stars) {
+          s->sparts[-s->gparts[k].id_or_neg_offset].gpart = &s->gparts[k];
+        }
+        if (s->gparts[nr_gparts].type == swift_type_gas) {
+          s->parts[-s->gparts[nr_gparts].id_or_neg_offset].gpart =
+              &s->gparts[nr_gparts];
+        } else if (s->gparts[nr_gparts].type == swift_type_stars) {
+          s->sparts[-s->gparts[nr_gparts].id_or_neg_offset].gpart =
+              &s->gparts[nr_gparts];
+        }
+
+        /* Swap the index */
+        memswap(&gind[k], &gind[nr_gparts], sizeof(int));
+      } else {
+        /* Increment when not exchanging otherwise we need to retest "k".*/
+        k++;
       }
-      /* Swap the index */
-      memswap(&gind[k], &gind[nr_gparts], sizeof(int));
-    } else {
-      /* Increment when not exchanging otherwise we need to retest "k".*/
-      k++;
     }
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
-  /* Check that all gparts are in the correct place (untested). */
+  /* Check that all gparts are in the correct place. */
+  int check_count_inhibited_gpart = 0;
   for (size_t k = 0; k < nr_gparts; k++) {
-    if (cells_top[gind[k]].nodeID != local_nodeID) {
+    if (gind[k] == -1 || cells_top[gind[k]].nodeID != local_nodeID) {
       error("Failed to move all non-local gparts to send list");
     }
   }
   for (size_t k = nr_gparts; k < s->nr_gparts; k++) {
-    if (cells_top[gind[k]].nodeID == local_nodeID) {
+    if (gind[k] != -1 && cells_top[gind[k]].nodeID == local_nodeID) {
       error("Failed to remove local gparts from send list");
     }
+    if (gind[k] == -1) ++check_count_inhibited_gpart;
   }
-#endif
+  if (check_count_inhibited_gpart != count_inhibited_gparts)
+    error("Counts of inhibited g-particles do not match!");
+#endif /* SWIFT_DEBUG_CHECKS */
+
+#ifdef WITH_MPI
 
   /* Exchange the strays, note that this potentially re-allocates
-     the parts arrays. */
-  size_t nr_parts_exchanged = s->nr_parts - nr_parts;
-  size_t nr_gparts_exchanged = s->nr_gparts - nr_gparts;
-  size_t nr_sparts_exchanged = s->nr_sparts - nr_sparts;
-  engine_exchange_strays(s->e, nr_parts, &ind[nr_parts], &nr_parts_exchanged,
-                         nr_gparts, &gind[nr_gparts], &nr_gparts_exchanged,
-                         nr_sparts, &sind[nr_sparts], &nr_sparts_exchanged);
-
-  /* Set the new particle counts. */
-  s->nr_parts = nr_parts + nr_parts_exchanged;
-  s->nr_gparts = nr_gparts + nr_gparts_exchanged;
-  s->nr_sparts = nr_sparts + nr_sparts_exchanged;
+     the parts arrays. This can be skipped if we just repartitioned aspace
+     there should be no strays */
+  if (!repartitioned) {
+
+    size_t nr_parts_exchanged = s->nr_parts - nr_parts;
+    size_t nr_gparts_exchanged = s->nr_gparts - nr_gparts;
+    size_t nr_sparts_exchanged = s->nr_sparts - nr_sparts;
+    engine_exchange_strays(s->e, nr_parts, &ind[nr_parts], &nr_parts_exchanged,
+                           nr_gparts, &gind[nr_gparts], &nr_gparts_exchanged,
+                           nr_sparts, &sind[nr_sparts], &nr_sparts_exchanged);
+
+    /* Set the new particle counts. */
+    s->nr_parts = nr_parts + nr_parts_exchanged;
+    s->nr_gparts = nr_gparts + nr_gparts_exchanged;
+    s->nr_sparts = nr_sparts + nr_sparts_exchanged;
+  } else {
+#ifdef SWIFT_DEBUG_CHECKS
+    if (s->nr_parts != nr_parts)
+      error("Number of parts changing after repartition");
+    if (s->nr_sparts != nr_sparts)
+      error("Number of sparts changing after repartition");
+    if (s->nr_gparts != nr_gparts)
+      error("Number of gparts changing after repartition");
+#endif
+  }
 
   /* Clear non-local cell counts. */
   for (int k = 0; k < s->nr_cells; k++) {
@@ -802,6 +915,12 @@ void space_rebuild(struct space *s, int verbose) {
   }
   nr_sparts = s->nr_sparts;
 
+#else /* WITH_MPI */
+
+  /* Update the part and spart counters */
+  s->nr_parts = nr_parts;
+  s->nr_sparts = nr_sparts;
+
 #endif /* WITH_MPI */
 
   /* Sort the parts according to their cells. */
@@ -814,6 +933,9 @@ void space_rebuild(struct space *s, int verbose) {
   for (size_t k = 0; k < nr_parts; k++) {
     const struct part *p = &s->parts[k];
 
+    if (p->time_bin == time_bin_inhibited)
+      error("Inhibited particle sorted into a cell!");
+
     /* New cell index */
     const int new_ind =
         cell_getid(s->cdim, p->x[0] * s->iwidth[0], p->x[1] * s->iwidth[1],
@@ -830,7 +952,7 @@ void space_rebuild(struct space *s, int verbose) {
         p->x[2] < c->loc[2] || p->x[2] > c->loc[2] + c->width[2])
       error("part not sorted into the right top-level cell!");
   }
-#endif
+#endif /* SWIFT_DEBUG_CHECKS */
 
   /* Sort the sparts according to their cells. */
   if (nr_sparts > 0)
@@ -841,6 +963,9 @@ void space_rebuild(struct space *s, int verbose) {
   for (size_t k = 0; k < nr_sparts; k++) {
     const struct spart *sp = &s->sparts[k];
 
+    if (sp->time_bin == time_bin_inhibited)
+      error("Inhibited particle sorted into a cell!");
+
     /* New cell index */
     const int new_sind =
         cell_getid(s->cdim, sp->x[0] * s->iwidth[0], sp->x[1] * s->iwidth[1],
@@ -857,14 +982,14 @@ void space_rebuild(struct space *s, int verbose) {
         sp->x[2] < c->loc[2] || sp->x[2] > c->loc[2] + c->width[2])
       error("spart not sorted into the right top-level cell!");
   }
-#endif
+#endif /* SWIFT_DEBUG_CHECKS */
 
   /* Extract the cell counts from the sorted indices. */
   size_t last_index = 0;
   ind[nr_parts] = s->nr_cells;  // sentinel.
   for (size_t k = 0; k < nr_parts; k++) {
     if (ind[k] < ind[k + 1]) {
-      cells_top[ind[k]].count = k - last_index + 1;
+      cells_top[ind[k]].hydro.count = k - last_index + 1;
       last_index = k + 1;
     }
   }
@@ -874,7 +999,7 @@ void space_rebuild(struct space *s, int verbose) {
   sind[nr_sparts] = s->nr_cells;  // sentinel.
   for (size_t k = 0; k < nr_sparts; k++) {
     if (sind[k] < sind[k + 1]) {
-      cells_top[sind[k]].scount = k - last_sindex + 1;
+      cells_top[sind[k]].stars.count = k - last_sindex + 1;
       last_sindex = k + 1;
     }
   }
@@ -911,8 +1036,18 @@ void space_rebuild(struct space *s, int verbose) {
   }
   nr_gparts = s->nr_gparts;
 
+#else /* WITH_MPI */
+
+  /* Update the gpart counter */
+  s->nr_gparts = nr_gparts;
+
 #endif /* WITH_MPI */
 
+  /* Mark that there are no inhibited particles left */
+  s->nr_inhibited_parts = 0;
+  s->nr_inhibited_gparts = 0;
+  s->nr_inhibited_sparts = 0;
+
   /* Sort the gparts according to their cells. */
   if (nr_gparts > 0)
     space_gparts_sort(s->gparts, s->parts, s->sparts, gind, cell_gpart_counts,
@@ -923,6 +1058,9 @@ void space_rebuild(struct space *s, int verbose) {
   for (size_t k = 0; k < nr_gparts; k++) {
     const struct gpart *gp = &s->gparts[k];
 
+    if (gp->time_bin == time_bin_inhibited)
+      error("Inhibited particle sorted into a cell!");
+
     /* New cell index */
     const int new_gind =
         cell_getid(s->cdim, gp->x[0] * s->iwidth[0], gp->x[1] * s->iwidth[1],
@@ -939,14 +1077,14 @@ void space_rebuild(struct space *s, int verbose) {
         gp->x[2] < c->loc[2] || gp->x[2] > c->loc[2] + c->width[2])
       error("gpart not sorted into the right top-level cell!");
   }
-#endif
+#endif /* SWIFT_DEBUG_CHECKS */
 
   /* Extract the cell counts from the sorted indices. */
   size_t last_gindex = 0;
   gind[nr_gparts] = s->nr_cells;
   for (size_t k = 0; k < nr_gparts; k++) {
     if (gind[k] < gind[k + 1]) {
-      cells_top[gind[k]].gcount = k - last_gindex + 1;
+      cells_top[gind[k]].grav.count = k - last_gindex + 1;
       last_gindex = k + 1;
     }
   }
@@ -962,40 +1100,64 @@ void space_rebuild(struct space *s, int verbose) {
                       nr_sparts, verbose);
 #endif
 
-  /* Hook the cells up to the parts. */
-  // tic = getticks();
+  /* Hook the cells up to the parts. Make list of local and non-empty cells */
+  ticks tic2 = getticks();
   struct part *finger = s->parts;
   struct xpart *xfinger = s->xparts;
   struct gpart *gfinger = s->gparts;
   struct spart *sfinger = s->sparts;
+  s->nr_cells_with_particles = 0;
+  s->nr_local_cells_with_particles = 0;
+  s->nr_local_cells = 0;
   for (int k = 0; k < s->nr_cells; k++) {
     struct cell *restrict c = &cells_top[k];
-    c->ti_old_part = ti_current;
-    c->ti_old_gpart = ti_current;
-    c->ti_old_multipole = ti_current;
+    c->hydro.ti_old_part = ti_current;
+    c->grav.ti_old_part = ti_current;
+    c->grav.ti_old_multipole = ti_current;
 
 #ifdef SWIFT_DEBUG_CHECKS
     c->cellID = -last_cell_id;
     last_cell_id++;
 #endif
 
-    if (c->nodeID == engine_rank) {
-      c->parts = finger;
-      c->xparts = xfinger;
-      c->gparts = gfinger;
-      c->sparts = sfinger;
-      finger = &finger[c->count];
-      xfinger = &xfinger[c->count];
-      gfinger = &gfinger[c->gcount];
-      sfinger = &sfinger[c->scount];
+    const int is_local = (c->nodeID == engine_rank);
+    const int has_particles =
+        (c->hydro.count > 0) || (c->grav.count > 0) || (c->stars.count > 0);
+
+    if (is_local) {
+      c->hydro.parts = finger;
+      c->hydro.xparts = xfinger;
+      c->grav.parts = gfinger;
+      c->stars.parts = sfinger;
+      finger = &finger[c->hydro.count];
+      xfinger = &xfinger[c->hydro.count];
+      gfinger = &gfinger[c->grav.count];
+      sfinger = &sfinger[c->stars.count];
+
+      /* Add this cell to the list of local cells */
+      s->local_cells_top[s->nr_local_cells] = k;
+      s->nr_local_cells++;
     }
+
+    if (is_local && has_particles) {
+
+      /* Add this cell to the list of non-empty cells */
+      s->local_cells_with_particles_top[s->nr_local_cells_with_particles] = k;
+      s->nr_local_cells_with_particles++;
+    }
+  }
+  if (verbose) {
+    message("Have %d local top-level cells with particles (total=%d)",
+            s->nr_local_cells_with_particles, s->nr_cells);
+    message("Have %d local top-level cells (total=%d)", s->nr_local_cells,
+            s->nr_cells);
+    message("hooking up cells took %.3f %s.",
+            clocks_from_ticks(getticks() - tic2), clocks_getunit());
   }
-  // message( "hooking up cells took %.3f %s." ,
-  // clocks_from_ticks(getticks() - tic), clocks_getunit());
 
   /* At this point, we have the upper-level cells, old or new. Now make
      sure that the parts in each cell are ok. */
-  space_split(s, cells_top, s->nr_cells, verbose);
+  space_split(s, verbose);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that the multipole construction went OK */
@@ -1013,22 +1175,21 @@ void space_rebuild(struct space *s, int verbose) {
 }
 
 /**
- * @brief Split particles between cells of a hierarchy
+ * @brief Split particles between cells of a hierarchy.
  *
  * This is done in parallel using threads in the #threadpool.
+ * Only do this for the local non-empty top-level cells.
  *
  * @param s The #space.
- * @param cells The cell hierarchy.
- * @param nr_cells The number of cells.
  * @param verbose Are we talkative ?
  */
-void space_split(struct space *s, struct cell *cells, int nr_cells,
-                 int verbose) {
+void space_split(struct space *s, int verbose) {
 
   const ticks tic = getticks();
 
-  threadpool_map(&s->e->threadpool, space_split_mapper, cells, nr_cells,
-                 sizeof(struct cell), 0, s);
+  threadpool_map(&s->e->threadpool, space_split_mapper,
+                 s->local_cells_with_particles_top,
+                 s->nr_local_cells_with_particles, sizeof(int), 0, s);
 
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
@@ -1098,6 +1259,7 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts,
   /* Init the local collectors */
   float min_mass = FLT_MAX;
   float sum_vel_norm = 0.f;
+  int count_inhibited_part = 0;
 
   /* Loop over the parts. */
   for (int k = 0; k < nr_parts; k++) {
@@ -1129,8 +1291,15 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts,
             pos_z);
 #endif
 
-    ind[k] = index;
-    cell_counts[index]++;
+    /* Is this particle to be removed? */
+    if (p->time_bin == time_bin_inhibited) {
+      ind[k] = -1;
+      ++count_inhibited_part;
+    } else {
+      /* List its top-level cell index */
+      ind[k] = index;
+      cell_counts[index]++;
+    }
 
     /* Compute minimal mass */
     min_mass = min(min_mass, hydro_get_mass(p));
@@ -1149,6 +1318,9 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts,
     if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]);
   free(cell_counts);
 
+  /* Write the count of inhibited parts */
+  atomic_add(&data->count_inhibited_part, count_inhibited_part);
+
   /* Write back the minimal part mass and velocity sum */
   atomic_min_f(&s->min_part_mass, min_mass);
   atomic_add_f(&s->sum_part_vel_norm, sum_vel_norm);
@@ -1187,6 +1359,7 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts,
   /* Init the local collectors */
   float min_mass = FLT_MAX;
   float sum_vel_norm = 0.f;
+  int count_inhibited_gpart = 0;
 
   for (int k = 0; k < nr_gparts; k++) {
 
@@ -1217,12 +1390,22 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts,
             pos_z);
 #endif
 
-    ind[k] = index;
-    cell_counts[index]++;
+    /* Is this particle to be removed? */
+    if (gp->time_bin == time_bin_inhibited) {
+      ind[k] = -1;
+      ++count_inhibited_gpart;
+    } else {
+      /* List its top-level cell index */
+      ind[k] = index;
+      cell_counts[index]++;
+    }
 
-    /* Compute minimal mass */
     if (gp->type == swift_type_dark_matter) {
+
+      /* Compute minimal mass */
       min_mass = min(min_mass, gp->mass);
+
+      /* Compute sum of velocity norm */
       sum_vel_norm += gp->v_full[0] * gp->v_full[0] +
                       gp->v_full[1] * gp->v_full[1] +
                       gp->v_full[2] * gp->v_full[2];
@@ -1239,6 +1422,9 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts,
     if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]);
   free(cell_counts);
 
+  /* Write the count of inhibited gparts */
+  atomic_add(&data->count_inhibited_gpart, count_inhibited_gpart);
+
   /* Write back the minimal part mass and velocity sum */
   atomic_min_f(&s->min_gpart_mass, min_mass);
   atomic_add_f(&s->sum_gpart_vel_norm, sum_vel_norm);
@@ -1277,6 +1463,7 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
   /* Init the local collectors */
   float min_mass = FLT_MAX;
   float sum_vel_norm = 0.f;
+  int count_inhibited_spart = 0;
 
   for (int k = 0; k < nr_sparts; k++) {
 
@@ -1307,8 +1494,15 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
             pos_z);
 #endif
 
-    ind[k] = index;
-    cell_counts[index]++;
+    /* Is this particle to be removed? */
+    if (sp->time_bin == time_bin_inhibited) {
+      ind[k] = -1;
+      ++count_inhibited_spart;
+    } else {
+      /* List its top-level cell index */
+      ind[k] = index;
+      cell_counts[index]++;
+    }
 
     /* Compute minimal mass */
     min_mass = min(min_mass, sp->mass);
@@ -1328,6 +1522,9 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
     if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]);
   free(cell_counts);
 
+  /* Write the count of inhibited parts */
+  atomic_add(&data->count_inhibited_spart, count_inhibited_spart);
+
   /* Write back the minimal part mass and velocity sum */
   atomic_min_f(&s->min_spart_mass, min_mass);
   atomic_add_f(&s->sum_spart_vel_norm, sum_vel_norm);
@@ -1341,11 +1538,11 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
  * @param s The #space.
  * @param ind The array of indices to fill.
  * @param cell_counts The cell counters to update.
- * @param cells The array of #cell to update.
+ * @param count_inhibited_parts (return) The number of #part to remove.
  * @param verbose Are we talkative ?
  */
 void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts,
-                                struct cell *cells, int verbose) {
+                                int *count_inhibited_parts, int verbose) {
 
   const ticks tic = getticks();
 
@@ -1356,13 +1553,17 @@ void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts,
   /* Pack the extra information */
   struct index_data data;
   data.s = s;
-  data.cells = cells;
   data.ind = ind;
   data.cell_counts = cell_counts;
+  data.count_inhibited_part = 0;
+  data.count_inhibited_gpart = 0;
+  data.count_inhibited_spart = 0;
 
   threadpool_map(&s->e->threadpool, space_parts_get_cell_index_mapper, s->parts,
                  s->nr_parts, sizeof(struct part), 0, &data);
 
+  *count_inhibited_parts = data.count_inhibited_part;
+
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
@@ -1376,11 +1577,11 @@ void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts,
  * @param s The #space.
  * @param gind The array of indices to fill.
  * @param cell_counts The cell counters to update.
- * @param cells The array of #cell to update.
+ * @param count_inhibited_gparts (return) The number of #gpart to remove.
  * @param verbose Are we talkative ?
  */
 void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
-                                 struct cell *cells, int verbose) {
+                                 int *count_inhibited_gparts, int verbose) {
 
   const ticks tic = getticks();
 
@@ -1391,13 +1592,17 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
   /* Pack the extra information */
   struct index_data data;
   data.s = s;
-  data.cells = cells;
   data.ind = gind;
   data.cell_counts = cell_counts;
+  data.count_inhibited_part = 0;
+  data.count_inhibited_gpart = 0;
+  data.count_inhibited_spart = 0;
 
   threadpool_map(&s->e->threadpool, space_gparts_get_cell_index_mapper,
                  s->gparts, s->nr_gparts, sizeof(struct gpart), 0, &data);
 
+  *count_inhibited_gparts = data.count_inhibited_gpart;
+
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
@@ -1411,11 +1616,11 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
  * @param s The #space.
  * @param sind The array of indices to fill.
  * @param cell_counts The cell counters to update.
- * @param cells The array of #cell to update.
+ * @param count_inhibited_sparts (return) The number of #spart to remove.
  * @param verbose Are we talkative ?
  */
 void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
-                                 struct cell *cells, int verbose) {
+                                 int *count_inhibited_sparts, int verbose) {
 
   const ticks tic = getticks();
 
@@ -1426,13 +1631,17 @@ void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
   /* Pack the extra information */
   struct index_data data;
   data.s = s;
-  data.cells = cells;
   data.ind = sind;
   data.cell_counts = cell_counts;
+  data.count_inhibited_part = 0;
+  data.count_inhibited_gpart = 0;
+  data.count_inhibited_spart = 0;
 
   threadpool_map(&s->e->threadpool, space_sparts_get_cell_index_mapper,
                  s->sparts, s->nr_sparts, sizeof(struct spart), 0, &data);
 
+  *count_inhibited_sparts = data.count_inhibited_spart;
+
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
@@ -1449,8 +1658,9 @@ void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
  * @param num_bins Total number of bins (length of count).
  * @param parts_offset Offset of the #part array from the global #part array.
  */
-void space_parts_sort(struct part *parts, struct xpart *xparts, int *ind,
-                      int *counts, int num_bins, ptrdiff_t parts_offset) {
+void space_parts_sort(struct part *parts, struct xpart *xparts,
+                      int *restrict ind, int *restrict counts, int num_bins,
+                      ptrdiff_t parts_offset) {
   /* Create the offsets array. */
   size_t *offsets = NULL;
   if (posix_memalign((void **)&offsets, SWIFT_STRUCT_ALIGNMENT,
@@ -1511,8 +1721,9 @@ void space_parts_sort(struct part *parts, struct xpart *xparts, int *ind,
  * @param sparts_offset Offset of the #spart array from the global #spart.
  * array.
  */
-void space_sparts_sort(struct spart *sparts, int *ind, int *counts,
-                       int num_bins, ptrdiff_t sparts_offset) {
+void space_sparts_sort(struct spart *sparts, int *restrict ind,
+                       int *restrict counts, int num_bins,
+                       ptrdiff_t sparts_offset) {
   /* Create the offsets array. */
   size_t *offsets = NULL;
   if (posix_memalign((void **)&offsets, SWIFT_STRUCT_ALIGNMENT,
@@ -1571,8 +1782,8 @@ void space_sparts_sort(struct spart *sparts, int *ind, int *counts,
  * @param num_bins Total number of bins (length of counts).
  */
 void space_gparts_sort(struct gpart *gparts, struct part *parts,
-                       struct spart *sparts, int *ind, int *counts,
-                       int num_bins) {
+                       struct spart *sparts, int *restrict ind,
+                       int *restrict counts, int num_bins) {
   /* Create the offsets array. */
   size_t *offsets = NULL;
   if (posix_memalign((void **)&offsets, SWIFT_STRUCT_ALIGNMENT,
@@ -1632,9 +1843,9 @@ void space_gparts_sort(struct gpart *gparts, struct part *parts,
 void space_map_clearsort(struct cell *c, void *data) {
 
   for (int i = 0; i < 13; i++)
-    if (c->sort[i] != NULL) {
-      free(c->sort[i]);
-      c->sort[i] = NULL;
+    if (c->hydro.sort[i] != NULL) {
+      free(c->hydro.sort[i]);
+      c->hydro.sort[i] = NULL;
     }
 }
 
@@ -1651,7 +1862,7 @@ static void rec_map_parts(struct cell *c,
                           void *data) {
   /* No progeny? */
   if (!c->split)
-    for (int k = 0; k < c->count; k++) fun(&c->parts[k], c, data);
+    for (int k = 0; k < c->hydro.count; k++) fun(&c->hydro.parts[k], c, data);
 
   /* Otherwise, recurse. */
   else
@@ -1687,7 +1898,8 @@ static void rec_map_parts_xparts(struct cell *c,
 
   /* No progeny? */
   if (!c->split)
-    for (int k = 0; k < c->count; k++) fun(&c->parts[k], &c->xparts[k], c);
+    for (int k = 0; k < c->hydro.count; k++)
+      fun(&c->hydro.parts[k], &c->hydro.xparts[k], c);
 
   /* Otherwise, recurse. */
   else
@@ -1783,31 +1995,32 @@ void space_map_cells_pre(struct space *s, int full,
  * @param s The #space in which the cell lives.
  * @param c The #cell to split recursively.
  * @param buff A buffer for particle sorting, should be of size at least
- *        c->count or @c NULL.
+ *        c->hydro.count or @c NULL.
  * @param sbuff A buffer for particle sorting, should be of size at least
- *        c->scount or @c NULL.
+ *        c->stars.count or @c NULL.
  * @param gbuff A buffer for particle sorting, should be of size at least
- *        c->gcount or @c NULL.
+ *        c->grav.count or @c NULL.
  */
 void space_split_recursive(struct space *s, struct cell *c,
                            struct cell_buff *buff, struct cell_buff *sbuff,
                            struct cell_buff *gbuff) {
 
-  const int count = c->count;
-  const int gcount = c->gcount;
-  const int scount = c->scount;
+  const int count = c->hydro.count;
+  const int gcount = c->grav.count;
+  const int scount = c->stars.count;
   const int with_gravity = s->gravity;
   const int depth = c->depth;
   int maxdepth = 0;
   float h_max = 0.0f;
+  float stars_h_max = 0.f;
   integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0,
                 ti_hydro_beg_max = 0;
   integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0,
                 ti_gravity_beg_max = 0;
-  struct part *parts = c->parts;
-  struct gpart *gparts = c->gparts;
-  struct spart *sparts = c->sparts;
-  struct xpart *xparts = c->xparts;
+  struct part *parts = c->hydro.parts;
+  struct gpart *gparts = c->grav.parts;
+  struct spart *sparts = c->stars.parts;
+  struct xpart *xparts = c->hydro.xparts;
   struct engine *e = s->e;
   const integertime_t ti_current = e->ti_current;
 
@@ -1819,6 +2032,10 @@ void space_split_recursive(struct space *s, struct cell *c,
                          sizeof(struct cell_buff) * count) != 0)
         error("Failed to allocate temporary indices.");
       for (int k = 0; k < count; k++) {
+#ifdef SWIFT_DEBUG_CHECKS
+        if (parts[k].time_bin == time_bin_inhibited)
+          error("Inhibited particle present in space_split()");
+#endif
         buff[k].x[0] = parts[k].x[0];
         buff[k].x[1] = parts[k].x[1];
         buff[k].x[2] = parts[k].x[2];
@@ -1829,6 +2046,10 @@ void space_split_recursive(struct space *s, struct cell *c,
                          sizeof(struct cell_buff) * gcount) != 0)
         error("Failed to allocate temporary indices.");
       for (int k = 0; k < gcount; k++) {
+#ifdef SWIFT_DEBUG_CHECKS
+        if (gparts[k].time_bin == time_bin_inhibited)
+          error("Inhibited particle present in space_split()");
+#endif
         gbuff[k].x[0] = gparts[k].x[0];
         gbuff[k].x[1] = gparts[k].x[1];
         gbuff[k].x[2] = gparts[k].x[2];
@@ -1839,6 +2060,10 @@ void space_split_recursive(struct space *s, struct cell *c,
                          sizeof(struct cell_buff) * scount) != 0)
         error("Failed to allocate temporary indices.");
       for (int k = 0; k < scount; k++) {
+#ifdef SWIFT_DEBUG_CHECKS
+        if (sparts[k].time_bin == time_bin_inhibited)
+          error("Inhibited particle present in space_split()");
+#endif
         sbuff[k].x[0] = sparts[k].x[0];
         sbuff[k].x[1] = sparts[k].x[1];
         sbuff[k].x[2] = sparts[k].x[2];
@@ -1869,12 +2094,12 @@ void space_split_recursive(struct space *s, struct cell *c,
     space_getcells(s, 8, c->progeny);
     for (int k = 0; k < 8; k++) {
       struct cell *cp = c->progeny[k];
-      cp->count = 0;
-      cp->gcount = 0;
-      cp->scount = 0;
-      cp->ti_old_part = c->ti_old_part;
-      cp->ti_old_gpart = c->ti_old_gpart;
-      cp->ti_old_multipole = c->ti_old_multipole;
+      cp->hydro.count = 0;
+      cp->grav.count = 0;
+      cp->stars.count = 0;
+      cp->hydro.ti_old_part = c->hydro.ti_old_part;
+      cp->grav.ti_old_part = c->grav.ti_old_part;
+      cp->grav.ti_old_multipole = c->grav.ti_old_multipole;
       cp->loc[0] = c->loc[0];
       cp->loc[1] = c->loc[1];
       cp->loc[2] = c->loc[2];
@@ -1887,19 +2112,21 @@ void space_split_recursive(struct space *s, struct cell *c,
       if (k & 1) cp->loc[2] += cp->width[2];
       cp->depth = c->depth + 1;
       cp->split = 0;
-      cp->h_max = 0.f;
-      cp->dx_max_part = 0.f;
-      cp->dx_max_sort = 0.f;
+      cp->hydro.h_max = 0.f;
+      cp->hydro.dx_max_part = 0.f;
+      cp->hydro.dx_max_sort = 0.f;
+      cp->stars.h_max = 0.f;
+      cp->stars.dx_max_part = 0.f;
       cp->nodeID = c->nodeID;
       cp->parent = c;
       cp->super = NULL;
-      cp->super_hydro = NULL;
-      cp->super_gravity = NULL;
-      cp->do_sub_sort = 0;
-      cp->do_grav_sub_drift = 0;
-      cp->do_sub_drift = 0;
+      cp->hydro.super = NULL;
+      cp->grav.super = NULL;
+      cp->hydro.do_sub_sort = 0;
+      cp->grav.do_sub_drift = 0;
+      cp->hydro.do_sub_drift = 0;
 #ifdef WITH_MPI
-      cp->tag = -1;
+      cp->mpi.tag = -1;
 #endif  // WITH_MPI
 #ifdef SWIFT_DEBUG_CHECKS
       cp->cellID = last_cell_id++;
@@ -1907,8 +2134,8 @@ void space_split_recursive(struct space *s, struct cell *c,
     }
 
     /* Split the cell's partcle data. */
-    cell_split(c, c->parts - s->parts, c->sparts - s->sparts, buff, sbuff,
-               gbuff);
+    cell_split(c, c->hydro.parts - s->parts, c->stars.parts - s->sparts, buff,
+               sbuff, gbuff);
 
     /* Buffers for the progenitors */
     struct cell_buff *progeny_buff = buff, *progeny_gbuff = gbuff,
@@ -1920,7 +2147,7 @@ void space_split_recursive(struct space *s, struct cell *c,
       struct cell *cp = c->progeny[k];
 
       /* Remove any progeny with zero particles. */
-      if (cp->count == 0 && cp->gcount == 0 && cp->scount == 0) {
+      if (cp->hydro.count == 0 && cp->grav.count == 0 && cp->stars.count == 0) {
 
         space_recycle(s, cp);
         c->progeny[k] = NULL;
@@ -1932,18 +2159,19 @@ void space_split_recursive(struct space *s, struct cell *c,
                               progeny_gbuff);
 
         /* Update the pointers in the buffers */
-        progeny_buff += cp->count;
-        progeny_gbuff += cp->gcount;
-        progeny_sbuff += cp->scount;
+        progeny_buff += cp->hydro.count;
+        progeny_gbuff += cp->grav.count;
+        progeny_sbuff += cp->stars.count;
 
         /* Update the cell-wide properties */
-        h_max = max(h_max, cp->h_max);
-        ti_hydro_end_min = min(ti_hydro_end_min, cp->ti_hydro_end_min);
-        ti_hydro_end_max = max(ti_hydro_end_max, cp->ti_hydro_end_max);
-        ti_hydro_beg_max = max(ti_hydro_beg_max, cp->ti_hydro_beg_max);
-        ti_gravity_end_min = min(ti_gravity_end_min, cp->ti_gravity_end_min);
-        ti_gravity_end_max = max(ti_gravity_end_max, cp->ti_gravity_end_max);
-        ti_gravity_beg_max = max(ti_gravity_beg_max, cp->ti_gravity_beg_max);
+        h_max = max(h_max, cp->hydro.h_max);
+        stars_h_max = max(h_max, cp->stars.h_max);
+        ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min);
+        ti_hydro_end_max = max(ti_hydro_end_max, cp->hydro.ti_end_max);
+        ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max);
+        ti_gravity_end_min = min(ti_gravity_end_min, cp->grav.ti_end_min);
+        ti_gravity_end_max = max(ti_gravity_end_max, cp->grav.ti_end_max);
+        ti_gravity_beg_max = max(ti_gravity_beg_max, cp->grav.ti_beg_max);
 
         /* Increase the depth */
         if (cp->maxdepth > maxdepth) maxdepth = cp->maxdepth;
@@ -1954,7 +2182,7 @@ void space_split_recursive(struct space *s, struct cell *c,
     if (s->gravity) {
 
       /* Reset everything */
-      gravity_reset(c->multipole);
+      gravity_reset(c->grav.multipole);
 
       /* Compute CoM and bulk velocity from all progenies */
       double CoM[3] = {0., 0., 0.};
@@ -1965,7 +2193,7 @@ void space_split_recursive(struct space *s, struct cell *c,
 
       for (int k = 0; k < 8; ++k) {
         if (c->progeny[k] != NULL) {
-          const struct gravity_tensors *m = c->progeny[k]->multipole;
+          const struct gravity_tensors *m = c->progeny[k]->grav.multipole;
 
           mass += m->m_pole.M_000;
 
@@ -1989,20 +2217,20 @@ void space_split_recursive(struct space *s, struct cell *c,
 
       /* Final operation on the CoM and bulk velocity */
       const double inv_mass = 1. / mass;
-      c->multipole->CoM[0] = CoM[0] * inv_mass;
-      c->multipole->CoM[1] = CoM[1] * inv_mass;
-      c->multipole->CoM[2] = CoM[2] * inv_mass;
-      c->multipole->m_pole.vel[0] = vel[0] * inv_mass;
-      c->multipole->m_pole.vel[1] = vel[1] * inv_mass;
-      c->multipole->m_pole.vel[2] = vel[2] * inv_mass;
+      c->grav.multipole->CoM[0] = CoM[0] * inv_mass;
+      c->grav.multipole->CoM[1] = CoM[1] * inv_mass;
+      c->grav.multipole->CoM[2] = CoM[2] * inv_mass;
+      c->grav.multipole->m_pole.vel[0] = vel[0] * inv_mass;
+      c->grav.multipole->m_pole.vel[1] = vel[1] * inv_mass;
+      c->grav.multipole->m_pole.vel[2] = vel[2] * inv_mass;
 
       /* Min max velocity along each axis */
-      c->multipole->m_pole.max_delta_vel[0] = max_delta_vel[0];
-      c->multipole->m_pole.max_delta_vel[1] = max_delta_vel[1];
-      c->multipole->m_pole.max_delta_vel[2] = max_delta_vel[2];
-      c->multipole->m_pole.min_delta_vel[0] = min_delta_vel[0];
-      c->multipole->m_pole.min_delta_vel[1] = min_delta_vel[1];
-      c->multipole->m_pole.min_delta_vel[2] = min_delta_vel[2];
+      c->grav.multipole->m_pole.max_delta_vel[0] = max_delta_vel[0];
+      c->grav.multipole->m_pole.max_delta_vel[1] = max_delta_vel[1];
+      c->grav.multipole->m_pole.max_delta_vel[2] = max_delta_vel[2];
+      c->grav.multipole->m_pole.min_delta_vel[0] = min_delta_vel[0];
+      c->grav.multipole->m_pole.min_delta_vel[1] = min_delta_vel[1];
+      c->grav.multipole->m_pole.min_delta_vel[2] = min_delta_vel[2];
 
       /* Now shift progeny multipoles and add them up */
       struct multipole temp;
@@ -2010,45 +2238,52 @@ void space_split_recursive(struct space *s, struct cell *c,
       for (int k = 0; k < 8; ++k) {
         if (c->progeny[k] != NULL) {
           const struct cell *cp = c->progeny[k];
-          const struct multipole *m = &cp->multipole->m_pole;
+          const struct multipole *m = &cp->grav.multipole->m_pole;
 
           /* Contribution to multipole */
-          gravity_M2M(&temp, m, c->multipole->CoM, cp->multipole->CoM);
-          gravity_multipole_add(&c->multipole->m_pole, &temp);
+          gravity_M2M(&temp, m, c->grav.multipole->CoM,
+                      cp->grav.multipole->CoM);
+          gravity_multipole_add(&c->grav.multipole->m_pole, &temp);
 
           /* Upper limit of max CoM<->gpart distance */
-          const double dx = c->multipole->CoM[0] - cp->multipole->CoM[0];
-          const double dy = c->multipole->CoM[1] - cp->multipole->CoM[1];
-          const double dz = c->multipole->CoM[2] - cp->multipole->CoM[2];
+          const double dx =
+              c->grav.multipole->CoM[0] - cp->grav.multipole->CoM[0];
+          const double dy =
+              c->grav.multipole->CoM[1] - cp->grav.multipole->CoM[1];
+          const double dz =
+              c->grav.multipole->CoM[2] - cp->grav.multipole->CoM[2];
           const double r2 = dx * dx + dy * dy + dz * dz;
-          r_max = max(r_max, cp->multipole->r_max + sqrt(r2));
+          r_max = max(r_max, cp->grav.multipole->r_max + sqrt(r2));
         }
       }
 
       /* Alternative upper limit of max CoM<->gpart distance */
-      const double dx = c->multipole->CoM[0] > c->loc[0] + c->width[0] / 2.
-                            ? c->multipole->CoM[0] - c->loc[0]
-                            : c->loc[0] + c->width[0] - c->multipole->CoM[0];
-      const double dy = c->multipole->CoM[1] > c->loc[1] + c->width[1] / 2.
-                            ? c->multipole->CoM[1] - c->loc[1]
-                            : c->loc[1] + c->width[1] - c->multipole->CoM[1];
-      const double dz = c->multipole->CoM[2] > c->loc[2] + c->width[2] / 2.
-                            ? c->multipole->CoM[2] - c->loc[2]
-                            : c->loc[2] + c->width[2] - c->multipole->CoM[2];
+      const double dx =
+          c->grav.multipole->CoM[0] > c->loc[0] + c->width[0] / 2.
+              ? c->grav.multipole->CoM[0] - c->loc[0]
+              : c->loc[0] + c->width[0] - c->grav.multipole->CoM[0];
+      const double dy =
+          c->grav.multipole->CoM[1] > c->loc[1] + c->width[1] / 2.
+              ? c->grav.multipole->CoM[1] - c->loc[1]
+              : c->loc[1] + c->width[1] - c->grav.multipole->CoM[1];
+      const double dz =
+          c->grav.multipole->CoM[2] > c->loc[2] + c->width[2] / 2.
+              ? c->grav.multipole->CoM[2] - c->loc[2]
+              : c->loc[2] + c->width[2] - c->grav.multipole->CoM[2];
 
       /* Take minimum of both limits */
-      c->multipole->r_max = min(r_max, sqrt(dx * dx + dy * dy + dz * dz));
+      c->grav.multipole->r_max = min(r_max, sqrt(dx * dx + dy * dy + dz * dz));
 
       /* Store the value at rebuild time */
-      c->multipole->r_max_rebuild = c->multipole->r_max;
-      c->multipole->CoM_rebuild[0] = c->multipole->CoM[0];
-      c->multipole->CoM_rebuild[1] = c->multipole->CoM[1];
-      c->multipole->CoM_rebuild[2] = c->multipole->CoM[2];
+      c->grav.multipole->r_max_rebuild = c->grav.multipole->r_max;
+      c->grav.multipole->CoM_rebuild[0] = c->grav.multipole->CoM[0];
+      c->grav.multipole->CoM_rebuild[1] = c->grav.multipole->CoM[1];
+      c->grav.multipole->CoM_rebuild[2] = c->grav.multipole->CoM[2];
 
       /* We know the first-order multipole (dipole) is 0. */
-      c->multipole->m_pole.M_100 = 0.f;
-      c->multipole->m_pole.M_010 = 0.f;
-      c->multipole->m_pole.M_001 = 0.f;
+      c->grav.multipole->m_pole.M_100 = 0.f;
+      c->grav.multipole->m_pole.M_010 = 0.f;
+      c->grav.multipole->m_pole.M_001 = 0.f;
 
     } /* Deal with gravity */
   }   /* Split or let it be? */
@@ -2100,6 +2335,12 @@ void space_split_recursive(struct space *s, struct cell *c,
 #endif
       gravity_time_bin_min = min(gravity_time_bin_min, sparts[k].time_bin);
       gravity_time_bin_max = max(gravity_time_bin_max, sparts[k].time_bin);
+      stars_h_max = max(stars_h_max, sparts[k].h);
+
+      /* Reset x_diff */
+      sparts[k].x_diff[0] = 0.f;
+      sparts[k].x_diff[1] = 0.f;
+      sparts[k].x_diff[2] = 0.f;
     }
 
     /* Convert into integer times */
@@ -2116,50 +2357,51 @@ void space_split_recursive(struct space *s, struct cell *c,
     if (s->gravity) {
       if (gcount > 0) {
 
-        gravity_P2M(c->multipole, c->gparts, c->gcount);
+        gravity_P2M(c->grav.multipole, c->grav.parts, c->grav.count);
 
       } else {
 
         /* No gparts in that leaf cell */
 
         /* Set the values to something sensible */
-        gravity_multipole_init(&c->multipole->m_pole);
+        gravity_multipole_init(&c->grav.multipole->m_pole);
         if (c->nodeID == engine_rank) {
-          c->multipole->CoM[0] = c->loc[0] + c->width[0] / 2.;
-          c->multipole->CoM[1] = c->loc[1] + c->width[1] / 2.;
-          c->multipole->CoM[2] = c->loc[2] + c->width[2] / 2.;
-          c->multipole->r_max = 0.;
+          c->grav.multipole->CoM[0] = c->loc[0] + c->width[0] / 2.;
+          c->grav.multipole->CoM[1] = c->loc[1] + c->width[1] / 2.;
+          c->grav.multipole->CoM[2] = c->loc[2] + c->width[2] / 2.;
+          c->grav.multipole->r_max = 0.;
         }
       }
 
       /* Store the value at rebuild time */
-      c->multipole->r_max_rebuild = c->multipole->r_max;
-      c->multipole->CoM_rebuild[0] = c->multipole->CoM[0];
-      c->multipole->CoM_rebuild[1] = c->multipole->CoM[1];
-      c->multipole->CoM_rebuild[2] = c->multipole->CoM[2];
+      c->grav.multipole->r_max_rebuild = c->grav.multipole->r_max;
+      c->grav.multipole->CoM_rebuild[0] = c->grav.multipole->CoM[0];
+      c->grav.multipole->CoM_rebuild[1] = c->grav.multipole->CoM[1];
+      c->grav.multipole->CoM_rebuild[2] = c->grav.multipole->CoM[2];
     }
   }
 
   /* Set the values for this cell. */
-  c->h_max = h_max;
-  c->ti_hydro_end_min = ti_hydro_end_min;
-  c->ti_hydro_end_max = ti_hydro_end_max;
-  c->ti_hydro_beg_max = ti_hydro_beg_max;
-  c->ti_gravity_end_min = ti_gravity_end_min;
-  c->ti_gravity_end_max = ti_gravity_end_max;
-  c->ti_gravity_beg_max = ti_gravity_beg_max;
+  c->hydro.h_max = h_max;
+  c->hydro.ti_end_min = ti_hydro_end_min;
+  c->hydro.ti_end_max = ti_hydro_end_max;
+  c->hydro.ti_beg_max = ti_hydro_beg_max;
+  c->grav.ti_end_min = ti_gravity_end_min;
+  c->grav.ti_end_max = ti_gravity_end_max;
+  c->grav.ti_beg_max = ti_gravity_beg_max;
+  c->stars.h_max = stars_h_max;
   c->maxdepth = maxdepth;
 
   /* Set ownership according to the start of the parts array. */
   if (s->nr_parts > 0)
-    c->owner =
-        ((c->parts - s->parts) % s->nr_parts) * s->nr_queues / s->nr_parts;
+    c->owner = ((c->hydro.parts - s->parts) % s->nr_parts) * s->nr_queues /
+               s->nr_parts;
   else if (s->nr_sparts > 0)
-    c->owner =
-        ((c->sparts - s->sparts) % s->nr_sparts) * s->nr_queues / s->nr_sparts;
+    c->owner = ((c->stars.parts - s->sparts) % s->nr_sparts) * s->nr_queues /
+               s->nr_sparts;
   else if (s->nr_gparts > 0)
-    c->owner =
-        ((c->gparts - s->gparts) % s->nr_gparts) * s->nr_queues / s->nr_gparts;
+    c->owner = ((c->grav.parts - s->gparts) % s->nr_gparts) * s->nr_queues /
+               s->nr_gparts;
   else
     c->owner = 0; /* Ok, there is really nothing on this rank... */
 
@@ -2183,10 +2425,12 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) {
 
   /* Unpack the inputs. */
   struct space *s = (struct space *)extra_data;
-  struct cell *restrict cells_top = (struct cell *)map_data;
+  struct cell *cells_top = s->cells_top;
+  int *local_cells_with_particles = (int *)map_data;
 
+  /* Loop over the non-empty cells */
   for (int ind = 0; ind < num_cells; ind++) {
-    struct cell *c = &cells_top[ind];
+    struct cell *c = &cells_top[local_cells_with_particles[ind]];
     space_split_recursive(s, c, NULL, NULL, NULL);
   }
 
@@ -2194,8 +2438,8 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) {
   /* All cells and particles should have consistent h_max values. */
   for (int ind = 0; ind < num_cells; ind++) {
     int depth = 0;
-    if (!checkCellhdxmax(&cells_top[ind], &depth))
-      message("    at cell depth %d", depth);
+    const struct cell *c = &cells_top[local_cells_with_particles[ind]];
+    if (!checkCellhdxmax(c, &depth)) message("    at cell depth %d", depth);
   }
 #endif
 }
@@ -2209,8 +2453,8 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) {
 void space_recycle(struct space *s, struct cell *c) {
 
   /* Clear the cell. */
-  if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->glock) != 0 ||
-      lock_destroy(&c->mlock) != 0 || lock_destroy(&c->slock) != 0)
+  if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->grav.plock) != 0 ||
+      lock_destroy(&c->mlock) != 0 || lock_destroy(&c->stars.lock) != 0)
     error("Failed to destroy spinlocks.");
 
   /* Lock the space. */
@@ -2218,8 +2462,8 @@ void space_recycle(struct space *s, struct cell *c) {
 
   /* Hook the multipole back in the buffer */
   if (s->gravity) {
-    c->multipole->next = s->multipoles_sub;
-    s->multipoles_sub = c->multipole;
+    c->grav.multipole->next = s->multipoles_sub;
+    s->multipoles_sub = c->grav.multipole;
   }
 
   /* Hook this cell into the buffer. */
@@ -2258,8 +2502,8 @@ void space_recycle_list(struct space *s, struct cell *cell_list_begin,
   /* Clean up the list of cells. */
   for (struct cell *c = cell_list_begin; c != NULL; c = c->next) {
     /* Clear the cell. */
-    if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->glock) != 0 ||
-        lock_destroy(&c->mlock) != 0 || lock_destroy(&c->slock) != 0)
+    if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->grav.plock) != 0 ||
+        lock_destroy(&c->mlock) != 0 || lock_destroy(&c->stars.lock) != 0)
       error("Failed to destroy spinlocks.");
 
     /* Count this cell. */
@@ -2338,8 +2582,8 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) {
 
     /* Hook the multipole */
     if (s->gravity) {
-      cells[j]->multipole = s->multipoles_sub;
-      s->multipoles_sub = cells[j]->multipole->next;
+      cells[j]->grav.multipole = s->multipoles_sub;
+      s->multipoles_sub = cells[j]->grav.multipole->next;
     }
   }
 
@@ -2349,13 +2593,15 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) {
   /* Init some things in the cell we just got. */
   for (int j = 0; j < nr_cells; j++) {
     for (int k = 0; k < 13; k++)
-      if (cells[j]->sort[k] != NULL) free(cells[j]->sort[k]);
-    struct gravity_tensors *temp = cells[j]->multipole;
+      if (cells[j]->hydro.sort[k] != NULL) free(cells[j]->hydro.sort[k]);
+    struct gravity_tensors *temp = cells[j]->grav.multipole;
     bzero(cells[j], sizeof(struct cell));
-    cells[j]->multipole = temp;
+    cells[j]->grav.multipole = temp;
     cells[j]->nodeID = -1;
-    if (lock_init(&cells[j]->lock) != 0 || lock_init(&cells[j]->glock) != 0 ||
-        lock_init(&cells[j]->mlock) != 0 || lock_init(&cells[j]->slock) != 0)
+    if (lock_init(&cells[j]->hydro.lock) != 0 ||
+        lock_init(&cells[j]->grav.plock) != 0 ||
+        lock_init(&cells[j]->grav.mlock) != 0 ||
+        lock_init(&cells[j]->stars.lock) != 0)
       error("Failed to initialize cell spinlocks.");
   }
 }
@@ -2369,55 +2615,56 @@ void space_free_buff_sort_indices(struct space *s) {
   for (struct cell *finger = s->cells_sub; finger != NULL;
        finger = finger->next) {
     for (int k = 0; k < 13; k++)
-      if (finger->sort[k] != NULL) {
-        free(finger->sort[k]);
-        finger->sort[k] = NULL;
+      if (finger->hydro.sort[k] != NULL) {
+        free(finger->hydro.sort[k]);
+        finger->hydro.sort[k] = NULL;
       }
   }
 }
 
 /**
  * @brief Construct the list of top-level cells that have any tasks in
- * their hierarchy on this MPI rank.
+ * their hierarchy on this MPI rank. Also construct the list of top-level
+ * cells on any rank that have > 0 particles (of any kind).
  *
  * This assumes the list has been pre-allocated at a regrid.
  *
  * @param s The #space.
  */
-void space_list_cells_with_tasks(struct space *s) {
+void space_list_useful_top_level_cells(struct space *s) {
+
+  const ticks tic = getticks();
 
   s->nr_local_cells_with_tasks = 0;
+  s->nr_cells_with_particles = 0;
+
+  for (int i = 0; i < s->nr_cells; ++i) {
+    struct cell *c = &s->cells_top[i];
 
-  for (int i = 0; i < s->nr_cells; ++i)
-    if (cell_has_tasks(&s->cells_top[i])) {
+    if (cell_has_tasks(c)) {
       s->local_cells_with_tasks_top[s->nr_local_cells_with_tasks] = i;
       s->nr_local_cells_with_tasks++;
     }
-  if (s->e->verbose)
-    message("Have %d local top-level cells with tasks (total=%d)",
-            s->nr_local_cells_with_tasks, s->nr_cells);
-}
-
-/**
- * @brief Construct the list of local top-level cells.
- *
- * This assumes the list has been pre-allocated at a regrid.
- *
- * @param s The #space.
- */
-void space_list_local_cells(struct space *s) {
 
-  s->nr_local_cells = 0;
+    const int has_particles =
+        (c->hydro.count > 0) || (c->grav.count > 0) || (c->stars.count > 0) ||
+        (c->grav.multipole != NULL && c->grav.multipole->m_pole.M_000 > 0.f);
 
-  for (int i = 0; i < s->nr_cells; ++i)
-    if (s->cells_top[i].nodeID == engine_rank) {
-      s->local_cells_top[s->nr_local_cells] = i;
-      s->nr_local_cells++;
+    if (has_particles) {
+      s->cells_with_particles_top[s->nr_cells_with_particles] = i;
+      s->nr_cells_with_particles++;
     }
+  }
+  if (s->e->verbose) {
+    message("Have %d local top-level cells with tasks (total=%d)",
+            s->nr_local_cells_with_tasks, s->nr_cells);
+    message("Have %d top-level cells with particles (total=%d)",
+            s->nr_cells_with_particles, s->nr_cells);
+  }
 
   if (s->e->verbose)
-    message("Have %d local top-level cells (total=%d)", s->nr_local_cells,
-            s->nr_cells);
+    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
+            clocks_getunit());
 }
 
 void space_synchronize_particle_positions_mapper(void *map_data, int nr_gparts,
@@ -2465,11 +2712,17 @@ void space_synchronize_particle_positions_mapper(void *map_data, int nr_gparts,
 
 void space_synchronize_particle_positions(struct space *s) {
 
+  const ticks tic = getticks();
+
   if ((s->nr_gparts > 0 && s->nr_parts > 0) ||
       (s->nr_gparts > 0 && s->nr_sparts > 0))
     threadpool_map(&s->e->threadpool,
                    space_synchronize_particle_positions_mapper, s->gparts,
                    s->nr_gparts, sizeof(struct gpart), 0, (void *)s);
+
+  if (s->e->verbose)
+    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
+            clocks_getunit());
 }
 
 void space_first_init_parts_mapper(void *restrict map_data, int count,
@@ -2515,6 +2768,9 @@ void space_first_init_parts_mapper(void *restrict map_data, int count,
   for (int k = 0; k < count; k++) {
 
     hydro_first_init_part(&p[k], &xp[k]);
+#ifdef WITH_LOGGER
+    logger_part_data_init(&xp[k].logger_data);
+#endif
 
     /* Overwrite the internal energy? */
     if (u_init > 0.f) hydro_set_init_internal_energy(&p[k], u_init);
@@ -2849,7 +3105,7 @@ void space_init(struct space *s, struct swift_params *params,
 
   /* Are we generating gas from the DM-only ICs? */
   if (generate_gas_in_ics) {
-    space_generate_gas(s, cosmo, verbose);
+    space_generate_gas(s, cosmo, periodic, dim, verbose);
     parts = s->parts;
     gparts = s->gparts;
     Npart = s->nr_parts;
@@ -3162,10 +3418,12 @@ void space_replicate(struct space *s, int replicate, int verbose) {
  *
  * @param s The #space to create the particles in.
  * @param cosmo The current #cosmology model.
+ * @param periodic Are we using periodic boundary conditions?
+ * @param dim The size of the box (for periodic wrapping).
  * @param verbose Are we talkative?
  */
 void space_generate_gas(struct space *s, const struct cosmology *cosmo,
-                        int verbose) {
+                        int periodic, const double dim[3], int verbose) {
 
   /* Check that this is a sensible ting to do */
   if (!s->hydro)
@@ -3207,7 +3465,7 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo,
 
   /* Compute some constants */
   const double mass_ratio = cosmo->Omega_b / cosmo->Omega_m;
-  const double bg_density = cosmo->Omega_m * cosmo->critical_density;
+  const double bg_density = cosmo->Omega_m * cosmo->critical_density_0;
   const double bg_density_inv = 1. / bg_density;
 
   /* Update the particle properties */
@@ -3221,9 +3479,11 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo,
     p->id = gp_gas->id_or_neg_offset * 2 + 1;
     gp_dm->id_or_neg_offset *= 2;
 
-    if (gp_dm->id_or_neg_offset <= 0) error("DM particle ID overflowd");
+    if (gp_dm->id_or_neg_offset < 0)
+      error("DM particle ID overflowd (DM id=%lld gas id=%lld)",
+            gp_dm->id_or_neg_offset, p->id);
 
-    if (p->id <= 0) error("gas particle ID overflowd");
+    if (p->id < 0) error("gas particle ID overflowd (id=%lld)", p->id);
 
     /* Set the links correctly */
     p->gpart = gp_gas;
@@ -3232,8 +3492,8 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo,
 
     /* Compute positions shift */
     const double d = cbrt(gp_dm->mass * bg_density_inv);
-    const double shift_dm = d * mass_ratio;
-    const double shift_gas = d * (1. - mass_ratio);
+    const double shift_dm = 0.5 * d * mass_ratio;
+    const double shift_gas = 0.5 * d * (1. - mass_ratio);
 
     /* Set the masses */
     gp_dm->mass *= (1. - mass_ratio);
@@ -3244,20 +3504,35 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo,
     gp_dm->x[0] += shift_dm;
     gp_dm->x[1] += shift_dm;
     gp_dm->x[2] += shift_dm;
-    gp_gas->x[0] += shift_gas;
-    gp_gas->x[1] += shift_gas;
-    gp_gas->x[2] += shift_gas;
+    gp_gas->x[0] -= shift_gas;
+    gp_gas->x[1] -= shift_gas;
+    gp_gas->x[2] -= shift_gas;
+
+    /* Make sure the positions are identical between linked particles */
     p->x[0] = gp_gas->x[0];
     p->x[1] = gp_gas->x[1];
     p->x[2] = gp_gas->x[2];
 
+    /* Box-wrap the whole thing to be safe */
+    if (periodic) {
+      gp_dm->x[0] = box_wrap(gp_dm->x[0], 0., dim[0]);
+      gp_dm->x[1] = box_wrap(gp_dm->x[1], 0., dim[1]);
+      gp_dm->x[2] = box_wrap(gp_dm->x[2], 0., dim[2]);
+      gp_gas->x[0] = box_wrap(gp_gas->x[0], 0., dim[0]);
+      gp_gas->x[1] = box_wrap(gp_gas->x[1], 0., dim[1]);
+      gp_gas->x[2] = box_wrap(gp_gas->x[2], 0., dim[2]);
+      p->x[0] = box_wrap(p->x[0], 0., dim[0]);
+      p->x[1] = box_wrap(p->x[1], 0., dim[1]);
+      p->x[2] = box_wrap(p->x[2], 0., dim[2]);
+    }
+
     /* Also copy the velocities */
     p->v[0] = gp_gas->v_full[0];
     p->v[1] = gp_gas->v_full[1];
     p->v[2] = gp_gas->v_full[2];
 
     /* Set the smoothing length to the mean inter-particle separation */
-    p->h = 30. * d;
+    p->h = d;
 
     /* Note that the thermodynamic properties (u, S, ...) will be set later */
   }
@@ -3409,6 +3684,8 @@ void space_clean(struct space *s) {
   free(s->multipoles_top);
   free(s->local_cells_top);
   free(s->local_cells_with_tasks_top);
+  free(s->cells_with_particles_top);
+  free(s->local_cells_with_particles_top);
   free(s->parts);
   free(s->xparts);
   free(s->gparts);
@@ -3465,7 +3742,11 @@ void space_struct_restore(struct space *s, FILE *stream) {
   s->multipoles_sub = NULL;
   s->local_cells_top = NULL;
   s->local_cells_with_tasks_top = NULL;
+  s->cells_with_particles_top = NULL;
+  s->local_cells_with_particles_top = NULL;
   s->grav_top_level = NULL;
+  s->nr_local_cells_with_tasks = 0;
+  s->nr_cells_with_particles = 0;
 #ifdef WITH_MPI
   s->parts_foreign = NULL;
   s->size_parts_foreign = 0;
diff --git a/src/space.h b/src/space.h
index 003a2c3a348db2d43d7ced6b4931000c4c11662a..064d657a84bd2a32602af15ac76247931133c1f3 100644
--- a/src/space.h
+++ b/src/space.h
@@ -119,6 +119,12 @@ struct space {
   /*! Number of *local* top-level cells with tasks */
   int nr_local_cells_with_tasks;
 
+  /*! Number of top-level cells that have >0 particle (of any kind) */
+  int nr_cells_with_particles;
+
+  /*! Number of top-level cells that have >0 particle (of any kind) */
+  int nr_local_cells_with_particles;
+
   /*! The (level 0) cells themselves. */
   struct cell *cells_top;
 
@@ -137,6 +143,12 @@ struct space {
   /*! The indices of the *local* top-level cells with tasks */
   int *local_cells_with_tasks_top;
 
+  /*! The indices of the top-level cells that have >0 particles (of any kind) */
+  int *cells_with_particles_top;
+
+  /*! The indices of the top-level cells that have >0 particles (of any kind) */
+  int *local_cells_with_particles_top;
+
   /*! The total number of parts in the space. */
   size_t nr_parts, size_parts;
 
@@ -146,6 +158,15 @@ struct space {
   /*! The total number of g-parts in the space. */
   size_t nr_sparts, size_sparts;
 
+  /*! Number of inhibted gas particles in the space */
+  size_t nr_inhibited_parts;
+
+  /*! Number of inhibted gravity particles in the space */
+  size_t nr_inhibited_gparts;
+
+  /*! Number of inhibted star particles in the space */
+  size_t nr_inhibited_sparts;
+
   /*! The particle data (cells have pointers to this). */
   struct part *parts;
 
@@ -214,7 +235,7 @@ struct space {
 #endif
 };
 
-/* function prototypes. */
+/* Function prototypes. */
 void space_free_buff_sort_indices(struct space *s);
 void space_parts_sort(struct part *parts, struct xpart *xparts, int *ind,
                       int *counts, int num_bins, ptrdiff_t parts_offset);
@@ -241,23 +262,21 @@ void space_map_parts_xparts(struct space *s,
                                         struct cell *c));
 void space_map_cells_post(struct space *s, int full,
                           void (*fun)(struct cell *c, void *data), void *data);
-void space_rebuild(struct space *s, int verbose);
+void space_rebuild(struct space *s, int repartitioned, int verbose);
 void space_recycle(struct space *s, struct cell *c);
 void space_recycle_list(struct space *s, struct cell *cell_list_begin,
                         struct cell *cell_list_end,
                         struct gravity_tensors *multipole_list_begin,
                         struct gravity_tensors *multipole_list_end);
-void space_split(struct space *s, struct cell *cells, int nr_cells,
-                 int verbose);
+void space_split(struct space *s, int verbose);
 void space_split_mapper(void *map_data, int num_elements, void *extra_data);
-void space_list_local_cells(struct space *s);
-void space_list_cells_with_tasks(struct space *s);
+void space_list_useful_top_level_cells(struct space *s);
 void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts,
-                                struct cell *cells, int verbose);
+                                int *count_inibibited_parts, int verbose);
 void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
-                                 struct cell *cells, int verbose);
+                                 int *count_inibibited_gparts, int verbose);
 void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
-                                 struct cell *cells, int verbose);
+                                 int *count_inibibited_sparts, int verbose);
 void space_synchronize_particle_positions(struct space *s);
 void space_do_parts_sort(void);
 void space_do_gparts_sort(void);
@@ -277,7 +296,7 @@ void space_check_top_multipoles_drift_point(struct space *s,
 void space_check_timesteps(struct space *s);
 void space_replicate(struct space *s, int replicate, int verbose);
 void space_generate_gas(struct space *s, const struct cosmology *cosmo,
-                        int verbose);
+                        int periodic, const double dim[3], int verbose);
 void space_check_cosmology(struct space *s, const struct cosmology *cosmo,
                            int rank);
 void space_reset_task_counters(struct space *s);
diff --git a/src/stars/Default/stars_part.h b/src/stars/Default/stars_part.h
index fe5be3c7f6f190adea2c81ee14c0cad7a10fbd2c..1922cc0e260a3c0f2052649a87252019514e637f 100644
--- a/src/stars/Default/stars_part.h
+++ b/src/stars/Default/stars_part.h
@@ -38,6 +38,9 @@ struct spart {
   /*! Particle position. */
   double x[3];
 
+  /* Offset between current position and position at last tree rebuild. */
+  float x_diff[3];
+
   /*! Particle velocity. */
   float v[3];
 
diff --git a/src/statistics.c b/src/statistics.c
index 22ddc2e971cd6ce16c5310c7fcbf19927c549ceb..47365dd9e388940a187aa5a4568e60bc62bbe934 100644
--- a/src/statistics.c
+++ b/src/statistics.c
@@ -166,8 +166,8 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) {
     hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, v);
     const double x[3] = {p->x[0], p->x[1], p->x[2]};
     const float m = hydro_get_mass(p);
-    const float entropy = hydro_get_physical_entropy(p, cosmo);
-    const float u_inter = hydro_get_physical_internal_energy(p, cosmo);
+    const float entropy = hydro_get_drifted_physical_entropy(p, cosmo);
+    const float u_inter = hydro_get_drifted_physical_internal_energy(p, cosmo);
 
     /* Collect mass */
     stats.mass += m;
diff --git a/src/swift.h b/src/swift.h
index 4398f3f69a66320e13d0ed1a6a0a63fafc7d1e52..153c4ae0d4440d083f1b0c9850e1f2649c0df6fb 100644
--- a/src/swift.h
+++ b/src/swift.h
@@ -46,6 +46,7 @@
 #include "hydro_properties.h"
 #include "lock.h"
 #include "logger.h"
+#include "logger_io.h"
 #include "map.h"
 #include "mesh_gravity.h"
 #include "multipole.h"
diff --git a/src/swift_velociraptor_part.h b/src/swift_velociraptor_part.h
index 80ee94ba612299dbe8b451cf1ef9d0ee45f8bf53..adae884c2f930c44edf4d48f47f168475bc65885 100644
--- a/src/swift_velociraptor_part.h
+++ b/src/swift_velociraptor_part.h
@@ -19,6 +19,8 @@
 #ifndef SWIFT_VELOCIRAPTOR_PART_H
 #define SWIFT_VELOCIRAPTOR_PART_H
 
+#include "part_type.h"
+
 /* SWIFT/VELOCIraptor particle. */
 struct swift_vel_part {
 
diff --git a/src/task.c b/src/task.c
index fd1fbe0e2e8bdf11190124f86ac06b699d6a6282..996c5113bac9935f70a3aafafd58da965b13f5aa 100644
--- a/src/task.c
+++ b/src/task.c
@@ -48,16 +48,17 @@
 
 /* Task type names. */
 const char *taskID_names[task_type_count] = {
-    "none",           "sort",          "self",
-    "pair",           "sub_self",      "sub_pair",
-    "init_grav",      "init_grav_out", "ghost_in",
-    "ghost",          "ghost_out",     "extra_ghost",
-    "drift_part",     "drift_gpart",   "end_force",
-    "kick1",          "kick2",         "timestep",
-    "send",           "recv",          "grav_long_range",
-    "grav_mm",        "grav_down_in",  "grav_down",
-    "grav_mesh",      "cooling",       "sourceterms",
-    "stars_ghost_in", "stars_ghost",   "stars_ghost_out"};
+    "none",        "sort",           "self",
+    "pair",        "sub_self",       "sub_pair",
+    "init_grav",   "init_grav_out",  "ghost_in",
+    "ghost",       "ghost_out",      "extra_ghost",
+    "drift_part",  "drift_gpart",    "end_force",
+    "kick1",       "kick2",          "timestep",
+    "send",        "recv",           "grav_long_range",
+    "grav_mm",     "grav_down_in",   "grav_down",
+    "grav_mesh",   "cooling",        "star_formation",
+    "sourceterms", "logger",         "stars_ghost_in",
+    "stars_ghost", "stars_ghost_out"};
 
 /* Sub-task type names. */
 const char *subtaskID_names[task_subtype_count] = {
@@ -95,9 +96,9 @@ MPI_Comm subtaskMPI_comms[task_subtype_count];
     return 0;                                                               \
   }
 
-TASK_CELL_OVERLAP(part, parts, count);
-TASK_CELL_OVERLAP(gpart, gparts, gcount);
-TASK_CELL_OVERLAP(spart, sparts, scount);
+TASK_CELL_OVERLAP(part, hydro.parts, hydro.count);
+TASK_CELL_OVERLAP(gpart, grav.parts, grav.count);
+TASK_CELL_OVERLAP(spart, stars.parts, stars.count);
 
 /**
  * @brief Returns the #task_actions for a given task.
@@ -122,6 +123,9 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
       return task_action_part;
       break;
 
+    case task_type_star_formation:
+      return task_action_all;
+
     case task_type_stars_ghost:
       return task_action_spart;
       break;
@@ -157,14 +161,15 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
     case task_type_end_force:
     case task_type_kick1:
     case task_type_kick2:
+    case task_type_logger:
     case task_type_timestep:
     case task_type_send:
     case task_type_recv:
-      if (t->ci->count > 0 && t->ci->gcount > 0)
+      if (t->ci->hydro.count > 0 && t->ci->grav.count > 0)
         return task_action_all;
-      else if (t->ci->count > 0)
+      else if (t->ci->hydro.count > 0)
         return task_action_part;
-      else if (t->ci->gcount > 0)
+      else if (t->ci->grav.count > 0)
         return task_action_gpart;
       else
         error("Task without particles");
@@ -172,13 +177,13 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
 
     case task_type_init_grav:
     case task_type_grav_mm:
+    case task_type_grav_long_range:
       return task_action_multipole;
       break;
 
     case task_type_drift_gpart:
     case task_type_grav_down:
     case task_type_grav_mesh:
-    case task_type_grav_long_range:
       return task_action_gpart;
       break;
 
@@ -228,10 +233,10 @@ float task_overlap(const struct task *restrict ta,
 
     /* Compute the union of the cell data. */
     size_t size_union = 0;
-    if (ta->ci != NULL) size_union += ta->ci->count;
-    if (ta->cj != NULL) size_union += ta->cj->count;
-    if (tb->ci != NULL) size_union += tb->ci->count;
-    if (tb->cj != NULL) size_union += tb->cj->count;
+    if (ta->ci != NULL) size_union += ta->ci->hydro.count;
+    if (ta->cj != NULL) size_union += ta->cj->hydro.count;
+    if (tb->ci != NULL) size_union += tb->ci->hydro.count;
+    if (tb->cj != NULL) size_union += tb->cj->hydro.count;
 
     /* Compute the intersection of the cell data. */
     const size_t size_intersect = task_cell_overlap_part(ta->ci, tb->ci) +
@@ -247,10 +252,10 @@ float task_overlap(const struct task *restrict ta,
 
     /* Compute the union of the cell data. */
     size_t size_union = 0;
-    if (ta->ci != NULL) size_union += ta->ci->gcount;
-    if (ta->cj != NULL) size_union += ta->cj->gcount;
-    if (tb->ci != NULL) size_union += tb->ci->gcount;
-    if (tb->cj != NULL) size_union += tb->cj->gcount;
+    if (ta->ci != NULL) size_union += ta->ci->grav.count;
+    if (ta->cj != NULL) size_union += ta->cj->grav.count;
+    if (tb->ci != NULL) size_union += tb->ci->grav.count;
+    if (tb->cj != NULL) size_union += tb->cj->grav.count;
 
     /* Compute the intersection of the cell data. */
     const size_t size_intersect = task_cell_overlap_gpart(ta->ci, tb->ci) +
@@ -266,10 +271,10 @@ float task_overlap(const struct task *restrict ta,
 
     /* Compute the union of the cell data. */
     size_t size_union = 0;
-    if (ta->ci != NULL) size_union += ta->ci->scount;
-    if (ta->cj != NULL) size_union += ta->cj->scount;
-    if (tb->ci != NULL) size_union += tb->ci->scount;
-    if (tb->cj != NULL) size_union += tb->cj->scount;
+    if (ta->ci != NULL) size_union += ta->ci->stars.count;
+    if (ta->cj != NULL) size_union += ta->cj->stars.count;
+    if (tb->ci != NULL) size_union += tb->ci->stars.count;
+    if (tb->cj != NULL) size_union += tb->cj->stars.count;
 
     /* Compute the intersection of the cell data. */
     const size_t size_intersect = task_cell_overlap_spart(ta->ci, tb->ci) +
@@ -301,6 +306,7 @@ void task_unlock(struct task *t) {
     case task_type_end_force:
     case task_type_kick1:
     case task_type_kick2:
+    case task_type_logger:
     case task_type_timestep:
       cell_unlocktree(ci);
       cell_gunlocktree(ci);
@@ -396,8 +402,9 @@ int task_lock(struct task *t) {
     case task_type_end_force:
     case task_type_kick1:
     case task_type_kick2:
+    case task_type_logger:
     case task_type_timestep:
-      if (ci->hold || ci->ghold) return 0;
+      if (ci->hydro.hold || ci->grav.phold) return 0;
       if (cell_locktree(ci) != 0) return 0;
       if (cell_glocktree(ci) != 0) {
         cell_unlocktree(ci);
@@ -407,13 +414,13 @@ int task_lock(struct task *t) {
 
     case task_type_drift_part:
     case task_type_sort:
-      if (ci->hold) return 0;
+      if (ci->hydro.hold) return 0;
       if (cell_locktree(ci) != 0) return 0;
       break;
 
     case task_type_drift_gpart:
     case task_type_grav_mesh:
-      if (ci->ghold) return 0;
+      if (ci->grav.phold) return 0;
       if (cell_glocktree(ci) != 0) return 0;
       break;
 
@@ -421,7 +428,7 @@ int task_lock(struct task *t) {
     case task_type_sub_self:
       if (subtype == task_subtype_grav) {
         /* Lock the gparts and the m-pole */
-        if (ci->ghold || ci->mhold) return 0;
+        if (ci->grav.phold || ci->grav.mhold) return 0;
         if (cell_glocktree(ci) != 0)
           return 0;
         else if (cell_mlocktree(ci) != 0) {
@@ -437,7 +444,7 @@ int task_lock(struct task *t) {
     case task_type_sub_pair:
       if (subtype == task_subtype_grav) {
         /* Lock the gparts and the m-pole in both cells */
-        if (ci->ghold || cj->ghold) return 0;
+        if (ci->grav.phold || cj->grav.phold) return 0;
         if (cell_glocktree(ci) != 0) return 0;
         if (cell_glocktree(cj) != 0) {
           cell_gunlocktree(ci);
@@ -454,7 +461,7 @@ int task_lock(struct task *t) {
         }
       } else {
         /* Lock the parts in both cells */
-        if (ci->hold || cj->hold) return 0;
+        if (ci->hydro.hold || cj->hydro.hold) return 0;
         if (cell_locktree(ci) != 0) return 0;
         if (cell_locktree(cj) != 0) {
           cell_unlocktree(ci);
@@ -465,7 +472,7 @@ int task_lock(struct task *t) {
 
     case task_type_grav_down:
       /* Lock the gparts and the m-poles */
-      if (ci->ghold || ci->mhold) return 0;
+      if (ci->grav.phold || ci->grav.mhold) return 0;
       if (cell_glocktree(ci) != 0)
         return 0;
       else if (cell_mlocktree(ci) != 0) {
@@ -476,13 +483,13 @@ int task_lock(struct task *t) {
 
     case task_type_grav_long_range:
       /* Lock the m-poles */
-      if (ci->mhold) return 0;
+      if (ci->grav.mhold) return 0;
       if (cell_mlocktree(ci) != 0) return 0;
       break;
 
     case task_type_grav_mm:
       /* Lock both m-poles */
-      if (ci->mhold || cj->mhold) return 0;
+      if (ci->grav.mhold || cj->grav.mhold) return 0;
       if (cell_mlocktree(ci) != 0) return 0;
       if (cell_mlocktree(cj) != 0) {
         cell_munlocktree(ci);
diff --git a/src/task.h b/src/task.h
index 866465e4997770d3e0e48818c6eebc6e9abf67a2..9b3225fcf27b768059a2984847d20750d184f8d2 100644
--- a/src/task.h
+++ b/src/task.h
@@ -65,7 +65,9 @@ enum task_types {
   task_type_grav_down,
   task_type_grav_mesh,
   task_type_cooling,
+  task_type_star_formation,
   task_type_sourceterms,
+  task_type_logger,
   task_type_stars_ghost_in,
   task_type_stars_ghost,
   task_type_stars_ghost_out,
diff --git a/src/timers.c b/src/timers.c
index 51d0e5f6dc4fd9e4e0567592750b8de45ecda06b..898c833c3b6764f05ed9205efa0db6220e911a7e 100644
--- a/src/timers.c
+++ b/src/timers.c
@@ -80,6 +80,7 @@ const char* timers_names[timer_count] = {
     "dorecv_gpart",
     "dorecv_spart",
     "do_cooling",
+    "do_star_formation",
     "gettask",
     "qget",
     "qsteal",
@@ -93,6 +94,7 @@ const char* timers_names[timer_count] = {
     "dopair_subset_stars_density",
     "dosubpair_stars_density",
     "dosub_self_stars_density",
+    "logger",
 };
 
 /* File to store the timers */
diff --git a/src/timers.h b/src/timers.h
index aba6ae33e3b8268c088694863967b96851715153..c43f0154d2aaaa9d1c5aed3ad51b912e4fc5d751 100644
--- a/src/timers.h
+++ b/src/timers.h
@@ -81,6 +81,7 @@ enum {
   timer_dorecv_gpart,
   timer_dorecv_spart,
   timer_do_cooling,
+  timer_do_star_formation,
   timer_gettask,
   timer_qget,
   timer_qsteal,
@@ -94,6 +95,7 @@ enum {
   timer_dopair_subset_stars_density,
   timer_dosubpair_stars_density,
   timer_dosub_self_stars_density,
+  timer_logger,
   timer_count,
 };
 
diff --git a/src/timestep.h b/src/timestep.h
index 3b957f0c4b9d103023c77837a1a6ca6388856d22..e9943a41a0536b65944f0256c827d43386aadd88 100644
--- a/src/timestep.h
+++ b/src/timestep.h
@@ -126,8 +126,9 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep(
   /* Compute the next timestep (cooling condition) */
   float new_dt_cooling = FLT_MAX;
   if (e->policy & engine_policy_cooling)
-    new_dt_cooling = cooling_timestep(e->cooling_func, e->physical_constants,
-                                      e->cosmology, e->internal_units, p);
+    new_dt_cooling =
+        cooling_timestep(e->cooling_func, e->physical_constants, e->cosmology,
+                         e->internal_units, e->hydro_properties, p, xp);
 
   /* Compute the next timestep (gravity condition) */
   float new_dt_grav = FLT_MAX, new_dt_self_grav = FLT_MAX,
diff --git a/src/tools.c b/src/tools.c
index 93f0c6f970a1fa530951d1d9d8e3ef169a688a70..ff33afbca3fdc97ede61ff09998d8dc27bf0154b 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -195,23 +195,23 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) {
   const float H = cosmo->H;
 
   /* Implements a double-for loop and checks every interaction */
-  for (int i = 0; i < ci->count; ++i) {
+  for (int i = 0; i < ci->hydro.count; ++i) {
 
-    pi = &ci->parts[i];
+    pi = &ci->hydro.parts[i];
     hi = pi->h;
     hig2 = hi * hi * kernel_gamma2;
 
     /* Skip inactive particles. */
     if (!part_is_active(pi, e)) continue;
 
-    for (int j = 0; j < cj->count; ++j) {
+    for (int j = 0; j < cj->hydro.count; ++j) {
 
-      pj = &cj->parts[j];
+      pj = &cj->hydro.parts[j];
 
       /* Pairwise distance */
       r2 = 0.0f;
       for (int k = 0; k < 3; k++) {
-        dx[k] = ci->parts[i].x[k] - cj->parts[j].x[k];
+        dx[k] = ci->hydro.parts[i].x[k] - cj->hydro.parts[j].x[k];
         dx[k] = nearest(dx[k], dim[k]);
         r2 += dx[k] * dx[k];
       }
@@ -227,23 +227,23 @@ void pairs_all_density(struct runner *r, struct cell *ci, struct cell *cj) {
   }
 
   /* Reverse double-for loop and checks every interaction */
-  for (int j = 0; j < cj->count; ++j) {
+  for (int j = 0; j < cj->hydro.count; ++j) {
 
-    pj = &cj->parts[j];
+    pj = &cj->hydro.parts[j];
     hj = pj->h;
     hjg2 = hj * hj * kernel_gamma2;
 
     /* Skip inactive particles. */
     if (!part_is_active(pj, e)) continue;
 
-    for (int i = 0; i < ci->count; ++i) {
+    for (int i = 0; i < ci->hydro.count; ++i) {
 
-      pi = &ci->parts[i];
+      pi = &ci->hydro.parts[i];
 
       /* Pairwise distance */
       r2 = 0.0f;
       for (int k = 0; k < 3; k++) {
-        dx[k] = cj->parts[j].x[k] - ci->parts[i].x[k];
+        dx[k] = cj->hydro.parts[j].x[k] - ci->hydro.parts[i].x[k];
         dx[k] = nearest(dx[k], dim[k]);
         r2 += dx[k] * dx[k];
       }
@@ -270,25 +270,25 @@ void pairs_all_force(struct runner *r, struct cell *ci, struct cell *cj) {
   const float H = cosmo->H;
 
   /* Implements a double-for loop and checks every interaction */
-  for (int i = 0; i < ci->count; ++i) {
+  for (int i = 0; i < ci->hydro.count; ++i) {
 
-    pi = &ci->parts[i];
+    pi = &ci->hydro.parts[i];
     hi = pi->h;
     hig2 = hi * hi * kernel_gamma2;
 
     /* Skip inactive particles. */
     if (!part_is_active(pi, e)) continue;
 
-    for (int j = 0; j < cj->count; ++j) {
+    for (int j = 0; j < cj->hydro.count; ++j) {
 
-      pj = &cj->parts[j];
+      pj = &cj->hydro.parts[j];
       hj = pj->h;
       hjg2 = hj * hj * kernel_gamma2;
 
       /* Pairwise distance */
       r2 = 0.0f;
       for (int k = 0; k < 3; k++) {
-        dx[k] = ci->parts[i].x[k] - cj->parts[j].x[k];
+        dx[k] = ci->hydro.parts[i].x[k] - cj->hydro.parts[j].x[k];
         dx[k] = nearest(dx[k], dim[k]);
         r2 += dx[k] * dx[k];
       }
@@ -303,25 +303,25 @@ void pairs_all_force(struct runner *r, struct cell *ci, struct cell *cj) {
   }
 
   /* Reverse double-for loop and checks every interaction */
-  for (int j = 0; j < cj->count; ++j) {
+  for (int j = 0; j < cj->hydro.count; ++j) {
 
-    pj = &cj->parts[j];
+    pj = &cj->hydro.parts[j];
     hj = pj->h;
     hjg2 = hj * hj * kernel_gamma2;
 
     /* Skip inactive particles. */
     if (!part_is_active(pj, e)) continue;
 
-    for (int i = 0; i < ci->count; ++i) {
+    for (int i = 0; i < ci->hydro.count; ++i) {
 
-      pi = &ci->parts[i];
+      pi = &ci->hydro.parts[i];
       hi = pi->h;
       hig2 = hi * hi * kernel_gamma2;
 
       /* Pairwise distance */
       r2 = 0.0f;
       for (int k = 0; k < 3; k++) {
-        dx[k] = cj->parts[j].x[k] - ci->parts[i].x[k];
+        dx[k] = cj->hydro.parts[j].x[k] - ci->hydro.parts[i].x[k];
         dx[k] = nearest(dx[k], dim[k]);
         r2 += dx[k] * dx[k];
       }
@@ -347,8 +347,8 @@ void pairs_all_stars_density(struct runner *r, struct cell *ci,
   const float H = cosmo->H;
 
   /* Implements a double-for loop and checks every interaction */
-  for (int i = 0; i < ci->scount; ++i) {
-    struct spart *spi = &ci->sparts[i];
+  for (int i = 0; i < ci->stars.count; ++i) {
+    struct spart *spi = &ci->stars.parts[i];
 
     float hi = spi->h;
     float hig2 = hi * hi * kernel_gamma2;
@@ -356,9 +356,9 @@ void pairs_all_stars_density(struct runner *r, struct cell *ci,
     /* Skip inactive particles. */
     if (!spart_is_active(spi, e)) continue;
 
-    for (int j = 0; j < cj->count; ++j) {
+    for (int j = 0; j < cj->hydro.count; ++j) {
 
-      struct part *pj = &cj->parts[j];
+      struct part *pj = &cj->hydro.parts[j];
 
       /* Pairwise distance */
       r2 = 0.0f;
@@ -377,18 +377,18 @@ void pairs_all_stars_density(struct runner *r, struct cell *ci,
   }
 
   /* Reverse double-for loop and checks every interaction */
-  for (int j = 0; j < cj->scount; ++j) {
+  for (int j = 0; j < cj->stars.count; ++j) {
 
-    struct spart *spj = &cj->sparts[j];
+    struct spart *spj = &cj->stars.parts[j];
     float hj = spj->h;
     float hjg2 = hj * hj * kernel_gamma2;
 
     /* Skip inactive particles. */
     if (!spart_is_active(spj, e)) continue;
 
-    for (int i = 0; i < ci->count; ++i) {
+    for (int i = 0; i < ci->hydro.count; ++i) {
 
-      struct part *pi = &ci->parts[i];
+      struct part *pi = &ci->hydro.parts[i];
 
       /* Pairwise distance */
       r2 = 0.0f;
@@ -416,15 +416,15 @@ void self_all_density(struct runner *r, struct cell *ci) {
   const float H = cosmo->H;
 
   /* Implements a double-for loop and checks every interaction */
-  for (int i = 0; i < ci->count; ++i) {
+  for (int i = 0; i < ci->hydro.count; ++i) {
 
-    pi = &ci->parts[i];
+    pi = &ci->hydro.parts[i];
     hi = pi->h;
     hig2 = hi * hi * kernel_gamma2;
 
-    for (int j = i + 1; j < ci->count; ++j) {
+    for (int j = i + 1; j < ci->hydro.count; ++j) {
 
-      pj = &ci->parts[j];
+      pj = &ci->hydro.parts[j];
       hj = pj->h;
       hjg2 = hj * hj * kernel_gamma2;
 
@@ -433,7 +433,7 @@ void self_all_density(struct runner *r, struct cell *ci) {
       /* Pairwise distance */
       r2 = 0.0f;
       for (int k = 0; k < 3; k++) {
-        dxi[k] = ci->parts[i].x[k] - ci->parts[j].x[k];
+        dxi[k] = ci->hydro.parts[i].x[k] - ci->hydro.parts[j].x[k];
         r2 += dxi[k] * dxi[k];
       }
 
@@ -469,15 +469,15 @@ void self_all_force(struct runner *r, struct cell *ci) {
   const float H = cosmo->H;
 
   /* Implements a double-for loop and checks every interaction */
-  for (int i = 0; i < ci->count; ++i) {
+  for (int i = 0; i < ci->hydro.count; ++i) {
 
-    pi = &ci->parts[i];
+    pi = &ci->hydro.parts[i];
     hi = pi->h;
     hig2 = hi * hi * kernel_gamma2;
 
-    for (int j = i + 1; j < ci->count; ++j) {
+    for (int j = i + 1; j < ci->hydro.count; ++j) {
 
-      pj = &ci->parts[j];
+      pj = &ci->hydro.parts[j];
       hj = pj->h;
       hjg2 = hj * hj * kernel_gamma2;
 
@@ -486,7 +486,7 @@ void self_all_force(struct runner *r, struct cell *ci) {
       /* Pairwise distance */
       r2 = 0.0f;
       for (int k = 0; k < 3; k++) {
-        dxi[k] = ci->parts[i].x[k] - ci->parts[j].x[k];
+        dxi[k] = ci->hydro.parts[i].x[k] - ci->hydro.parts[j].x[k];
         r2 += dxi[k] * dxi[k];
       }
 
@@ -510,17 +510,17 @@ void self_all_stars_density(struct runner *r, struct cell *ci) {
   const float H = cosmo->H;
 
   /* Implements a double-for loop and checks every interaction */
-  for (int i = 0; i < ci->scount; ++i) {
+  for (int i = 0; i < ci->stars.count; ++i) {
 
-    spi = &ci->sparts[i];
+    spi = &ci->stars.parts[i];
     hi = spi->h;
     hig2 = hi * hi * kernel_gamma2;
 
     if (!spart_is_active(spi, e)) continue;
 
-    for (int j = 0; j < ci->count; ++j) {
+    for (int j = 0; j < ci->hydro.count; ++j) {
 
-      pj = &ci->parts[j];
+      pj = &ci->hydro.parts[j];
       hj = pj->h;
 
       /* Pairwise distance */
diff --git a/src/velociraptor_dummy.c b/src/velociraptor_dummy.c
new file mode 100644
index 0000000000000000000000000000000000000000..8f14a3230d341993122f09f2bccf3d8232550fd9
--- /dev/null
+++ b/src/velociraptor_dummy.c
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 James Willis (james.s.willis@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <stddef.h>
+
+/* Local includes. */
+#include "error.h"
+#include "swift_velociraptor_part.h"
+#include "velociraptor_interface.h"
+
+/* Dummy VELOCIraptor interface for testing compilation without linking the
+ * actual VELOCIraptor library. */
+#ifdef HAVE_DUMMY_VELOCIRAPTOR
+struct cosmoinfo {};
+struct unitinfo {};
+struct cell_loc {};
+struct siminfo {};
+
+int InitVelociraptor(char *config_name, char *output_name,
+                     struct cosmoinfo cosmo_info, struct unitinfo unit_info,
+                     struct siminfo sim_info) {
+
+  error("This is only a dummy. Call the real one!");
+  return 0;
+}
+int InvokeVelociraptor(const size_t num_gravity_parts,
+                       const size_t num_hydro_parts,
+                       struct swift_vel_part *swift_parts,
+                       const int *cell_node_ids, char *output_name) {
+
+  error("This is only a dummy. Call the real one!");
+  return 0;
+}
+#endif /* HAVE_DUMMY_VELOCIRAPTOR */
diff --git a/src/velociraptor_interface.c b/src/velociraptor_interface.c
index 7afaa2e66ee9993166caaead457b9bc226b9548b..7756fe4b937986c108d223c56183f7d31cdfaa98 100644
--- a/src/velociraptor_interface.c
+++ b/src/velociraptor_interface.c
@@ -35,6 +35,79 @@
 
 #ifdef HAVE_VELOCIRAPTOR
 
+/* Structure for passing cosmological information to VELOCIraptor. */
+struct cosmoinfo {
+
+  /*! Current expansion factor of the Universe. (cosmology.a) */
+  double atime;
+
+  /*! Reduced Hubble constant (H0 / (100km/s/Mpc) (cosmology.h) */
+  double littleh;
+
+  /*! Matter density parameter (cosmology.Omega_m) */
+  double Omega_m;
+
+  /*! Baryon density parameter (cosmology.Omega_b) */
+  double Omega_b;
+
+  /*! Radiation constant density parameter (cosmology.Omega_lambda) */
+  double Omega_Lambda;
+
+  /*! Dark matter density parameter (cosmology.Omega_m - cosmology.Omega_b) */
+  double Omega_cdm;
+
+  /*! Dark-energy equation of state at the current time (cosmology.w)*/
+  double w_de;
+};
+
+/* Structure for passing unit information to VELOCIraptor. */
+struct unitinfo {
+
+  /* Length conversion factor to kpc. */
+  double lengthtokpc;
+
+  /* Velocity conversion factor to km/s. */
+  double velocitytokms;
+
+  /* Mass conversion factor to solar masses. */
+  double masstosolarmass;
+
+  /* Potential conversion factor. */
+  double energyperunitmass;
+
+  /*! Newton's gravitationl constant (phys_const.const_newton_G)*/
+  double gravity;
+
+  /*! Hubble constant at the current redshift (cosmology.H) */
+  double hubbleunit;
+};
+
+/* Structure to hold the location of a top-level cell. */
+struct cell_loc {
+
+  /* Coordinates x,y,z */
+  double loc[3];
+};
+
+/* Structure for passing simulation information to VELOCIraptor. */
+struct siminfo {
+  double period, zoomhigresolutionmass, interparticlespacing, spacedimension[3];
+
+  /* Number of top-cells. */
+  int numcells;
+
+  /*! Locations of top-level cells. */
+  struct cell_loc *cell_loc;
+
+  /*! Top-level cell width. */
+  double cellwidth[3];
+
+  /*! Inverse of the top-level cell width. */
+  double icellwidth[3];
+
+  int icosmologicalsim;
+};
+
 /* VELOCIraptor interface. */
 int InitVelociraptor(char *config_name, char *output_name,
                      struct cosmoinfo cosmo_info, struct unitinfo unit_info,
@@ -185,6 +258,7 @@ void velociraptor_invoke(struct engine *e) {
   struct space *s = e->s;
   struct gpart *gparts = s->gparts;
   struct part *parts = s->parts;
+  struct xpart *xparts = s->xparts;
   const size_t nr_gparts = s->nr_gparts;
   const size_t nr_hydro_parts = s->nr_parts;
   const int nr_cells = s->nr_cells;
@@ -261,7 +335,8 @@ void velociraptor_invoke(struct engine *e) {
       swift_parts[i].id = parts[-gparts[i].id_or_neg_offset].id;
       swift_parts[i].u =
           hydro_get_physical_internal_energy(
-              &parts[-gparts[i].id_or_neg_offset], e->cosmology) *
+              &parts[-gparts[i].id_or_neg_offset],
+              &xparts[-gparts[i].id_or_neg_offset], e->cosmology) *
           energy_scale;
     } else if (gparts[i].type == swift_type_dark_matter) {
       swift_parts[i].id = gparts[i].id_or_neg_offset;
diff --git a/src/velociraptor_interface.h b/src/velociraptor_interface.h
index 0f6b8d339471f4bb1409baae62475a74e68cb5b1..1f29be11c9dd8e267c87201b0a438979fec3775b 100644
--- a/src/velociraptor_interface.h
+++ b/src/velociraptor_interface.h
@@ -22,81 +22,16 @@
 /* Config parameters. */
 #include "../config.h"
 
-/* Forward declaration */
-struct engine;
-
-/* Structure for passing cosmological information to VELOCIraptor. */
-struct cosmoinfo {
-
-  /*! Current expansion factor of the Universe. (cosmology.a) */
-  double atime;
-
-  /*! Reduced Hubble constant (H0 / (100km/s/Mpc) (cosmology.h) */
-  double littleh;
-
-  /*! Matter density parameter (cosmology.Omega_m) */
-  double Omega_m;
-
-  /*! Baryon density parameter (cosmology.Omega_b) */
-  double Omega_b;
-
-  /*! Radiation constant density parameter (cosmology.Omega_lambda) */
-  double Omega_Lambda;
-
-  /*! Dark matter density parameter (cosmology.Omega_m - cosmology.Omega_b) */
-  double Omega_cdm;
-
-  /*! Dark-energy equation of state at the current time (cosmology.w)*/
-  double w_de;
+/**
+ * @brief The different formats for when to run structure finding.
+ */
+enum io_stf_output_format {
+  io_stf_steps = 0, /*!< Output every N steps */
+  io_stf_time       /*!< Output at fixed time intervals */
 };
 
-/* Structure for passing unit information to VELOCIraptor. */
-struct unitinfo {
-
-  /* Length conversion factor to kpc. */
-  double lengthtokpc;
-
-  /* Velocity conversion factor to km/s. */
-  double velocitytokms;
-
-  /* Mass conversion factor to solar masses. */
-  double masstosolarmass;
-
-  /* Potential conversion factor. */
-  double energyperunitmass;
-
-  /*! Newton's gravitationl constant (phys_const.const_newton_G)*/
-  double gravity;
-
-  /*! Hubble constant at the current redshift (cosmology.H) */
-  double hubbleunit;
-};
-
-/* Structure to hold the location of a top-level cell. */
-struct cell_loc {
-
-  /* Coordinates x,y,z */
-  double loc[3];
-};
-
-/* Structure for passing simulation information to VELOCIraptor. */
-struct siminfo {
-  double period, zoomhigresolutionmass, interparticlespacing, spacedimension[3];
-
-  /* Number of top-cells. */
-  int numcells;
-
-  /*! Locations of top-level cells. */
-  struct cell_loc *cell_loc;
-
-  /*! Top-level cell width. */
-  double cellwidth[3];
-
-  /*! Inverse of the top-level cell width. */
-  double icellwidth[3];
-
-  int icosmologicalsim;
-};
+/* Forward declaration */
+struct engine;
 
 /* VELOCIraptor wrapper functions. */
 void velociraptor_init(struct engine *e);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 640c837b3bd1c3bf19d3d57ef080839b170427bf..9698e66d1a7a80566a9347bb629e9670eafbb98c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -20,7 +20,7 @@ AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS)
 AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS)
 
 # List of programs and scripts to run in the test suite
-TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetry \
+TESTS = testGreetings testMaths testReading.sh testSingle testKernel \
         testActivePair.sh test27cells.sh test27cellsPerturbed.sh  \
         testParser.sh testSPHStep test125cells.sh test125cellsPerturbed.sh testFFT \
         testAdiabaticIndex \
diff --git a/tests/logger.yml b/tests/logger.yml
new file mode 100644
index 0000000000000000000000000000000000000000..eaf8731f0e09df40b891c7b57be35cd9e14fc5cc
--- /dev/null
+++ b/tests/logger.yml
@@ -0,0 +1,5 @@
+# Parameters governing the logger snapshot system
+Logger:
+  delta_step:           10     # (Optional) Update the particle log every this many updates
+  initial_buffer_size:   .1    # buffer size in GB
+  basename:             indice # Common part of the filenames
diff --git a/tests/test125cells.c b/tests/test125cells.c
index 2a2c20dbb064539b481e169b49b74389e79a8174..93a85bea87eb1b7c204f0cf9e6ea37ecea6d18f4 100644
--- a/tests/test125cells.c
+++ b/tests/test125cells.c
@@ -31,19 +31,12 @@
 #include "swift.h"
 
 #if defined(WITH_VECTORIZATION)
-#define DOSELF2 runner_doself2_force_vec
-#define DOPAIR2 runner_dopair2_branch_force
 #define DOSELF2_NAME "runner_doself2_force_vec"
 #define DOPAIR2_NAME "runner_dopair2_force_vec"
 #endif
 
-#ifndef DOSELF2
-#define DOSELF2 runner_doself2_force
+#ifndef DOSELF2_NAME
 #define DOSELF2_NAME "runner_doself2_density"
-#endif
-
-#ifndef DOPAIR2
-#define DOPAIR2 runner_dopair2_branch_force
 #define DOPAIR2_NAME "runner_dopair2_force"
 #endif
 
@@ -152,19 +145,19 @@ void get_solution(const struct cell *main_cell, struct solution_part *solution,
                   float density, enum velocity_field vel,
                   enum pressure_field press, float size) {
 
-  for (int i = 0; i < main_cell->count; ++i) {
+  for (int i = 0; i < main_cell->hydro.count; ++i) {
 
-    solution[i].id = main_cell->parts[i].id;
+    solution[i].id = main_cell->hydro.parts[i].id;
 
-    solution[i].x[0] = main_cell->parts[i].x[0];
-    solution[i].x[1] = main_cell->parts[i].x[1];
-    solution[i].x[2] = main_cell->parts[i].x[2];
+    solution[i].x[0] = main_cell->hydro.parts[i].x[0];
+    solution[i].x[1] = main_cell->hydro.parts[i].x[1];
+    solution[i].x[2] = main_cell->hydro.parts[i].x[2];
 
-    solution[i].v[0] = main_cell->parts[i].v[0];
-    solution[i].v[1] = main_cell->parts[i].v[1];
-    solution[i].v[2] = main_cell->parts[i].v[2];
+    solution[i].v[0] = main_cell->hydro.parts[i].v[0];
+    solution[i].v[1] = main_cell->hydro.parts[i].v[1];
+    solution[i].v[2] = main_cell->hydro.parts[i].v[2];
 
-    solution[i].h = main_cell->parts[i].h;
+    solution[i].h = main_cell->hydro.parts[i].h;
 
     solution[i].rho = density;
 
@@ -213,9 +206,9 @@ void reset_particles(struct cell *c, struct hydro_space *hs,
                      enum velocity_field vel, enum pressure_field press,
                      float size, float density) {
 
-  for (int i = 0; i < c->count; ++i) {
+  for (int i = 0; i < c->hydro.count; ++i) {
 
-    struct part *p = &c->parts[i];
+    struct part *p = &c->hydro.parts[i];
 
     set_velocity(p, vel, size);
     set_energy_state(p, press, size, density);
@@ -272,20 +265,20 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
   struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
 
-  if (posix_memalign((void **)&cell->parts, part_align,
+  if (posix_memalign((void **)&cell->hydro.parts, part_align,
                      count * sizeof(struct part)) != 0)
     error("couldn't allocate particles, no. of particles: %d", (int)count);
-  if (posix_memalign((void **)&cell->xparts, xpart_align,
+  if (posix_memalign((void **)&cell->hydro.xparts, xpart_align,
                      count * sizeof(struct xpart)) != 0)
     error("couldn't allocate particles, no. of x-particles: %d", (int)count);
-  bzero(cell->parts, count * sizeof(struct part));
-  bzero(cell->xparts, count * sizeof(struct xpart));
+  bzero(cell->hydro.parts, count * sizeof(struct part));
+  bzero(cell->hydro.xparts, count * sizeof(struct xpart));
 
   float h_max = 0.f;
 
   /* Construct the parts */
-  struct part *part = cell->parts;
-  struct xpart *xpart = cell->xparts;
+  struct part *part = cell->hydro.parts;
+  struct xpart *xpart = cell->hydro.xparts;
   for (size_t x = 0; x < n; ++x) {
     for (size_t y = 0; y < n; ++y) {
       for (size_t z = 0; z < n; ++z) {
@@ -346,11 +339,11 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
 
   /* Cell properties */
   cell->split = 0;
-  cell->h_max = h_max;
-  cell->count = count;
-  cell->gcount = 0;
-  cell->dx_max_part = 0.;
-  cell->dx_max_sort = 0.;
+  cell->hydro.h_max = h_max;
+  cell->hydro.count = count;
+  cell->grav.count = 0;
+  cell->hydro.dx_max_part = 0.;
+  cell->hydro.dx_max_sort = 0.;
   cell->width[0] = size;
   cell->width[1] = size;
   cell->width[2] = size;
@@ -358,24 +351,24 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
   cell->loc[1] = offset[1];
   cell->loc[2] = offset[2];
 
-  cell->ti_old_part = 8;
-  cell->ti_hydro_end_min = 8;
-  cell->ti_hydro_end_max = 8;
+  cell->hydro.ti_old_part = 8;
+  cell->hydro.ti_end_min = 8;
+  cell->hydro.ti_end_max = 8;
   cell->nodeID = NODE_ID;
 
-  // shuffle_particles(cell->parts, cell->count);
+  // shuffle_particles(cell->hydro.parts, cell->hydro.count);
 
-  cell->sorted = 0;
-  for (int k = 0; k < 13; k++) cell->sort[k] = NULL;
+  cell->hydro.sorted = 0;
+  for (int k = 0; k < 13; k++) cell->hydro.sort[k] = NULL;
 
   return cell;
 }
 
 void clean_up(struct cell *ci) {
-  free(ci->parts);
-  free(ci->xparts);
+  free(ci->hydro.parts);
+  free(ci->hydro.xparts);
   for (int k = 0; k < 13; k++)
-    if (ci->sort[k] != NULL) free(ci->sort[k]);
+    if (ci->hydro.sort[k] != NULL) free(ci->hydro.sort[k]);
   free(ci);
 }
 
@@ -397,37 +390,41 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
   fprintf(file, "# Main cell --------------------------------------------\n");
 
   /* Write main cell */
-  for (int pid = 0; pid < main_cell->count; pid++) {
+  for (int pid = 0; pid < main_cell->hydro.count; pid++) {
     fprintf(file,
             "%6llu %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f "
             "%8.5f "
             "%8.5f %8.5f %13e %13e %13e %13e %13e %8.5f %8.5f\n",
-            main_cell->parts[pid].id, main_cell->parts[pid].x[0],
-            main_cell->parts[pid].x[1], main_cell->parts[pid].x[2],
-            main_cell->parts[pid].v[0], main_cell->parts[pid].v[1],
-            main_cell->parts[pid].v[2], main_cell->parts[pid].h,
-            hydro_get_comoving_density(&main_cell->parts[pid]),
+            main_cell->hydro.parts[pid].id, main_cell->hydro.parts[pid].x[0],
+            main_cell->hydro.parts[pid].x[1], main_cell->hydro.parts[pid].x[2],
+            main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1],
+            main_cell->hydro.parts[pid].v[2], main_cell->hydro.parts[pid].h,
+            hydro_get_comoving_density(&main_cell->hydro.parts[pid]),
 #if defined(MINIMAL_SPH) || defined(PLANETARY_SPH) ||   \
     defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) || \
     defined(HOPKINS_PU_SPH)
             0.f,
 #else
-            main_cell->parts[pid].density.div_v,
+            main_cell->hydro.parts[pid].density.div_v,
 #endif
-            hydro_get_comoving_entropy(&main_cell->parts[pid]),
-            hydro_get_comoving_internal_energy(&main_cell->parts[pid]),
-            hydro_get_comoving_pressure(&main_cell->parts[pid]),
-            hydro_get_comoving_soundspeed(&main_cell->parts[pid]),
-            main_cell->parts[pid].a_hydro[0], main_cell->parts[pid].a_hydro[1],
-            main_cell->parts[pid].a_hydro[2], main_cell->parts[pid].force.h_dt,
+            hydro_get_drifted_comoving_entropy(&main_cell->hydro.parts[pid]),
+            hydro_get_drifted_comoving_internal_energy(
+                &main_cell->hydro.parts[pid]),
+            hydro_get_comoving_pressure(&main_cell->hydro.parts[pid]),
+            hydro_get_comoving_soundspeed(&main_cell->hydro.parts[pid]),
+            main_cell->hydro.parts[pid].a_hydro[0],
+            main_cell->hydro.parts[pid].a_hydro[1],
+            main_cell->hydro.parts[pid].a_hydro[2],
+            main_cell->hydro.parts[pid].force.h_dt,
 #if defined(GADGET2_SPH)
-            main_cell->parts[pid].force.v_sig, main_cell->parts[pid].entropy_dt,
-            0.f
+            main_cell->hydro.parts[pid].force.v_sig,
+            main_cell->hydro.parts[pid].entropy_dt, 0.f
 #elif defined(DEFAULT_SPH)
-            main_cell->parts[pid].force.v_sig, 0.f,
-            main_cell->parts[pid].force.u_dt
+            main_cell->hydro.parts[pid].force.v_sig, 0.f,
+            main_cell->hydro.parts[pid].force.u_dt
 #elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH)
-            main_cell->parts[pid].force.v_sig, 0.f, main_cell->parts[pid].u_dt
+            main_cell->hydro.parts[pid].force.v_sig, 0.f,
+            main_cell->hydro.parts[pid].u_dt
 #else
             0.f, 0.f, 0.f
 #endif
@@ -438,7 +435,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
 
     fprintf(file, "# Solution ---------------------------------------------\n");
 
-    for (int pid = 0; pid < main_cell->count; pid++) {
+    for (int pid = 0; pid < main_cell->hydro.count; pid++) {
       fprintf(file,
               "%6llu %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f "
               "%8.5f %8.5f "
@@ -586,6 +583,7 @@ int main(int argc, char *argv[]) {
   prog_const.const_newton_G = 1.f;
 
   struct hydro_props hp;
+  hydro_props_init_no_hydro(&hp);
   hp.eta_neighbours = h;
   hp.h_tolerance = 1e0;
   hp.h_max = FLT_MAX;
@@ -640,7 +638,7 @@ int main(int argc, char *argv[]) {
 
   /* Construct the real solution */
   struct solution_part *solution = (struct solution_part *)malloc(
-      main_cell->count * sizeof(struct solution_part));
+      main_cell->hydro.count * sizeof(struct solution_part));
   get_solution(main_cell, solution, rho, vel, press, size);
 
   ticks timings[27];
@@ -657,22 +655,21 @@ int main(int argc, char *argv[]) {
 
     /* Reset particles. */
     for (int i = 0; i < 125; ++i) {
-      for (int pid = 0; pid < cells[i]->count; ++pid)
-        hydro_init_part(&cells[i]->parts[pid], &space.hs);
+      for (int pid = 0; pid < cells[i]->hydro.count; ++pid)
+        hydro_init_part(&cells[i]->hydro.parts[pid], &space.hs);
     }
 
     /* First, sort stuff */
     for (int j = 0; j < 125; ++j)
       runner_do_sort(&runner, cells[j], 0x1FFF, 0, 0);
 
-/* Do the density calculation */
-#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
+      /* Do the density calculation */
 
 /* Initialise the particle cache. */
 #ifdef WITH_VECTORIZATION
     runner.ci_cache.count = 0;
-    cache_init(&runner.ci_cache, 512);
     runner.cj_cache.count = 0;
+    cache_init(&runner.ci_cache, 512);
     cache_init(&runner.cj_cache, 512);
 #endif
 
@@ -710,18 +707,15 @@ int main(int argc, char *argv[]) {
     for (int j = 0; j < 27; ++j)
       runner_doself1_density(&runner, inner_cells[j]);
 
-#endif
-
     /* Ghost to finish everything on the central cells */
     for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0);
 
-/* Do the force calculation */
-#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
+      /* Do the force calculation */
 
 #ifdef WITH_VECTORIZATION
     /* Initialise the cache. */
-    runner.ci_cache.count = 0;
-    runner.cj_cache.count = 0;
+    cache_clean(&runner.ci_cache);
+    cache_clean(&runner.cj_cache);
     cache_init(&runner.ci_cache, 512);
     cache_init(&runner.cj_cache, 512);
 #endif
@@ -738,7 +732,7 @@ int main(int argc, char *argv[]) {
 
             const ticks sub_tic = getticks();
 
-            DOPAIR2(&runner, main_cell, cj);
+            runner_dopair2_branch_force(&runner, main_cell, cj);
 
             timings[ctr++] += getticks() - sub_tic;
           }
@@ -749,10 +743,9 @@ int main(int argc, char *argv[]) {
     ticks self_tic = getticks();
 
     /* And now the self-interaction for the main cell */
-    DOSELF2(&runner, main_cell);
+    runner_doself2_force(&runner, main_cell);
 
     timings[26] += getticks() - self_tic;
-#endif
 
     /* Finally, give a gentle kick */
     runner_do_end_force(&runner, main_cell, 0);
@@ -767,8 +760,8 @@ int main(int argc, char *argv[]) {
     }
 
     for (int i = 0; i < 125; ++i) {
-      for (int pid = 0; pid < cells[i]->count; ++pid)
-        hydro_init_part(&cells[i]->parts[pid], &space.hs);
+      for (int pid = 0; pid < cells[i]->hydro.count; ++pid)
+        hydro_init_part(&cells[i]->hydro.parts[pid], &space.hs);
     }
   }
 
@@ -798,18 +791,17 @@ int main(int argc, char *argv[]) {
 
   const ticks tic = getticks();
 
-/* Kick the central cell */
-// runner_do_kick1(&runner, main_cell, 0);
+  /* Kick the central cell */
+  // runner_do_kick1(&runner, main_cell, 0);
 
-/* And drift it */
-// runner_do_drift_particles(&runner, main_cell, 0);
+  /* And drift it */
+  // runner_do_drift_particles(&runner, main_cell, 0);
 
-/* Initialise the particles */
-// for (int j = 0; j < 125; ++j) runner_do_drift_particles(&runner, cells[j],
-// 0);
+  /* Initialise the particles */
+  // for (int j = 0; j < 125; ++j) runner_do_drift_particles(&runner, cells[j],
+  // 0);
 
-/* Do the density calculation */
-#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
+  /* Do the density calculation */
 
   /* Run all the pairs (only once !)*/
   for (int i = 0; i < 5; i++) {
@@ -844,13 +836,10 @@ int main(int argc, char *argv[]) {
   /* And now the self-interaction for the central cells*/
   for (int j = 0; j < 27; ++j) self_all_density(&runner, inner_cells[j]);
 
-#endif
-
   /* Ghost to finish everything on the central cells */
   for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0);
 
-/* Do the force calculation */
-#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
+  /* Do the force calculation */
 
   /* Do the pairs (for the central 27 cells) */
   for (int i = 1; i < 4; i++) {
@@ -867,8 +856,6 @@ int main(int argc, char *argv[]) {
   /* And now the self-interaction for the main cell */
   self_all_force(&runner, main_cell);
 
-#endif
-
   /* Finally, give a gentle kick */
   runner_do_end_force(&runner, main_cell, 0);
   // runner_do_kick2(&runner, main_cell, 0);
@@ -886,5 +873,10 @@ int main(int argc, char *argv[]) {
   for (int i = 0; i < 125; ++i) clean_up(cells[i]);
   free(solution);
 
+#ifdef WITH_VECTORIZATION
+  cache_clean(&runner.ci_cache);
+  cache_clean(&runner.cj_cache);
+#endif
+
   return 0;
 }
diff --git a/tests/test27cells.c b/tests/test27cells.c
index 1ca6b2c54d901943b0cc748a2241a3a2f9ae9244..ba98d91a4250865f301bd96594b1364d95034bbb 100644
--- a/tests/test27cells.c
+++ b/tests/test27cells.c
@@ -101,14 +101,14 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
   struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
 
-  if (posix_memalign((void **)&cell->parts, part_align,
+  if (posix_memalign((void **)&cell->hydro.parts, part_align,
                      count * sizeof(struct part)) != 0) {
     error("couldn't allocate particles, no. of particles: %d", (int)count);
   }
-  bzero(cell->parts, count * sizeof(struct part));
+  bzero(cell->hydro.parts, count * sizeof(struct part));
 
   /* Construct the parts */
-  struct part *part = cell->parts;
+  struct part *part = cell->hydro.parts;
   for (size_t x = 0; x < n; ++x) {
     for (size_t y = 0; y < n; ++y) {
       for (size_t z = 0; z < n; ++z) {
@@ -182,10 +182,10 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
 
   /* Cell properties */
   cell->split = 0;
-  cell->h_max = h_max;
-  cell->count = count;
-  cell->dx_max_part = 0.;
-  cell->dx_max_sort = 0.;
+  cell->hydro.h_max = h_max;
+  cell->hydro.count = count;
+  cell->hydro.dx_max_part = 0.;
+  cell->hydro.dx_max_sort = 0.;
   cell->width[0] = size;
   cell->width[1] = size;
   cell->width[2] = size;
@@ -193,23 +193,23 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
   cell->loc[1] = offset[1];
   cell->loc[2] = offset[2];
 
-  cell->ti_old_part = 8;
-  cell->ti_hydro_end_min = 8;
-  cell->ti_hydro_end_max = 8;
+  cell->hydro.ti_old_part = 8;
+  cell->hydro.ti_end_min = 8;
+  cell->hydro.ti_end_max = 8;
   cell->nodeID = NODE_ID;
 
-  shuffle_particles(cell->parts, cell->count);
+  shuffle_particles(cell->hydro.parts, cell->hydro.count);
 
-  cell->sorted = 0;
-  for (int k = 0; k < 13; k++) cell->sort[k] = NULL;
+  cell->hydro.sorted = 0;
+  for (int k = 0; k < 13; k++) cell->hydro.sort[k] = NULL;
 
   return cell;
 }
 
 void clean_up(struct cell *ci) {
-  free(ci->parts);
+  free(ci->hydro.parts);
   for (int k = 0; k < 13; k++)
-    if (ci->sort[k] != NULL) free(ci->sort[k]);
+    if (ci->hydro.sort[k] != NULL) free(ci->hydro.sort[k]);
   free(ci);
 }
 
@@ -229,8 +229,8 @@ void zero_particle_fields(struct cell *c) {
 #else
   struct hydro_space *hspointer = NULL;
 #endif
-  for (int pid = 0; pid < c->count; pid++) {
-    hydro_init_part(&c->parts[pid], hspointer);
+  for (int pid = 0; pid < c->hydro.count; pid++) {
+    hydro_init_part(&c->hydro.parts[pid], hspointer);
   }
 }
 
@@ -238,12 +238,12 @@ void zero_particle_fields(struct cell *c) {
  * @brief Ends the loop by adding the appropriate coefficients
  */
 void end_calculation(struct cell *c, const struct cosmology *cosmo) {
-  for (int pid = 0; pid < c->count; pid++) {
-    hydro_end_density(&c->parts[pid], cosmo);
+  for (int pid = 0; pid < c->hydro.count; pid++) {
+    hydro_end_density(&c->hydro.parts[pid], cosmo);
 
     /* Recover the common "Neighbour number" definition */
-    c->parts[pid].density.wcount *= pow_dimension(c->parts[pid].h);
-    c->parts[pid].density.wcount *= kernel_norm;
+    c->hydro.parts[pid].density.wcount *= pow_dimension(c->hydro.parts[pid].h);
+    c->hydro.parts[pid].density.wcount *= kernel_norm;
   }
 }
 
@@ -264,30 +264,30 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
   fprintf(file, "# Main cell --------------------------------------------\n");
 
   /* Write main cell */
-  for (int pid = 0; pid < main_cell->count; pid++) {
+  for (int pid = 0; pid < main_cell->hydro.count; pid++) {
     fprintf(file,
             "%6llu %10f %10f %10f %10f %10f %10f %13e %13e %13e %13e %13e "
             "%13e %13e %13e\n",
-            main_cell->parts[pid].id, main_cell->parts[pid].x[0],
-            main_cell->parts[pid].x[1], main_cell->parts[pid].x[2],
-            main_cell->parts[pid].v[0], main_cell->parts[pid].v[1],
-            main_cell->parts[pid].v[2],
-            hydro_get_comoving_density(&main_cell->parts[pid]),
+            main_cell->hydro.parts[pid].id, main_cell->hydro.parts[pid].x[0],
+            main_cell->hydro.parts[pid].x[1], main_cell->hydro.parts[pid].x[2],
+            main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1],
+            main_cell->hydro.parts[pid].v[2],
+            hydro_get_comoving_density(&main_cell->hydro.parts[pid]),
 #if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
             0.f,
 #elif defined(HOPKINS_PU_SPH)
-            main_cell->parts[pid].density.pressure_bar_dh,
+            main_cell->hydro.parts[pid].density.pressure_bar_dh,
 #else
-            main_cell->parts[pid].density.rho_dh,
+            main_cell->hydro.parts[pid].density.rho_dh,
 #endif
-            main_cell->parts[pid].density.wcount,
-            main_cell->parts[pid].density.wcount_dh,
+            main_cell->hydro.parts[pid].density.wcount,
+            main_cell->hydro.parts[pid].density.wcount_dh,
 #if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) || \
     defined(HOPKINS_PU_SPH)
-            main_cell->parts[pid].density.div_v,
-            main_cell->parts[pid].density.rot_v[0],
-            main_cell->parts[pid].density.rot_v[1],
-            main_cell->parts[pid].density.rot_v[2]
+            main_cell->hydro.parts[pid].density.div_v,
+            main_cell->hydro.parts[pid].density.rot_v[0],
+            main_cell->hydro.parts[pid].density.rot_v[1],
+            main_cell->hydro.parts[pid].density.rot_v[2]
 #else
             0., 0., 0., 0.
 #endif
@@ -305,23 +305,28 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
                 "# Offset: [%2d %2d %2d] -----------------------------------\n",
                 i - 1, j - 1, k - 1);
 
-        for (int pjd = 0; pjd < cj->count; pjd++) {
+        for (int pjd = 0; pjd < cj->hydro.count; pjd++) {
           fprintf(
               file,
               "%6llu %10f %10f %10f %10f %10f %10f %13e %13e %13e %13e %13e "
               "%13e %13e %13e\n",
-              cj->parts[pjd].id, cj->parts[pjd].x[0], cj->parts[pjd].x[1],
-              cj->parts[pjd].x[2], cj->parts[pjd].v[0], cj->parts[pjd].v[1],
-              cj->parts[pjd].v[2], hydro_get_comoving_density(&cj->parts[pjd]),
+              cj->hydro.parts[pjd].id, cj->hydro.parts[pjd].x[0],
+              cj->hydro.parts[pjd].x[1], cj->hydro.parts[pjd].x[2],
+              cj->hydro.parts[pjd].v[0], cj->hydro.parts[pjd].v[1],
+              cj->hydro.parts[pjd].v[2],
+              hydro_get_comoving_density(&cj->hydro.parts[pjd]),
 #if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
               0.f,
 #else
-              main_cell->parts[pjd].density.rho_dh,
+              main_cell->hydro.parts[pjd].density.rho_dh,
 #endif
-              cj->parts[pjd].density.wcount, cj->parts[pjd].density.wcount_dh,
+              cj->hydro.parts[pjd].density.wcount,
+              cj->hydro.parts[pjd].density.wcount_dh,
 #if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH)
-              cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0],
-              cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2]
+              cj->hydro.parts[pjd].density.div_v,
+              cj->hydro.parts[pjd].density.rot_v[0],
+              cj->hydro.parts[pjd].density.rot_v[1],
+              cj->hydro.parts[pjd].density.rot_v[2]
 #else
               0., 0., 0., 0.
 #endif
@@ -451,6 +456,7 @@ int main(int argc, char *argv[]) {
   space.dim[2] = 3.;
 
   struct hydro_props hp;
+  hydro_props_init_no_hydro(&hp);
   hp.eta_neighbours = h;
   hp.h_tolerance = 1e0;
   hp.h_max = FLT_MAX;
@@ -514,10 +520,10 @@ int main(int argc, char *argv[]) {
 #if defined(TEST_DOSELF_SUBSET) || defined(TEST_DOPAIR_SUBSET)
     int *pid = NULL;
     int count = 0;
-    if ((pid = (int *)malloc(sizeof(int) * main_cell->count)) == NULL)
+    if ((pid = (int *)malloc(sizeof(int) * main_cell->hydro.count)) == NULL)
       error("Can't allocate memory for pid.");
-    for (int k = 0; k < main_cell->count; k++)
-      if (part_is_active(&main_cell->parts[k], &engine)) {
+    for (int k = 0; k < main_cell->hydro.count; k++)
+      if (part_is_active(&main_cell->hydro.parts[k], &engine)) {
         pid[count] = k;
         ++count;
       }
@@ -529,7 +535,7 @@ int main(int argc, char *argv[]) {
         const ticks sub_tic = getticks();
 
 #ifdef TEST_DOPAIR_SUBSET
-        DOPAIR1_SUBSET(&runner, main_cell, main_cell->parts, pid, count,
+        DOPAIR1_SUBSET(&runner, main_cell, main_cell->hydro.parts, pid, count,
                        cells[j]);
 #else
         DOPAIR1(&runner, main_cell, cells[j]);
@@ -543,7 +549,7 @@ int main(int argc, char *argv[]) {
     const ticks self_tic = getticks();
 
 #ifdef TEST_DOSELF_SUBSET
-    DOSELF1_SUBSET(&runner, main_cell, main_cell->parts, pid, count);
+    DOSELF1_SUBSET(&runner, main_cell, main_cell->hydro.parts, pid, count);
 #else
     DOSELF1(&runner, main_cell);
 #endif
@@ -610,5 +616,10 @@ int main(int argc, char *argv[]) {
   /* Clean things to make the sanitizer happy ... */
   for (int i = 0; i < 27; ++i) clean_up(cells[i]);
 
+#ifdef WITH_VECTORIZATION
+  cache_clean(&runner.ci_cache);
+  cache_clean(&runner.cj_cache);
+#endif
+
   return 0;
 }
diff --git a/tests/test27cellsStars.c b/tests/test27cellsStars.c
index 1ed4798c773c7db01c19d7305365eccf1a9fe8af..3875bf75b1bb315bf48acae13b9553c689416a18 100644
--- a/tests/test27cellsStars.c
+++ b/tests/test27cellsStars.c
@@ -73,14 +73,14 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size,
   struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
 
-  if (posix_memalign((void **)&cell->parts, part_align,
+  if (posix_memalign((void **)&cell->hydro.parts, part_align,
                      count * sizeof(struct part)) != 0) {
     error("couldn't allocate particles, no. of particles: %d", (int)count);
   }
-  bzero(cell->parts, count * sizeof(struct part));
+  bzero(cell->hydro.parts, count * sizeof(struct part));
 
   /* Construct the parts */
-  struct part *part = cell->parts;
+  struct part *part = cell->hydro.parts;
   for (size_t x = 0; x < n; ++x) {
     for (size_t y = 0; y < n; ++y) {
       for (size_t z = 0; z < n; ++z) {
@@ -116,13 +116,13 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size,
   }
 
   /* Construct the sparts */
-  if (posix_memalign((void **)&cell->sparts, spart_align,
+  if (posix_memalign((void **)&cell->stars.parts, spart_align,
                      scount * sizeof(struct spart)) != 0) {
     error("couldn't allocate particles, no. of particles: %d", (int)scount);
   }
-  bzero(cell->sparts, scount * sizeof(struct spart));
+  bzero(cell->stars.parts, scount * sizeof(struct spart));
 
-  struct spart *spart = cell->sparts;
+  struct spart *spart = cell->stars.parts;
   for (size_t x = 0; x < n_stars; ++x) {
     for (size_t y = 0; y < n_stars; ++y) {
       for (size_t z = 0; z < n_stars; ++z) {
@@ -159,11 +159,11 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size,
 
   /* Cell properties */
   cell->split = 0;
-  cell->h_max = h_max;
-  cell->count = count;
-  cell->scount = scount;
-  cell->dx_max_part = 0.;
-  cell->dx_max_sort = 0.;
+  cell->hydro.h_max = h_max;
+  cell->hydro.count = count;
+  cell->stars.count = scount;
+  cell->hydro.dx_max_part = 0.;
+  cell->hydro.dx_max_sort = 0.;
   cell->width[0] = size;
   cell->width[1] = size;
   cell->width[2] = size;
@@ -171,27 +171,27 @@ struct cell *make_cell(size_t n, size_t n_stars, double *offset, double size,
   cell->loc[1] = offset[1];
   cell->loc[2] = offset[2];
 
-  cell->ti_old_part = 8;
-  cell->ti_hydro_end_min = 8;
-  cell->ti_hydro_end_max = 8;
-  cell->ti_gravity_end_min = 8;
-  cell->ti_gravity_end_max = 8;
+  cell->hydro.ti_old_part = 8;
+  cell->hydro.ti_end_min = 8;
+  cell->hydro.ti_end_max = 8;
+  cell->grav.ti_end_min = 8;
+  cell->grav.ti_end_max = 8;
   cell->nodeID = NODE_ID;
 
-  shuffle_particles(cell->parts, cell->count);
-  shuffle_sparticles(cell->sparts, cell->scount);
+  shuffle_particles(cell->hydro.parts, cell->hydro.count);
+  shuffle_sparticles(cell->stars.parts, cell->stars.count);
 
-  cell->sorted = 0;
-  for (int k = 0; k < 13; k++) cell->sort[k] = NULL;
+  cell->hydro.sorted = 0;
+  for (int k = 0; k < 13; k++) cell->hydro.sort[k] = NULL;
 
   return cell;
 }
 
 void clean_up(struct cell *ci) {
-  free(ci->parts);
-  free(ci->sparts);
+  free(ci->hydro.parts);
+  free(ci->stars.parts);
   for (int k = 0; k < 13; k++)
-    if (ci->sort[k] != NULL) free(ci->sort[k]);
+    if (ci->hydro.sort[k] != NULL) free(ci->hydro.sort[k]);
   free(ci);
 }
 
@@ -199,8 +199,8 @@ void clean_up(struct cell *ci) {
  * @brief Initializes all particles field to be ready for a density calculation
  */
 void zero_particle_fields(struct cell *c) {
-  for (int pid = 0; pid < c->scount; pid++) {
-    stars_init_spart(&c->sparts[pid]);
+  for (int pid = 0; pid < c->stars.count; pid++) {
+    stars_init_spart(&c->stars.parts[pid]);
   }
 }
 
@@ -208,12 +208,12 @@ void zero_particle_fields(struct cell *c) {
  * @brief Ends the loop by adding the appropriate coefficients
  */
 void end_calculation(struct cell *c, const struct cosmology *cosmo) {
-  for (int pid = 0; pid < c->scount; pid++) {
-    stars_end_density(&c->sparts[pid], cosmo);
+  for (int pid = 0; pid < c->stars.count; pid++) {
+    stars_end_density(&c->stars.parts[pid], cosmo);
 
     /* Recover the common "Neighbour number" definition */
-    c->sparts[pid].density.wcount *= pow_dimension(c->sparts[pid].h);
-    c->sparts[pid].density.wcount *= kernel_norm;
+    c->stars.parts[pid].density.wcount *= pow_dimension(c->stars.parts[pid].h);
+    c->stars.parts[pid].density.wcount *= kernel_norm;
   }
 }
 
@@ -231,11 +231,12 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
   fprintf(file, "# Main cell --------------------------------------------\n");
 
   /* Write main cell */
-  for (int pid = 0; pid < main_cell->scount; pid++) {
-    fprintf(file, "%6llu %10f %10f %10f %13e %13e\n", main_cell->sparts[pid].id,
-            main_cell->sparts[pid].x[0], main_cell->sparts[pid].x[1],
-            main_cell->sparts[pid].x[2], main_cell->sparts[pid].density.wcount,
-            main_cell->sparts[pid].density.wcount_dh);
+  for (int pid = 0; pid < main_cell->stars.count; pid++) {
+    fprintf(file, "%6llu %10f %10f %10f %13e %13e\n",
+            main_cell->stars.parts[pid].id, main_cell->stars.parts[pid].x[0],
+            main_cell->stars.parts[pid].x[1], main_cell->stars.parts[pid].x[2],
+            main_cell->stars.parts[pid].density.wcount,
+            main_cell->stars.parts[pid].density.wcount_dh);
   }
 
   /* Write all other cells */
@@ -249,11 +250,12 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
                 "# Offset: [%2d %2d %2d] -----------------------------------\n",
                 i - 1, j - 1, k - 1);
 
-        for (int pjd = 0; pjd < cj->scount; pjd++) {
-          fprintf(file, "%6llu %10f %10f %10f %13e %13e\n", cj->sparts[pjd].id,
-                  cj->sparts[pjd].x[0], cj->sparts[pjd].x[1],
-                  cj->sparts[pjd].x[2], cj->sparts[pjd].density.wcount,
-                  cj->sparts[pjd].density.wcount_dh);
+        for (int pjd = 0; pjd < cj->stars.count; pjd++) {
+          fprintf(file, "%6llu %10f %10f %10f %13e %13e\n",
+                  cj->stars.parts[pjd].id, cj->stars.parts[pjd].x[0],
+                  cj->stars.parts[pjd].x[1], cj->stars.parts[pjd].x[2],
+                  cj->stars.parts[pjd].density.wcount,
+                  cj->stars.parts[pjd].density.wcount_dh);
         }
       }
     }
@@ -433,10 +435,10 @@ int main(int argc, char *argv[]) {
 #if defined(TEST_DOSELF_SUBSET) || defined(TEST_DOPAIR_SUBSET)
     int *pid = NULL;
     int scount = 0;
-    if ((pid = (int *)malloc(sizeof(int) * main_cell->scount)) == NULL)
+    if ((pid = (int *)malloc(sizeof(int) * main_cell->stars.count)) == NULL)
       error("Can't allocate memory for pid.");
-    for (int k = 0; k < main_cell->scount; k++)
-      if (spart_is_active(&main_cell->sparts[k], &engine)) {
+    for (int k = 0; k < main_cell->stars.count; k++)
+      if (spart_is_active(&main_cell->stars.parts[k], &engine)) {
         pid[scount] = k;
         ++scount;
       }
@@ -448,7 +450,7 @@ int main(int argc, char *argv[]) {
         const ticks sub_tic = getticks();
 
 #ifdef TEST_DOPAIR_SUBSET
-        DOPAIR1_SUBSET(&runner, main_cell, main_cell->sparts, pid, scount,
+        DOPAIR1_SUBSET(&runner, main_cell, main_cell->stars.parts, pid, scount,
                        cells[j]);
 #else
         DOPAIR1(&runner, main_cell, cells[j]);
@@ -462,7 +464,7 @@ int main(int argc, char *argv[]) {
     const ticks self_tic = getticks();
 
 #ifdef TEST_DOSELF_SUBSET
-    DOSELF1_SUBSET(&runner, main_cell, main_cell->sparts, pid, scount);
+    DOSELF1_SUBSET(&runner, main_cell, main_cell->stars.parts, pid, scount);
 #else
     DOSELF1(&runner, main_cell);
 #endif
diff --git a/tests/testActivePair.c b/tests/testActivePair.c
index be9c2928f3f9af1ca05dc6a4cb4f715d0751d490..dd7b4c6277142f4e074fc154aac455c355200816 100644
--- a/tests/testActivePair.c
+++ b/tests/testActivePair.c
@@ -33,7 +33,8 @@
 
 /* Typdef function pointer for interaction function. */
 typedef void (*interaction_func)(struct runner *, struct cell *, struct cell *);
-typedef void (*init_func)(struct cell *, const struct cosmology *);
+typedef void (*init_func)(struct cell *, const struct cosmology *,
+                          const struct hydro_props *);
 typedef void (*finalise_func)(struct cell *, const struct cosmology *);
 
 /**
@@ -62,14 +63,14 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
   struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
 
-  if (posix_memalign((void **)&cell->parts, part_align,
+  if (posix_memalign((void **)&cell->hydro.parts, part_align,
                      count * sizeof(struct part)) != 0) {
     error("couldn't allocate particles, no. of particles: %d", (int)count);
   }
-  bzero(cell->parts, count * sizeof(struct part));
+  bzero(cell->hydro.parts, count * sizeof(struct part));
 
   /* Construct the parts */
-  struct part *part = cell->parts;
+  struct part *part = cell->hydro.parts;
   for (size_t x = 0; x < n; ++x) {
     for (size_t y = 0; y < n; ++y) {
       for (size_t z = 0; z < n; ++z) {
@@ -135,10 +136,10 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
 
   /* Cell properties */
   cell->split = 0;
-  cell->h_max = h_max;
-  cell->count = count;
-  cell->dx_max_part = 0.;
-  cell->dx_max_sort = 0.;
+  cell->hydro.h_max = h_max;
+  cell->hydro.count = count;
+  cell->hydro.dx_max_part = 0.;
+  cell->hydro.dx_max_sort = 0.;
   cell->width[0] = size;
   cell->width[1] = size;
   cell->width[2] = size;
@@ -146,43 +147,44 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
   cell->loc[1] = offset[1];
   cell->loc[2] = offset[2];
 
-  cell->ti_old_part = 8;
-  cell->ti_hydro_end_min = 8;
-  cell->ti_hydro_end_max = 10;
+  cell->hydro.ti_old_part = 8;
+  cell->hydro.ti_end_min = 8;
+  cell->hydro.ti_end_max = 10;
   cell->nodeID = NODE_ID;
 
-  shuffle_particles(cell->parts, cell->count);
+  shuffle_particles(cell->hydro.parts, cell->hydro.count);
 
-  cell->sorted = 0;
-  for (int k = 0; k < 13; k++) cell->sort[k] = NULL;
+  cell->hydro.sorted = 0;
+  for (int k = 0; k < 13; k++) cell->hydro.sort[k] = NULL;
 
   return cell;
 }
 
 void clean_up(struct cell *ci) {
-  free(ci->parts);
+  free(ci->hydro.parts);
   for (int k = 0; k < 13; k++)
-    if (ci->sort[k] != NULL) free(ci->sort[k]);
+    if (ci->hydro.sort[k] != NULL) free(ci->hydro.sort[k]);
   free(ci);
 }
 
 /**
  * @brief Initializes all particles field to be ready for a density calculation
  */
-void zero_particle_fields_density(struct cell *c,
-                                  const struct cosmology *cosmo) {
-  for (int pid = 0; pid < c->count; pid++) {
-    hydro_init_part(&c->parts[pid], NULL);
+void zero_particle_fields_density(struct cell *c, const struct cosmology *cosmo,
+                                  const struct hydro_props *hydro_props) {
+  for (int pid = 0; pid < c->hydro.count; pid++) {
+    hydro_init_part(&c->hydro.parts[pid], NULL);
   }
 }
 
 /**
  * @brief Initializes all particles field to be ready for a force calculation
  */
-void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo) {
-  for (int pid = 0; pid < c->count; pid++) {
-    struct part *p = &c->parts[pid];
-    struct xpart *xp = &c->xparts[pid];
+void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo,
+                                const struct hydro_props *hydro_props) {
+  for (int pid = 0; pid < c->hydro.count; pid++) {
+    struct part *p = &c->hydro.parts[pid];
+    struct xpart *xp = &c->hydro.xparts[pid];
 
 /* Mimic the result of a density calculation */
 #ifdef GADGET2_SPH
@@ -219,7 +221,7 @@ void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo) {
 #endif /* PRESSURE-ENERGY */
 
     /* And prepare for a round of force tasks. */
-    hydro_prepare_force(p, xp, cosmo, 0.);
+    hydro_prepare_force(p, xp, cosmo, hydro_props, 0.);
     hydro_reset_acceleration(p);
   }
 }
@@ -228,12 +230,12 @@ void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo) {
  * @brief Ends the density loop by adding the appropriate coefficients
  */
 void end_calculation_density(struct cell *c, const struct cosmology *cosmo) {
-  for (int pid = 0; pid < c->count; pid++) {
-    hydro_end_density(&c->parts[pid], cosmo);
+  for (int pid = 0; pid < c->hydro.count; pid++) {
+    hydro_end_density(&c->hydro.parts[pid], cosmo);
 
     /* Recover the common "Neighbour number" definition */
-    c->parts[pid].density.wcount *= pow_dimension(c->parts[pid].h);
-    c->parts[pid].density.wcount *= kernel_norm;
+    c->hydro.parts[pid].density.wcount *= pow_dimension(c->hydro.parts[pid].h);
+    c->hydro.parts[pid].density.wcount *= kernel_norm;
   }
 }
 
@@ -241,8 +243,8 @@ void end_calculation_density(struct cell *c, const struct cosmology *cosmo) {
  * @brief Ends the force loop by adding the appropriate coefficients
  */
 void end_calculation_force(struct cell *c, const struct cosmology *cosmo) {
-  for (int pid = 0; pid < c->count; pid++) {
-    hydro_end_force(&c->parts[pid], cosmo);
+  for (int pid = 0; pid < c->hydro.count; pid++) {
+    hydro_end_force(&c->hydro.parts[pid], cosmo);
   }
 }
 
@@ -257,16 +259,18 @@ void dump_particle_fields(char *fileName, struct cell *ci, struct cell *cj) {
 
   fprintf(file, "# ci --------------------------------------------\n");
 
-  for (int pid = 0; pid < ci->count; pid++) {
-    fprintf(file, "%6llu %13e %13e\n", ci->parts[pid].id,
-            ci->parts[pid].density.wcount, ci->parts[pid].force.h_dt);
+  for (int pid = 0; pid < ci->hydro.count; pid++) {
+    fprintf(file, "%6llu %13e %13e\n", ci->hydro.parts[pid].id,
+            ci->hydro.parts[pid].density.wcount,
+            ci->hydro.parts[pid].force.h_dt);
   }
 
   fprintf(file, "# cj --------------------------------------------\n");
 
-  for (int pjd = 0; pjd < cj->count; pjd++) {
-    fprintf(file, "%6llu %13e %13e\n", cj->parts[pjd].id,
-            cj->parts[pjd].density.wcount, cj->parts[pjd].force.h_dt);
+  for (int pjd = 0; pjd < cj->hydro.count; pjd++) {
+    fprintf(file, "%6llu %13e %13e\n", cj->hydro.parts[pjd].id,
+            cj->hydro.parts[pjd].density.wcount,
+            cj->hydro.parts[pjd].force.h_dt);
   }
 
   fclose(file);
@@ -297,8 +301,8 @@ void test_pair_interactions(struct runner *runner, struct cell **ci,
   runner_do_sort(runner, *cj, 0x1FFF, 0, 0);
 
   /* Zero the fields */
-  init(*ci, runner->e->cosmology);
-  init(*cj, runner->e->cosmology);
+  init(*ci, runner->e->cosmology, runner->e->hydro_properties);
+  init(*cj, runner->e->cosmology, runner->e->hydro_properties);
 
   /* Run the test */
   vec_interaction(runner, *ci, *cj);
@@ -313,8 +317,8 @@ void test_pair_interactions(struct runner *runner, struct cell **ci,
   /* Now perform a brute-force version for accuracy tests */
 
   /* Zero the fields */
-  init(*ci, runner->e->cosmology);
-  init(*cj, runner->e->cosmology);
+  init(*ci, runner->e->cosmology, runner->e->hydro_properties);
+  init(*cj, runner->e->cosmology, runner->e->hydro_properties);
 
   /* Run the brute-force test */
   serial_interaction(runner, *ci, *cj);
@@ -485,6 +489,7 @@ int main(int argc, char *argv[]) {
   struct space space;
   struct engine engine;
   struct cosmology cosmo;
+  struct hydro_props hydro_props;
   struct runner *runner;
   char c;
   static long long partId = 0;
@@ -569,6 +574,8 @@ int main(int argc, char *argv[]) {
 
   cosmology_init_no_cosmo(&cosmo);
   engine.cosmology = &cosmo;
+  hydro_props_init_no_hydro(&hydro_props);
+  engine.hydro_properties = &hydro_props;
 
   if (posix_memalign((void **)&runner, SWIFT_STRUCT_ALIGNMENT,
                      sizeof(struct runner)) != 0) {
diff --git a/tests/testAdiabaticIndex.c b/tests/testAdiabaticIndex.c
index 60ecefa264f48bed2d4df205766dc392a1a03d0f..6aa794207f0e23e6a26060f3ef98b7ee841d7a32 100644
--- a/tests/testAdiabaticIndex.c
+++ b/tests/testAdiabaticIndex.c
@@ -34,7 +34,8 @@
  */
 void check_value(float a, float b, const char* s) {
   if (fabsf(a - b) / fabsf(a + b) > 1.e-6f)
-    error("Values are inconsistent: %12.15e %12.15e (%s)!", a, b, s);
+    error("Values are inconsistent: %12.15e %12.15e rel=%e (%s)!", a, b,
+          fabsf(a - b) / fabsf(a + b), s);
 }
 
 /**
@@ -77,36 +78,61 @@ void check_constants(void) {
 void check_functions(float x) {
 
   float val_a, val_b;
+  const double xx = x;
+
+#if defined(HYDRO_GAMMA_5_3)
+#define hydro_gamma_d (5. / 3.)
+#elif defined(HYDRO_GAMMA_7_5)
+#define hydro_gamma_d (7. / 5.)
+#elif defined(HYDRO_GAMMA_4_3)
+#define hydro_gamma_d (4. / 3.)
+#elif defined(HYDRO_GAMMA_2_1)
+#define hydro_gamma_d (2. / 1.)
+#else
+#error "Need to choose an adiabatic index!"
+#endif
+
+  val_a = pow(xx, hydro_gamma_d);
+  val_b = pow_gamma(x);
+  check_value(val_a, val_b, "x^gamma");
+
+  val_a = pow(xx, hydro_gamma_d - 1.0);
+  val_b = pow_gamma_minus_one(x);
+  check_value(val_a, val_b, "x^(gamma - 1)");
+
+  val_a = pow(xx, -(hydro_gamma_d - 1.0));
+  val_b = pow_minus_gamma_minus_one(x);
+  check_value(val_a, val_b, "x^(-(gamma - 1))");
 
-  val_a = powf(x, -hydro_gamma);
+  val_a = pow(xx, -hydro_gamma_d);
   val_b = pow_minus_gamma(x);
   check_value(val_a, val_b, "x^(-gamma)");
 
-  val_a = powf(x, 2.0f / (hydro_gamma - 1.0f));
+  val_a = pow(xx, 2.0 / (hydro_gamma_d - 1.0));
   val_b = pow_two_over_gamma_minus_one(x);
   check_value(val_a, val_b, "x^(2/(gamma-1))");
 
-  val_a = powf(x, 2.0f * hydro_gamma / (hydro_gamma - 1.0f));
+  val_a = pow(xx, 2.0 * hydro_gamma_d / (hydro_gamma_d - 1.0));
   val_b = pow_two_gamma_over_gamma_minus_one(x);
   check_value(val_a, val_b, "x^((2 gamma)/(gamma-1))");
 
-  val_a = powf(x, 0.5f * (hydro_gamma - 1.0f) / hydro_gamma);
+  val_a = pow(xx, (hydro_gamma_d - 1.0) / (2.0 * hydro_gamma_d));
   val_b = pow_gamma_minus_one_over_two_gamma(x);
   check_value(val_a, val_b, "x^((gamma-1)/(2 gamma))");
 
-  val_a = powf(x, -0.5f * (hydro_gamma + 1.0f) / hydro_gamma);
+  val_a = pow(xx, -(hydro_gamma_d + 1.0) / (2.0 * hydro_gamma_d));
   val_b = pow_minus_gamma_plus_one_over_two_gamma(x);
   check_value(val_a, val_b, "x^(-(gamma+1)/(2 gamma))");
 
-  val_a = powf(x, 1.0f / hydro_gamma);
+  val_a = pow(xx, 1.0 / hydro_gamma_d);
   val_b = pow_one_over_gamma(x);
   check_value(val_a, val_b, "x^(1/gamma)");
 
-  val_a = powf(x, 3.f * hydro_gamma - 2.f);
+  val_a = pow(xx, 3. * hydro_gamma_d - 2.);
   val_b = pow_three_gamma_minus_two(x);
   check_value(val_a, val_b, "x^(3gamma - 2)");
 
-  val_a = powf(x, (3.f * hydro_gamma - 5.f) / 2.f);
+  val_a = pow(xx, (3. * hydro_gamma_d - 5.) / 2.);
   val_b = pow_three_gamma_minus_five_over_two(x);
   check_value(val_a, val_b, "x^((3gamma - 5)/2)");
 }
diff --git a/tests/testCbrt.c b/tests/testCbrt.c
index b608f9a00d619570c298f4123038f930584a245c..3663e0e19ad2a5ad35d67703e00f5c0309a3eb00 100644
--- a/tests/testCbrt.c
+++ b/tests/testCbrt.c
@@ -125,5 +125,6 @@ int main(int argc, char *argv[]) {
   message("x * icbrtf   took %9.3f %s (acc = %18.11e).",
           clocks_from_ticks(getticks() - tic_ours), clocks_getunit(), acc_ours);
 
+  free(data);
   return 0;
 }
diff --git a/tests/testDump.c b/tests/testDump.c
index f47a44256536d6ac1d9676c844f7081a6daa5ca4..878daae9cc0deddd6f9fb02857041f705110743c 100644
--- a/tests/testDump.c
+++ b/tests/testDump.c
@@ -73,7 +73,7 @@ int main(int argc, char *argv[]) {
   for (int run = 0; run < num_runs; run++) {
 
     /* Ensure capacity. */
-    dump_ensure(&d, 7 * chunk_size);
+    dump_ensure(&d, 7 * chunk_size, 7 * chunk_size);
 
     /* Dump a few numbers. */
     printf("dumping %i chunks...\n", chunk_size);
diff --git a/tests/testInteractions.c b/tests/testInteractions.c
index 306f14a35ca047430f67e33e9fd63848e9207b68..19eb44fa79759f50da2611cd764a1b59bd1dc579 100644
--- a/tests/testInteractions.c
+++ b/tests/testInteractions.c
@@ -16,12 +16,16 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#include "../config.h"
 
+/* Some standard headers. */
 #include <fenv.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+
+/* Local includes */
 #include "swift.h"
 
 /* Other schemes need to be added here if they are not vectorized, otherwise
@@ -141,7 +145,8 @@ void dump_indv_particle_fields(char *fileName, struct part *p) {
 #else
           p->density.div_v,
 #endif
-          hydro_get_comoving_entropy(p), hydro_get_comoving_internal_energy(p),
+          hydro_get_drifted_comoving_entropy(p),
+          hydro_get_drifted_comoving_internal_energy(p),
           hydro_get_comoving_pressure(p), hydro_get_comoving_soundspeed(p),
           p->a_hydro[0], p->a_hydro[1], p->a_hydro[2], p->force.h_dt,
 #if defined(GADGET2_SPH)
diff --git a/tests/testLogger.c b/tests/testLogger.c
index ee933500ab585d286c9dea7370b0d208573ca7d2..c5be0d7cc18742bdc2fa6167462579c45fd43e92 100644
--- a/tests/testLogger.c
+++ b/tests/testLogger.c
@@ -20,7 +20,8 @@
 /* Config parameters. */
 #include "../config.h"
 
-#ifdef HAVE_POSIX_FALLOCATE /* Are we on a sensible platform? */
+#if defined(HAVE_POSIX_FALLOCATE) && \
+    defined(WITH_LOGGER) /* Are we on a sensible platform? */
 
 /* Some standard headers. */
 #include <stdio.h>
@@ -31,7 +32,8 @@
 /* Local headers. */
 #include "swift.h"
 
-void test_log_parts(struct dump *d) {
+void test_log_parts(struct logger *log) {
+  struct dump *d = log->dump;
 
   /* Write several copies of a part to the dump. */
   struct part p;
@@ -43,22 +45,22 @@ void test_log_parts(struct dump *d) {
   size_t offset = d->count;
 
   /* Write the full part. */
-  logger_log_part(&p,
+  logger_log_part(log, &p,
                   logger_mask_x | logger_mask_v | logger_mask_a |
                       logger_mask_u | logger_mask_h | logger_mask_rho |
                       logger_mask_consts,
-                  &offset, d);
+                  &offset);
   printf("Wrote part at offset %#016zx.\n", offset);
 
   /* Write only the position. */
   p.x[0] = 2.0;
-  logger_log_part(&p, logger_mask_x, &offset, d);
+  logger_log_part(log, &p, logger_mask_x, &offset);
   printf("Wrote part at offset %#016zx.\n", offset);
 
   /* Write the position and velocity. */
   p.x[0] = 3.0;
   p.v[0] = 0.3;
-  logger_log_part(&p, logger_mask_x | logger_mask_v, &offset, d);
+  logger_log_part(log, &p, logger_mask_x | logger_mask_v, &offset);
   printf("Wrote part at offset %#016zx.\n", offset);
 
   /* Recover the last part from the dump. */
@@ -101,7 +103,8 @@ void test_log_parts(struct dump *d) {
   }
 }
 
-void test_log_gparts(struct dump *d) {
+void test_log_gparts(struct logger *log) {
+  struct dump *d = log->dump;
 
   /* Write several copies of a part to the dump. */
   struct gpart p;
@@ -113,21 +116,21 @@ void test_log_gparts(struct dump *d) {
   size_t offset = d->count;
 
   /* Write the full part. */
-  logger_log_gpart(&p,
+  logger_log_gpart(log, &p,
                    logger_mask_x | logger_mask_v | logger_mask_a |
                        logger_mask_h | logger_mask_consts,
-                   &offset, d);
+                   &offset);
   printf("Wrote gpart at offset %#016zx.\n", offset);
 
   /* Write only the position. */
   p.x[0] = 2.0;
-  logger_log_gpart(&p, logger_mask_x, &offset, d);
+  logger_log_gpart(log, &p, logger_mask_x, &offset);
   printf("Wrote gpart at offset %#016zx.\n", offset);
 
   /* Write the position and velocity. */
   p.x[0] = 3.0;
   p.v_full[0] = 0.3;
-  logger_log_gpart(&p, logger_mask_x | logger_mask_v, &offset, d);
+  logger_log_gpart(log, &p, logger_mask_x | logger_mask_v, &offset);
   printf("Wrote gpart at offset %#016zx.\n", offset);
 
   /* Recover the last part from the dump. */
@@ -170,82 +173,100 @@ void test_log_gparts(struct dump *d) {
   }
 }
 
-void test_log_timestamps(struct dump *d) {
+void test_log_timestamps(struct logger *log) {
+  struct dump *d = log->dump;
 
   /* The timestamp to log. */
   unsigned long long int t = 10;
+  double time = 0.1;
 
   /* Start with an offset at the end of the dump. */
   size_t offset = d->count;
 
   /* Log three consecutive timestamps. */
-  logger_log_timestamp(t, &offset, d);
+  logger_log_timestamp(log, t, time, &offset);
   printf("Logged timestamp %020llu at offset %#016zx.\n", t, offset);
   t += 10;
-  logger_log_timestamp(t, &offset, d);
+  time = 0.2;
+  logger_log_timestamp(log, t, time, &offset);
   printf("Logged timestamp %020llu at offset %#016zx.\n", t, offset);
   t += 10;
-  logger_log_timestamp(t, &offset, d);
+  time = 0.3;
+  logger_log_timestamp(log, t, time, &offset);
   printf("Logged timestamp %020llu at offset %#016zx.\n", t, offset);
 
   /* Recover the three timestamps. */
   size_t offset_old = offset;
   t = 0;
-  int mask = logger_read_timestamp(&t, &offset, (const char *)d->data);
+  time = 0;
+  int mask = logger_read_timestamp(&t, &time, &offset, (const char *)d->data);
   printf("Recovered timestamp %020llu at offset %#016zx with mask %#04x.\n", t,
          offset_old, mask);
   if (t != 30) {
     printf("FAIL: could not recover correct timestamp.\n");
     abort();
   }
+  if (time != 0.3) {
+    printf("FAIL: could not recover correct time %g.\n", time);
+    abort();
+  }
 
   offset_old = offset;
   t = 0;
-  mask = logger_read_timestamp(&t, &offset, (const char *)d->data);
+  time = 0;
+  mask = logger_read_timestamp(&t, &time, &offset, (const char *)d->data);
   printf("Recovered timestamp %020llu at offset %#016zx with mask %#04x.\n", t,
          offset_old, mask);
   if (t != 20) {
     printf("FAIL: could not recover correct timestamp.\n");
     abort();
   }
+  if (time != 0.2) {
+    printf("FAIL: could not recover correct time.\n");
+    abort();
+  }
 
   offset_old = offset;
   t = 0;
-  mask = logger_read_timestamp(&t, &offset, (const char *)d->data);
+  time = 0;
+  mask = logger_read_timestamp(&t, &time, &offset, (const char *)d->data);
   printf("Recovered timestamp %020llu at offset %#016zx with mask %#04x.\n", t,
          offset_old, mask);
   if (t != 10) {
     printf("FAIL: could not recover correct timestamp.\n");
     abort();
   }
+  if (time != 0.1) {
+    printf("FAIL: could not recover correct time.\n");
+    abort();
+  }
 }
 
 int main(int argc, char *argv[]) {
 
-  /* Some constants. */
-  char filename[256];
-  const int now = time(NULL);
-  sprintf(filename, "/tmp/SWIFT_logger_test_%d.out", now);
-
-  /* Prepare a dump. */
-  struct dump d;
-  dump_init(&d, filename, 1024 * 1024);
+  /* Prepare a logger. */
+  struct logger log;
+  struct swift_params params;
+  parser_read_file("logger.yml", &params);
+  logger_init(&log, &params);
 
   /* Test writing/reading parts. */
-  test_log_parts(&d);
+  test_log_parts(&log);
 
   /* Test writing/reading gparts. */
-  test_log_gparts(&d);
+  test_log_gparts(&log);
 
   /* Test writing/reading timestamps. */
-  test_log_timestamps(&d);
-
-  /* Finalize the dump. */
-  dump_close(&d);
+  test_log_timestamps(&log);
 
   /* Be clean */
+  char filename[256];
+  sprintf(filename, "%s.dump", log.base_name);
   remove(filename);
 
+  /* Clean the logger. */
+  logger_clean(&log);
+
   /* Return a happy number. */
   return 0;
 }
diff --git a/tests/testMatrixInversion.c b/tests/testMatrixInversion.c
index a15e0dab7ec793cf4a914b6eb89c63863ab24fb0..8cd0f4e272a6b7e587619117e1aa143409976c51 100644
--- a/tests/testMatrixInversion.c
+++ b/tests/testMatrixInversion.c
@@ -16,9 +16,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#include "../config.h"
 
+/* Some standard headers. */
 #include <stdlib.h>
 #include <string.h>
+
+/* Local headers */
 #include "const.h"
 #include "dimension.h"
 #include "error.h"
diff --git a/tests/testOutputList.c b/tests/testOutputList.c
index b7df197405ee095cf9bf0a63e8cf7f00585f269f..cd9c62c3abc7462406950e24fa330788b76ed249 100644
--- a/tests/testOutputList.c
+++ b/tests/testOutputList.c
@@ -76,7 +76,7 @@ void test_no_cosmo(struct engine *e, char *name, int with_assert) {
     output_time = (double)(ti_next * e->time_base) + e->time_begin;
   }
 
-  output_list_clean(list);
+  output_list_clean(&list);
 };
 
 void test_cosmo(struct engine *e, char *name, int with_assert) {
@@ -112,7 +112,7 @@ void test_cosmo(struct engine *e, char *name, int with_assert) {
     output_time = (double)exp(ti_next * e->time_base) * e->cosmology->a_begin;
   }
 
-  output_list_clean(list);
+  output_list_clean(&list);
 };
 
 int main(int argc, char *argv[]) {
@@ -151,6 +151,8 @@ int main(int argc, char *argv[]) {
   test_cosmo(&e, "ScaleFactor", with_assert);
   test_cosmo(&e, "Time", without_assert);
 
+  cosmology_clean(&cosmo);
+
   /* Write message and leave */
   message("Test done");
   return 0;
diff --git a/tests/testParser.c b/tests/testParser.c
index 3944e86fa19a1f623623383eabefe1094bf5addf..84ce70ff44fad0482573c740d5a174285655c08d 100644
--- a/tests/testParser.c
+++ b/tests/testParser.c
@@ -114,6 +114,8 @@ int main(int argc, char *argv[]) {
   int haveoptwords1 = parser_get_opt_param_string_array(
       &param_file, "Simulation:optwords", &nvar_result, &var_result, noptwords,
       optwords);
+  parser_free_param_string_array(nvar_result, var_result);
+
   /* Check if we can read it again */
   int haveoptwords2 = parser_get_opt_param_string_array(
       &param_file, "Simulation:optwords", &nvar_result, &var_result, noptwords,
diff --git a/tests/testPeriodicBC.c b/tests/testPeriodicBC.c
index de30b1af9ac8595cb081eb0702e9a7e7da13a162..e4a5a85845f92bfbecae85d9dc9453b38b9b7646 100644
--- a/tests/testPeriodicBC.c
+++ b/tests/testPeriodicBC.c
@@ -81,16 +81,16 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
   struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
 
-  if (posix_memalign((void **)&cell->parts, part_align,
+  if (posix_memalign((void **)&cell->hydro.parts, part_align,
                      count * sizeof(struct part)) != 0) {
     error("couldn't allocate particles, no. of particles: %d", (int)count);
   }
-  bzero(cell->parts, count * sizeof(struct part));
+  bzero(cell->hydro.parts, count * sizeof(struct part));
 
   float h_max = 0.f;
 
   /* Construct the parts */
-  struct part *part = cell->parts;
+  struct part *part = cell->hydro.parts;
   for (size_t x = 0; x < n; ++x) {
     for (size_t y = 0; y < n; ++y) {
       for (size_t z = 0; z < n; ++z) {
@@ -161,10 +161,10 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
 
   /* Cell properties */
   cell->split = 0;
-  cell->h_max = h_max;
-  cell->count = count;
-  cell->dx_max_part = 0.;
-  cell->dx_max_sort = 0.;
+  cell->hydro.h_max = h_max;
+  cell->hydro.count = count;
+  cell->hydro.dx_max_part = 0.;
+  cell->hydro.dx_max_sort = 0.;
   cell->width[0] = size;
   cell->width[1] = size;
   cell->width[2] = size;
@@ -172,23 +172,23 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
   cell->loc[1] = offset[1];
   cell->loc[2] = offset[2];
 
-  cell->ti_old_part = 8;
-  cell->ti_hydro_end_min = 8;
-  cell->ti_hydro_end_max = 8;
+  cell->hydro.ti_old_part = 8;
+  cell->hydro.ti_end_min = 8;
+  cell->hydro.ti_end_max = 8;
   cell->nodeID = NODE_ID;
 
-  shuffle_particles(cell->parts, cell->count);
+  shuffle_particles(cell->hydro.parts, cell->hydro.count);
 
-  cell->sorted = 0;
-  for (int k = 0; k < 13; k++) cell->sort[k] = NULL;
+  cell->hydro.sorted = 0;
+  for (int k = 0; k < 13; k++) cell->hydro.sort[k] = NULL;
 
   return cell;
 }
 
 void clean_up(struct cell *ci) {
-  free(ci->parts);
+  free(ci->hydro.parts);
   for (int k = 0; k < 13; k++)
-    if (ci->sort[k] != NULL) free(ci->sort[k]);
+    if (ci->hydro.sort[k] != NULL) free(ci->hydro.sort[k]);
   free(ci);
 }
 
@@ -196,8 +196,8 @@ void clean_up(struct cell *ci) {
  * @brief Initializes all particles field to be ready for a density calculation
  */
 void zero_particle_fields(struct cell *c) {
-  for (int pid = 0; pid < c->count; pid++) {
-    hydro_init_part(&c->parts[pid], NULL);
+  for (int pid = 0; pid < c->hydro.count; pid++) {
+    hydro_init_part(&c->hydro.parts[pid], NULL);
   }
 }
 
@@ -205,8 +205,8 @@ void zero_particle_fields(struct cell *c) {
  * @brief Ends the loop by adding the appropriate coefficients
  */
 void end_calculation(struct cell *c, const struct cosmology *cosmo) {
-  for (int pid = 0; pid < c->count; pid++) {
-    hydro_end_density(&c->parts[pid], cosmo);
+  for (int pid = 0; pid < c->hydro.count; pid++) {
+    hydro_end_density(&c->hydro.parts[pid], cosmo);
   }
 }
 
@@ -228,27 +228,27 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, int i, int j,
           i, j, k);
 
   /* Write main cell */
-  for (int pid = 0; pid < main_cell->count; pid++) {
+  for (int pid = 0; pid < main_cell->hydro.count; pid++) {
     fprintf(file,
             "%6llu %10f %10f %10f %10f %10f %10f %13e %13e %13e %13e %13e "
             "%13e %13e %13e\n",
-            main_cell->parts[pid].id, main_cell->parts[pid].x[0],
-            main_cell->parts[pid].x[1], main_cell->parts[pid].x[2],
-            main_cell->parts[pid].v[0], main_cell->parts[pid].v[1],
-            main_cell->parts[pid].v[2],
-            hydro_get_comoving_density(&main_cell->parts[pid]),
+            main_cell->hydro.parts[pid].id, main_cell->hydro.parts[pid].x[0],
+            main_cell->hydro.parts[pid].x[1], main_cell->hydro.parts[pid].x[2],
+            main_cell->hydro.parts[pid].v[0], main_cell->hydro.parts[pid].v[1],
+            main_cell->hydro.parts[pid].v[2],
+            hydro_get_comoving_density(&main_cell->hydro.parts[pid]),
 #if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
             0.f,
 #else
-            main_cell->parts[pid].density.rho_dh,
+            main_cell->hydro.parts[pid].density.rho_dh,
 #endif
-            main_cell->parts[pid].density.wcount,
-            main_cell->parts[pid].density.wcount_dh,
+            main_cell->hydro.parts[pid].density.wcount,
+            main_cell->hydro.parts[pid].density.wcount_dh,
 #if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH)
-            main_cell->parts[pid].density.div_v,
-            main_cell->parts[pid].density.rot_v[0],
-            main_cell->parts[pid].density.rot_v[1],
-            main_cell->parts[pid].density.rot_v[2]
+            main_cell->hydro.parts[pid].density.div_v,
+            main_cell->hydro.parts[pid].density.rot_v[0],
+            main_cell->hydro.parts[pid].density.rot_v[1],
+            main_cell->hydro.parts[pid].density.rot_v[2]
 #else
             0., 0., 0., 0.
 #endif
diff --git a/tests/testPotentialPair.c b/tests/testPotentialPair.c
index 12845bea9c44991ff615fbbf4c74dbb3b1a33520..064c86d42f8df907d1ffaaab164b6a2f8b534b19 100644
--- a/tests/testPotentialPair.c
+++ b/tests/testPotentialPair.c
@@ -149,11 +149,11 @@ int main(int argc, char *argv[]) {
   ci.loc[0] = 0.;
   ci.loc[1] = 0.;
   ci.loc[2] = 0.;
-  ci.gcount = 1;
-  ci.ti_old_gpart = 8;
-  ci.ti_old_multipole = 8;
-  ci.ti_gravity_end_min = 8;
-  ci.ti_gravity_end_max = 8;
+  ci.grav.count = 1;
+  ci.grav.ti_old_part = 8;
+  ci.grav.ti_old_multipole = 8;
+  ci.grav.ti_end_min = 8;
+  ci.grav.ti_end_max = 8;
 
   cj.nodeID = 0;
   cj.width[0] = 1.;
@@ -162,39 +162,39 @@ int main(int argc, char *argv[]) {
   cj.loc[0] = 1.;
   cj.loc[1] = 0.;
   cj.loc[2] = 0.;
-  cj.gcount = num_tests;
-  cj.ti_old_gpart = 8;
-  cj.ti_old_multipole = 8;
-  cj.ti_gravity_end_min = 8;
-  cj.ti_gravity_end_max = 8;
+  cj.grav.count = num_tests;
+  cj.grav.ti_old_part = 8;
+  cj.grav.ti_old_multipole = 8;
+  cj.grav.ti_end_min = 8;
+  cj.grav.ti_end_max = 8;
 
   /* Allocate multipoles */
-  ci.multipole =
+  ci.grav.multipole =
       (struct gravity_tensors *)malloc(sizeof(struct gravity_tensors));
-  cj.multipole =
+  cj.grav.multipole =
       (struct gravity_tensors *)malloc(sizeof(struct gravity_tensors));
-  bzero(ci.multipole, sizeof(struct gravity_tensors));
-  bzero(cj.multipole, sizeof(struct gravity_tensors));
+  bzero(ci.grav.multipole, sizeof(struct gravity_tensors));
+  bzero(cj.grav.multipole, sizeof(struct gravity_tensors));
 
   /* Set the multipoles */
-  ci.multipole->r_max = 0.1;
-  cj.multipole->r_max = 0.1;
+  ci.grav.multipole->r_max = 0.1;
+  cj.grav.multipole->r_max = 0.1;
 
   /* Allocate the particles */
-  if (posix_memalign((void **)&ci.gparts, gpart_align,
-                     ci.gcount * sizeof(struct gpart)) != 0)
+  if (posix_memalign((void **)&ci.grav.parts, gpart_align,
+                     ci.grav.count * sizeof(struct gpart)) != 0)
     error("Error allocating gparts for cell ci");
-  bzero(ci.gparts, ci.gcount * sizeof(struct gpart));
+  bzero(ci.grav.parts, ci.grav.count * sizeof(struct gpart));
 
-  if (posix_memalign((void **)&cj.gparts, gpart_align,
-                     cj.gcount * sizeof(struct gpart)) != 0)
+  if (posix_memalign((void **)&cj.grav.parts, gpart_align,
+                     cj.grav.count * sizeof(struct gpart)) != 0)
     error("Error allocating gparts for cell ci");
-  bzero(cj.gparts, cj.gcount * sizeof(struct gpart));
+  bzero(cj.grav.parts, cj.grav.count * sizeof(struct gpart));
 
   /* Create the mass-less test particles */
   for (int n = 0; n < num_tests; ++n) {
 
-    struct gpart *gp = &cj.gparts[n];
+    struct gpart *gp = &cj.grav.parts[n];
 
     gp->x[0] = 1. + (n + 1) / ((double)num_tests);
     gp->x[1] = 0.5;
@@ -205,6 +205,7 @@ int main(int argc, char *argv[]) {
     gp->id_or_neg_offset = n + 1;
 #ifdef SWIFT_DEBUG_CHECKS
     gp->ti_drift = 8;
+    gp->initialised = 1;
 #endif
   }
 
@@ -213,15 +214,16 @@ int main(int argc, char *argv[]) {
   /***********************************************/
 
   /* Create the massive particle */
-  ci.gparts[0].x[0] = 0.;
-  ci.gparts[0].x[1] = 0.5;
-  ci.gparts[0].x[2] = 0.5;
-  ci.gparts[0].mass = 1.;
-  ci.gparts[0].time_bin = 1;
-  ci.gparts[0].type = swift_type_dark_matter;
-  ci.gparts[0].id_or_neg_offset = 1;
+  ci.grav.parts[0].x[0] = 0.;
+  ci.grav.parts[0].x[1] = 0.5;
+  ci.grav.parts[0].x[2] = 0.5;
+  ci.grav.parts[0].mass = 1.;
+  ci.grav.parts[0].time_bin = 1;
+  ci.grav.parts[0].type = swift_type_dark_matter;
+  ci.grav.parts[0].id_or_neg_offset = 1;
 #ifdef SWIFT_DEBUG_CHECKS
-  ci.gparts[0].ti_drift = 8;
+  ci.grav.parts[0].ti_drift = 8;
+  ci.grav.parts[0].initialised = 1;
 #endif
 
   /* Now compute the forces */
@@ -229,18 +231,18 @@ int main(int argc, char *argv[]) {
 
   /* Verify everything */
   for (int n = 0; n < num_tests; ++n) {
-    const struct gpart *gp = &cj.gparts[n];
-    const struct gpart *gp2 = &ci.gparts[0];
+    const struct gpart *gp = &cj.grav.parts[n];
+    const struct gpart *gp2 = &ci.grav.parts[0];
     const double epsilon = gravity_get_softening(gp, &props);
 
 #if defined(POTENTIAL_GRAVITY)
     double pot_true =
-        potential(ci.gparts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr);
+        potential(ci.grav.parts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr);
     check_value(gp->potential, pot_true, "potential");
 #endif
 
     double acc_true =
-        acceleration(ci.gparts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr);
+        acceleration(ci.grav.parts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr);
 
     /* message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] - gp2->x[0],
        gp->a_grav[0], acc_true, gp->potential, pot_true); */
@@ -251,7 +253,7 @@ int main(int argc, char *argv[]) {
   message("\n\t\t P-P interactions all good\n");
 
   /* Reset the accelerations */
-  for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.gparts[n]);
+  for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.grav.parts[n]);
 
   /**********************************/
   /* Test the basic PM interactions */
@@ -260,22 +262,22 @@ int main(int argc, char *argv[]) {
   /* Set an opening angle that allows P-M interactions */
   props.theta_crit2 = 1.;
 
-  ci.gparts[0].mass = 0.;
-  ci.multipole->CoM[0] = 0.;
-  ci.multipole->CoM[1] = 0.5;
-  ci.multipole->CoM[2] = 0.5;
+  ci.grav.parts[0].mass = 0.;
+  ci.grav.multipole->CoM[0] = 0.;
+  ci.grav.multipole->CoM[1] = 0.5;
+  ci.grav.multipole->CoM[2] = 0.5;
 
-  bzero(&ci.multipole->m_pole, sizeof(struct multipole));
-  bzero(&cj.multipole->m_pole, sizeof(struct multipole));
-  ci.multipole->m_pole.M_000 = 1.;
+  bzero(&ci.grav.multipole->m_pole, sizeof(struct multipole));
+  bzero(&cj.grav.multipole->m_pole, sizeof(struct multipole));
+  ci.grav.multipole->m_pole.M_000 = 1.;
 
   /* Now compute the forces */
   runner_dopair_grav_pp(&r, &ci, &cj, 1, 1);
 
   /* Verify everything */
   for (int n = 0; n < num_tests; ++n) {
-    const struct gpart *gp = &cj.gparts[n];
-    const struct gravity_tensors *mpole = ci.multipole;
+    const struct gpart *gp = &cj.grav.parts[n];
+    const struct gravity_tensors *mpole = ci.grav.multipole;
     const double epsilon = gravity_get_softening(gp, &props);
 
 #if defined(POTENTIAL_GRAVITY)
@@ -297,7 +299,7 @@ int main(int argc, char *argv[]) {
 #ifndef GADGET2_LONG_RANGE_CORRECTION
 
   /* Reset the accelerations */
-  for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.gparts[n]);
+  for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.grav.parts[n]);
 
   /***************************************/
   /* Test the truncated PM interactions  */
@@ -314,8 +316,8 @@ int main(int argc, char *argv[]) {
 
   /* Verify everything */
   for (int n = 0; n < num_tests; ++n) {
-    const struct gpart *gp = &cj.gparts[n];
-    const struct gravity_tensors *mpole = ci.multipole;
+    const struct gpart *gp = &cj.grav.parts[n];
+    const struct gravity_tensors *mpole = ci.grav.multipole;
     const double epsilon = gravity_get_softening(gp, &props);
 
 #if defined(POTENTIAL_GRAVITY)
@@ -342,57 +344,58 @@ int main(int argc, char *argv[]) {
   /************************************************/
 
   /* Reset the accelerations */
-  for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.gparts[n]);
+  for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.grav.parts[n]);
 
 #if SELF_GRAVITY_MULTIPOLE_ORDER >= 3
 
   /* Let's make ci more interesting */
-  free(ci.gparts);
-  ci.gcount = 8;
-  if (posix_memalign((void **)&ci.gparts, gpart_align,
-                     ci.gcount * sizeof(struct gpart)) != 0)
+  free(ci.grav.parts);
+  ci.grav.count = 8;
+  if (posix_memalign((void **)&ci.grav.parts, gpart_align,
+                     ci.grav.count * sizeof(struct gpart)) != 0)
     error("Error allocating gparts for cell ci");
-  bzero(ci.gparts, ci.gcount * sizeof(struct gpart));
+  bzero(ci.grav.parts, ci.grav.count * sizeof(struct gpart));
 
   /* Place particles on a simple cube of side-length 0.2 */
   for (int n = 0; n < 8; ++n) {
     if (n & 1)
-      ci.gparts[n].x[0] = 0.0 - 0.1;
+      ci.grav.parts[n].x[0] = 0.0 - 0.1;
     else
-      ci.gparts[n].x[0] = 0.0 + 0.1;
+      ci.grav.parts[n].x[0] = 0.0 + 0.1;
 
     if (n & 2)
-      ci.gparts[n].x[1] = 0.5 - 0.1;
+      ci.grav.parts[n].x[1] = 0.5 - 0.1;
     else
-      ci.gparts[n].x[1] = 0.5 + 0.1;
+      ci.grav.parts[n].x[1] = 0.5 + 0.1;
 
     if (n & 2)
-      ci.gparts[n].x[2] = 0.5 - 0.1;
+      ci.grav.parts[n].x[2] = 0.5 - 0.1;
     else
-      ci.gparts[n].x[2] = 0.5 + 0.1;
+      ci.grav.parts[n].x[2] = 0.5 + 0.1;
 
-    ci.gparts[n].mass = 1. / 8.;
+    ci.grav.parts[n].mass = 1. / 8.;
 
-    ci.gparts[n].time_bin = 1;
-    ci.gparts[n].type = swift_type_dark_matter;
-    ci.gparts[n].id_or_neg_offset = 1;
+    ci.grav.parts[n].time_bin = 1;
+    ci.grav.parts[n].type = swift_type_dark_matter;
+    ci.grav.parts[n].id_or_neg_offset = 1;
 #ifdef SWIFT_DEBUG_CHECKS
-    ci.gparts[n].ti_drift = 8;
+    ci.grav.parts[n].ti_drift = 8;
+    ci.grav.parts[n].initialised = 1;
 #endif
   }
 
   /* Now let's make a multipole out of it. */
-  gravity_reset(ci.multipole);
-  gravity_P2M(ci.multipole, ci.gparts, ci.gcount);
+  gravity_reset(ci.grav.multipole);
+  gravity_P2M(ci.grav.multipole, ci.grav.parts, ci.grav.count);
 
-  gravity_multipole_print(&ci.multipole->m_pole);
+  gravity_multipole_print(&ci.grav.multipole->m_pole);
 
   /* Compute the forces */
   runner_dopair_grav_pp(&r, &ci, &cj, 1, 1);
 
   /* Verify everything */
   for (int n = 0; n < num_tests; ++n) {
-    const struct gpart *gp = &cj.gparts[n];
+    const struct gpart *gp = &cj.grav.parts[n];
 
 #if defined(POTENTIAL_GRAVITY)
     double pot_true = 0;
@@ -400,7 +403,7 @@ int main(int argc, char *argv[]) {
     double acc_true[3] = {0., 0., 0.};
 
     for (int i = 0; i < 8; ++i) {
-      const struct gpart *gp2 = &ci.gparts[i];
+      const struct gpart *gp2 = &ci.grav.parts[i];
       const double epsilon = gravity_get_softening(gp, &props);
 
       const double dx[3] = {gp2->x[0] - gp->x[0], gp2->x[1] - gp->x[1],
@@ -421,7 +424,7 @@ int main(int argc, char *argv[]) {
 #endif
     check_value_backend(gp->a_grav[0], acc_true[0], "acceleration", 1e-2, 1e-6);
 
-    /* const struct gravity_tensors *mpole = ci.multipole; */
+    /* const struct gravity_tensors *mpole = ci.grav.multipole; */
     /* message("x=%e f=%e f_true=%e pot=%e pot_true=%e %e %e", */
     /*         gp->x[0] - mpole->CoM[0], gp->a_grav[0], acc_true[0],
      * gp->potential, */
@@ -432,9 +435,14 @@ int main(int argc, char *argv[]) {
 
 #endif
 
-  free(ci.multipole);
-  free(cj.multipole);
-  free(ci.gparts);
-  free(cj.gparts);
+  free(ci.grav.multipole);
+  free(cj.grav.multipole);
+  free(ci.grav.parts);
+  free(cj.grav.parts);
+
+  /* Clean up the caches */
+  gravity_cache_clean(&r.ci_gravity_cache);
+  gravity_cache_clean(&r.cj_gravity_cache);
+
   return 0;
 }
diff --git a/tests/testPotentialSelf.c b/tests/testPotentialSelf.c
index 6bf5dbd405830f1ba1c58d8627606a67111f5fb0..10eb499570a591daaf0de2e011f2346077905e8e 100644
--- a/tests/testPotentialSelf.c
+++ b/tests/testPotentialSelf.c
@@ -137,32 +137,33 @@ int main(int argc, char *argv[]) {
   c.loc[0] = 0.;
   c.loc[1] = 0.;
   c.loc[2] = 0.;
-  c.gcount = 1 + num_tests;
-  c.ti_old_gpart = 8;
-  c.ti_gravity_end_min = 8;
-  c.ti_gravity_end_max = 8;
+  c.grav.count = 1 + num_tests;
+  c.grav.ti_old_part = 8;
+  c.grav.ti_end_min = 8;
+  c.grav.ti_end_max = 8;
 
-  if (posix_memalign((void **)&c.gparts, gpart_align,
-                     c.gcount * sizeof(struct gpart)) != 0)
+  if (posix_memalign((void **)&c.grav.parts, gpart_align,
+                     c.grav.count * sizeof(struct gpart)) != 0)
     error("Impossible to allocate memory for the gparts.");
-  bzero(c.gparts, c.gcount * sizeof(struct gpart));
+  bzero(c.grav.parts, c.grav.count * sizeof(struct gpart));
 
   /* Create the massive particle */
-  c.gparts[0].x[0] = 0.;
-  c.gparts[0].x[1] = 0.5;
-  c.gparts[0].x[2] = 0.5;
-  c.gparts[0].mass = 1.;
-  c.gparts[0].time_bin = 1;
-  c.gparts[0].type = swift_type_dark_matter;
-  c.gparts[0].id_or_neg_offset = 1;
+  c.grav.parts[0].x[0] = 0.;
+  c.grav.parts[0].x[1] = 0.5;
+  c.grav.parts[0].x[2] = 0.5;
+  c.grav.parts[0].mass = 1.;
+  c.grav.parts[0].time_bin = 1;
+  c.grav.parts[0].type = swift_type_dark_matter;
+  c.grav.parts[0].id_or_neg_offset = 1;
 #ifdef SWIFT_DEBUG_CHECKS
-  c.gparts[0].ti_drift = 8;
+  c.grav.parts[0].ti_drift = 8;
+  c.grav.parts[0].initialised = 1;
 #endif
 
   /* Create the mass-less particles */
   for (int n = 1; n < num_tests + 1; ++n) {
 
-    struct gpart *gp = &c.gparts[n];
+    struct gpart *gp = &c.grav.parts[n];
 
     gp->x[0] = n / ((double)num_tests);
     gp->x[1] = 0.5;
@@ -173,6 +174,7 @@ int main(int argc, char *argv[]) {
     gp->id_or_neg_offset = n + 1;
 #ifdef SWIFT_DEBUG_CHECKS
     gp->ti_drift = 8;
+    gp->initialised = 1;
 #endif
   }
 
@@ -181,21 +183,27 @@ int main(int argc, char *argv[]) {
 
   /* Verify everything */
   for (int n = 1; n < num_tests + 1; ++n) {
-    const struct gpart *gp = &c.gparts[n];
+    const struct gpart *gp = &c.grav.parts[n];
 
     const double epsilon = gravity_get_softening(gp, &props);
 
 #if defined(POTENTIAL_GRAVITY)
-    double pot_true = potential(c.gparts[0].mass, gp->x[0], epsilon, rlr);
+    double pot_true = potential(c.grav.parts[0].mass, gp->x[0], epsilon, rlr);
     check_value(gp->potential, pot_true, "potential");
 #endif
 
-    double acc_true = acceleration(c.gparts[0].mass, gp->x[0], epsilon, rlr);
+    double acc_true =
+        acceleration(c.grav.parts[0].mass, gp->x[0], epsilon, rlr);
     check_value(gp->a_grav[0], acc_true, "acceleration");
 
     // message("x=%e f=%e f_true=%e", gp->x[0], gp->a_grav[0], acc_true);
   }
 
-  free(c.gparts);
+  free(c.grav.parts);
+
+  /* Clean up the caches */
+  gravity_cache_clean(&r.ci_gravity_cache);
+
+  /* All done! */
   return 0;
 }
diff --git a/tests/testReading.c b/tests/testReading.c
index 5e6cee7f1e37f7615eb2c3b4edcaee1d4ebba319..d7d3fcbdae2f3ab744f338bb74e105644a5d88be 100644
--- a/tests/testReading.c
+++ b/tests/testReading.c
@@ -17,6 +17,9 @@
  *
  ******************************************************************************/
 
+/* Some standard headers. */
+#include "../config.h"
+
 /* Some standard headers. */
 #include <stdlib.h>
 
@@ -26,7 +29,6 @@
 int main(int argc, char *argv[]) {
 
   size_t Ngas = 0, Ngpart = 0, Nspart = 0;
-  int periodic = -1;
   int flag_entropy_ICs = -1;
   int i, j, k;
   double dim[3];
@@ -48,8 +50,8 @@ int main(int argc, char *argv[]) {
 
   /* Read data */
   read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &Ngas,
-                 &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, 1, 1, 0, 0, 0,
-                 1., 1., 1, 0);
+                 &Ngpart, &Nspart, &flag_entropy_ICs, 1, 1, 0, 0, 0, 1., 1., 1,
+                 0);
 
   /* Check global properties read are correct */
   assert(dim[0] == boxSize);
@@ -57,7 +59,6 @@ int main(int argc, char *argv[]) {
   assert(dim[2] == boxSize);
   assert(Ngas == L * L * L);
   assert(Ngpart == L * L * L);
-  assert(periodic == 1);
 
   /* Check particles */
   for (size_t n = 0; n < Ngas; ++n) {
diff --git a/tests/testRiemannTRRS.c b/tests/testRiemannTRRS.c
index 2c7098367a1ca8db84f097ad01aa2e1e411c433d..e975230c61cd58ad1a077e9b66949044cb7708da 100644
--- a/tests/testRiemannTRRS.c
+++ b/tests/testRiemannTRRS.c
@@ -16,8 +16,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#include "../config.h"
 
+/* Local headers. */
 #include <string.h>
+
+/* Local includes */
 #include "error.h"
 #include "riemann/riemann_trrs.h"
 #include "tools.h"
diff --git a/tests/testSPHStep.c b/tests/testSPHStep.c
index 63834d94b7696e160dd7ca487ab7e9f1e943abfb..41694872efbfc4d9611127eb1e6324b2b0fa5500 100644
--- a/tests/testSPHStep.c
+++ b/tests/testSPHStep.c
@@ -35,19 +35,19 @@ struct cell *make_cell(size_t N, float cellSize, int offset[3], int id_offset) {
   size_t x, y, z, size;
 
   size = count * sizeof(struct part);
-  if (posix_memalign((void **)&cell->parts, part_align, size) != 0) {
+  if (posix_memalign((void **)&cell->hydro.parts, part_align, size) != 0) {
     error("couldn't allocate particles");
   }
 
   size = count * sizeof(struct xpart);
-  if (posix_memalign((void **)&cell->xparts, xpart_align, size) != 0) {
+  if (posix_memalign((void **)&cell->hydro.xparts, xpart_align, size) != 0) {
     error("couldn't allocate extended particles");
   }
 
   h = 1.2348 * cellSize / N;
 
-  part = cell->parts;
-  xpart = cell->xparts;
+  part = cell->hydro.parts;
+  xpart = cell->hydro.xparts;
   memset(part, 0, count * sizeof(struct part));
   memset(xpart, 0, count * sizeof(struct xpart));
   for (x = 0; x < N; ++x) {
@@ -68,20 +68,20 @@ struct cell *make_cell(size_t N, float cellSize, int offset[3], int id_offset) {
   }
 
   cell->split = 0;
-  cell->h_max = h;
-  cell->count = count;
-  cell->gcount = 0;
-  cell->dx_max_part = 0.;
-  cell->dx_max_sort = 0.;
+  cell->hydro.h_max = h;
+  cell->hydro.count = count;
+  cell->grav.count = 0;
+  cell->hydro.dx_max_part = 0.;
+  cell->hydro.dx_max_sort = 0.;
   cell->width[0] = cellSize;
   cell->width[1] = cellSize;
   cell->width[2] = cellSize;
 
-  cell->ti_hydro_end_min = 1;
-  cell->ti_hydro_end_max = 1;
+  cell->hydro.ti_end_min = 1;
+  cell->hydro.ti_end_max = 1;
 
-  cell->sorted = 0;
-  for (int k = 0; k < 13; k++) cell->sort[k] = NULL;
+  cell->hydro.sorted = 0;
+  for (int k = 0; k < 13; k++) cell->hydro.sort[k] = NULL;
 
   return cell;
 }
@@ -128,9 +128,9 @@ int main(int argc, char *argv[]) {
 
   /* Set particle properties */
   for (j = 0; j < 27; ++j)
-    for (i = 0; i < cells[j]->count; ++i) {
-      cells[j]->parts[i].mass = dim * dim * dim * rho / (N * N * N);
-      cells[j]->parts[i].u = P / (hydro_gamma_minus_one * rho);
+    for (i = 0; i < cells[j]->hydro.count; ++i) {
+      cells[j]->hydro.parts[i].mass = dim * dim * dim * rho / (N * N * N);
+      cells[j]->hydro.parts[i].u = P / (hydro_gamma_minus_one * rho);
     }
 
   message("m=%f", dim * dim * dim * rho / (N * N * N));
@@ -171,7 +171,7 @@ int main(int argc, char *argv[]) {
   e.ti_current = 1;
 
   /* The tracked particle */
-  p = &(ci->parts[N * N * N / 2 + N * N / 2 + N / 2]);
+  p = &(ci->hydro.parts[N * N * N / 2 + N * N / 2 + N / 2]);
 
   message("Studying particle p->id=%lld", p->id);
 
@@ -209,10 +209,10 @@ int main(int argc, char *argv[]) {
   message("ti_end=%d", p->ti_end);
 
   for (int j = 0; j < 27; ++j) {
-    free(cells[j]->parts);
-    free(cells[j]->xparts);
+    free(cells[j]->hydro.parts);
+    free(cells[j]->hydro.xparts);
     for (int k = 0; k < 13; k++)
-      if (cells[j]->sort[k] != NULL) free(cells[j]->sort[k]);
+      if (cells[j]->hydro.sort[k] != NULL) free(cells[j]->hydro.sort[k]);
     free(cells[j]);
   }
 
diff --git a/tests/testSelectOutput.c b/tests/testSelectOutput.c
index 7a02b7fb7ed0542036c60b125dcbe9a36a331e6d..01c80ce8f15f2be7d264ceecdb397950b822de35 100644
--- a/tests/testSelectOutput.c
+++ b/tests/testSelectOutput.c
@@ -85,8 +85,8 @@ int main(int argc, char *argv[]) {
 
   char *base_name = "testSelectOutput";
   size_t Ngas = 0, Ngpart = 0, Nspart = 0;
-  int periodic = -1;
   int flag_entropy_ICs = -1;
+  int periodic = 1;
   double dim[3];
   struct part *parts = NULL;
   struct gpart *gparts = NULL;
@@ -111,8 +111,8 @@ int main(int argc, char *argv[]) {
   /* Read data */
   message("Reading initial conditions.");
   read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &Ngas,
-                 &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, 1, 0, 0, 0, 0,
-                 1., 1., 1, 0);
+                 &Ngpart, &Nspart, &flag_entropy_ICs, 1, 0, 0, 0, 0, 1., 1., 1,
+                 0);
 
   /* pseudo initialization of the space */
   message("Initialization of the space.");
diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c
index 886290ab984603d0afb3201377611598cd7163e4..ce1e2e9354c4d59a6e58619d43b743864ed38585 100644
--- a/tests/testSymmetry.c
+++ b/tests/testSymmetry.c
@@ -27,7 +27,10 @@
 
 void print_bytes(void *p, size_t len) {
   printf("(");
-  for (size_t i = 0; i < len; ++i) printf("%02x", ((unsigned char *)p)[i]);
+  for (size_t i = 0; i < len; ++i) {
+    printf("%02x", ((unsigned char *)p)[i]);
+    if (i % 4 == 3) printf("|");
+  }
   printf(")\n");
 }
 
@@ -162,8 +165,8 @@ void test(void) {
   if (i_not_ok) {
     printParticle_single(&pi, &xpi);
     printParticle_single(&pi2, &xpi);
-    print_bytes(&pj, sizeof(struct part));
-    print_bytes(&pj2, sizeof(struct part));
+    print_bytes(&pi, sizeof(struct part));
+    print_bytes(&pi2, sizeof(struct part));
     error("Particles 'pi' do not match after density (byte = %d)", i_not_ok);
   }
   if (j_not_ok) {
@@ -220,17 +223,15 @@ void test(void) {
     j_not_ok |= c_is_d;
   }
 #else
-  i_not_ok =
-      strncmp((const char *)&pi, (const char *)&pi2, sizeof(struct part));
-  j_not_ok =
-      strncmp((const char *)&pj, (const char *)&pj2, sizeof(struct part));
+  i_not_ok = memcmp((char *)&pi, (char *)&pi2, sizeof(struct part));
+  j_not_ok = memcmp((char *)&pj, (char *)&pj2, sizeof(struct part));
 #endif
 
   if (i_not_ok) {
     printParticle_single(&pi, &xpi);
     printParticle_single(&pi2, &xpi);
-    print_bytes(&pj, sizeof(struct part));
-    print_bytes(&pj2, sizeof(struct part));
+    print_bytes(&pi, sizeof(struct part));
+    print_bytes(&pi2, sizeof(struct part));
     error("Particles 'pi' do not match after force (byte = %d)", i_not_ok);
   }
   if (j_not_ok) {
diff --git a/tests/testTimeIntegration.c b/tests/testTimeIntegration.c
index 2034c402a2d626a7b503613f6cade821ec438151..b7f5201356ee52419038c8379dde14c9bab82055 100644
--- a/tests/testTimeIntegration.c
+++ b/tests/testTimeIntegration.c
@@ -83,9 +83,9 @@ int main(int argc, char *argv[]) {
   xparts[0].v_full[2] = 0.;
 
   /* Set the particle in the cell */
-  c.parts = parts;
-  c.xparts = xparts;
-  c.count = 1;
+  c.hydro.parts = parts;
+  c.hydro.xparts = xparts;
+  c.hydro.count = 1;
   c.split = 0;
 
   /* Create an engine and a fake runner */
@@ -108,11 +108,13 @@ int main(int argc, char *argv[]) {
     eng.time += dt;
 
     /* Compute gravitational acceleration */
-    float r2 =
-        c.parts[0].x[0] * c.parts[0].x[0] + c.parts[0].x[1] * c.parts[0].x[1];
+    float r2 = c.hydro.parts[0].x[0] * c.hydro.parts[0].x[0] +
+               c.hydro.parts[0].x[1] * c.hydro.parts[0].x[1];
     float r = sqrtf(r2);
-    c.parts[0].a_hydro[0] = -(G * M_sun * c.parts[0].x[0] / r * r * r);
-    c.parts[0].a_hydro[1] = -(G * M_sun * c.parts[0].x[1] / r * r * r);
+    c.hydro.parts[0].a_hydro[0] =
+        -(G * M_sun * c.hydro.parts[0].x[0] / r * r * r);
+    c.hydro.parts[0].a_hydro[1] =
+        -(G * M_sun * c.hydro.parts[0].x[1] / r * r * r);
 
     /* Kick... */
     runner_do_kick2(&run, &c, 0);
diff --git a/tests/testVoronoi2D.c b/tests/testVoronoi2D.c
index 60a71624904c11a3cdb3b90906189df60bfc6956..5057278efaa3ba0e1ccec2ba6b032cd12b029ff9 100644
--- a/tests/testVoronoi2D.c
+++ b/tests/testVoronoi2D.c
@@ -16,6 +16,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#include "../config.h"
+
+/* Local headers. */
 #include "hydro/Shadowswift/voronoi2d_algorithm.h"
 #include "tools.h"
 
diff --git a/tests/testVoronoi3D.c b/tests/testVoronoi3D.c
index db5c33aa6e4ef0792373febd5d773a6d1198db29..5e0288fa9b3e13e0c6a6fb13db202e0f73f29a5b 100644
--- a/tests/testVoronoi3D.c
+++ b/tests/testVoronoi3D.c
@@ -16,8 +16,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#include "../config.h"
 
+/* Some standard headers. */
 #include <stdlib.h>
+
+/* Local headers. */
 #include "error.h"
 #include "hydro/Shadowswift/voronoi3d_algorithm.h"
 #include "part.h"
diff --git a/theory/Multipoles/bibliography.bib b/theory/Multipoles/bibliography.bib
index 077525a9e4db781ea58bd46ef2ba109d6c074be0..245a5223d43aff3ed871cc7ce278fb319d88a938 100644
--- a/theory/Multipoles/bibliography.bib
+++ b/theory/Multipoles/bibliography.bib
@@ -275,6 +275,21 @@ keywords = "adaptive algorithms"
   adsnote = {Provided by the SAO/NASA Astrophysics Data System}
 }
 
+@BOOK{Abramowitz1972,
+   author = {{Abramowitz}, M. and {Stegun}, I.~A.},
+    title = "{Handbook of Mathematical Functions}",
+booktitle = {Handbook of Mathematical Functions, New York: Dover, 1972},
+     year = 1972,
+   adsurl = {http://cdsads.u-strasbg.fr/abs/1972hmfw.book.....A},
+  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
+}
 
-
+@book{Hastings1955
+ author = {Hastings, Cecil},
+ title = {Approximations for Digital Computers},
+ year = {1955},
+ isbn = {0691079145},
+ publisher = {Princeton University Press},
+ address = {Princeton, NJ, USA},
+}