diff --git a/INSTALL.swift b/INSTALL.swift
index db6c6677b202e55e76114373f3e037cf50de10cc..bf8dbc92f5ccb06f6988c5672e5fdac54d2d2598 100644
--- a/INSTALL.swift
+++ b/INSTALL.swift
@@ -154,6 +154,12 @@ before you can build it.
         distributing the threads among the different cores on each
         computing node.
 
+        Note that if you have libNUMA outside of the system include
+        directories it may fail to compile as the headers do not pass
+        the -Wstrict-prototype check of GCC. In that case you will need
+        to use --enable-compiler-warnings=yes configure option to stop
+        this being an error.
+
  - tcmalloc / jemalloc / TBBmalloc:
 	a build of the tcmalloc library (part of gperftools), jemalloc
 	or TBBmalloc can be used be used to obtain faster and more
diff --git a/README b/README
index 97d096ef1804a4348e88dfaf67c22b2c427a3ad8..5b0b5f3ebf8b322e121e0398ca01062e226be649 100644
--- a/README
+++ b/README
@@ -6,7 +6,7 @@
  /____/ |__/|__/___/_/    /_/
  SPH With Inter-dependent Fine-grained Tasking
 
-Website: www.swiftsim.com
+ Website: www.swiftsim.com
  Twitter: @SwiftSimulation
 
 See INSTALL.swift for install instructions.
@@ -27,7 +27,7 @@ Parameters:
     -D, --drift-all                   Always drift all particles even the ones
                                       far from active particles. This emulates
                                       Gadget-[23] and GIZMO's default behaviours.
-    -F, --sourceterms
+    -F, --star-formation	      Run with star formation
     -g, --external-gravity            Run with an external gravitational potential.
     -G, --self-gravity                Run with self-gravity.
     -M, --multipole-reconstruction    Reconstruct the multipoles every time-step.
diff --git a/README.md b/README.md
index 94e95776cd80c1bb822f0f68290c2add9d2bb58b..92925017d9a8f44521a542e1df3760cc58ba73cb 100644
--- a/README.md
+++ b/README.md
@@ -75,7 +75,7 @@ Parameters:
     -D, --drift-all                   Always drift all particles even the ones
                                       far from active particles. This emulates
                                       Gadget-[23] and GIZMO's default behaviours.
-    -F, --sourceterms
+    -F, --star-formation	      Run with star formation
     -g, --external-gravity            Run with an external gravitational potential.
     -G, --self-gravity                Run with self-gravity.
     -M, --multipole-reconstruction    Reconstruct the multipoles every time-step.
diff --git a/configure.ac b/configure.ac
index ca1aeb626b30d3170f71420e9700673f9597e07e..bf909a315c59cd0234c31461de88cad7d93f819e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -19,6 +19,22 @@
 AC_INIT([SWIFT],[0.8.0],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim])
 swift_config_flags="$*"
 
+#  We want to stop when given unrecognised options. No subdirs so this is safe.
+enable_option_checking=${enable_option_checking:-fatal}
+if test -n "$ac_unrecognized_opts"; then
+    case $enable_option_checking in
+        no)
+        ;;
+        fatal)
+            { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2
+              { (exit 1); exit 1; }; }
+        ;;
+        *)
+            $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2
+        ;;
+    esac
+fi
+
 AC_COPYRIGHT
 AC_CONFIG_SRCDIR([src/space.c])
 AC_CONFIG_AUX_DIR([.])
@@ -335,7 +351,7 @@ AC_ARG_ENABLE([vec],
    [enable_vec="yes"]
 )
 
-#  Disable hand written vectorisation. Slightly odd implementation as want 
+#  Disable hand written vectorisation. Slightly odd implementation as want
 # to describe as --disable-hand-vec, but macro is enable (there is no enable action).
 AC_ARG_ENABLE([hand-vec],
    [AS_HELP_STRING([--disable-hand-vec],
@@ -941,7 +957,7 @@ if test "x$with_velociraptor" != "xno"; then
    AC_PROG_FC
    AC_FC_LIBRARY_LDFLAGS
    if test "x$with_velociraptor" != "xyes" -a "x$with_velociraptor" != "x"; then
-      VELOCIRAPTOR_LIBS="-L$with_velociraptor -lstf -lstdc++ -lhdf5_cpp"
+      VELOCIRAPTOR_LIBS="-L$with_velociraptor -lvelociraptor -lstdc++ -lhdf5_cpp"
       CFLAGS="$CFLAGS -fopenmp"
    else
       VELOCIRAPTOR_LIBS=""
@@ -950,7 +966,7 @@ if test "x$with_velociraptor" != "xno"; then
    have_velociraptor="yes"
 
    AC_CHECK_LIB(
-      [stf],
+      [velociraptor],
       [InitVelociraptor],
       [AC_DEFINE([HAVE_VELOCIRAPTOR],1,[The VELOCIraptor library appears to be present.])],
       [AC_MSG_ERROR(Cannot find VELOCIraptor library at $with_velociraptor)],
@@ -986,16 +1002,55 @@ AC_CHECK_FUNC(pthread_setaffinity_np, AC_DEFINE([HAVE_SETAFFINITY],[1],
 AM_CONDITIONAL(HAVESETAFFINITY,
     [test "$ac_cv_func_pthread_setaffinity_np" = "yes"])
 
+# If available check for NUMA as well. There is a problem with the headers of
+# this library, mainly that they do not pass the strict prototypes check when
+# installed outside of the system directories. So we actually do this check
+# in two phases. The basic ones first (before strict-prototypes is added to CFLAGS).
 have_numa="no"
-if test "$ac_cv_func_pthread_setaffinity_np" = "yes"; then
-  # Check for libnuma.
-  AC_CHECK_HEADER([numa.h])
-  if test "$ac_cv_header_numa_h" = "yes"; then
-    AC_CHECK_LIB([numa], [numa_available])
-    have_numa="yes"
-  fi
-fi
+AC_ARG_WITH([numa],
+    [AS_HELP_STRING([--with-numa=PATH],
+       [Directory where the NUMA library exists @<:@yes/no@:>@]
+    )],
+    [with_numa="$withval"],
+    [with_numa="yes"]
+)
+if test "$ac_cv_func_pthread_setaffinity_np" = "yes" -a "x$with_numa" != "xno"; then
 
+    if test "x$with_numa" != "xyes" -a "x$with_numa" != "x"; then
+        NUMA_LIBS="-L$with_numa/lib -lnuma"
+        NUMA_INCS="-I$with_numa/include"
+    else
+        NUMA_LIBS="-lnuma"
+        NUMA_INCS=""
+    fi
+
+    #  Test for header file.
+    old_CPPFLAGS="$CPPFLAGS"
+    CPPFLAGS="$CPPFLAGS $NUMA_INCS"
+    AC_CHECK_HEADER([numa.h])
+    CPPFLAGS="$old_CPPFLAGS"
+    if test "$ac_cv_header_numa_h" = "yes"; then
+
+        #  If NUMA location is specified check if we have it.
+        if test "x$with_numa" != "xyes" -a "x$with_numa" != "x"; then
+            AC_CHECK_LIB([numa],[numa_available],
+                AC_DEFINE([HAVE_LIBNUMA],1,[The NUMA library appears to be present.]),
+                AC_MSG_ERROR(something is wrong with the NUMA library!), $NUMA_LIBS)
+            have_numa="yes"
+        else
+            AC_CHECK_LIB([numa],[numa_available],[have_numa="yes"],[have_numa="no"],$NUMA_LIBS)
+            if test "x$have_numa" != "xno"; then
+                AC_DEFINE([HAVE_LIBNUMA],1,[The NUMA library appears to be present.])
+            fi
+        fi
+    fi
+
+    #  We can live without this.
+    if test "$have_numa" = "no"; then
+       NUMA_LIBS=""
+    fi
+fi
+AC_SUBST([NUMA_LIBS])
 
 # Check for Intel and PowerPC intrinsics header optionally used by vector.h.
 AC_CHECK_HEADERS([immintrin.h])
@@ -1082,6 +1137,35 @@ if test "$enable_warn" != "no"; then
                           [CFLAGS="$CFLAGS"],[$CFLAGS],[AC_LANG_SOURCE([int main(void){return 0;}])])
 fi
 
+# Second part of the NUMA library checks. We now decide if we need to use
+# -isystem to get around the strict-prototypes problem. Assumes isystem
+# is available when strict-prototypes is.
+if test "$have_numa" != "no"; then
+    if test "x$with_numa" != "xyes" -a "x$with_numa" != "x"; then
+        case "$CFLAGS" in
+            *strict-prototypes*)
+                NUMA_INCS="-isystem$with_numa/include"
+                # This may still fail if CPATH is used, so we check if the
+                # headers are usable.
+                AS_UNSET(ac_cv_header_numa_h)
+                old_CPPFLAGS="$CPPFLAGS"
+                CPPFLAGS="$CPPFLAGS $NUMA_INCS"
+                numa_failed="no"
+                AC_CHECK_HEADER([numa.h],[numa_failed="no"],
+                                [numa_failed="yes"])
+                if test "$numa_failed" = "yes"; then
+                    AC_MSG_ERROR([Failed to compile the numa.h header file: you may need to set --enable-compiler-warnings to yes or no])
+                fi
+                CPPFLAGS="$old_CPPFLAGS"
+            ;;
+            *)
+                NUMA_INCS="-I$with_numa/include"
+            ;;
+        esac
+   fi
+fi
+AC_SUBST([NUMA_INCS])
+
 # Various package configuration options.
 
 # Master subgrid options
diff --git a/doc/RTD/source/CommandLineOptions/index.rst b/doc/RTD/source/CommandLineOptions/index.rst
index 9fb9d784d6057e4d9aa4a923143d622e577f142c..de969730bebc6c20950086cb1ba9a20b94e01af6 100644
--- a/doc/RTD/source/CommandLineOptions/index.rst
+++ b/doc/RTD/source/CommandLineOptions/index.rst
@@ -22,7 +22,7 @@ can be found by typing ``./swift -h``::
     -D, --drift-all                   Always drift all particles even the ones
                                       far from active particles. This emulates
                                       Gadget-[23] and GIZMO's default behaviours.
-    -F, --sourceterms
+    -F, --star-formation	      Run with star formation
     -g, --external-gravity            Run with an external gravitational potential.
     -G, --self-gravity                Run with self-gravity.
     -M, --multipole-reconstruction    Reconstruct the multipoles every time-step.
diff --git a/doc/RTD/source/GettingStarted/compiling_code.rst b/doc/RTD/source/GettingStarted/compiling_code.rst
index c40f06965e15146c41bf210aec3b195032cef0e7..a0ce1c08eaf6b08a298ac4b720017273d4fa6559 100644
--- a/doc/RTD/source/GettingStarted/compiling_code.rst
+++ b/doc/RTD/source/GettingStarted/compiling_code.rst
@@ -38,7 +38,7 @@ METIS is used for domain decomposition and load balancing.
 
 libNUMA
 ~~~~~~~
-libNUMA is used to pin threads.
+libNUMA is used to pin threads (but see INSTALL.swift).
 
 GSL
 ~~~
diff --git a/doc/RTD/source/VELOCIraptorInterface/stfalone.rst b/doc/RTD/source/VELOCIraptorInterface/stfalone.rst
index 191d990c3d485bbc548c435d9b548686b9446397..e6bd0a72b207d7ca54bae67283326e7dcff51c02 100644
--- a/doc/RTD/source/VELOCIraptorInterface/stfalone.rst
+++ b/doc/RTD/source/VELOCIraptorInterface/stfalone.rst
@@ -23,24 +23,18 @@ 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
+master 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::
+Again we need to configure VELOCIraptor::
 
-  cd stf
-  cp Makefile.config.SWIFT-template Makefile.config
+  cmake . -DVR_USE_GAS=ON
 
-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"``.
+In this case, we do not need the SWIFT interface, therefore we can drop
+this option (disabled by default).
 
 Compiling VELOCIraptor
 ----------------------
@@ -50,9 +44,7 @@ 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]_)
+After this an executable is created (``VELOCIraptor-stf/stf``).
 
 Running VELOCIraptor on a Snapshot
 ----------------------------------
@@ -61,10 +53,12 @@ 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
+  ./stf -h
 
 which gives the information about the usage of the command::
 
+  VELOCIraptor/STF running with MPI. Number of mpi threads: 1
+  VELOCIraptor/STF running with OpenMP. Number of openmp threads: 8
   USAGE:
 
   -C <configuration file (overrides other options)> 
@@ -80,13 +74,6 @@ which gives the information about the usage of the command::
    ===== EXTRA OPTIONS REQUIRED FOR RAMSES INPUT ====== 
   -t <ramses snapnumber>
 
-After this we can run a VELOCIraptor on a snapshot as::
+After this we can run 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.
-
+  ./stf -i input -o output -C configfile.txt
diff --git a/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst b/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst
index 245b455d583d3ccdca02463e2afc6100e14dfb31..a663c37f93a6cede8c4528583c44183059414432 100644
--- a/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst
+++ b/doc/RTD/source/VELOCIraptorInterface/stfwithswift.rst
@@ -22,23 +22,17 @@ 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
+Currently the best version that works with SWIFT is the master
 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``::
+To get VELOCIraptor working with SWIFT simply use::
 
-  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. 
+  cmake . -DVR_USE_SWIFT_INTERFACE=ON -DCMAKE_CXX_FLAGS="-fPIC" -DVR_USE_GAS=ON
 
+If you wish to run swift without MPI, you will need to add ``-DVR_MPI=OFF``.
 
 Compiling VELOCIraptor
 ----------------------
@@ -46,13 +40,11 @@ Compiling VELOCIraptor
 After we downloaded the files and made a configuration file we can compile
 VELOCIraptor as follows::
 
-  make lib 
-  make libstf
+  make -j 4
 
-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
+After the compilation of your code, you will find a static library ``libvelociraptor.a``,
+that is required to run SWIFT with VELOCIraptor.
+Note that VELOCIraptor needs a serial version of the
 HDF5 library, not a parallel build.
 
 Compiling SWIFT
@@ -61,7 +53,7 @@ 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 
+  ./configure --with-velociraptor=/path/to/VELOCIraptor-STF/src 
   make 
 
 In which ``./autogen.sh`` only needs to be run once after the code is cloned
diff --git a/examples/ComovingSodShock_1D/makeIC.py b/examples/ComovingSodShock_1D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..371acc685cd00bd211e024ecc17c976ea5a08f68
--- /dev/null
+++ b/examples/ComovingSodShock_1D/makeIC.py
@@ -0,0 +1,123 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ #               2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+ # 
+ # 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
+from numpy import *
+
+# Generates a swift IC file for the 1D Sod Shock in a periodic box
+
+unit_l_in_cgs = 3.086e18
+unit_m_in_cgs = 2.94e55
+unit_t_in_cgs = 3.086e18
+
+# Parameters
+gamma = 5./3.          # Gas adiabatic index
+numPart_L = 800        # Number of particles in the left state
+x_min = -1.
+x_max = 1.
+rho_L = 1.             # Density left state
+rho_R = 0.125          # Density right state
+v_L = 0.               # Velocity left state
+v_R = 0.               # Velocity right state
+P_L = 1.               # Pressure left state
+P_R = 0.1              # Pressure right state
+a_beg = 0.001
+fileName = "sodShock.hdf5" 
+
+
+#---------------------------------------------------
+
+# Find how many particles we actually have
+boxSize = x_max - x_min
+numPart_R = int(numPart_L * (rho_R / rho_L))
+numPart = numPart_L + numPart_R
+
+# Now get the distances
+delta_L = (boxSize/2)  / numPart_L
+delta_R = (boxSize/2)  / numPart_R
+offset_L = delta_L / 2
+offset_R = delta_R / 2
+
+# Build the arrays
+coords = zeros((numPart, 3))
+v = zeros((numPart, 3))
+ids = linspace(1, numPart, numPart)
+m = zeros(numPart)
+h = zeros(numPart)
+u = zeros(numPart)
+
+# Set the particles on the left
+for i in range(numPart_L):
+    coords[i,0] = x_min + offset_L + i * delta_L
+    u[i] = P_L / (rho_L * (gamma - 1.))
+    h[i] = 1.2348 * delta_L
+    m[i] = boxSize * rho_L / (2. * numPart_L)
+    v[i,0] = v_L
+    
+# Set the particles on the right
+for j in range(numPart_R):
+    i = numPart_L + j
+    coords[i,0] = offset_R + j * delta_R
+    u[i] = P_R / (rho_R * (gamma - 1.))
+    h[i] = 1.2348 * delta_R
+    m[i] = boxSize * rho_R / (2. * numPart_R)
+    v[i,0] = v_R
+
+# Shift particles
+coords[:,0] -= x_min
+
+u /= (a_beg**(3. * (gamma - 1.)))
+    
+#File
+file = h5py.File(fileName, 'w')
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] =  [numPart, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [numPart, 0, 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
+grp.attrs["Dimension"] = 1
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = unit_l_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = unit_m_in_cgs
+grp.attrs["Unit time in cgs (U_t)"] = unit_t_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+#Particle group
+grp = file.create_group("/PartType0")
+grp.create_dataset('Coordinates', data=coords, dtype='d')
+grp.create_dataset('Velocities', data=v, dtype='f')
+grp.create_dataset('Masses', data=m, dtype='f')
+grp.create_dataset('SmoothingLength', data=h, dtype='f')
+grp.create_dataset('InternalEnergy', data=u, dtype='f')
+grp.create_dataset('ParticleIDs', data=ids, dtype='L')
+
+
+file.close()
+
+
diff --git a/examples/ComovingSodShock_1D/plotSolution.py b/examples/ComovingSodShock_1D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..95674c04bfafd0cd549b69814df82f9a4f80a949
--- /dev/null
+++ b/examples/ComovingSodShock_1D/plotSolution.py
@@ -0,0 +1,310 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ #               2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+ # 
+ # 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 analytical solution of the Sod shock and plots the SPH answer
+ 
+
+# Generates the analytical  solution for the Sod shock test case
+# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t.
+# This follows the solution given in (Toro, 2009)
+
+
+# Parameters
+gas_gamma = 5./3.      # Polytropic index
+rho_L = 1.             # Density left state
+rho_R = 0.125          # Density right state
+v_L = 0.               # Velocity left state
+v_R = 0.               # Velocity right state
+P_L = 1.               # Pressure left state
+P_R = 0.1              # Pressure right state
+
+
+import matplotlib
+matplotlib.use("Agg")
+from pylab import *
+import h5py
+
+# Plot parameters
+params = {'axes.labelsize': 10,
+'axes.titlesize': 10,
+'font.size': 12,
+'legend.fontsize': 12,
+'xtick.labelsize': 10,
+'ytick.labelsize': 10,
+'text.usetex': True,
+ 'figure.figsize' : (9.90,6.45),
+'figure.subplot.left'    : 0.045,
+'figure.subplot.right'   : 0.99,
+'figure.subplot.bottom'  : 0.05,
+'figure.subplot.top'     : 0.99,
+'figure.subplot.wspace'  : 0.15,
+'figure.subplot.hspace'  : 0.12,
+'lines.markersize' : 6,
+'lines.linewidth' : 3.,
+'text.latex.unicode': True
+}
+rcParams.update(params)
+rc('font',**{'family':'sans-serif','sans-serif':['Times']})
+
+
+snap = int(sys.argv[1])
+
+
+# Read the simulation data
+sim = h5py.File("sodShock_%04d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+anow = sim["/Header"].attrs["Scale-factor"]
+a_i = sim["/Cosmology"].attrs["a_beg"]
+H_0 = sim["/Cosmology"].attrs["H0 [internal units]"]
+time = 2. * (1. / np.sqrt(a_i) - 1. / np.sqrt(anow)) / H_0
+scheme = str(sim["/HydroScheme"].attrs["Scheme"])
+kernel = str(sim["/HydroScheme"].attrs["Kernel function"])
+neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"]
+eta = sim["/HydroScheme"].attrs["Kernel eta"]
+git = str(sim["Code"].attrs["Git Revision"])
+
+x = sim["/PartType0/Coordinates"][:,0]
+v = sim["/PartType0/Velocities"][:,0] * anow
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+rho = sim["/PartType0/Density"][:]
+try:
+    alpha = sim["/PartType0/Viscosity"][:]
+    plot_alpha = True 
+except:
+    plot_alpha = False
+
+N = 1000  # Number of points
+x_min = -1.
+x_max = 1.
+
+x += x_min
+
+# ---------------------------------------------------------------
+# Don't touch anything after this.
+# ---------------------------------------------------------------
+
+c_L = sqrt(gas_gamma * P_L / rho_L)   # Speed of the rarefaction wave
+c_R = sqrt(gas_gamma * P_R / rho_R)   # Speed of the shock front
+
+# Helpful variable
+Gama = (gas_gamma - 1.) / (gas_gamma + 1.)
+beta = (gas_gamma - 1.) / (2. * gas_gamma)
+
+# Characteristic function and its derivative, following Toro (2009)
+def compute_f(P_3, P, c):
+    u = P_3 / P
+    if u > 1:
+        term1 = gas_gamma*((gas_gamma+1.)*u + gas_gamma-1.)
+        term2 = sqrt(2./term1)
+        fp = (u - 1.)*c*term2
+        dfdp = c*term2/P + (u - 1.)*c/term2*(-1./term1**2)*gas_gamma*(gas_gamma+1.)/P
+    else:
+        fp = (u**beta - 1.)*(2.*c/(gas_gamma-1.))
+        dfdp = 2.*c/(gas_gamma-1.)*beta*u**(beta-1.)/P
+    return (fp, dfdp)
+
+# Solution of the Riemann problem following Toro (2009) 
+def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R):
+    P_new = ((c_L + c_R + (v_L - v_R)*0.5*(gas_gamma-1.))/(c_L / P_L**beta + c_R / P_R**beta))**(1./beta)
+    P_3 = 0.5*(P_R + P_L)
+    f_L = 1.
+    while fabs(P_3 - P_new) > 1e-6:
+        P_3 = P_new
+        (f_L, dfdp_L) = compute_f(P_3, P_L, c_L)
+        (f_R, dfdp_R) = compute_f(P_3, P_R, c_R)
+        f = f_L + f_R + (v_R - v_L)
+        df = dfdp_L + dfdp_R
+        dp =  -f/df
+        prnew = P_3 + dp
+    v_3 = v_L - f_L
+    return (P_new, v_3)
+
+
+# Solve Riemann problem for post-shock region
+(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R)
+
+# Check direction of shocks and wave
+shock_R = (P_3 > P_R)
+shock_L = (P_3 > P_L)
+
+# Velocity of shock front and and rarefaction wave
+if shock_R:
+    v_right = v_R + c_R**2*(P_3/P_R - 1.)/(gas_gamma*(v_3-v_R))
+else:
+    v_right = c_R + 0.5*(gas_gamma+1.)*v_3 - 0.5*(gas_gamma-1.)*v_R
+
+if shock_L:
+    v_left = v_L + c_L**2*(P_3/p_L - 1.)/(gas_gamma*(v_3-v_L))
+else:
+    v_left = c_L - 0.5*(gas_gamma+1.)*v_3 + 0.5*(gas_gamma-1.)*v_L
+
+# Compute position of the transitions
+x_23 = -fabs(v_left) * time
+if shock_L :
+    x_12 = -fabs(v_left) * time
+else:
+    x_12 = -(c_L - v_L) * time
+
+x_34 = v_3 * time
+
+x_45 = fabs(v_right) * time
+if shock_R:
+    x_56 = fabs(v_right) * time
+else:
+    x_56 = (c_R + v_R) * time
+
+
+# Prepare arrays
+delta_x = (x_max - x_min) / N
+x_s = arange(x_min, x_max, delta_x)
+rho_s = zeros(N)
+P_s = zeros(N)
+v_s = zeros(N)
+
+# Compute solution in the different regions
+for i in range(N):
+    if x_s[i] <= x_12:
+        rho_s[i] = rho_L
+        P_s[i] = P_L
+        v_s[i] = v_L
+    if x_s[i] >= x_12 and x_s[i] < x_23:
+        if shock_L:
+            rho_s[i] = rho_L*(Gama + P_3/P_L)/(1. + Gama * P_3/P_L)
+            P_s[i] = P_3
+            v_s[i] = v_3
+        else:
+            rho_s[i] = rho_L*(Gama * (0. - x_s[i])/(c_L * time) + Gama * v_L/c_L + (1.-Gama))**(2./(gas_gamma-1.))
+            P_s[i] = P_L*(rho_s[i] / rho_L)**gas_gamma
+            v_s[i] = (1.-Gama)*(c_L -(0. - x_s[i]) / time) + Gama*v_L
+    if x_s[i] >= x_23 and x_s[i] < x_34:
+        if shock_L:
+            rho_s[i] = rho_L*(Gama + P_3/P_L)/(1+Gama * P_3/p_L)
+        else:
+            rho_s[i] = rho_L*(P_3 / P_L)**(1./gas_gamma)
+        P_s[i] = P_3
+        v_s[i] = v_3
+    if x_s[i] >= x_34 and x_s[i] < x_45:
+        if shock_R:
+            rho_s[i] = rho_R*(Gama + P_3/P_R)/(1. + Gama * P_3/P_R)
+        else:
+            rho_s[i] = rho_R*(P_3 / P_R)**(1./gas_gamma)
+        P_s[i] = P_3
+        v_s[i] = v_3
+    if x_s[i] >= x_45 and x_s[i] < x_56:
+        if shock_R:
+            rho_s[i] = rho_R
+            P_s[i] = P_R
+            v_s[i] = v_R
+        else:
+            rho_s[i] = rho_R*(Gama*(x_s[i])/(c_R*time) - Gama*v_R/c_R + (1.-Gama))**(2./(gas_gamma-1.))
+            P_s[i] = p_R*(rho_s[i]/rho_R)**gas_gamma
+            v_s[i] = (1.-Gama)*(-c_R - (-x_s[i])/time) + Gama*v_R
+    if x_s[i] >= x_56:
+        rho_s[i] = rho_R
+        P_s[i] = P_R
+        v_s[i] = v_R
+
+
+# Additional arrays
+u_s = P_s / (rho_s * (gas_gamma - 1.))  #internal energy
+s_s = P_s / rho_s**gas_gamma # entropic function
+        
+
+# Plot the interesting quantities
+figure()
+
+# Velocity profile --------------------------------
+subplot(231)
+plot(x, v, '.', color='r', ms=4.0)
+plot(x_s, v_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Velocity}}~v_x$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(-0.1, 0.95)
+
+# Density profile --------------------------------
+subplot(232)
+plot(x, rho, '.', color='r', ms=4.0)
+plot(x_s, rho_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.05, 1.1)
+
+# Pressure profile --------------------------------
+subplot(233)
+plot(x, P, '.', color='r', ms=4.0)
+plot(x_s, P_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.01, 1.1)
+
+# Internal energy profile -------------------------
+subplot(234)
+plot(x, u, '.', color='r', ms=4.0)
+plot(x_s, u_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.8, 2.2)
+
+# Entropy/alpha profile ---------------------------------
+subplot(235)
+
+if plot_alpha:
+    plot(x, alpha, '.', color='r', ms=4.0)
+    ylabel(r"${\rm{Viscosity}}~\alpha$", labelpad=0)
+    # Show location of shock
+    plot([x_56, x_56], [-100, 100], color="k", alpha=0.5, ls="dashed", lw=1.2)
+    ylim(0, 1)
+else:
+    plot(x, S, '.', color='r', ms=4.0)
+    plot(x_s, s_s, '--', color='k', alpha=0.8, lw=1.2)
+    ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+    ylim(0.8, 3.8)
+
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+xlim(-0.5, 0.5)
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+z_now = 1. / anow - 1.
+text(-0.49, 0.9, "Sod shock with  $\\gamma=%.3f$ in 1D at $z=%.2f$"%(gas_gamma,z_now), fontsize=10)
+text(-0.49, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$"%(P_L, rho_L, v_L), fontsize=10)
+text(-0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$"%(P_R, rho_R, v_R), fontsize=10)
+z_i = 1. / a_i - 1.
+text(-0.49, 0.6, "Initial redshift: $%.2f$"%z_i, fontsize=10)
+plot([-0.49, 0.1], [0.52, 0.52], 'k-', lw=1)
+text(-0.49, 0.4, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.3, scheme, fontsize=10)
+text(-0.49, 0.2, kernel, fontsize=10)
+text(-0.49, 0.1, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+tight_layout()
+
+savefig("SodShock.png", dpi=200)
diff --git a/examples/ComovingSodShock_1D/run.sh b/examples/ComovingSodShock_1D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0d1fc4f1be8699929b2cf3b2ea2c8813ebef9f10
--- /dev/null
+++ b/examples/ComovingSodShock_1D/run.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+if [ ! -e sodShock.hdf5 ]
+then
+    echo "Generating initial conditions for the 1D SodShock example..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift --cosmology --hydro --threads=1 sodShock.yml 2>&1 | tee output.log
+
+# Plot the result
+python plotSolution.py 1
diff --git a/examples/ComovingSodShock_1D/sodShock.yml b/examples/ComovingSodShock_1D/sodShock.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2d7a5727cbbc2cd417527ce05d7a8ea8ea05dd71
--- /dev/null
+++ b/examples/ComovingSodShock_1D/sodShock.yml
@@ -0,0 +1,43 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     2.94e55   # Grams
+  UnitLength_in_cgs:   3.086e18   # pc
+  UnitVelocity_in_cgs: 1.   # km per s
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  dt_min:     1e-7  # 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:            sodShock # Common part of the name of output files
+  time_first:          0.       # Time of the first output (in internal units)
+  delta_time:          1.06638      # Time difference between consecutive outputs (in internal units)
+  scale_factor_first:  0.001
+  compression:         1
+  
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1.02 # Time between statistics output
+
+# 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.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./sodShock.hdf5       # The file to read
+  periodic:   1
+
+Cosmology:
+  Omega_m: 1.
+  Omega_lambda: 0.
+  Omega_b: 1.
+  h: 1.
+  a_begin: 0.001
+  a_end: 0.00106638
+
diff --git a/examples/ComovingSodShock_2D/getGlass.sh b/examples/ComovingSodShock_2D/getGlass.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f4cb4ebcb4b452b2b123462bc97eed532f43ba25
--- /dev/null
+++ b/examples/ComovingSodShock_2D/getGlass.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_48.hdf5
diff --git a/examples/ComovingSodShock_2D/makeIC.py b/examples/ComovingSodShock_2D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..51a408866047534f86fbded071d604ec294ed0b7
--- /dev/null
+++ b/examples/ComovingSodShock_2D/makeIC.py
@@ -0,0 +1,127 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ #               2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+ # 
+ # 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
+from numpy import *
+
+# Generates a swift IC file for the 2D Sod Shock in a periodic box
+
+unit_l_in_cgs = 3.086e18
+unit_m_in_cgs = 2.94e55
+unit_t_in_cgs = 3.086e18
+
+# Parameters
+gamma = 5./3.          # Gas adiabatic index
+x_min = -1.
+x_max = 1.
+rho_L = 1.             # Density left state
+rho_R = 0.140625       # Density right state
+v_L = 0.               # Velocity left state
+v_R = 0.               # Velocity right state
+P_L = 1.               # Pressure left state
+P_R = 0.1              # Pressure right state
+a_beg = 0.001
+fileName = "sodShock.hdf5" 
+
+
+#---------------------------------------------------
+boxSize = (x_max - x_min)
+
+glass_L = h5py.File("glassPlane_128.hdf5", "r")
+glass_R = h5py.File("glassPlane_48.hdf5", "r")
+
+pos_L = glass_L["/PartType0/Coordinates"][:,:] * 0.5
+pos_R = glass_R["/PartType0/Coordinates"][:,:] * 0.5
+h_L = glass_L["/PartType0/SmoothingLength"][:] * 0.5
+h_R = glass_R["/PartType0/SmoothingLength"][:] * 0.5
+
+# Merge things
+aa = pos_L - array([0.5, 0., 0.])
+pos_LL = append(pos_L, pos_L + array([0.5, 0., 0.]), axis=0)
+pos_RR = append(pos_R, pos_R + array([0.5, 0., 0.]), axis=0)
+pos = append(pos_LL - array([1.0, 0., 0.]), pos_RR, axis=0)
+h_LL = append(h_L, h_L)
+h_RR = append(h_R, h_R)
+h = append(h_LL, h_RR)
+
+numPart_L = size(h_LL)
+numPart_R = size(h_RR)
+numPart = size(h)
+
+vol_L = 0.5
+vol_R = 0.5
+
+# Generate extra arrays
+v = zeros((numPart, 3))
+ids = linspace(1, numPart, numPart)
+m = zeros(numPart)
+u = zeros(numPart)
+
+for i in range(numPart):
+    x = pos[i,0]
+
+    if x < 0: #left
+        u[i] = P_L / (rho_L * (gamma - 1.))
+        m[i] = rho_L * vol_L / numPart_L
+        v[i,0] = v_L
+    else:     #right
+        u[i] = P_R / (rho_R * (gamma - 1.))
+        m[i] = rho_R * vol_R / numPart_R
+        v[i,0] = v_R
+        
+# Shift particles
+pos[:,0] -= x_min
+
+u /= (a_beg**(3. * (gamma - 1.)))
+
+#File
+file = h5py.File(fileName, 'w')
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = [boxSize, 0.5, 1.0]
+grp.attrs["NumPart_Total"] =  [numPart, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [numPart, 0, 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
+grp.attrs["Dimension"] = 2
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = unit_l_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = unit_m_in_cgs
+grp.attrs["Unit time in cgs (U_t)"] = unit_t_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+#Particle group
+grp = file.create_group("/PartType0")
+grp.create_dataset('Coordinates', data=pos, dtype='d')
+grp.create_dataset('Velocities', data=v, dtype='f')
+grp.create_dataset('Masses', data=m, dtype='f')
+grp.create_dataset('SmoothingLength', data=h, dtype='f')
+grp.create_dataset('InternalEnergy', data=u, dtype='f')
+grp.create_dataset('ParticleIDs', data=ids, dtype='L')
+
+
+file.close()
diff --git a/examples/ComovingSodShock_2D/plotSolution.py b/examples/ComovingSodShock_2D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..8adb3cf5c550ab9724f6a8f34c1a1260a25712e1
--- /dev/null
+++ b/examples/ComovingSodShock_2D/plotSolution.py
@@ -0,0 +1,318 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ #               2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+ # 
+ # 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 analytical solution of the Sod shock and plots the SPH answer
+ 
+
+# Generates the analytical  solution for the Sod shock test case
+# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t.
+# This follows the solution given in (Toro, 2009)
+
+
+# Parameters
+gas_gamma = 5./3.      # Polytropic index
+rho_L = 1.             # Density left state
+rho_R = 0.140625       # Density right state
+v_L = 0.               # Velocity left state
+v_R = 0.               # Velocity right state
+P_L = 1.               # Pressure left state
+P_R = 0.1              # Pressure right state
+
+
+import matplotlib
+matplotlib.use("Agg")
+from pylab import *
+from scipy import stats
+import h5py
+
+# Plot parameters
+params = {'axes.labelsize': 10,
+'axes.titlesize': 10,
+'font.size': 12,
+'legend.fontsize': 12,
+'xtick.labelsize': 10,
+'ytick.labelsize': 10,
+'text.usetex': True,
+ 'figure.figsize' : (9.90,6.45),
+'figure.subplot.left'    : 0.045,
+'figure.subplot.right'   : 0.99,
+'figure.subplot.bottom'  : 0.05,
+'figure.subplot.top'     : 0.99,
+'figure.subplot.wspace'  : 0.15,
+'figure.subplot.hspace'  : 0.12,
+'lines.markersize' : 6,
+'lines.linewidth' : 3.,
+'text.latex.unicode': True
+}
+rcParams.update(params)
+rc('font',**{'family':'sans-serif','sans-serif':['Times']})
+
+
+snap = int(sys.argv[1])
+
+
+# Read the simulation data
+sim = h5py.File("sodShock_%04d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+anow = sim["/Header"].attrs["Scale-factor"]
+a_i = sim["/Cosmology"].attrs["a_beg"]
+H_0 = sim["/Cosmology"].attrs["H0 [internal units]"]
+time = 2. * (1. / np.sqrt(a_i) - 1. / np.sqrt(anow)) / H_0
+scheme = sim["/HydroScheme"].attrs["Scheme"]
+kernel = sim["/HydroScheme"].attrs["Kernel function"]
+neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"]
+eta = sim["/HydroScheme"].attrs["Kernel eta"]
+git = sim["Code"].attrs["Git Revision"]
+
+x = sim["/PartType0/Coordinates"][:,0]
+v = sim["/PartType0/Velocities"][:,0] * anow
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+rho = sim["/PartType0/Density"][:]
+
+N = 1000  # Number of points
+x_min = -1.
+x_max = 1.
+x += x_min
+
+
+# Bin te data
+x_bin_edge = np.arange(-0.6, 0.6, 0.02)
+x_bin = 0.5*(x_bin_edge[1:] + x_bin_edge[:-1])
+rho_bin,_,_ = stats.binned_statistic(x, rho, statistic='mean', bins=x_bin_edge)
+v_bin,_,_ = stats.binned_statistic(x, v, statistic='mean', bins=x_bin_edge)
+P_bin,_,_ = stats.binned_statistic(x, P, statistic='mean', bins=x_bin_edge)
+S_bin,_,_ = stats.binned_statistic(x, S, statistic='mean', bins=x_bin_edge)
+u_bin,_,_ = stats.binned_statistic(x, u, statistic='mean', bins=x_bin_edge)
+rho2_bin,_,_ = stats.binned_statistic(x, rho**2, statistic='mean', bins=x_bin_edge)
+v2_bin,_,_ = stats.binned_statistic(x, v**2, statistic='mean', bins=x_bin_edge)
+P2_bin,_,_ = stats.binned_statistic(x, P**2, statistic='mean', bins=x_bin_edge)
+S2_bin,_,_ = stats.binned_statistic(x, S**2, statistic='mean', bins=x_bin_edge)
+u2_bin,_,_ = stats.binned_statistic(x, u**2, statistic='mean', bins=x_bin_edge)
+rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2)
+v_sigma_bin = np.sqrt(v2_bin - v_bin**2)
+P_sigma_bin = np.sqrt(P2_bin - P_bin**2)
+S_sigma_bin = np.sqrt(S2_bin - S_bin**2)
+u_sigma_bin = np.sqrt(u2_bin - u_bin**2)
+
+
+# Analytic solution
+c_L = sqrt(gas_gamma * P_L / rho_L)   # Speed of the rarefaction wave
+c_R = sqrt(gas_gamma * P_R / rho_R)   # Speed of the shock front
+
+# Helpful variable
+Gama = (gas_gamma - 1.) / (gas_gamma + 1.)
+beta = (gas_gamma - 1.) / (2. * gas_gamma)
+
+# Characteristic function and its derivative, following Toro (2009)
+def compute_f(P_3, P, c):
+    u = P_3 / P
+    if u > 1:
+        term1 = gas_gamma*((gas_gamma+1.)*u + gas_gamma-1.)
+        term2 = sqrt(2./term1)
+        fp = (u - 1.)*c*term2
+        dfdp = c*term2/P + (u - 1.)*c/term2*(-1./term1**2)*gas_gamma*(gas_gamma+1.)/P
+    else:
+        fp = (u**beta - 1.)*(2.*c/(gas_gamma-1.))
+        dfdp = 2.*c/(gas_gamma-1.)*beta*u**(beta-1.)/P
+    return (fp, dfdp)
+
+# Solution of the Riemann problem following Toro (2009) 
+def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R):
+    P_new = ((c_L + c_R + (v_L - v_R)*0.5*(gas_gamma-1.))/(c_L / P_L**beta + c_R / P_R**beta))**(1./beta)
+    P_3 = 0.5*(P_R + P_L)
+    f_L = 1.
+    while fabs(P_3 - P_new) > 1e-6:
+        P_3 = P_new
+        (f_L, dfdp_L) = compute_f(P_3, P_L, c_L)
+        (f_R, dfdp_R) = compute_f(P_3, P_R, c_R)
+        f = f_L + f_R + (v_R - v_L)
+        df = dfdp_L + dfdp_R
+        dp =  -f/df
+        prnew = P_3 + dp
+    v_3 = v_L - f_L
+    return (P_new, v_3)
+
+
+# Solve Riemann problem for post-shock region
+(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R)
+
+# Check direction of shocks and wave
+shock_R = (P_3 > P_R)
+shock_L = (P_3 > P_L)
+
+# Velocity of shock front and and rarefaction wave
+if shock_R:
+    v_right = v_R + c_R**2*(P_3/P_R - 1.)/(gas_gamma*(v_3-v_R))
+else:
+    v_right = c_R + 0.5*(gas_gamma+1.)*v_3 - 0.5*(gas_gamma-1.)*v_R
+
+if shock_L:
+    v_left = v_L + c_L**2*(P_3/p_L - 1.)/(gas_gamma*(v_3-v_L))
+else:
+    v_left = c_L - 0.5*(gas_gamma+1.)*v_3 + 0.5*(gas_gamma-1.)*v_L
+
+# Compute position of the transitions
+x_23 = -fabs(v_left) * time
+if shock_L :
+    x_12 = -fabs(v_left) * time
+else:
+    x_12 = -(c_L - v_L) * time
+
+x_34 = v_3 * time
+
+x_45 = fabs(v_right) * time
+if shock_R:
+    x_56 = fabs(v_right) * time
+else:
+    x_56 = (c_R + v_R) * time
+
+
+# Prepare arrays
+delta_x = (x_max - x_min) / N
+x_s = arange(x_min, x_max, delta_x)
+rho_s = zeros(N)
+P_s = zeros(N)
+v_s = zeros(N)
+
+# Compute solution in the different regions
+for i in range(N):
+    if x_s[i] <= x_12:
+        rho_s[i] = rho_L
+        P_s[i] = P_L
+        v_s[i] = v_L
+    if x_s[i] >= x_12 and x_s[i] < x_23:
+        if shock_L:
+            rho_s[i] = rho_L*(Gama + P_3/P_L)/(1. + Gama * P_3/P_L)
+            P_s[i] = P_3
+            v_s[i] = v_3
+        else:
+            rho_s[i] = rho_L*(Gama * (0. - x_s[i])/(c_L * time) + Gama * v_L/c_L + (1.-Gama))**(2./(gas_gamma-1.))
+            P_s[i] = P_L*(rho_s[i] / rho_L)**gas_gamma
+            v_s[i] = (1.-Gama)*(c_L -(0. - x_s[i]) / time) + Gama*v_L
+    if x_s[i] >= x_23 and x_s[i] < x_34:
+        if shock_L:
+            rho_s[i] = rho_L*(Gama + P_3/P_L)/(1+Gama * P_3/p_L)
+        else:
+            rho_s[i] = rho_L*(P_3 / P_L)**(1./gas_gamma)
+        P_s[i] = P_3
+        v_s[i] = v_3
+    if x_s[i] >= x_34 and x_s[i] < x_45:
+        if shock_R:
+            rho_s[i] = rho_R*(Gama + P_3/P_R)/(1. + Gama * P_3/P_R)
+        else:
+            rho_s[i] = rho_R*(P_3 / P_R)**(1./gas_gamma)
+        P_s[i] = P_3
+        v_s[i] = v_3
+    if x_s[i] >= x_45 and x_s[i] < x_56:
+        if shock_R:
+            rho_s[i] = rho_R
+            P_s[i] = P_R
+            v_s[i] = v_R
+        else:
+            rho_s[i] = rho_R*(Gama*(x_s[i])/(c_R*time) - Gama*v_R/c_R + (1.-Gama))**(2./(gas_gamma-1.))
+            P_s[i] = p_R*(rho_s[i]/rho_R)**gas_gamma
+            v_s[i] = (1.-Gama)*(-c_R - (-x_s[i])/time) + Gama*v_R
+    if x_s[i] >= x_56:
+        rho_s[i] = rho_R
+        P_s[i] = P_R
+        v_s[i] = v_R
+
+
+# Additional arrays
+u_s = P_s / (rho_s * (gas_gamma - 1.))  #internal energy
+s_s = P_s / rho_s**gas_gamma # entropic function
+        
+
+# Plot the interesting quantities
+figure()
+
+# Velocity profile --------------------------------
+subplot(231)
+plot(x, v, '.', color='r', ms=0.2)
+plot(x_s, v_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Velocity}}~v_x$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(-0.1, 0.95)
+
+# Density profile --------------------------------
+subplot(232)
+plot(x, rho, '.', color='r', ms=0.2)
+plot(x_s, rho_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.05, 1.1)
+
+# Pressure profile --------------------------------
+subplot(233)
+plot(x, P, '.', color='r', ms=0.2)
+plot(x_s, P_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.01, 1.1)
+
+# Internal energy profile -------------------------
+subplot(234)
+plot(x, u, '.', color='r', ms=0.2)
+plot(x_s, u_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.8, 2.2)
+
+# Entropy profile ---------------------------------
+subplot(235)
+plot(x, S, '.', color='r', ms=0.2)
+plot(x_s, s_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.8, 3.8)
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+z_now = 1. / anow - 1.
+text(-0.49, 0.9, "Sod shock with  $\\gamma=%.3f$ in 2D at $z=%.2f$"%(gas_gamma,z_now), fontsize=10)
+text(-0.49, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$"%(P_L, rho_L, v_L), fontsize=10)
+text(-0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$"%(P_R, rho_R, v_R), fontsize=10)
+z_i = 1. / a_i - 1.
+text(-0.49, 0.6, "Initial redshift: $%.2f$"%z_i, fontsize=10)
+plot([-0.49, 0.1], [0.52, 0.52], 'k-', lw=1)
+text(-0.49, 0.4, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.3, scheme, fontsize=10)
+text(-0.49, 0.2, kernel, fontsize=10)
+text(-0.49, 0.1, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+tight_layout()
+savefig("SodShock.png", dpi=200)
diff --git a/examples/ComovingSodShock_2D/run.sh b/examples/ComovingSodShock_2D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..bd6cc317d75a3bd415f074c9eaf48511ab693598
--- /dev/null
+++ b/examples/ComovingSodShock_2D/run.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+if [ ! -e glassPlane_128.hdf5 ]
+then
+    echo "Fetching initial glass file for the Sod shock example..."
+    ./getGlass.sh
+fi
+if [ ! -e sodShock.hdf5 ]
+then
+    echo "Generating initial conditions for the Sod shock example..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift --cosmology --hydro --threads=4 sodShock.yml 2>&1 | tee output.log
+
+python plotSolution.py 1
diff --git a/examples/ComovingSodShock_2D/sodShock.yml b/examples/ComovingSodShock_2D/sodShock.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2d7a5727cbbc2cd417527ce05d7a8ea8ea05dd71
--- /dev/null
+++ b/examples/ComovingSodShock_2D/sodShock.yml
@@ -0,0 +1,43 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     2.94e55   # Grams
+  UnitLength_in_cgs:   3.086e18   # pc
+  UnitVelocity_in_cgs: 1.   # km per s
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  dt_min:     1e-7  # 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:            sodShock # Common part of the name of output files
+  time_first:          0.       # Time of the first output (in internal units)
+  delta_time:          1.06638      # Time difference between consecutive outputs (in internal units)
+  scale_factor_first:  0.001
+  compression:         1
+  
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1.02 # Time between statistics output
+
+# 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.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./sodShock.hdf5       # The file to read
+  periodic:   1
+
+Cosmology:
+  Omega_m: 1.
+  Omega_lambda: 0.
+  Omega_b: 1.
+  h: 1.
+  a_begin: 0.001
+  a_end: 0.00106638
+
diff --git a/examples/ComovingSodShock_3D/README.txt b/examples/ComovingSodShock_3D/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2b2f0d16207079f5d29e9ec281fe5255cc5886fb
--- /dev/null
+++ b/examples/ComovingSodShock_3D/README.txt
@@ -0,0 +1,20 @@
+Cosmological version of the standard Sod shock test.
+
+In the co-moving coordinates that SWIFT uses, the Euler equations of 
+hydrodynamics have an elegant form with a few additional factors that 
+involve the scale factor a. For the specific case of a polytropic index 
+gamma = 5/3, all additional factors are in fact the same: 1/a^2. For 
+this case, hydrodynamics in the co-moving frame is identical to 
+hydrodynamics in a physical non-cosmological frame with a rescaled time 
+variable dt'=a^2*dt.
+
+We choose an Einstein-de Sitter cosmology with H(a)=H_0*a^(3/2) and a 
+box size of 1 pc and rescale the Sod shock initial conditions so that 
+the internal coordinates, density and pressure values are still the same 
+as for the original Sod shock in the non-cosmological case. We then 
+evolve the initial condition from z=999 to z=960.5, which corresponds to 
+a rescaled time interval dt'~0.12. If the co-moving coordinates are 
+implemented correctly, the resulting co-moving density, internal 
+velocity and co-moving pressure profiles should match those for the 
+non-co-moving variables in the ordinary non-cosmological Sod shock at 
+t=0.12.
diff --git a/examples/ComovingSodShock_3D/getGlass.sh b/examples/ComovingSodShock_3D/getGlass.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f61b61d4e6c51b44576fd7cdd6242cb9f0133039
--- /dev/null
+++ b/examples/ComovingSodShock_3D/getGlass.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_32.hdf5
diff --git a/examples/ComovingSodShock_3D/makeIC.py b/examples/ComovingSodShock_3D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..b6528bc5ab3670d4423945d194fc537c1bb672a1
--- /dev/null
+++ b/examples/ComovingSodShock_3D/makeIC.py
@@ -0,0 +1,126 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ #               2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+ # 
+ # 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
+from numpy import *
+
+# Generates a swift IC file for the 3D Sod Shock in a periodic box
+
+unit_l_in_cgs = 3.086e18
+unit_m_in_cgs = 2.94e55
+unit_t_in_cgs = 3.086e18
+
+# Parameters
+gamma = 5./3.          # Gas adiabatic index
+x_min = -1.
+x_max = 1.
+rho_L = 1.             # Density left state
+rho_R = 0.125          # Density right state
+v_L = 0.               # Velocity left state
+v_R = 0.               # Velocity right state
+P_L = 1.               # Pressure left state
+P_R = 0.1              # Pressure right state
+a_beg = 0.001
+fileName = "sodShock.hdf5" 
+
+
+#---------------------------------------------------
+boxSize = (x_max - x_min)
+
+glass_L = h5py.File("glassCube_64.hdf5", "r")
+glass_R = h5py.File("glassCube_32.hdf5", "r")
+
+pos_L = glass_L["/PartType0/Coordinates"][:,:] * 0.5
+pos_R = glass_R["/PartType0/Coordinates"][:,:] * 0.5
+h_L = glass_L["/PartType0/SmoothingLength"][:] * 0.5
+h_R = glass_R["/PartType0/SmoothingLength"][:] * 0.5
+
+# Merge things
+aa = pos_L - array([0.5, 0., 0.])
+pos_LL = append(pos_L, pos_L + array([0.5, 0., 0.]), axis=0)
+pos_RR = append(pos_R, pos_R + array([0.5, 0., 0.]), axis=0)
+pos = append(pos_LL - array([1.0, 0., 0.]), pos_RR, axis=0)
+h_LL = append(h_L, h_L)
+h_RR = append(h_R, h_R)
+h = append(h_LL, h_RR)
+
+numPart_L = size(h_LL)
+numPart_R = size(h_RR)
+numPart = size(h)
+
+vol_L = (0.25 * boxSize)**2 * (0.5 * boxSize)
+vol_R = (0.25 * boxSize)**2 * (0.5 * boxSize)
+
+# Generate extra arrays
+v = zeros((numPart, 3))
+ids = linspace(1, numPart, numPart)
+m = zeros(numPart)
+u = zeros(numPart)
+
+for i in range(numPart):
+    x = pos[i,0]
+
+    if x < 0: #left
+        u[i] = P_L / (rho_L * (gamma - 1.))
+        m[i] = rho_L * vol_L / numPart_L
+        v[i,0] = v_L
+    else:     #right
+        u[i] = P_R / (rho_R * (gamma - 1.))
+        m[i] = rho_R * vol_R / numPart_R
+        v[i,0] = v_R
+        
+# Shift particles
+pos[:,0] -= x_min
+
+u /= (a_beg**(3. * (gamma - 1.)))
+
+#File
+file = h5py.File(fileName, 'w')
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = [boxSize, 0.25 * boxSize, 0.25 * boxSize]
+grp.attrs["NumPart_Total"] =  [numPart, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [numPart, 0, 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
+grp.attrs["Dimension"] = 3
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = unit_l_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = unit_m_in_cgs
+grp.attrs["Unit time in cgs (U_t)"] = unit_t_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+#Particle group
+grp = file.create_group("/PartType0")
+grp.create_dataset('Coordinates', data=pos, dtype='d')
+grp.create_dataset('Velocities', data=v, dtype='f')
+grp.create_dataset('Masses', data=m, dtype='f')
+grp.create_dataset('SmoothingLength', data=h, dtype='f')
+grp.create_dataset('InternalEnergy', data=u, dtype='f')
+grp.create_dataset('ParticleIDs', data=ids, dtype='L')
+
+file.close()
diff --git a/examples/ComovingSodShock_3D/plotSolution.py b/examples/ComovingSodShock_3D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..f05a385e8620b18189d2e7abca8aebb8ae65060e
--- /dev/null
+++ b/examples/ComovingSodShock_3D/plotSolution.py
@@ -0,0 +1,316 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ #               2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+ # 
+ # 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 analytical solution of the Sod shock and plots the SPH answer
+ 
+
+# Generates the analytical  solution for the Sod shock test case
+# The script works for a given left (x<0) and right (x>0) state and computes the solution at a later time t.
+# This follows the solution given in (Toro, 2009)
+
+
+# Parameters
+gas_gamma = 5./3.      # Polytropic index
+rho_L = 1.             # Density left state
+rho_R = 0.125          # Density right state
+v_L = 0.               # Velocity left state
+v_R = 0.               # Velocity right state
+P_L = 1.               # Pressure left state
+P_R = 0.1              # Pressure right state
+
+
+import matplotlib
+matplotlib.use("Agg")
+from pylab import *
+from scipy import stats
+import h5py
+
+# Plot parameters
+params = {'axes.labelsize': 10,
+'axes.titlesize': 10,
+'font.size': 12,
+'legend.fontsize': 12,
+'xtick.labelsize': 10,
+'ytick.labelsize': 10,
+'text.usetex': True,
+ 'figure.figsize' : (9.90,6.45),
+'figure.subplot.left'    : 0.045,
+'figure.subplot.right'   : 0.99,
+'figure.subplot.bottom'  : 0.05,
+'figure.subplot.top'     : 0.99,
+'figure.subplot.wspace'  : 0.15,
+'figure.subplot.hspace'  : 0.12,
+'lines.markersize' : 6,
+'lines.linewidth' : 3.,
+'text.latex.unicode': True
+}
+rcParams.update(params)
+rc('font',**{'family':'sans-serif','sans-serif':['Times']})
+
+
+snap = int(sys.argv[1])
+
+
+# Read the simulation data
+sim = h5py.File("sodShock_%04d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+anow = sim["/Header"].attrs["Scale-factor"]
+a_i = sim["/Cosmology"].attrs["a_beg"]
+H_0 = sim["/Cosmology"].attrs["H0 [internal units]"]
+time = 2. * (1. / np.sqrt(a_i) - 1. / np.sqrt(anow)) / H_0
+scheme = sim["/HydroScheme"].attrs["Scheme"]
+kernel = sim["/HydroScheme"].attrs["Kernel function"]
+neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"]
+eta = sim["/HydroScheme"].attrs["Kernel eta"]
+git = sim["Code"].attrs["Git Revision"]
+
+x = sim["/PartType0/Coordinates"][:,0]
+v = sim["/PartType0/Velocities"][:,0] * anow
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+rho = sim["/PartType0/Density"][:]
+
+x_min = -1.
+x_max = 1.
+x += x_min
+N = 1000
+
+# Bin te data
+x_bin_edge = np.arange(-0.6, 0.6, 0.02)
+x_bin = 0.5*(x_bin_edge[1:] + x_bin_edge[:-1])
+rho_bin,_,_ = stats.binned_statistic(x, rho, statistic='mean', bins=x_bin_edge)
+v_bin,_,_ = stats.binned_statistic(x, v, statistic='mean', bins=x_bin_edge)
+P_bin,_,_ = stats.binned_statistic(x, P, statistic='mean', bins=x_bin_edge)
+S_bin,_,_ = stats.binned_statistic(x, S, statistic='mean', bins=x_bin_edge)
+u_bin,_,_ = stats.binned_statistic(x, u, statistic='mean', bins=x_bin_edge)
+rho2_bin,_,_ = stats.binned_statistic(x, rho**2, statistic='mean', bins=x_bin_edge)
+v2_bin,_,_ = stats.binned_statistic(x, v**2, statistic='mean', bins=x_bin_edge)
+P2_bin,_,_ = stats.binned_statistic(x, P**2, statistic='mean', bins=x_bin_edge)
+S2_bin,_,_ = stats.binned_statistic(x, S**2, statistic='mean', bins=x_bin_edge)
+u2_bin,_,_ = stats.binned_statistic(x, u**2, statistic='mean', bins=x_bin_edge)
+rho_sigma_bin = np.sqrt(rho2_bin - rho_bin**2)
+v_sigma_bin = np.sqrt(v2_bin - v_bin**2)
+P_sigma_bin = np.sqrt(P2_bin - P_bin**2)
+S_sigma_bin = np.sqrt(S2_bin - S_bin**2)
+u_sigma_bin = np.sqrt(u2_bin - u_bin**2)
+
+
+# Analytic solution 
+c_L = sqrt(gas_gamma * P_L / rho_L)   # Speed of the rarefaction wave
+c_R = sqrt(gas_gamma * P_R / rho_R)   # Speed of the shock front
+
+# Helpful variable
+Gama = (gas_gamma - 1.) / (gas_gamma + 1.)
+beta = (gas_gamma - 1.) / (2. * gas_gamma)
+
+# Characteristic function and its derivative, following Toro (2009)
+def compute_f(P_3, P, c):
+    u = P_3 / P
+    if u > 1:
+        term1 = gas_gamma*((gas_gamma+1.)*u + gas_gamma-1.)
+        term2 = sqrt(2./term1)
+        fp = (u - 1.)*c*term2
+        dfdp = c*term2/P + (u - 1.)*c/term2*(-1./term1**2)*gas_gamma*(gas_gamma+1.)/P
+    else:
+        fp = (u**beta - 1.)*(2.*c/(gas_gamma-1.))
+        dfdp = 2.*c/(gas_gamma-1.)*beta*u**(beta-1.)/P
+    return (fp, dfdp)
+
+# Solution of the Riemann problem following Toro (2009) 
+def RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R):
+    P_new = ((c_L + c_R + (v_L - v_R)*0.5*(gas_gamma-1.))/(c_L / P_L**beta + c_R / P_R**beta))**(1./beta)
+    P_3 = 0.5*(P_R + P_L)
+    f_L = 1.
+    while fabs(P_3 - P_new) > 1e-6:
+        P_3 = P_new
+        (f_L, dfdp_L) = compute_f(P_3, P_L, c_L)
+        (f_R, dfdp_R) = compute_f(P_3, P_R, c_R)
+        f = f_L + f_R + (v_R - v_L)
+        df = dfdp_L + dfdp_R
+        dp =  -f/df
+        prnew = P_3 + dp
+    v_3 = v_L - f_L
+    return (P_new, v_3)
+
+
+# Solve Riemann problem for post-shock region
+(P_3, v_3) = RiemannProblem(rho_L, P_L, v_L, rho_R, P_R, v_R)
+
+# Check direction of shocks and wave
+shock_R = (P_3 > P_R)
+shock_L = (P_3 > P_L)
+
+# Velocity of shock front and and rarefaction wave
+if shock_R:
+    v_right = v_R + c_R**2*(P_3/P_R - 1.)/(gas_gamma*(v_3-v_R))
+else:
+    v_right = c_R + 0.5*(gas_gamma+1.)*v_3 - 0.5*(gas_gamma-1.)*v_R
+
+if shock_L:
+    v_left = v_L + c_L**2*(P_3/p_L - 1.)/(gas_gamma*(v_3-v_L))
+else:
+    v_left = c_L - 0.5*(gas_gamma+1.)*v_3 + 0.5*(gas_gamma-1.)*v_L
+
+# Compute position of the transitions
+x_23 = -fabs(v_left) * time
+if shock_L :
+    x_12 = -fabs(v_left) * time
+else:
+    x_12 = -(c_L - v_L) * time
+
+x_34 = v_3 * time
+
+x_45 = fabs(v_right) * time
+if shock_R:
+    x_56 = fabs(v_right) * time
+else:
+    x_56 = (c_R + v_R) * time
+
+
+# Prepare arrays
+delta_x = (x_max - x_min) / N
+x_s = arange(x_min, x_max, delta_x)
+rho_s = zeros(N)
+P_s = zeros(N)
+v_s = zeros(N)
+
+# Compute solution in the different regions
+for i in range(N):
+    if x_s[i] <= x_12:
+        rho_s[i] = rho_L
+        P_s[i] = P_L
+        v_s[i] = v_L
+    if x_s[i] >= x_12 and x_s[i] < x_23:
+        if shock_L:
+            rho_s[i] = rho_L*(Gama + P_3/P_L)/(1. + Gama * P_3/P_L)
+            P_s[i] = P_3
+            v_s[i] = v_3
+        else:
+            rho_s[i] = rho_L*(Gama * (0. - x_s[i])/(c_L * time) + Gama * v_L/c_L + (1.-Gama))**(2./(gas_gamma-1.))
+            P_s[i] = P_L*(rho_s[i] / rho_L)**gas_gamma
+            v_s[i] = (1.-Gama)*(c_L -(0. - x_s[i]) / time) + Gama*v_L
+    if x_s[i] >= x_23 and x_s[i] < x_34:
+        if shock_L:
+            rho_s[i] = rho_L*(Gama + P_3/P_L)/(1+Gama * P_3/p_L)
+        else:
+            rho_s[i] = rho_L*(P_3 / P_L)**(1./gas_gamma)
+        P_s[i] = P_3
+        v_s[i] = v_3
+    if x_s[i] >= x_34 and x_s[i] < x_45:
+        if shock_R:
+            rho_s[i] = rho_R*(Gama + P_3/P_R)/(1. + Gama * P_3/P_R)
+        else:
+            rho_s[i] = rho_R*(P_3 / P_R)**(1./gas_gamma)
+        P_s[i] = P_3
+        v_s[i] = v_3
+    if x_s[i] >= x_45 and x_s[i] < x_56:
+        if shock_R:
+            rho_s[i] = rho_R
+            P_s[i] = P_R
+            v_s[i] = v_R
+        else:
+            rho_s[i] = rho_R*(Gama*(x_s[i])/(c_R*time) - Gama*v_R/c_R + (1.-Gama))**(2./(gas_gamma-1.))
+            P_s[i] = p_R*(rho_s[i]/rho_R)**gas_gamma
+            v_s[i] = (1.-Gama)*(-c_R - (-x_s[i])/time) + Gama*v_R
+    if x_s[i] >= x_56:
+        rho_s[i] = rho_R
+        P_s[i] = P_R
+        v_s[i] = v_R
+
+
+# Additional arrays
+u_s = P_s / (rho_s * (gas_gamma - 1.))  #internal energy
+s_s = P_s / rho_s**gas_gamma # entropic function
+        
+# Plot the interesting quantities
+figure()
+
+# Velocity profile --------------------------------
+subplot(231)
+plot(x, v, '.', color='r', ms=0.5, alpha=0.2)
+plot(x_s, v_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Velocity}}~v_x$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(-0.1, 0.95)
+
+# Density profile --------------------------------
+subplot(232)
+plot(x, rho, '.', color='r', ms=0.5, alpha=0.2)
+plot(x_s, rho_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.05, 1.1)
+
+# Pressure profile --------------------------------
+subplot(233)
+plot(x, P, '.', color='r', ms=0.5, alpha=0.2)
+plot(x_s, P_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.01, 1.1)
+
+# Internal energy profile -------------------------
+subplot(234)
+plot(x, u, '.', color='r', ms=0.5, alpha=0.2)
+plot(x_s, u_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.8, 2.2)
+
+# Entropy profile ---------------------------------
+subplot(235)
+plot(x, S, '.', color='r', ms=0.5, alpha=0.2)
+plot(x_s, s_s, '--', color='k', alpha=0.8, lw=1.2)
+errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt='.', ms=8.0, color='b', lw=1.2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+xlim(-0.5, 0.5)
+ylim(0.8, 3.8)
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+znow = 1. / anow - 1.
+text(-0.49, 0.9, "Sod shock with  $\\gamma=%.3f$ in 3D at $z=%.2f$"%(gas_gamma,znow), fontsize=10)
+text(-0.49, 0.8, "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$"%(P_L, rho_L, v_L), fontsize=10)
+text(-0.49, 0.7, "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$"%(P_R, rho_R, v_R), fontsize=10)
+z_i = 1. / a_i - 1.
+text(-0.49, 0.6, "Initial redshift: $%.2f$"%z_i, fontsize = 10)
+plot([-0.49, 0.1], [0.52, 0.52], 'k-', lw=1)
+text(-0.49, 0.4, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.3, scheme, fontsize=10)
+text(-0.49, 0.2, kernel, fontsize=10)
+text(-0.49, 0.1, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+tight_layout()
+savefig("SodShock.png", dpi=200)
diff --git a/examples/ComovingSodShock_3D/run.sh b/examples/ComovingSodShock_3D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..00e4f669fdb1347ab1b34cfa11821ca011b73120
--- /dev/null
+++ b/examples/ComovingSodShock_3D/run.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+if [ ! -e glassCube_64.hdf5 ]
+then
+    echo "Fetching initial glass file for the Sod shock example..."
+    ./getGlass.sh
+fi
+if [ ! -e sodShock.hdf5 ]
+then
+    echo "Generating initial conditions for the Sod shock example..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift --cosmology --hydro --threads=4 sodShock.yml 2>&1 | tee output.log
+
+python plotSolution.py 1
diff --git a/examples/ComovingSodShock_3D/sodShock.yml b/examples/ComovingSodShock_3D/sodShock.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2d7a5727cbbc2cd417527ce05d7a8ea8ea05dd71
--- /dev/null
+++ b/examples/ComovingSodShock_3D/sodShock.yml
@@ -0,0 +1,43 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     2.94e55   # Grams
+  UnitLength_in_cgs:   3.086e18   # pc
+  UnitVelocity_in_cgs: 1.   # km per s
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  dt_min:     1e-7  # 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:            sodShock # Common part of the name of output files
+  time_first:          0.       # Time of the first output (in internal units)
+  delta_time:          1.06638      # Time difference between consecutive outputs (in internal units)
+  scale_factor_first:  0.001
+  compression:         1
+  
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1.02 # Time between statistics output
+
+# 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.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./sodShock.hdf5       # The file to read
+  periodic:   1
+
+Cosmology:
+  Omega_m: 1.
+  Omega_lambda: 0.
+  Omega_b: 1.
+  h: 1.
+  a_begin: 0.001
+  a_end: 0.00106638
+
diff --git a/examples/CoolingRates/Makefile.am b/examples/CoolingRates/Makefile.am
index 058cdaf2efa3df3647af6f6e0263f65a0e515a15..f81188298d733be2be84cd0be02728c5c6c64113 100644
--- a/examples/CoolingRates/Makefile.am
+++ b/examples/CoolingRates/Makefile.am
@@ -15,12 +15,12 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # 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) $(NUMA_INCS)
 
-AM_LDFLAGS = $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS)
+AM_LDFLAGS = $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS)
 
 # Extra libraries.
-EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(VELOCIRAPTOR_LIBS) $(GSL_LIBS)
+EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(VELOCIRAPTOR_LIBS) $(GSL_LIBS)
 
 # Programs.
 bin_PROGRAMS = cooling_rates
diff --git a/examples/EAGLE_12/eagle_12.yml b/examples/EAGLE_12/eagle_12.yml
index 90b546f311be7e617929eb53146aa7b3daf8114c..1d19320491130ce6d98ac8cd0adff8753f2c2e54 100644
--- a/examples/EAGLE_12/eagle_12.yml
+++ b/examples/EAGLE_12/eagle_12.yml
@@ -72,9 +72,8 @@ EAGLEChemistry: 	    # Solar abundances
   SulphurOverSilicon:       0.6054160
 
 EagleCooling:
-  filename:                /cosma5/data/Eagle/BG_Tables/CoolingTables/
+  filename:                ./coolingtables/
   reionisation_redshift:   11.5
-  he_reion_z_center:       3.5
-  he_reion_z_sigma:        0.5
-  he_reion_ev_pH:          2.0
-
+  He_reion_z_centre:       3.5
+  He_reion_z_sigma:        0.5
+  He_reion_ev_pH:          2.0
diff --git a/examples/EAGLE_25/eagle_25.yml b/examples/EAGLE_25/eagle_25.yml
index bd74473d13acd235a703d7391d187495fc33204f..3e1e5907df7bf76e8ad96a1c9d3c667945c090c8 100644
--- a/examples/EAGLE_25/eagle_25.yml
+++ b/examples/EAGLE_25/eagle_25.yml
@@ -81,9 +81,9 @@ EAGLEChemistry: 	    # Solar abundances
   SulphurOverSilicon:       0.6054160
 
 EagleCooling:
-  filename:                /cosma5/data/Eagle/BG_Tables/CoolingTables/
+  filename:                ./coolingtables/
   reionisation_redshift:   11.5
-  he_reion_z_center:       3.5
-  he_reion_z_sigma:        0.5
-  he_reion_ev_pH:          2.0
+  He_reion_z_centre:       3.5
+  He_reion_z_sigma:        0.5
+  He_reion_ev_pH:          2.0
 
diff --git a/examples/EAGLE_50/eagle_50.yml b/examples/EAGLE_50/eagle_50.yml
index b86a3d87ddc5561002a5dc3adf2e82d47fb1b02f..87bef197cd357116d4b67015c5f58774c23d36b7 100644
--- a/examples/EAGLE_50/eagle_50.yml
+++ b/examples/EAGLE_50/eagle_50.yml
@@ -74,8 +74,8 @@ EAGLEChemistry: 	    # Solar abundances
   SulphurOverSilicon:       0.6054160
 
 EagleCooling:
-  filename:		   /cosma5/data/Eagle/BG_Tables/CoolingTables/
+  filename:                ./coolingtables/
   reionisation_redshift:   11.5
-  he_reion_z_center:       3.5
-  he_reion_z_sigma:        0.5
-  he_reion_ev_pH:          2.0
+  He_reion_z_centre:       3.5
+  He_reion_z_sigma:        0.5
+  He_reion_ev_pH:          2.0
\ No newline at end of file
diff --git a/examples/EAGLE_6/eagle_6.yml b/examples/EAGLE_6/eagle_6.yml
index 494f48b833f124ffcdc816f170b6b077c4c59857..95a5d3398f13d42700963a0abbae118083662440 100644
--- a/examples/EAGLE_6/eagle_6.yml
+++ b/examples/EAGLE_6/eagle_6.yml
@@ -85,8 +85,8 @@ EAGLEChemistry: 	    # Solar abundances
   SulphurOverSilicon:       0.6054160
 
 EagleCooling:
-  filename:                /cosma5/data/Eagle/BG_Tables/CoolingTables/
+  filename:                ./coolingtables/
   reionisation_redshift:   11.5
-  he_reion_z_center:       3.5
-  he_reion_z_sigma:        0.5
-  he_reion_ev_pH:          2.0
+  He_reion_z_centre:       3.5
+  He_reion_z_sigma:        0.5
+  He_reion_ev_pH:          2.0
\ No newline at end of file
diff --git a/examples/Makefile.am b/examples/Makefile.am
index fae4e132cdf1ac24f293060d6e3a293729d109f4..e6f014ca7bc596af803af2d6963b32c02915dd93 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -20,13 +20,13 @@ MYFLAGS =
 
 # Add the source directory and the non-standard paths to the included library headers to CFLAGS
 AM_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/argparse $(HDF5_CPPFLAGS) \
-	$(GSL_INCS) $(FFTW_INCS) $(GRACKLE_INCS)
+	$(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(GRACKLE_INCS)
 
 AM_LDFLAGS = $(HDF5_LDFLAGS)
 
 # Extra libraries.
-EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) \
-	$(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) \
+EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) \
+	$(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) \
 	$(VELOCIRAPTOR_LIBS) $(GSL_LIBS)
 
 # MPI libraries.
diff --git a/examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml b/examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml
index 8ad9ae074f4d6c3f00ad95ec5dfb11255045d01a..969626d97bf98334dd87cb9dc6862b77795b1643 100644
--- a/examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml
+++ b/examples/SmallCosmoVolume_cooling/small_cosmo_volume.yml
@@ -66,10 +66,9 @@ LambdaCooling:
 EagleCooling:
   filename:                ./coolingtables/
   reionisation_redshift:   8.5
-  he_reion:                1
-  he_reion_z_center:       3.5
-  he_reion_z_sigma:        0.5
-  he_reion_ev_pH:          2.0
+  He_reion_z_centre:       3.5
+  He_reion_z_sigma:        0.5
+  He_reion_ev_pH:          2.0
 
 # Impose primoridal metallicity
 EAGLEChemistry:
diff --git a/examples/main.c b/examples/main.c
index 93074a637e7427ac7bec53c51d7e6e608b6cdff2..db7df3b4a129b5469758485c7a042af173e63123 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -52,7 +52,7 @@
 /* Global profiler. */
 struct profiler prof;
 
-//  Usage string.
+/*  Usage string. */
 static const char *const swift_usage[] = {
     "swift [options] [[--] param-file]",
     "swift [options] param-file",
@@ -61,7 +61,7 @@ static const char *const swift_usage[] = {
     NULL,
 };
 
-// Function to handle multiple -P arguments.
+/* Function to handle multiple -P arguments. */
 struct cmdparams {
   const char *param[PARSER_MAX_NO_OF_PARAMS];
   int nparam;
@@ -97,7 +97,6 @@ int main(int argc, char *argv[]) {
   struct stars_props stars_properties;
   struct part *parts = NULL;
   struct phys_const prog_const;
-  struct sourceterms sourceterms;
   struct space s;
   struct spart *sparts = NULL;
   struct unit_system us;
@@ -147,11 +146,11 @@ int main(int argc, char *argv[]) {
   int restart = 0;
   int with_cosmology = 0;
   int with_external_gravity = 0;
-  int with_sourceterms = 0;
   int with_cooling = 0;
   int with_self_gravity = 0;
   int with_hydro = 0;
   int with_stars = 0;
+  int with_star_formation = 0;
   int with_feedback = 0;
   int with_fp_exceptions = 0;
   int with_drift_all = 0;
@@ -186,7 +185,8 @@ int main(int argc, char *argv[]) {
                   "particles. This emulates Gadget-[23] and GIZMO's default "
                   "behaviours.",
                   NULL, 0, 0),
-      OPT_BOOLEAN('F', "sourceterms", &with_sourceterms, "", NULL, 0, 0),
+      OPT_BOOLEAN('F', "star-formation", &with_star_formation,
+                  "Run with star formation", NULL, 0, 0),
       OPT_BOOLEAN('g', "external-gravity", &with_external_gravity,
                   "Run with an external gravitational potential.", NULL, 0, 0),
       OPT_BOOLEAN('G', "self-gravity", &with_self_gravity,
@@ -449,10 +449,9 @@ int main(int argc, char *argv[]) {
 #ifdef WITH_MPI
   if (with_mpole_reconstruction && nr_nodes > 1)
     error("Cannot reconstruct m-poles every step over MPI (yet).");
-#endif
-
-#ifdef WITH_MPI
   if (with_feedback) error("Can't run with feedback over MPI (yet).");
+  if (with_star_formation)
+    error("Can't run with star formation over MPI (yet)");
 #endif
 
 #if defined(WITH_MPI) && defined(HAVE_VELOCIRAPTOR)
@@ -779,7 +778,7 @@ int main(int argc, char *argv[]) {
     if (myrank == 0) clocks_gettime(&tic);
     space_init(&s, params, &cosmo, dim, parts, gparts, sparts, Ngas, Ngpart,
                Nspart, periodic, replicate, generate_gas_in_ics, with_hydro,
-               with_self_gravity, talking, dry_run);
+               with_self_gravity, with_star_formation, talking, dry_run);
 
     if (myrank == 0) {
       clocks_gettime(&toc);
@@ -860,10 +859,6 @@ int main(int argc, char *argv[]) {
     chemistry_init(params, &us, &prog_const, &chemistry);
     if (myrank == 0) chemistry_print(&chemistry);
 
-    /* Initialise the feedback properties */
-    if (with_sourceterms) sourceterms_init(params, &us, &sourceterms);
-    if (with_sourceterms && myrank == 0) sourceterms_print(&sourceterms);
-
     /* Construct the engine policy */
     int engine_policies = ENGINE_POLICY | engine_policy_steal;
     if (with_drift_all) engine_policies |= engine_policy_drift_all;
@@ -875,21 +870,18 @@ int main(int argc, char *argv[]) {
       engine_policies |= engine_policy_external_gravity;
     if (with_cosmology) engine_policies |= engine_policy_cosmology;
     if (with_cooling) engine_policies |= engine_policy_cooling;
-    if (with_sourceterms) engine_policies |= engine_policy_sourceterms;
     if (with_stars) engine_policies |= engine_policy_stars;
+    if (with_star_formation) engine_policies |= engine_policy_star_formation;
     if (with_feedback) engine_policies |= engine_policy_feedback;
     if (with_structure_finding)
       engine_policies |= engine_policy_structure_finding;
 
-    // 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],
                 engine_policies, talking, &reparttype, &us, &prog_const, &cosmo,
                 &hydro_properties, &gravity_properties, &stars_properties,
-                &mesh, &potential, &cooling_func, &chemistry, &sourceterms);
+                &mesh, &potential, &cooling_func, &chemistry);
     engine_config(0, &e, params, nr_nodes, myrank, nr_threads, with_aff,
                   talking, restart_file);
 
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index 6adccf2963dbeff67755bdac946e7bfb10d4a897..6258782ab802ae85d783543bdeedf34f538333cf 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -63,6 +63,9 @@ Scheduler:
   cell_sub_size_self_stars:  32000     # (Optional) Maximal number of interactions per sub-self stars task  (this is the default value).
   cell_split_size:           400       # (Optional) Maximal number of particles per cell (this is the default value).
   cell_subdepth_diff_grav:   4         # (Optional) Maximal depth difference between leaves and a cell that gravity tasks can be pushed down to (this is the default value).
+  cell_extra_parts:          0         # (Optional) Number of spare parts per top-level allocated at rebuild time for on-the-fly creation.
+  cell_extra_gparts:         0         # (Optional) Number of spare gparts per top-level allocated at rebuild time for on-the-fly creation.
+  cell_extra_sparts:         400       # (Optional) Number of spare sparts per top-level allocated at rebuild time for on-the-fly creation.
   max_top_level_cells:       12        # (Optional) Maximal number of top-level cells in any dimension. The number of top-level cells will be the cube of this (this is the default value).
   tasks_per_cell:            0         # (Optional) The average number of tasks per cell. If not large enough the simulation will fail (means guess...).
   mpi_message_limit:         4096      # (Optional) Maximum MPI task message size to send non-buffered, KB.
@@ -245,6 +248,13 @@ LambdaCooling:
   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.
 
+EagleCooling:
+  filename:                ./coolingtables/  # Location of the Wiersma+08 cooling tables
+  reionisation_redshift:   11.5              # Redshift of Hydrogen re-ionization
+  He_reion_z_centre:       3.5               # Redshift of the centre of the Helium re-ionization Gaussian
+  He_reion_z_sigma:        0.5               # Spread in redshift of the  Helium re-ionization Gaussian
+  He_reion_ev_pH:          2.0               # Energy inject by Helium re-ionization in electron-volt per Hydrogen atom
+  
 # Cooling with Grackle 3.0
 GrackleCooling:
   CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
diff --git a/src/Makefile.am b/src/Makefile.am
index 78531f1f03205374231b78df4de4bc697fed3178..9bf6c9bc3a51880e12ed18f59ea68305b5d124da 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) $(GRACKLE_INCS)
+AM_CFLAGS = $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_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_LIBS) $(GSL_LIBS)
+EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS)
 
 # MPI libraries.
 MPI_LIBS = $(PARMETIS_LIBS) $(METIS_LIBS) $(MPI_THREAD_LIBS)
@@ -44,11 +44,11 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \
     common_io.h single_io.h multipole.h map.h tools.h partition.h clocks.h parser.h \
     physical_constants.h physical_constants_cgs.h potential.h version.h \
     hydro_properties.h riemann.h threadpool.h cooling_io.h cooling.h cooling_struct.h \
-    sourceterms.h sourceterms_struct.h statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h \
+    statistics.h memswap.h cache.h runner_doiact_vec.h profiler.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 \
+    mesh_gravity.h cbrt.h exp10.h velociraptor_interface.h swift_velociraptor_part.h outputlist.h \
     logger_io.h
 
 # source files for EAGLE cooling
@@ -63,7 +63,7 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c engine_maketasks.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 \
-    threadpool.c cooling.c sourceterms.c \
+    threadpool.c cooling.c \
     statistics.c runner_doiact_vec.c profiler.c dump.c logger.c \
     part_type.c xmf.c gravity_properties.c gravity.c \
     collectgroup.c hydro_space.c equation_of_state.c \
@@ -81,7 +81,6 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h
 		 gravity/Default/gravity_debug.h gravity/Default/gravity_part.h  \
 		 gravity/Potential/gravity.h gravity/Potential/gravity_iact.h gravity/Potential/gravity_io.h \
 		 gravity/Potential/gravity_debug.h gravity/Potential/gravity_part.h  \
-		 sourceterms.h \
 		 equation_of_state.h \
 		 equation_of_state/ideal_gas/equation_of_state.h equation_of_state/isothermal/equation_of_state.h \
 	 	 hydro.h hydro_io.h \
diff --git a/src/cell.c b/src/cell.c
index 3fe5e21e7c888f2358395a27e13710db460fd74c..ed54163a008f7fad8294f50458ef8d2004be832d 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -972,6 +972,7 @@ 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]->hydro.count = bucket_count[k];
+    c->progeny[k]->hydro.count_total = c->progeny[k]->hydro.count;
     c->progeny[k]->hydro.parts = &c->hydro.parts[bucket_offset[k]];
     c->progeny[k]->hydro.xparts = &c->hydro.xparts[bucket_offset[k]];
   }
@@ -1089,6 +1090,7 @@ 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]->stars.count = bucket_count[k];
+    c->progeny[k]->stars.count_total = c->progeny[k]->stars.count;
     c->progeny[k]->stars.parts = &c->stars.parts[bucket_offset[k]];
   }
 
@@ -1151,6 +1153,7 @@ 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]->grav.count = bucket_count[k];
+    c->progeny[k]->grav.count_total = c->progeny[k]->grav.count;
     c->progeny[k]->grav.parts = &c->grav.parts[bucket_offset[k]];
   }
 }
@@ -3813,6 +3816,190 @@ void cell_check_timesteps(struct cell *c) {
 #endif
 }
 
+void cell_check_spart_pos(const struct cell *c,
+                          const struct spart *global_sparts) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+
+  /* Recurse */
+  if (c->split) {
+    for (int k = 0; k < 8; ++k)
+      if (c->progeny[k] != NULL)
+        cell_check_spart_pos(c->progeny[k], global_sparts);
+  }
+
+  struct spart *sparts = c->stars.parts;
+  const int count = c->stars.count;
+  for (int i = 0; i < count; ++i) {
+
+    const struct spart *sp = &sparts[i];
+    if ((sp->x[0] < c->loc[0] / space_stretch) ||
+        (sp->x[1] < c->loc[1] / space_stretch) ||
+        (sp->x[2] < c->loc[2] / space_stretch) ||
+        (sp->x[0] >= (c->loc[0] + c->width[0]) * space_stretch) ||
+        (sp->x[1] >= (c->loc[1] + c->width[1]) * space_stretch) ||
+        (sp->x[2] >= (c->loc[2] + c->width[2]) * space_stretch))
+      error("spart not in its cell!");
+
+    if (sp->time_bin != time_bin_not_created &&
+        sp->time_bin != time_bin_inhibited) {
+
+      const struct gpart *gp = sp->gpart;
+      if (gp == NULL && sp->time_bin != time_bin_not_created)
+        error("Unlinked spart!");
+
+      if (&global_sparts[-gp->id_or_neg_offset] != sp)
+        error("Incorrectly linked spart!");
+    }
+  }
+
+#else
+  error("Calling a degugging function outside debugging mode.");
+#endif
+}
+
+/**
+ * @brief Recursively update the pointer and counter for #spart after the
+ * addition of a new particle.
+ *
+ * @param c The cell we are working on.
+ * @param progeny_list The list of the progeny index at each level for the
+ * leaf-cell where the particle was added.
+ * @param main_branch Are we in a cell directly above the leaf where the new
+ * particle was added?
+ */
+void cell_recursively_shift_sparts(struct cell *c,
+                                   const int progeny_list[space_cell_maxdepth],
+                                   const int main_branch) {
+  if (c->split) {
+
+    /* No need to recurse in progenies located before the insestion point */
+    const int first_progeny = main_branch ? progeny_list[(int)c->depth] : 0;
+
+    for (int k = first_progeny; k < 8; ++k) {
+
+      if (c->progeny[k] != NULL)
+        cell_recursively_shift_sparts(c->progeny[k], progeny_list,
+                                      main_branch && (k == first_progeny));
+    }
+  }
+
+  /* When directly above the leaf with the new particle: increase the particle
+   * count */
+  /* When after the leaf with the new particle: shift by one position */
+  if (main_branch)
+    c->stars.count++;
+  else
+    c->stars.parts++;
+}
+
+/**
+ * @brief "Add" a #spart in a given #cell.
+ *
+ * This function will a a #spart at the start of the current cell's array by
+ * shifting all the #spart in the top-level cell by one position. All the
+ * pointers and cell counts are updated accordingly.
+ *
+ * @param e The #engine.
+ * @param c The leaf-cell in which to add the #spart.
+ *
+ * @return A pointer to the newly added #spart. The spart has a been zeroed and
+ * given a position within the cell as well as set to the minimal active time
+ * bin.
+ */
+struct spart *cell_add_spart(struct engine *e, struct cell *const c) {
+
+  /* Perform some basic consitency checks */
+  if (c->nodeID != engine_rank) error("Adding spart on a foreign node");
+  if (c->grav.ti_old_part != e->ti_current) error("Undrifted cell!");
+  if (c->split) error("Addition of spart performed above the leaf level");
+
+  /* Progeny number at each level */
+  int progeny[space_cell_maxdepth];
+#ifdef SWIFT_DEBUG_CHECKS
+  for (int i = 0; i < space_cell_maxdepth; ++i) progeny[i] = -1;
+#endif
+
+  /* Get the top-level this leaf cell is in and compute the progeny indices at
+     each level */
+  struct cell *top = c;
+  while (top->parent != NULL) {
+    for (int k = 0; k < 8; ++k) {
+      if (top->parent->progeny[k] == top) {
+        progeny[(int)top->parent->depth] = k;
+      }
+    }
+    top = top->parent;
+  }
+
+  /* Are there any extra particles left? */
+  if (top->stars.count == top->stars.count_total - 1) {
+    message("We ran out of star particles!");
+    atomic_inc(&e->forcerebuild);
+    return NULL;
+  }
+
+  /* Number of particles to shift in order to get a free space. */
+  const size_t n_copy = &top->stars.parts[top->stars.count] - c->stars.parts;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (c->stars.parts + n_copy > top->stars.parts + top->stars.count)
+    error("Copying beyond the allowed range");
+#endif
+
+  if (n_copy > 0) {
+
+    // MATTHIEU: This can be improved. We don't need to copy everything, just
+    // need to swap a few particles.
+    memmove(&c->stars.parts[1], &c->stars.parts[0],
+            n_copy * sizeof(struct spart));
+
+    /* Update the gpart->spart links (shift by 1) */
+    for (size_t i = 0; i < n_copy; ++i) {
+#ifdef SWIFT_DEBUG_CHECKS
+      if (c->stars.parts[i + 1].gpart == NULL) {
+        error("Incorrectly linked spart!");
+      }
+#endif
+      c->stars.parts[i + 1].gpart->id_or_neg_offset--;
+    }
+  }
+
+  /* Recursively shift all the stars to get a free spot at the start of the
+   * current cell*/
+  cell_recursively_shift_sparts(top, progeny, /* main_branch=*/1);
+
+  /* We now have an empty spart as the first particle in that cell */
+  struct spart *sp = &c->stars.parts[0];
+  bzero(sp, sizeof(struct spart));
+
+  /* Give it a decent position */
+  sp->x[0] = c->loc[0] + 0.5 * c->width[0];
+  sp->x[1] = c->loc[1] + 0.5 * c->width[1];
+  sp->x[2] = c->loc[2] + 0.5 * c->width[2];
+
+  /* Set it to the current time-bin */
+  sp->time_bin = e->min_active_bin;
+
+  top = c;
+  while (top->parent != NULL) {
+    top->grav.ti_end_min = e->ti_current;
+    top = top->parent;
+  }
+  top->grav.ti_end_min = e->ti_current;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Specify it was drifted to this point */
+  sp->ti_drift = e->ti_current;
+#endif
+
+  /* Register that we used one of the free slots. */
+  const size_t one = 1;
+  atomic_sub(&e->s->nr_extra_sparts, one);
+
+  return sp;
+}
+
 /**
  * @brief "Remove" a gas particle from the calculation.
  *
@@ -3899,15 +4086,21 @@ void cell_remove_spart(const struct engine *e, struct cell *c,
  * @brief "Remove" a gas particle from the calculation and convert its gpart
  * friend to a dark matter particle.
  *
+ * Note that the #part is not destroyed. The pointer is still valid
+ * after this call and the properties of the #part are not altered
+ * apart from the time-bin and #gpart pointer.
  * 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.
+ *
+ * @return Pointer to the #gpart the #part has become. It carries the
+ * ID of the #part and has a dark matter type.
  */
-void cell_convert_part_to_gpart(const struct engine *e, struct cell *c,
-                                struct part *p, struct xpart *xp) {
+struct gpart *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)
@@ -3932,20 +4125,28 @@ void cell_convert_part_to_gpart(const struct engine *e, struct cell *c,
 #ifdef SWIFT_DEBUG_CHECKS
   gp->ti_kick = p->ti_kick;
 #endif
+
+  return gp;
 }
 
 /**
  * @brief "Remove" a spart particle from the calculation and convert its gpart
  * friend to a dark matter particle.
  *
+ * Note that the #spart is not destroyed. The pointer is still valid
+ * after this call and the properties of the #spart are not altered
+ * apart from the time-bin and #gpart pointer.
  * 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.
+ *
+ * @return Pointer to the #gpart the #spart has become. It carries the
+ * ID of the #spart and has a dark matter type.
  */
-void cell_convert_spart_to_gpart(const struct engine *e, struct cell *c,
-                                 struct spart *sp) {
+struct gpart *cell_convert_spart_to_gpart(const struct engine *e,
+                                          struct cell *c, struct spart *sp) {
 
   /* Quick cross-check */
   if (c->nodeID != e->nodeID)
@@ -3970,6 +4171,210 @@ void cell_convert_spart_to_gpart(const struct engine *e, struct cell *c,
 #ifdef SWIFT_DEBUG_CHECKS
   gp->ti_kick = sp->ti_kick;
 #endif
+
+  return gp;
+}
+
+/**
+ * @brief "Remove" a #part from a #cell and replace it with a #spart
+ * connected to the same #gpart.
+ *
+ * Note that the #part is not destroyed. The pointer is still valid
+ * after this call and the properties of the #part are not altered
+ * apart from the time-bin and #gpart pointer.
+ * The particle is inhibited and will officially be removed at the next rebuild.
+ *
+ * @param e The #engine.
+ * @param c The #cell from which to remove the #part.
+ * @param p The #part to remove (must be inside c).
+ * @param xp The extended data of the #part.
+ *
+ * @return A fresh #spart with the same ID, position, velocity and
+ * time-bin as the original #part.
+ */
+struct spart *cell_convert_part_to_spart(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.");
+
+  if (p->gpart == NULL)
+    error("Trying to convert part without gpart friend to star!");
+
+  /* Create a fresh (empty) spart */
+  struct spart *sp = cell_add_spart(e, c);
+
+  /* Did we run out of free spart slots? */
+  if (sp == NULL) return NULL;
+
+  /* Destroy the gas particle and get it's gpart friend */
+  struct gpart *gp = cell_convert_part_to_gpart(e, c, p, xp);
+
+  /* Assign the ID back */
+  sp->id = gp->id_or_neg_offset;
+  gp->type = swift_type_stars;
+
+  /* Re-link things */
+  sp->gpart = gp;
+  gp->id_or_neg_offset = -(sp - e->s->sparts);
+
+  /* Synchronize clocks */
+  gp->time_bin = sp->time_bin;
+
+  /* Synchronize masses, positions and velocities */
+  sp->mass = gp->mass;
+  sp->x[0] = gp->x[0];
+  sp->x[1] = gp->x[1];
+  sp->x[2] = gp->x[2];
+  sp->v[0] = gp->v_full[0];
+  sp->v[1] = gp->v_full[1];
+  sp->v[2] = gp->v_full[2];
+
+#ifdef SWIFT_DEBUG_CHECKS
+  sp->ti_kick = gp->ti_kick;
+  gp->ti_drift = sp->ti_drift;
+#endif
+
+  /* Set a smoothing length */
+  sp->h = max(c->stars.h_max, c->hydro.h_max);
+
+  /* Here comes the Sun! */
+  return sp;
+}
+
+/**
+ * @brief Re-arrange the #part in a top-level cell such that all the extra ones
+ * for on-the-fly creation are located at the end of the array.
+ *
+ * @param c The #cell to sort.
+ * @param parts_offset The offset between the first #part in the array and the
+ * first #part in the global array in the space structure (for re-linking).
+ */
+void cell_reorder_extra_parts(struct cell *c, const ptrdiff_t parts_offset) {
+
+  struct part *parts = c->hydro.parts;
+  struct xpart *xparts = c->hydro.xparts;
+  const int count_real = c->hydro.count;
+
+  if (c->depth != 0 || c->nodeID != engine_rank)
+    error("This function should only be called on local top-level cells!");
+
+  int first_not_extra = count_real;
+
+  /* Find extra particles */
+  for (int i = 0; i < count_real; ++i) {
+    if (parts[i].time_bin == time_bin_not_created) {
+
+      /* Find the first non-extra particle after the end of the
+         real particles */
+      while (parts[first_not_extra].time_bin == time_bin_not_created) {
+        ++first_not_extra;
+      }
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (first_not_extra >= count_real + space_extra_parts)
+        error("Looking for extra particles beyond this cell's range!");
+#endif
+
+      /* Swap everything, including g-part pointer */
+      memswap(&parts[i], &parts[first_not_extra], sizeof(struct part));
+      memswap(&xparts[i], &xparts[first_not_extra], sizeof(struct xpart));
+      if (parts[i].gpart)
+        parts[i].gpart->id_or_neg_offset = -(i + parts_offset);
+    }
+  }
+}
+
+/**
+ * @brief Re-arrange the #spart in a top-level cell such that all the extra ones
+ * for on-the-fly creation are located at the end of the array.
+ *
+ * @param c The #cell to sort.
+ * @param sparts_offset The offset between the first #spart in the array and the
+ * first #spart in the global array in the space structure (for re-linking).
+ */
+void cell_reorder_extra_sparts(struct cell *c, const ptrdiff_t sparts_offset) {
+
+  struct spart *sparts = c->stars.parts;
+  const int count_real = c->stars.count;
+
+  if (c->depth != 0 || c->nodeID != engine_rank)
+    error("This function should only be called on local top-level cells!");
+
+  int first_not_extra = count_real;
+
+  /* Find extra particles */
+  for (int i = 0; i < count_real; ++i) {
+    if (sparts[i].time_bin == time_bin_not_created) {
+
+      /* Find the first non-extra particle after the end of the
+         real particles */
+      while (sparts[first_not_extra].time_bin == time_bin_not_created) {
+        ++first_not_extra;
+      }
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (first_not_extra >= count_real + space_extra_sparts)
+        error("Looking for extra particles beyond this cell's range!");
+#endif
+
+      /* Swap everything, including g-part pointer */
+      memswap(&sparts[i], &sparts[first_not_extra], sizeof(struct spart));
+      if (sparts[i].gpart)
+        sparts[i].gpart->id_or_neg_offset = -(i + sparts_offset);
+      sparts[first_not_extra].gpart = NULL;
+#ifdef SWIFT_DEBUG_CHECKS
+      if (sparts[first_not_extra].time_bin != time_bin_not_created)
+        error("Incorrect swap occured!");
+#endif
+    }
+  }
+}
+
+/**
+ * @brief Re-arrange the #gpart in a top-level cell such that all the extra ones
+ * for on-the-fly creation are located at the end of the array.
+ *
+ * @param c The #cell to sort.
+ * @param parts The global array of #part (for re-linking).
+ * @param sparts The global array of #spart (for re-linking).
+ */
+void cell_reorder_extra_gparts(struct cell *c, struct part *parts,
+                               struct spart *sparts) {
+
+  struct gpart *gparts = c->grav.parts;
+  const int count_real = c->grav.count;
+
+  if (c->depth != 0 || c->nodeID != engine_rank)
+    error("This function should only be called on local top-level cells!");
+
+  int first_not_extra = count_real;
+
+  /* Find extra particles */
+  for (int i = 0; i < count_real; ++i) {
+    if (gparts[i].time_bin == time_bin_not_created) {
+
+      /* Find the first non-extra particle after the end of the
+         real particles */
+      while (gparts[first_not_extra].time_bin == time_bin_not_created) {
+        ++first_not_extra;
+      }
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (first_not_extra >= count_real + space_extra_gparts)
+        error("Looking for extra particles beyond this cell's range!");
+#endif
+
+      /* Swap everything (including pointers) */
+      memswap(&gparts[i], &gparts[first_not_extra], sizeof(struct gpart));
+      if (gparts[i].type == swift_type_gas) {
+        parts[-gparts[i].id_or_neg_offset].gpart = &gparts[i];
+      } else if (gparts[i].type == swift_type_stars) {
+        sparts[-gparts[i].id_or_neg_offset].gpart = &gparts[i];
+      }
+    }
+  }
 }
 
 /**
diff --git a/src/cell.h b/src/cell.h
index 97ca22e584c67de20ca0826425f60523b8158ffa..7178698c425c3791223789930c82761d13fc0116 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -220,12 +220,12 @@ struct cell {
   /*! The cell dimensions. */
   double width[3];
 
-  /*! Linking pointer for "memory management". */
-  struct cell *next;
-
   /*! Pointers to the next level of cells. */
   struct cell *progeny[8];
 
+  /*! Linking pointer for "memory management". */
+  struct cell *next;
+
   /*! Parent cell. */
   struct cell *parent;
 
@@ -248,18 +248,45 @@ struct cell {
      * pair/self tasks */
     struct cell *super;
 
-    /*! Last (integer) time the cell's part were drifted forward in time. */
-    integertime_t ti_old_part;
+    /*! The task computing this cell's sorts. */
+    struct task *sorts;
 
-    /*! Maximum part movement in this cell since last construction. */
-    float dx_max_part;
+    /*! The drift task for parts */
+    struct task *drift;
 
-    /*! Maximum particle movement in this cell since the last sort. */
-    float dx_max_sort;
+    /*! Linked list of the tasks computing this cell's hydro density. */
+    struct link *density;
+
+    /* Linked list of the tasks computing this cell's hydro gradients. */
+    struct link *gradient;
+
+    /*! Linked list of the tasks computing this cell's hydro forces. */
+    struct link *force;
+
+    /*! Dependency implicit task for the ghost  (in->ghost->out)*/
+    struct task *ghost_in;
+
+    /*! Dependency implicit task for the ghost  (in->ghost->out)*/
+    struct task *ghost_out;
+
+    /*! The ghost task itself */
+    struct task *ghost;
+
+    /*! The extra ghost task for complex hydro schemes */
+    struct task *extra_ghost;
+
+    /*! Task for cooling */
+    struct task *cooling;
+
+    /*! Task for star formation */
+    struct task *star_formation;
 
     /*! Max smoothing length in this cell. */
     double h_max;
 
+    /*! Last (integer) time the cell's part were drifted forward in time. */
+    integertime_t ti_old_part;
+
     /*! Minimum end of (integer) time step in this cell for hydro tasks. */
     integertime_t ti_end_min;
 
@@ -270,20 +297,14 @@ struct cell {
      */
     integertime_t ti_beg_max;
 
-    /*! Nr of #part in this cell. */
-    int count;
-
     /*! Spin lock for various uses (#part case). */
     swift_lock_type lock;
 
-    /*! Number of #part updated in this cell. */
-    int updated;
-
-    /*! Number of #part inhibited in this cell. */
-    int inhibited;
+    /*! Maximum part movement in this cell since last construction. */
+    float dx_max_part;
 
-    /*! Is the #part data of this cell being used in a sub-cell? */
-    int hold;
+    /*! Maximum particle movement in this cell since the last sort. */
+    float dx_max_sort;
 
     /*! Values of h_max before the drifts, used for sub-cell tasks. */
     float h_max_old;
@@ -294,12 +315,30 @@ struct cell {
     /*! Values of dx_max_sort before the drifts, used for sub-cell tasks. */
     float dx_max_sort_old;
 
+    /*! Nr of #part in this cell. */
+    int count;
+
+    /*! Nr of #part this cell can hold after addition of new #part. */
+    int count_total;
+
+    /*! Number of #part updated in this cell. */
+    int updated;
+
+    /*! Number of #part inhibited in this cell. */
+    int inhibited;
+
+    /*! Is the #part data of this cell being used in a sub-cell? */
+    int hold;
+
     /*! Bit mask of sort directions that will be needed in the next timestep. */
     unsigned int requires_sorts;
 
     /*! Bit mask of sorts that need to be computed for this cell. */
     unsigned int do_sort;
 
+    /*! Bit-mask indicating the sorted directions */
+    unsigned int sorted;
+
     /*! Does this cell need to be drifted (hydro)? */
     char do_drift;
 
@@ -309,42 +348,6 @@ struct cell {
     /*! Do any of this cell's sub-cells need to be sorted? */
     char do_sub_sort;
 
-    /*! Bit-mask indicating the sorted directions */
-    unsigned int sorted;
-
-    /*! The task computing this cell's sorts. */
-    struct task *sorts;
-
-    /*! The drift task for parts */
-    struct task *drift;
-
-    /*! Linked list of the tasks computing this cell's hydro density. */
-    struct link *density;
-
-    /* Linked list of the tasks computing this cell's hydro gradients. */
-    struct link *gradient;
-
-    /*! Linked list of the tasks computing this cell's hydro forces. */
-    struct link *force;
-
-    /*! Dependency implicit task for the ghost  (in->ghost->out)*/
-    struct task *ghost_in;
-
-    /*! Dependency implicit task for the ghost  (in->ghost->out)*/
-    struct task *ghost_out;
-
-    /*! The ghost task itself */
-    struct task *ghost;
-
-    /*! The extra ghost task for complex hydro schemes */
-    struct task *extra_ghost;
-
-    /*! Task for cooling */
-    struct task *cooling;
-
-    /*! Task for star formation */
-    struct task *star_formation;
-
 #ifdef SWIFT_DEBUG_CHECKS
 
     /*! Last (integer) time the cell's sort arrays were updated. */
@@ -367,6 +370,36 @@ struct cell {
      * tasks */
     struct cell *super;
 
+    /*! The drift task for gparts */
+    struct task *drift;
+
+    /*! Implicit task (going up- and down the tree) for the #gpart drifts */
+    struct task *drift_out;
+
+    /*! Linked list of the tasks computing this cell's gravity forces. */
+    struct link *grav;
+
+    /*! Linked list of the tasks computing this cell's gravity M-M forces. */
+    struct link *mm;
+
+    /*! The multipole initialistation task */
+    struct task *init;
+
+    /*! Implicit task for the gravity initialisation */
+    struct task *init_out;
+
+    /*! Task computing long range non-periodic gravity interactions */
+    struct task *long_range;
+
+    /*! Implicit task for the down propagation */
+    struct task *down_in;
+
+    /*! Task propagating the mesh forces to the particles */
+    struct task *mesh;
+
+    /*! Task propagating the multipole to the particles */
+    struct task *down;
+
     /*! Minimum end of (integer) time step in this cell for gravity tasks. */
     integertime_t ti_end_min;
 
@@ -383,15 +416,18 @@ struct cell {
     /*! Last (integer) time the cell's multipole was drifted forward in time. */
     integertime_t ti_old_multipole;
 
-    /*! Nr of #gpart in this cell. */
-    int count;
-
     /*! Spin lock for various uses (#gpart case). */
     swift_lock_type plock;
 
     /*! Spin lock for various uses (#multipole case). */
     swift_lock_type mlock;
 
+    /*! Nr of #gpart in this cell. */
+    int count;
+
+    /*! Nr of #gpart this cell can hold after addition of new #gpart. */
+    int count_total;
+
     /*! Number of #gpart updated in this cell. */
     int updated;
 
@@ -404,58 +440,49 @@ struct cell {
     /*! Is the #multipole data of this cell being used in a sub-cell? */
     int mhold;
 
+    /*! Number of M-M tasks that are associated with this cell. */
+    short int nr_mm_tasks;
+
     /*! Does this cell need to be drifted (gravity)? */
     char do_drift;
 
     /*! Do any of this cell's sub-cells need to be drifted (gravity)? */
     char do_sub_drift;
 
-    /*! The drift task for gparts */
-    struct task *drift;
-
-    /*! Implicit task (going up- and down the tree) for the #gpart drifts */
-    struct task *drift_out;
-
-    /*! Linked list of the tasks computing this cell's gravity forces. */
-    struct link *grav;
-
-    /*! Linked list of the tasks computing this cell's gravity M-M forces. */
-    struct link *mm;
-
-    /*! The multipole initialistation task */
-    struct task *init;
+  } grav;
 
-    /*! Implicit task for the gravity initialisation */
-    struct task *init_out;
+  /*! Stars variables */
+  struct {
 
-    /*! Task computing long range non-periodic gravity interactions */
-    struct task *long_range;
+    /*! Pointer to the #spart data. */
+    struct spart *parts;
 
-    /*! Implicit task for the down propagation */
-    struct task *down_in;
+    /*! Dependency implicit task for the star ghost  (in->ghost->out)*/
+    struct task *ghost_in;
 
-    /*! Task propagating the mesh forces to the particles */
-    struct task *mesh;
+    /*! Dependency implicit task for the star ghost  (in->ghost->out)*/
+    struct task *ghost_out;
 
-    /*! Task propagating the multipole to the particles */
-    struct task *down;
+    /*! The star ghost task itself */
+    struct task *ghost;
 
-    /*! Number of M-M tasks that are associated with this cell. */
-    short int nr_mm_tasks;
+    /*! Linked list of the tasks computing this cell's star density. */
+    struct link *density;
 
-  } grav;
+    /*! The task computing this cell's sorts. */
+    struct task *sorts;
 
-  /*! Stars variables */
-  struct {
+    /*! Max smoothing length in this cell. */
+    double h_max;
 
-    /*! Pointer to the #spart data. */
-    struct spart *parts;
+    /*! Spin lock for various uses (#spart case). */
+    swift_lock_type lock;
 
     /*! Nr of #spart in this cell. */
     int count;
 
-    /*! Max smoothing length in this cell. */
-    double h_max;
+    /*! Nr of #spart this cell can hold after addition of new #spart. */
+    int count_total;
 
     /*! Values of h_max before the drifts, used for sub-cell tasks. */
     float h_max_old;
@@ -490,21 +517,6 @@ struct cell {
     /*! Maximum end of (integer) time step in this cell for gravity tasks. */
     integertime_t ti_end_min;
 
-    /*! 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;
-
-    /*! The task computing this cell's sorts. */
-    struct task *sorts;
-
-    /*! Linked list of the tasks computing this cell's star density. */
-    struct link *density;
-
     /*! Number of #spart updated in this cell. */
     int updated;
 
@@ -514,9 +526,6 @@ struct cell {
     /*! 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;
-
 #ifdef SWIFT_DEBUG_CHECKS
     /*! Last (integer) time the cell's sort arrays were updated. */
     integertime_t ti_sort;
@@ -695,6 +704,8 @@ void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s);
 void cell_activate_stars_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);
+void cell_check_spart_pos(const struct cell *c,
+                          const struct spart *global_sparts);
 int cell_has_tasks(struct cell *c);
 void cell_remove_part(const struct engine *e, struct cell *c, struct part *p,
                       struct xpart *xp);
@@ -702,10 +713,17 @@ 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);
+struct spart *cell_add_spart(struct engine *e, struct cell *c);
+struct gpart *cell_convert_part_to_gpart(const struct engine *e, struct cell *c,
+                                         struct part *p, struct xpart *xp);
+struct gpart *cell_convert_spart_to_gpart(const struct engine *e,
+                                          struct cell *c, struct spart *sp);
+struct spart *cell_convert_part_to_spart(struct engine *e, struct cell *c,
+                                         struct part *p, struct xpart *xp);
+void cell_reorder_extra_parts(struct cell *c, const ptrdiff_t parts_offset);
+void cell_reorder_extra_gparts(struct cell *c, struct part *parts,
+                               struct spart *sparts);
+void cell_reorder_extra_sparts(struct cell *c, const ptrdiff_t sparts_offset);
 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,
diff --git a/src/common_io.c b/src/common_io.c
index 087697b489269d97a268966d341093dd666dd9c9..24e74014fd52936023b5c7a41378faf3268bfdb3 100644
--- a/src/common_io.c
+++ b/src/common_io.c
@@ -875,7 +875,8 @@ void io_collect_parts_to_write(const struct part* restrict 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) {
+    if (parts[i].time_bin != time_bin_inhibited &&
+        parts[i].time_bin != time_bin_not_created) {
 
       parts_written[count] = parts[i];
       xparts_written[count] = xparts[i];
@@ -909,7 +910,8 @@ void io_collect_sparts_to_write(const struct spart* restrict sparts,
   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) {
+    if (sparts[i].time_bin != time_bin_inhibited &&
+        sparts[i].time_bin != time_bin_not_created) {
 
       sparts_written[count] = sparts[i];
       count++;
@@ -943,6 +945,7 @@ void io_collect_gparts_to_write(const struct gpart* restrict gparts,
 
     /* And collect the ones that have not been removed */
     if ((gparts[i].time_bin != time_bin_inhibited) &&
+        (gparts[i].time_bin != time_bin_not_created) &&
         (gparts[i].type == swift_type_dark_matter)) {
 
       gparts_written[count] = gparts[i];
diff --git a/src/cooling/Compton/cooling.h b/src/cooling/Compton/cooling.h
index f440cd03455c07d2eeb64c37189aed36efe78e09..77252140d21ca910c0b98c68fbb0e89eea37f6ee 100644
--- a/src/cooling/Compton/cooling.h
+++ b/src/cooling/Compton/cooling.h
@@ -49,61 +49,11 @@ INLINE static void cooling_update(const struct cosmology* cosmo,
   // Add content if required.
 }
 
-/**
- * @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 phys_const The physical constants in internal units.
  * @param hydro_props The properties of the hydro scheme.
  * @param cooling The #cooling_function_data used in the run.
  * @param z The current redshift.
@@ -113,7 +63,8 @@ temperature_from_internal_energy(const double u_cgs,
  * 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 cosmology* cosmo, const struct phys_const* restrict phys_const,
+    const struct hydro_props* hydro_props,
     const struct cooling_function_data* cooling, const double z, const double u,
     const struct part* p) {
 
@@ -129,15 +80,27 @@ __attribute__((always_inline)) INLINE static double Compton_cooling_rate_cgs(
   /* CMB temperature at this redshift */
   const double T_CMB = cooling->const_T_CMB_0 * zp1;
 
+  /* Physical constants */
+  const double m_H = phys_const->const_proton_mass;
+  const double k_B = phys_const->const_boltzmann_k;
+
   /* Gas properties */
-  const double H_mass_fraction = hydro_props->hydrogen_mass_fraction;
   const double T_transition = hydro_props->hydrogen_ionization_temperature;
+  const double mu_neutral = hydro_props->mu_neutral;
+  const double mu_ionised = hydro_props->mu_ionised;
 
-  /* 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.
+  /* Temperature over mean molecular weight */
+  const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B;
+
+  double T;
+
+  /* Are we above or below the HII -> HI transition? */
+  if (T_over_mu > (T_transition + 1.) / mu_ionised)
+    T = T_over_mu * mu_ionised;
+  else if (T_over_mu < (T_transition - 1.) / mu_neutral)
+    T = T_over_mu * mu_neutral;
+  else
+    T = T_transition;
 
   /* Electron abundance */
   double electron_abundance = 0.;  // MATTHIEU: To do: compute X_e
@@ -189,8 +152,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
   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);
+  const double cooling_du_dt_cgs = Compton_cooling_rate_cgs(
+      cosmo, phys_const, hydro_props, cooling, cosmo->z, u_old, p);
 
   /* Convert to internal units */
   float cooling_du_dt =
@@ -273,6 +236,49 @@ __attribute__((always_inline)) INLINE static void cooling_first_init_part(
   xp->cooling_data.radiated_energy = 0.f;
 }
 
+/**
+ * @brief Compute the temperature of a #part based on the cooling function.
+ *
+ * @param phys_const #phys_const data structure.
+ * @param hydro_props The properties of the hydro scheme.
+ * @param us The internal system of units.
+ * @param cosmo #cosmology data structure.
+ * @param cooling #cooling_function_data struct.
+ * @param p #part data.
+ * @param xp Pointer to the #xpart data.
+ */
+INLINE static float cooling_get_temperature(
+    const struct phys_const* restrict phys_const,
+    const struct hydro_props* restrict hydro_props,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, const struct xpart* restrict xp) {
+
+  /* Physical constants */
+  const double m_H = phys_const->const_proton_mass;
+  const double k_B = phys_const->const_boltzmann_k;
+
+  /* Gas properties */
+  const double T_transition = hydro_props->hydrogen_ionization_temperature;
+  const double mu_neutral = hydro_props->mu_neutral;
+  const double mu_ionised = hydro_props->mu_ionised;
+
+  /* Particle temperature */
+  const double u = hydro_get_physical_internal_energy(p, xp, cosmo);
+
+  /* Temperature over mean molecular weight */
+  const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B;
+
+  /* Are we above or below the HII -> HI transition? */
+  if (T_over_mu > (T_transition + 1.) / mu_ionised)
+    return T_over_mu * mu_ionised;
+  else if (T_over_mu < (T_transition - 1.) / mu_neutral)
+    return T_over_mu * mu_neutral;
+  else
+    return T_transition;
+}
+
 /**
  * @brief Returns the total radiated energy by this particle.
  *
diff --git a/src/cooling/Compton/cooling_io.h b/src/cooling/Compton/cooling_io.h
index d020587c920f781450a5183954bc6c429e461512..8fa3944ff78e7592da3978ee9465451c96e1d533 100644
--- a/src/cooling/Compton/cooling_io.h
+++ b/src/cooling/Compton/cooling_io.h
@@ -23,6 +23,7 @@
 #include "../config.h"
 
 /* Local includes */
+#include "cooling.h"
 #include "io_properties.h"
 
 #ifdef HAVE_HDF5
@@ -41,11 +42,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
 }
 #endif
 
+INLINE static void convert_part_T(const struct engine* e, const struct part* p,
+                                  const struct xpart* xp, float* ret) {
+
+  ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties,
+                                   e->internal_units, e->cosmology,
+                                   e->cooling_func, p, xp);
+}
+
 /**
  * @brief Specifies which particle fields to write to a dataset
  *
- * Nothing to write for this scheme.
- *
+ * @param parts The particle array.
  * @param xparts The extended particle array.
  * @param list The list of i/o properties to write.
  * @param cooling The #cooling_function_data
@@ -53,10 +61,14 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
  * @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 part* parts, const struct xpart* xparts, struct io_props* list,
     const struct cooling_function_data* cooling) {
 
-  return 0;
+  list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1,
+                                              UNIT_CONV_TEMPERATURE, parts,
+                                              xparts, convert_part_T);
+
+  return 1;
 }
 
 #endif /* SWIFT_COOLING_IO_COMPTON_H */
diff --git a/src/cooling/EAGLE/cooling.c b/src/cooling/EAGLE/cooling.c
index 8dcef4035f633954906a8539f0e92e1ea4e89ca0..f85b4f43a1d689c6216f3a6703453c49cd72ce8b 100644
--- a/src/cooling/EAGLE/cooling.c
+++ b/src/cooling/EAGLE/cooling.c
@@ -37,6 +37,7 @@
 #include "cooling_struct.h"
 #include "cooling_tables.h"
 #include "error.h"
+#include "exp10.h"
 #include "hydro.h"
 #include "interpolate.h"
 #include "io_properties.h"
@@ -151,9 +152,9 @@ void cooling_update(const struct cosmology *cosmo,
  *
  * @param logu_init Initial guess for log(internal energy)
  * @param u_ini Internal energy at beginning of hydro step
- * @param n_h_i Particle hydrogen number density index
- * @param d_n_h Particle hydrogen number density offset
- * @param He_i Particle helium fraction index
+ * @param n_H_index Particle hydrogen number density index
+ * @param d_n_H Particle hydrogen number density offset
+ * @param He_index Particle helium fraction index
  * @param d_He Particle helium fraction offset
  * @param He_reion_heat Heating due to helium reionization
  * (only depends on redshift, so passed as parameter)
@@ -166,8 +167,8 @@ void cooling_update(const struct cosmology *cosmo,
  * @param bisection_flag Flag to identify if scheme failed to converge
  */
 INLINE static float newton_iter(
-    float logu_init, double u_ini, int n_h_i, float d_n_h, int He_i, float d_He,
-    float He_reion_heat, struct part *restrict p,
+    float logu_init, double u_ini, int n_H_index, float d_n_H, int He_index,
+    float d_He, float He_reion_heat, struct part *restrict p,
     const struct cosmology *restrict cosmo,
     const struct cooling_function_data *restrict cooling,
     const struct phys_const *restrict phys_const,
@@ -202,10 +203,10 @@ INLINE static float newton_iter(
   {
     logu_old = logu;
     LambdaNet_old = LambdaNet;
-    LambdaNet =
-        (He_reion_heat / (dt * ratefact_cgs)) +
-        eagle_cooling_rate(logu_old, cosmo->z, n_H_cgs, abundance_ratio, n_h_i,
-                           d_n_h, He_i, d_He, cooling, &dLambdaNet_du);
+    LambdaNet = (He_reion_heat / (dt * ratefact_cgs)) +
+                eagle_cooling_rate(logu_old, cosmo->z, n_H_cgs, abundance_ratio,
+                                   n_H_index, d_n_H, He_index, d_He, cooling,
+                                   &dLambdaNet_du);
 
     /* Newton iteration. For details on how the cooling equation is integrated
      * see documentation in theory/Cooling/ */
@@ -243,9 +244,9 @@ INLINE static float newton_iter(
  * @param u_ini_cgs Internal energy at beginning of hydro step in CGS.
  * @param n_H_cgs Hydrogen number density in CGS.
  * @param redshift Current redshift.
- * @param n_h_i Particle hydrogen number density index.
- * @param d_n_h Particle hydrogen number density offset.
- * @param He_i Particle helium fraction index.
+ * @param n_H_index Particle hydrogen number density index.
+ * @param d_n_H Particle hydrogen number density offset.
+ * @param He_index Particle helium fraction index.
  * @param d_He Particle helium fraction offset.
  * @param Lambda_He_reion_cgs Cooling rate coming from He reionization.
  * @param ratefact_cgs Multiplication factor to get a cooling rate.
@@ -256,8 +257,9 @@ INLINE static float newton_iter(
  */
 INLINE static double bisection_iter(
     const double u_ini_cgs, const double n_H_cgs, const double redshift,
-    int n_h_i, float d_n_h, int He_i, float d_He, double Lambda_He_reion_cgs,
-    double ratefact_cgs, const struct cooling_function_data *restrict cooling,
+    int n_H_index, float d_n_H, int He_index, float d_He,
+    double Lambda_He_reion_cgs, double ratefact_cgs,
+    const struct cooling_function_data *restrict cooling,
     const float abundance_ratio[chemistry_element_count + 2], double dt_cgs,
     long long ID) {
 
@@ -270,10 +272,10 @@ INLINE static double bisection_iter(
   /*************************************/
 
   double LambdaNet_cgs =
-      Lambda_He_reion_cgs + eagle_cooling_rate(log(u_ini_cgs), redshift,
-                                               n_H_cgs, abundance_ratio, n_h_i,
-                                               d_n_h, He_i, d_He, cooling,
-                                               /*dLambdaNet_du=*/NULL);
+      Lambda_He_reion_cgs +
+      eagle_cooling_rate(log(u_ini_cgs), redshift, n_H_cgs, abundance_ratio,
+                         n_H_index, d_n_H, He_index, d_He, cooling,
+                         /*dLambdaNet_du=*/NULL);
 
   /*************************************/
   /* Let's try to bracket the solution */
@@ -289,7 +291,7 @@ INLINE static double bisection_iter(
     LambdaNet_cgs =
         Lambda_He_reion_cgs +
         eagle_cooling_rate(log(u_lower_cgs), redshift, n_H_cgs, abundance_ratio,
-                           n_h_i, d_n_h, He_i, d_He, cooling,
+                           n_H_index, d_n_H, He_index, d_He, cooling,
                            /*dLambdaNet_du=*/NULL);
 
     int i = 0;
@@ -301,11 +303,11 @@ INLINE static double bisection_iter(
       u_upper_cgs /= bracket_factor;
 
       /* Compute a new rate */
-      LambdaNet_cgs =
-          Lambda_He_reion_cgs +
-          eagle_cooling_rate(log(u_lower_cgs), redshift, n_H_cgs,
-                             abundance_ratio, n_h_i, d_n_h, He_i, d_He, cooling,
-                             /*dLambdaNet_du=*/NULL);
+      LambdaNet_cgs = Lambda_He_reion_cgs +
+                      eagle_cooling_rate(log(u_lower_cgs), redshift, n_H_cgs,
+                                         abundance_ratio, n_H_index, d_n_H,
+                                         He_index, d_He, cooling,
+                                         /*dLambdaNet_du=*/NULL);
       i++;
     }
 
@@ -325,7 +327,7 @@ INLINE static double bisection_iter(
     LambdaNet_cgs =
         Lambda_He_reion_cgs +
         eagle_cooling_rate(log(u_upper_cgs), redshift, n_H_cgs, abundance_ratio,
-                           n_h_i, d_n_h, He_i, d_He, cooling,
+                           n_H_index, d_n_H, He_index, d_He, cooling,
                            /*dLambdaNet_du=*/NULL);
 
     int i = 0;
@@ -337,11 +339,11 @@ INLINE static double bisection_iter(
       u_upper_cgs *= bracket_factor;
 
       /* Compute a new rate */
-      LambdaNet_cgs =
-          Lambda_He_reion_cgs +
-          eagle_cooling_rate(log(u_upper_cgs), redshift, n_H_cgs,
-                             abundance_ratio, n_h_i, d_n_h, He_i, d_He, cooling,
-                             /*dLambdaNet_du=*/NULL);
+      LambdaNet_cgs = Lambda_He_reion_cgs +
+                      eagle_cooling_rate(log(u_upper_cgs), redshift, n_H_cgs,
+                                         abundance_ratio, n_H_index, d_n_H,
+                                         He_index, d_He, cooling,
+                                         /*dLambdaNet_du=*/NULL);
       i++;
     }
 
@@ -371,7 +373,7 @@ INLINE static double bisection_iter(
     LambdaNet_cgs =
         Lambda_He_reion_cgs +
         eagle_cooling_rate(log(u_next_cgs), redshift, n_H_cgs, abundance_ratio,
-                           n_h_i, d_n_h, He_i, d_He, cooling,
+                           n_H_index, d_n_H, He_index, d_He, cooling,
                            /*dLambdaNet_du=*/NULL);
 
     /* Where do we go next? */
@@ -453,12 +455,19 @@ void cooling_cool_part(const struct phys_const *restrict phys_const,
   const double u_0_cgs = u_0 * cooling->internal_energy_to_cgs;
   const double dt_cgs = dt * units_cgs_conversion_factor(us, UNIT_CONV_TIME);
 
+  /* Change in redshift over the course of this time-step
+     (See cosmology theory document for the derivation) */
+  const double delta_redshift = -dt * cosmo->H * cosmo->a_inv;
+
   /* Get this particle's abundance ratios */
   float abundance_ratio[chemistry_element_count + 2];
   abundance_ratio_to_solar(p, cooling, abundance_ratio);
 
-  /* Get the H and He mass fractions */
+  /* Get the Hydrogen mass fraction */
   const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H];
+
+  /* Get the Helium mass fraction. Note that this is He / (H + He), i.e. a
+   * metal-free Helium mass fraction as per the Wiersma+08 definition */
   const float HeFrac =
       p->chemistry_data.metal_mass_fraction[chemistry_element_He] /
       (XH + p->chemistry_data.metal_mass_fraction[chemistry_element_He]);
@@ -472,30 +481,35 @@ void cooling_cool_part(const struct phys_const *restrict phys_const,
    * equivalent expression  below */
   const double ratefact_cgs = n_H_cgs * (XH * cooling->inv_proton_mass_cgs);
 
+  /* compute hydrogen number density and helium fraction table indices and
+   * offsets (These are fixed for any value of u, so no need to recompute them)
+   */
+  int He_index, n_H_index;
+  float d_He, d_n_H;
+  get_index_1d(cooling->HeFrac, eagle_cooling_N_He_frac, HeFrac, &He_index,
+               &d_He);
+  get_index_1d(cooling->nH, eagle_cooling_N_density, log10(n_H_cgs), &n_H_index,
+               &d_n_H);
+
+  /* Start by computing the cooling (heating actually) rate from Helium
+     re-ionization as this needs to be added on no matter what */
+
   /* Get helium and hydrogen reheating term */
   const double Helium_reion_heat_cgs = eagle_helium_reionization_extraheat(
-      cooling->z_index, -dt * cosmo->H * cosmo->a_inv, cooling);
+      cooling->z_index, delta_redshift, cooling);
 
   /* Convert this into a rate */
   const double Lambda_He_reion_cgs =
       Helium_reion_heat_cgs / (dt_cgs * ratefact_cgs);
 
-  /* compute hydrogen number density and helium fraction table indices and
-   * offsets (These are fixed for of u, so no need to recompute them) */
-  int He_i, n_h_i;
-  float d_He, d_n_h;
-  get_index_1d(cooling->HeFrac, eagle_cooling_N_He_frac, HeFrac, &He_i, &d_He);
-  get_index_1d(cooling->nH, eagle_cooling_N_density, log10(n_H_cgs), &n_h_i,
-               &d_n_h);
-
   /* Let's compute the internal energy at the end of the step */
   double u_final_cgs;
 
   /* First try an explicit integration (note we ignore the derivative) */
   const double LambdaNet_cgs =
       Lambda_He_reion_cgs + eagle_cooling_rate(log(u_0_cgs), cosmo->z, n_H_cgs,
-                                               abundance_ratio, n_h_i, d_n_h,
-                                               He_i, d_He, cooling,
+                                               abundance_ratio, n_H_index,
+                                               d_n_H, He_index, d_He, cooling,
                                                /*dLambdaNet_du=*/NULL);
 
   /* if cooling rate is small, take the explicit solution */
@@ -508,33 +522,34 @@ void cooling_cool_part(const struct phys_const *restrict phys_const,
 
     int bisection_flag = 1;
 
-#ifdef TO_BE_DONE
-    if (cooling->newton_flag) {
+    // MATTHIEU: TO DO restore the Newton-Raphson scheme
+    if (0 && cooling->newton_flag) {
+
       /* Ok, try a Newton-Raphson scheme instead */
-      log_u_final_cgs = newton_iter(
-          log(u_0_cgs), u_0_cgs, n_h_i, d_n_h, He_i, d_He, LambdaTune, p, cosmo,
-          cooling, phys_const, abundance_ratio, dt_cgs, &bisection_flag);
+      double log_u_final_cgs =
+          newton_iter(log(u_0_cgs), u_0_cgs, n_H_index, d_n_H, He_index, d_He,
+                      Lambda_He_reion_cgs, p, cosmo, cooling, phys_const,
+                      abundance_ratio, dt_cgs, &bisection_flag);
 
       /* Check if newton scheme sent us to a higher energy despite being in
-    a
-       * cooling regime If it did try newton scheme with a better guess.
-    (Guess
-       * internal energy near equilibrium solution).  */
-      if (LambdaNet < 0 && log_u_final_cgs > log(u_0_cgs)) {
+         a  cooling regime If it did try newton scheme with a better guess.
+         (Guess internal energy near equilibrium solution).  */
+      if (LambdaNet_cgs < 0 && log_u_final_cgs > log(u_0_cgs)) {
         bisection_flag = 0;
         log_u_final_cgs =
-            newton_iter(newton_log_u_guess_cgs, u_0_cgs, n_h_i, d_n_h, He_i,
-                        d_He, LambdaTune, p, cosmo, cooling, phys_const,
-                        abundance_ratio, dt_cgs, &bisection_flag);
+            newton_iter(newton_log_u_guess_cgs, u_0_cgs, n_H_index, d_n_H,
+                        He_index, d_He, Lambda_He_reion_cgs, p, cosmo, cooling,
+                        phys_const, abundance_ratio, dt_cgs, &bisection_flag);
       }
+
+      u_final_cgs = exp(log_u_final_cgs);
     }
-#endif
 
     /* Alright, all else failed, let's bisect */
     if (bisection_flag || !(cooling->newton_flag)) {
       u_final_cgs =
-          bisection_iter(u_0_cgs, n_H_cgs, cosmo->z, n_h_i, d_n_h, He_i, d_He,
-                         Lambda_He_reion_cgs, ratefact_cgs, cooling,
+          bisection_iter(u_0_cgs, n_H_cgs, cosmo->z, n_H_index, d_n_H, He_index,
+                         d_He, Lambda_He_reion_cgs, ratefact_cgs, cooling,
                          abundance_ratio, dt_cgs, p->id);
     }
   }
@@ -620,6 +635,67 @@ __attribute__((always_inline)) INLINE void cooling_first_init_part(
   xp->cooling_data.radiated_energy = 0.f;
 }
 
+/**
+ * @brief Compute the temperature of a #part based on the cooling function.
+ *
+ * We use the Temperature table of the Wiersma+08 set. This computes the
+ * equilibirum temperature of a gas for a given redshift, Hydrogen density,
+ * internal energy per unit mass and Helium fraction.
+ *
+ * The temperature returned is consistent with the cooling rates.
+ *
+ * @param phys_const #phys_const data structure.
+ * @param hydro_props The properties of the hydro scheme.
+ * @param us The internal system of units.
+ * @param cosmo #cosmology data structure.
+ * @param cooling #cooling_function_data struct.
+ * @param p #part data.
+ * @param xp Pointer to the #xpart data.
+ */
+float cooling_get_temperature(
+    const struct phys_const *restrict phys_const,
+    const struct hydro_props *restrict hydro_props,
+    const struct unit_system *restrict us,
+    const struct cosmology *restrict cosmo,
+    const struct cooling_function_data *restrict cooling,
+    const struct part *restrict p, const struct xpart *restrict xp) {
+
+  /* Get physical internal energy */
+  const float u = hydro_get_physical_internal_energy(p, xp, cosmo);
+  const double u_cgs = u * cooling->internal_energy_to_cgs;
+
+  /* Get the Hydrogen mass fraction */
+  const float XH = p->chemistry_data.metal_mass_fraction[chemistry_element_H];
+
+  /* Get the Helium mass fraction. Note that this is He / (H + He), i.e. a
+   * metal-free Helium mass fraction as per the Wiersma+08 definition */
+  const float HeFrac =
+      p->chemistry_data.metal_mass_fraction[chemistry_element_He] /
+      (XH + p->chemistry_data.metal_mass_fraction[chemistry_element_He]);
+
+  /* Convert Hydrogen mass fraction into Hydrogen number density */
+  const float rho = hydro_get_physical_density(p, cosmo);
+  const double n_H = rho * XH / phys_const->const_proton_mass;
+  const double n_H_cgs = n_H * cooling->number_density_to_cgs;
+
+  /* compute hydrogen number density and helium fraction table indices and
+   * offsets */
+  int He_index, n_H_index;
+  float d_He, d_n_H;
+  get_index_1d(cooling->HeFrac, eagle_cooling_N_He_frac, HeFrac, &He_index,
+               &d_He);
+  get_index_1d(cooling->nH, eagle_cooling_N_density, log10(n_H_cgs), &n_H_index,
+               &d_n_H);
+
+  /* Compute the log10 of the temperature by interpolating the table */
+  const double log_10_T = eagle_convert_u_to_temp(
+      log10(u_cgs), cosmo->z, /*compute_dT_du=*/0, /*dT_du=*/NULL, n_H_index,
+      He_index, d_n_H, d_He, cooling);
+
+  /* Undo the log! */
+  return exp10(log_10_T);
+}
+
 /**
  * @brief Returns the total radiated energy by this particle.
  *
diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h
index 5685692c379c508bc8d6a4ab8d22bb89ba1f4a17..02ee37a416db322c2813cf4da7fe4105d5d8e84f 100644
--- a/src/cooling/EAGLE/cooling.h
+++ b/src/cooling/EAGLE/cooling.h
@@ -59,6 +59,14 @@ void cooling_first_init_part(
     const struct cooling_function_data *restrict cooling,
     const struct part *restrict p, struct xpart *restrict xp);
 
+float cooling_get_temperature(
+    const struct phys_const *restrict phys_const,
+    const struct hydro_props *restrict hydro_props,
+    const struct unit_system *restrict us,
+    const struct cosmology *restrict cosmo,
+    const struct cooling_function_data *restrict cooling,
+    const struct part *restrict p, const struct xpart *restrict xp);
+
 float cooling_get_radiated_energy(const struct xpart *restrict xp);
 
 void cooling_init_backend(struct swift_params *parameter_file,
diff --git a/src/cooling/EAGLE/cooling_io.h b/src/cooling/EAGLE/cooling_io.h
index 48c845c254b41f02d8b4ea39ae43a990b0436ac8..5508153afc094d84383893f55ac0362a6d427b24 100644
--- a/src/cooling/EAGLE/cooling_io.h
+++ b/src/cooling/EAGLE/cooling_io.h
@@ -23,6 +23,7 @@
 #include "../config.h"
 
 /* Local includes */
+#include "cooling.h"
 #include "io_properties.h"
 
 #ifdef HAVE_HDF5
@@ -40,9 +41,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
 }
 #endif
 
+INLINE static void convert_part_T(const struct engine* e, const struct part* p,
+                                  const struct xpart* xp, float* ret) {
+
+  ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties,
+                                   e->internal_units, e->cosmology,
+                                   e->cooling_func, p, xp);
+}
+
 /**
  * @brief Specifies which particle fields to write to a dataset
  *
+ * @param parts The particle array.
  * @param xparts The extended data particle array.
  * @param list The list of i/o properties to write.
  * @param cooling The #cooling_function_data
@@ -50,9 +60,13 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
  * @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 part* parts, const struct xpart* xparts, struct io_props* list,
     const struct cooling_function_data* cooling) {
-  return 0;
+
+  list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1,
+                                              UNIT_CONV_TEMPERATURE, parts,
+                                              xparts, convert_part_T);
+  return 1;
 }
 
 #endif /* SWIFT_COOLING_EAGLE_IO_H */
diff --git a/src/cooling/EAGLE/cooling_rates.h b/src/cooling/EAGLE/cooling_rates.h
index 621f7c7b0781ba9806b4b5922843947079941fee..c78be6a30849a7b8a79e56d7fbdefe2eede866f3 100644
--- a/src/cooling/EAGLE/cooling_rates.h
+++ b/src/cooling/EAGLE/cooling_rates.h
@@ -25,6 +25,7 @@
 
 /* Local includes. */
 #include "cooling_tables.h"
+#include "exp10.h"
 #include "interpolate.h"
 
 /**
@@ -332,7 +333,7 @@ __attribute__((always_inline)) INLINE double eagle_Compton_cooling_rate(
  * to solar metal abundances
  *
  * @param n_H_index Particle hydrogen number density index
- * @param d_n_h Particle hydrogen number density offset
+ * @param d_n_H Particle hydrogen number density offset
  * @param He_index Particle helium fraction index
  * @param d_He Particle helium fraction offset
  * @param cooling Cooling data structure
@@ -345,7 +346,7 @@ __attribute__((always_inline)) INLINE double eagle_Compton_cooling_rate(
 INLINE static double eagle_metal_cooling_rate(
     double log10_u_cgs, double redshift, double n_H_cgs,
     const float solar_ratio[chemistry_element_count + 2], int n_H_index,
-    float d_n_h, int He_index, float d_He,
+    float d_n_H, int He_index, float d_He,
     const struct cooling_function_data *restrict cooling, double *dlambda_du,
     double *element_lambda) {
 
@@ -364,14 +365,15 @@ INLINE static double eagle_metal_cooling_rate(
 
   /* Temperature */
   float dT_du = -1.f;
-  const double T =
+  const double log_10_T =
       eagle_convert_u_to_temp(log10_u_cgs, redshift, compute_dT_du, &dT_du,
-                              n_H_index, He_index, d_n_h, d_He, cooling);
+                              n_H_index, He_index, d_n_H, d_He, cooling);
 
   /* Get index along temperature dimension of the tables */
   int T_index;
   float d_T;
-  get_index_1d(cooling->Temp, eagle_cooling_N_temperature, T, &T_index, &d_T);
+  get_index_1d(cooling->Temp, eagle_cooling_N_temperature, log_10_T, &T_index,
+               &d_T);
 
 #ifdef TO_BE_DONE
   /* Difference between entries on the temperature table around u */
@@ -391,7 +393,7 @@ INLINE static double eagle_metal_cooling_rate(
      * in redshift */
     Lambda_free = interpolation_3d(cooling->table.H_plus_He_heating, /* */
                                    n_H_index, He_index, T_index,     /* */
-                                   d_n_h, d_He, d_T,                 /* */
+                                   d_n_H, d_He, d_T,                 /* */
                                    eagle_cooling_N_density,          /* */
                                    eagle_cooling_N_He_frac,          /* */
                                    eagle_cooling_N_temperature);     /* */
@@ -416,7 +418,7 @@ INLINE static double eagle_metal_cooling_rate(
     Lambda_free =
         interpolation_4d(cooling->table.H_plus_He_heating,            /* */
                          /*z_index=*/0, n_H_index, He_index, T_index, /* */
-                         cooling->dz, d_n_h, d_He, d_T,               /* */
+                         cooling->dz, d_n_H, d_He, d_T,               /* */
                          eagle_cooling_N_loaded_redshifts,            /* */
                          eagle_cooling_N_density,                     /* */
                          eagle_cooling_N_He_frac,                     /* */
@@ -460,7 +462,7 @@ INLINE static double eagle_metal_cooling_rate(
     H_plus_He_electron_abundance =
         interpolation_3d(cooling->table.H_plus_He_electron_abundance, /* */
                          n_H_index, He_index, T_index,                /* */
-                         d_n_h, d_He, d_T,                            /* */
+                         d_n_H, d_He, d_T,                            /* */
                          eagle_cooling_N_density,                     /* */
                          eagle_cooling_N_He_frac,                     /* */
                          eagle_cooling_N_temperature);                /* */
@@ -485,7 +487,7 @@ INLINE static double eagle_metal_cooling_rate(
     H_plus_He_electron_abundance =
         interpolation_4d(cooling->table.H_plus_He_electron_abundance, /* */
                          /*z_index=*/0, n_H_index, He_index, T_index, /* */
-                         cooling->dz, d_n_h, d_He, d_T,               /* */
+                         cooling->dz, d_n_H, d_He, d_T,               /* */
                          eagle_cooling_N_loaded_redshifts,            /* */
                          eagle_cooling_N_density,                     /* */
                          eagle_cooling_N_He_frac,                     /* */
@@ -516,6 +518,8 @@ INLINE static double eagle_metal_cooling_rate(
   if ((redshift > cooling->Redshifts[eagle_cooling_N_redshifts - 1]) ||
       (redshift > cooling->H_reion_z)) {
 
+    const double T = exp10(log_10_T);
+
     /* Note the minus sign */
     Lambda_Compton -= eagle_Compton_cooling_rate(cooling, redshift, n_H_cgs, T,
                                                  H_plus_He_electron_abundance);
@@ -539,7 +543,7 @@ INLINE static double eagle_metal_cooling_rate(
     solar_electron_abundance =
         interpolation_2d(cooling->table.electron_abundance, /* */
                          n_H_index, T_index,                /* */
-                         d_n_h, d_T,                        /* */
+                         d_n_H, d_T,                        /* */
                          eagle_cooling_N_density,           /* */
                          eagle_cooling_N_temperature);      /* */
 
@@ -562,7 +566,7 @@ INLINE static double eagle_metal_cooling_rate(
     solar_electron_abundance =
         interpolation_3d(cooling->table.electron_abundance, /* */
                          /*z_index=*/0, n_H_index, T_index, /* */
-                         cooling->dz, d_n_h, d_T,           /* */
+                         cooling->dz, d_n_H, d_T,           /* */
                          eagle_cooling_N_loaded_redshifts,  /* */
                          eagle_cooling_N_density,           /* */
                          eagle_cooling_N_temperature);      /* */
@@ -601,7 +605,7 @@ INLINE static double eagle_metal_cooling_rate(
       lambda_metal[elem] =
           interpolation_3d_no_x(cooling->table.metal_heating,   /* */
                                 elem, n_H_index, T_index,       /* */
-                                /*delta_elem=*/0.f, d_n_h, d_T, /* */
+                                /*delta_elem=*/0.f, d_n_H, d_T, /* */
                                 eagle_cooling_N_metal,          /* */
                                 eagle_cooling_N_density,        /* */
                                 eagle_cooling_N_temperature);   /* */
@@ -637,7 +641,7 @@ INLINE static double eagle_metal_cooling_rate(
       lambda_metal[elem] = interpolation_4d_no_x(
           cooling->table.metal_heating,                /* */
           elem, /*z_index=*/0, n_H_index, T_index,     /* */
-          /*delta_elem=*/0.f, cooling->dz, d_n_h, d_T, /* */
+          /*delta_elem=*/0.f, cooling->dz, d_n_H, d_T, /* */
           eagle_cooling_N_metal,                       /* */
           eagle_cooling_N_loaded_redshifts,            /* */
           eagle_cooling_N_density,                     /* */
@@ -696,7 +700,7 @@ INLINE static double eagle_metal_cooling_rate(
  * @param abundance_ratio Ratio of element abundance to solar.
  *
  * @param n_H_index Particle hydrogen number density index
- * @param d_n_h Particle hydrogen number density offset
+ * @param d_n_H Particle hydrogen number density offset
  * @param He_index Particle helium fraction index
  * @param d_He Particle helium fraction offset
  * @param cooling #cooling_function_data structure
@@ -709,12 +713,12 @@ INLINE static double eagle_metal_cooling_rate(
 INLINE static double eagle_cooling_rate(
     double log_u_cgs, double redshift, double n_H_cgs,
     const float abundance_ratio[chemistry_element_count + 2], int n_H_index,
-    float d_n_h, int He_index, float d_He,
+    float d_n_H, int He_index, float d_He,
     const struct cooling_function_data *restrict cooling,
     double *dLambdaNet_du) {
 
   return eagle_metal_cooling_rate(log_u_cgs / M_LN10, redshift, n_H_cgs,
-                                  abundance_ratio, n_H_index, d_n_h, He_index,
+                                  abundance_ratio, n_H_index, d_n_H, He_index,
                                   d_He, cooling, dLambdaNet_du,
                                   /*element_lambda=*/NULL);
 }
diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h
index dac92f09837cbb40cc49f1e8dc5d4c627ce7023a..8dc545f5b18cc784274921a226a7d679c367a7e4 100644
--- a/src/cooling/const_du/cooling.h
+++ b/src/cooling/const_du/cooling.h
@@ -164,6 +164,49 @@ __attribute__((always_inline)) INLINE static void cooling_first_init_part(
   xp->cooling_data.radiated_energy = 0.f;
 }
 
+/**
+ * @brief Compute the temperature of a #part based on the cooling function.
+ *
+ * @param phys_const #phys_const data structure.
+ * @param hydro_props The properties of the hydro scheme.
+ * @param us The internal system of units.
+ * @param cosmo #cosmology data structure.
+ * @param cooling #cooling_function_data struct.
+ * @param p #part data.
+ * @param xp Pointer to the #xpart data.
+ */
+INLINE static float cooling_get_temperature(
+    const struct phys_const* restrict phys_const,
+    const struct hydro_props* restrict hydro_props,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, const struct xpart* restrict xp) {
+
+  /* Physical constants */
+  const double m_H = phys_const->const_proton_mass;
+  const double k_B = phys_const->const_boltzmann_k;
+
+  /* Gas properties */
+  const double T_transition = hydro_props->hydrogen_ionization_temperature;
+  const double mu_neutral = hydro_props->mu_neutral;
+  const double mu_ionised = hydro_props->mu_ionised;
+
+  /* Particle temperature */
+  const double u = hydro_get_physical_internal_energy(p, xp, cosmo);
+
+  /* Temperature over mean molecular weight */
+  const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B;
+
+  /* Are we above or below the HII -> HI transition? */
+  if (T_over_mu > (T_transition + 1.) / mu_ionised)
+    return T_over_mu * mu_ionised;
+  else if (T_over_mu < (T_transition - 1.) / mu_neutral)
+    return T_over_mu * mu_neutral;
+  else
+    return T_transition;
+}
+
 /**
  * @brief Returns the total radiated energy by this particle.
  *
diff --git a/src/cooling/const_du/cooling_io.h b/src/cooling/const_du/cooling_io.h
index f4a327f14ec071bc62c4cf57bb118df71bab2b3e..a60aa5d282d0a244f206f74827f0c1979d3bcb75 100644
--- a/src/cooling/const_du/cooling_io.h
+++ b/src/cooling/const_du/cooling_io.h
@@ -34,6 +34,7 @@
 #include "../config.h"
 
 /* Local includes */
+#include "cooling.h"
 #include "io_properties.h"
 
 #ifdef HAVE_HDF5
@@ -50,9 +51,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
 }
 #endif
 
+INLINE static void convert_part_T(const struct engine* e, const struct part* p,
+                                  const struct xpart* xp, float* ret) {
+
+  ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties,
+                                   e->internal_units, e->cosmology,
+                                   e->cooling_func, p, xp);
+}
+
 /**
  * @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
@@ -60,9 +70,14 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
  * @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 part* parts, const struct xpart* xparts, struct io_props* list,
     const struct cooling_function_data* cooling) {
-  return 0;
+
+  list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1,
+                                              UNIT_CONV_TEMPERATURE, parts,
+                                              xparts, convert_part_T);
+
+  return 1;
 }
 
 #endif /* SWIFT_COOLING_CONST_DU_IO_H */
diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h
index 3c336060bdadae9b0cd0034bc0ccb1e9e9266aff..09c96413ce0d061c01bc267af46e65ee23d2834f 100644
--- a/src/cooling/const_lambda/cooling.h
+++ b/src/cooling/const_lambda/cooling.h
@@ -230,6 +230,49 @@ __attribute__((always_inline)) INLINE static void cooling_first_init_part(
   xp->cooling_data.radiated_energy = 0.f;
 }
 
+/**
+ * @brief Compute the temperature of a #part based on the cooling function.
+ *
+ * @param phys_const #phys_const data structure.
+ * @param hydro_props The properties of the hydro scheme.
+ * @param us The internal system of units.
+ * @param cosmo #cosmology data structure.
+ * @param cooling #cooling_function_data struct.
+ * @param p #part data.
+ * @param xp Pointer to the #xpart data.
+ */
+INLINE static float cooling_get_temperature(
+    const struct phys_const* restrict phys_const,
+    const struct hydro_props* restrict hydro_props,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, const struct xpart* restrict xp) {
+
+  /* Physical constants */
+  const double m_H = phys_const->const_proton_mass;
+  const double k_B = phys_const->const_boltzmann_k;
+
+  /* Gas properties */
+  const double T_transition = hydro_props->hydrogen_ionization_temperature;
+  const double mu_neutral = hydro_props->mu_neutral;
+  const double mu_ionised = hydro_props->mu_ionised;
+
+  /* Particle temperature */
+  const double u = hydro_get_physical_internal_energy(p, xp, cosmo);
+
+  /* Temperature over mean molecular weight */
+  const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B;
+
+  /* Are we above or below the HII -> HI transition? */
+  if (T_over_mu > (T_transition + 1.) / mu_ionised)
+    return T_over_mu * mu_ionised;
+  else if (T_over_mu < (T_transition - 1.) / mu_neutral)
+    return T_over_mu * mu_neutral;
+  else
+    return T_transition;
+}
+
 /**
  * @brief Returns the total radiated energy by this particle.
  *
diff --git a/src/cooling/const_lambda/cooling_io.h b/src/cooling/const_lambda/cooling_io.h
index 0dca5011ebe5bc6c2a4866387e9cf1ac0ba3447a..9437f0f94db41725d6715cf349843bf079137305 100644
--- a/src/cooling/const_lambda/cooling_io.h
+++ b/src/cooling/const_lambda/cooling_io.h
@@ -32,6 +32,7 @@
 #include "../config.h"
 
 /* Local includes */
+#include "cooling.h"
 #include "io_properties.h"
 
 #ifdef HAVE_HDF5
@@ -49,11 +50,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
 }
 #endif
 
+INLINE static void convert_part_T(const struct engine* e, const struct part* p,
+                                  const struct xpart* xp, float* ret) {
+
+  ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties,
+                                   e->internal_units, e->cosmology,
+                                   e->cooling_func, p, xp);
+}
+
 /**
  * @brief Specifies which particle fields to write to a dataset
  *
- * Nothing to write for this scheme.
- *
+ * @param parts The particle array.
  * @param xparts The extended particle array.
  * @param list The list of i/o properties to write.
  * @param cooling The #cooling_function_data
@@ -61,10 +69,14 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
  * @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 part* parts, const struct xpart* xparts, struct io_props* list,
     const struct cooling_function_data* cooling) {
 
-  return 0;
+  list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1,
+                                              UNIT_CONV_TEMPERATURE, parts,
+                                              xparts, convert_part_T);
+
+  return 1;
 }
 
 #endif /* SWIFT_COOLING_CONST_LAMBDA_IO_H */
diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h
index 868bfad7fc12c2f89d54949642bd5e9d902b42b6..579cf2ae9c2db290300f8367111f91aab9bd27d0 100644
--- a/src/cooling/none/cooling.h
+++ b/src/cooling/none/cooling.h
@@ -119,6 +119,49 @@ __attribute__((always_inline)) INLINE static void cooling_first_init_part(
     const struct cooling_function_data* data, const struct part* restrict p,
     struct xpart* restrict xp) {}
 
+/**
+ * @brief Compute the temperature of a #part based on the cooling function.
+ *
+ * @param phys_const #phys_const data structure.
+ * @param hydro_props The properties of the hydro scheme.
+ * @param us The internal system of units.
+ * @param cosmo #cosmology data structure.
+ * @param cooling #cooling_function_data struct.
+ * @param p #part data.
+ * @param xp Pointer to the #xpart data.
+ */
+INLINE static float cooling_get_temperature(
+    const struct phys_const* restrict phys_const,
+    const struct hydro_props* restrict hydro_props,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, const struct xpart* restrict xp) {
+
+  /* Physical constants */
+  const double m_H = phys_const->const_proton_mass;
+  const double k_B = phys_const->const_boltzmann_k;
+
+  /* Gas properties */
+  const double T_transition = hydro_props->hydrogen_ionization_temperature;
+  const double mu_neutral = hydro_props->mu_neutral;
+  const double mu_ionised = hydro_props->mu_ionised;
+
+  /* Particle temperature */
+  const double u = hydro_get_physical_internal_energy(p, xp, cosmo);
+
+  /* Temperature over mean molecular weight */
+  const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B;
+
+  /* Are we above or below the HII -> HI transition? */
+  if (T_over_mu > (T_transition + 1.) / mu_ionised)
+    return T_over_mu * mu_ionised;
+  else if (T_over_mu < (T_transition - 1.) / mu_neutral)
+    return T_over_mu * mu_neutral;
+  else
+    return T_transition;
+}
+
 /**
  * @brief Returns the total radiated energy by this particle.
  *
diff --git a/src/cooling/none/cooling_io.h b/src/cooling/none/cooling_io.h
index 518c166480a0b81f6856c8a39e2a64d34369dc84..16b4b4ca29f8ebd325decc25420d7db617e1e4ef 100644
--- a/src/cooling/none/cooling_io.h
+++ b/src/cooling/none/cooling_io.h
@@ -23,6 +23,7 @@
 #include "../config.h"
 
 /* Local includes */
+#include "cooling.h"
 #include "io_properties.h"
 
 #ifdef HAVE_HDF5
@@ -39,9 +40,18 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
 }
 #endif
 
+INLINE static void convert_part_T(const struct engine* e, const struct part* p,
+                                  const struct xpart* xp, float* ret) {
+
+  ret[0] = cooling_get_temperature(e->physical_constants, e->hydro_properties,
+                                   e->internal_units, e->cosmology,
+                                   e->cooling_func, p, xp);
+}
+
 /**
  * @brief Specifies which particle fields to write to a dataset
  *
+ * @param parts The particle array.
  * @param xparts The extended particle array.
  * @param list The list of i/o properties to write.
  * @param cooling The #cooling_function_data
@@ -49,9 +59,13 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
  * @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 part* parts, const struct xpart* xparts, struct io_props* list,
     const struct cooling_function_data* cooling) {
-  return 0;
+
+  list[0] = io_make_output_field_convert_part("Temperature", FLOAT, 1,
+                                              UNIT_CONV_TEMPERATURE, parts,
+                                              xparts, convert_part_T);
+  return 1;
 }
 
 #endif /* SWIFT_COOLING_NONE_IO_H */
diff --git a/src/engine.c b/src/engine.c
index 73b7d9b64e744e08e19145a574a3a2b896ab3995..841a92638b34dba3dce54576ea22a44bb346b935 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -83,7 +83,6 @@
 #include "serial_io.h"
 #include "single_io.h"
 #include "sort_part.h"
-#include "sourceterms.h"
 #include "stars_io.h"
 #include "statistics.h"
 #include "timers.h"
@@ -110,7 +109,6 @@ const char *engine_policy_names[] = {"none",
                                      "drift everything",
                                      "reconstruct multi-poles",
                                      "cooling",
-                                     "sourceterms",
                                      "stars",
                                      "structure finding",
                                      "star formation",
@@ -1925,8 +1923,8 @@ int engine_estimate_nr_tasks(struct engine *e) {
     /* Cooling task + extra space */
     n1 += 2;
   }
-  if (e->policy & engine_policy_sourceterms) {
-    n1 += 2;
+  if (e->policy & engine_policy_star_formation) {
+    n1 += 1;
   }
   if (e->policy & engine_policy_stars) {
     /* 2 self (density, feedback), 1 sort, 26/2 density pairs
@@ -2016,9 +2014,10 @@ void engine_rebuild(struct engine *e, int repartitioned,
   const ticks tic2 = getticks();
 
   /* Update the global counters of particles */
-  long long num_particles[3] = {(long long)e->s->nr_parts,
-                                (long long)e->s->nr_gparts,
-                                (long long)e->s->nr_sparts};
+  long long num_particles[3] = {
+      (long long)(e->s->nr_parts - e->s->nr_extra_parts),
+      (long long)(e->s->nr_gparts - e->s->nr_extra_gparts),
+      (long long)(e->s->nr_sparts - e->s->nr_extra_sparts)};
 #ifdef WITH_MPI
   MPI_Allreduce(MPI_IN_PLACE, num_particles, 3, MPI_LONG_LONG, MPI_SUM,
                 MPI_COMM_WORLD);
@@ -2588,8 +2587,7 @@ void engine_skip_force_and_kick(struct engine *e) {
         t->type == task_type_timestep || t->subtype == task_subtype_force ||
         t->subtype == task_subtype_grav || t->type == task_type_end_force ||
         t->type == task_type_grav_long_range || t->type == task_type_grav_mm ||
-        t->type == task_type_grav_down || t->type == task_type_cooling ||
-        t->type == task_type_sourceterms)
+        t->type == task_type_grav_down || t->type == task_type_cooling)
       t->skip = 1;
   }
 
@@ -2815,6 +2813,10 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
     double *prev_x = s->parts[0].x;
     long long *prev_id = &s->parts[0].id;
     for (size_t k = 1; k < s->nr_parts; k++) {
+
+      /* Ignore fake buffer particles for on-the-fly creation */
+      if (s->parts[k].time_bin == time_bin_not_created) continue;
+
       if (prev_x[0] == s->parts[k].x[0] && prev_x[1] == s->parts[k].x[1] &&
           prev_x[2] == s->parts[k].x[2]) {
         if (e->verbose)
@@ -2837,6 +2839,10 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
     int failed = 0;
     double *prev_x = s->gparts[0].x;
     for (size_t k = 1; k < s->nr_gparts; k++) {
+
+      /* Ignore fake buffer particles for on-the-fly creation */
+      if (s->gparts[k].time_bin == time_bin_not_created) continue;
+
       if (prev_x[0] == s->gparts[k].x[0] && prev_x[1] == s->gparts[k].x[1] &&
           prev_x[2] == s->gparts[k].x[2]) {
         if (e->verbose)
@@ -3776,8 +3782,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
   /* Re-allocate the local parts. */
   if (e->verbose)
     message("Re-allocating parts array from %zu to %zu.", s->size_parts,
-            (size_t)(s->nr_parts * 1.2));
-  s->size_parts = s->nr_parts * 1.2;
+            (size_t)(s->nr_parts * engine_redistribute_alloc_margin));
+  s->size_parts = s->nr_parts * engine_redistribute_alloc_margin;
   struct part *parts_new = NULL;
   struct xpart *xparts_new = NULL;
   if (posix_memalign((void **)&parts_new, part_align,
@@ -3801,8 +3807,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
   /* Re-allocate the local sparts. */
   if (e->verbose)
     message("Re-allocating sparts array from %zu to %zu.", s->size_sparts,
-            (size_t)(s->nr_sparts * 1.2));
-  s->size_sparts = s->nr_sparts * 1.2;
+            (size_t)(s->nr_sparts * engine_redistribute_alloc_margin));
+  s->size_sparts = s->nr_sparts * engine_redistribute_alloc_margin;
   struct spart *sparts_new = NULL;
   if (posix_memalign((void **)&sparts_new, spart_align,
                      sizeof(struct spart) * s->size_sparts) != 0)
@@ -3819,8 +3825,8 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
   /* Re-allocate the local gparts. */
   if (e->verbose)
     message("Re-allocating gparts array from %zu to %zu.", s->size_gparts,
-            (size_t)(s->nr_gparts * 1.2));
-  s->size_gparts = s->nr_gparts * 1.2;
+            (size_t)(s->nr_gparts * engine_redistribute_alloc_margin));
+  s->size_gparts = s->nr_gparts * engine_redistribute_alloc_margin;
   struct gpart *gparts_new = NULL;
   if (posix_memalign((void **)&gparts_new, gpart_align,
                      sizeof(struct gpart) * s->size_gparts) != 0)
@@ -4034,7 +4040,6 @@ void engine_unpin(void) {
  * @param potential The properties of the external potential.
  * @param cooling_func The properties of the cooling function.
  * @param chemistry The chemistry information.
- * @param sourceterms The properties of the source terms function.
  */
 void engine_init(struct engine *e, struct space *s, struct swift_params *params,
                  long long Ngas, long long Ngparts, long long Nstars,
@@ -4046,8 +4051,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params,
                  struct pm_mesh *mesh,
                  const struct external_potential *potential,
                  struct cooling_function_data *cooling_func,
-                 const struct chemistry_global_data *chemistry,
-                 struct sourceterms *sourceterms) {
+                 const struct chemistry_global_data *chemistry) {
 
   /* Clean-up everything */
   bzero(e, sizeof(struct engine));
@@ -4110,7 +4114,6 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params,
   e->external_potential = potential;
   e->cooling_func = cooling_func;
   e->chemistry = chemistry;
-  e->sourceterms = sourceterms;
   e->parameter_file = params;
   e->cell_loc = NULL;
 #ifdef WITH_MPI
@@ -4780,6 +4783,7 @@ void engine_print_policy(struct engine *e) {
  * @param e The #engine.
  */
 void engine_compute_next_snapshot_time(struct engine *e) {
+
   /* Do outputlist file case */
   if (e->output_list_snapshots) {
     output_list_read_next_time(e->output_list_snapshots, e, "snapshots",
@@ -4800,6 +4804,8 @@ void engine_compute_next_snapshot_time(struct engine *e) {
     time = e->a_first_snapshot;
   else
     time = e->time_first_snapshot;
+
+  int found_snapshot_time = 0;
   while (time < time_end) {
 
     /* Output time on the integer timeline */
@@ -4809,7 +4815,10 @@ void engine_compute_next_snapshot_time(struct engine *e) {
       e->ti_next_snapshot = (time - e->time_begin) / e->time_base;
 
     /* Found it? */
-    if (e->ti_next_snapshot > e->ti_current) break;
+    if (e->ti_next_snapshot > e->ti_current) {
+      found_snapshot_time = 1;
+      break;
+    }
 
     if (e->policy & engine_policy_cosmology)
       time *= e->delta_time_snapshot;
@@ -4818,7 +4827,7 @@ void engine_compute_next_snapshot_time(struct engine *e) {
   }
 
   /* Deal with last snapshot */
-  if (e->ti_next_snapshot >= max_nr_timesteps) {
+  if (!found_snapshot_time) {
     e->ti_next_snapshot = -1;
     if (e->verbose) message("No further output time.");
   } else {
@@ -4864,6 +4873,8 @@ void engine_compute_next_statistics_time(struct engine *e) {
     time = e->a_first_statistics;
   else
     time = e->time_first_statistics;
+
+  int found_stats_time = 0;
   while (time < time_end) {
 
     /* Output time on the integer timeline */
@@ -4873,7 +4884,10 @@ void engine_compute_next_statistics_time(struct engine *e) {
       e->ti_next_stats = (time - e->time_begin) / e->time_base;
 
     /* Found it? */
-    if (e->ti_next_stats > e->ti_current) break;
+    if (e->ti_next_stats > e->ti_current) {
+      found_stats_time = 1;
+      break;
+    }
 
     if (e->policy & engine_policy_cosmology)
       time *= e->delta_time_statistics;
@@ -4882,7 +4896,7 @@ void engine_compute_next_statistics_time(struct engine *e) {
   }
 
   /* Deal with last statistics */
-  if (e->ti_next_stats >= max_nr_timesteps) {
+  if (!found_stats_time) {
     e->ti_next_stats = -1;
     if (e->verbose) message("No further output time.");
   } else {
@@ -4929,6 +4943,8 @@ void engine_compute_next_stf_time(struct engine *e) {
     time = e->a_first_stf_output;
   else
     time = e->time_first_stf_output;
+
+  int found_stf_time = 0;
   while (time < time_end) {
 
     /* Output time on the integer timeline */
@@ -4938,7 +4954,10 @@ void engine_compute_next_stf_time(struct engine *e) {
       e->ti_next_stf = (time - e->time_begin) / e->time_base;
 
     /* Found it? */
-    if (e->ti_next_stf > e->ti_current) break;
+    if (e->ti_next_stf > e->ti_current) {
+      found_stf_time = 1;
+      break;
+    }
 
     if (e->policy & engine_policy_cosmology)
       time *= e->delta_time_stf;
@@ -4947,7 +4966,7 @@ void engine_compute_next_stf_time(struct engine *e) {
   }
 
   /* Deal with last snapshot */
-  if (e->ti_next_stf >= max_nr_timesteps) {
+  if (!found_stf_time) {
     e->ti_next_stf = -1;
     if (e->verbose) message("No further output time.");
   } else {
@@ -5042,9 +5061,11 @@ void engine_recompute_displacement_constraint(struct engine *e) {
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that the minimal mass collection worked */
   float min_part_mass_check = FLT_MAX;
-  for (size_t i = 0; i < e->s->nr_parts; ++i)
+  for (size_t i = 0; i < e->s->nr_parts; ++i) {
+    if (e->s->parts[i].time_bin >= num_time_bins) continue;
     min_part_mass_check =
         min(min_part_mass_check, hydro_get_mass(&e->s->parts[i]));
+  }
   if (min_part_mass_check != min_mass[swift_type_gas])
     error("Error collecting minimal mass of gas particles.");
 #endif
@@ -5204,7 +5225,6 @@ void engine_struct_dump(struct engine *e, FILE *stream) {
   potential_struct_dump(e->external_potential, stream);
   cooling_struct_dump(e->cooling_func, stream);
   chemistry_struct_dump(e->chemistry, stream);
-  sourceterms_struct_dump(e->sourceterms, stream);
   parser_struct_dump(e->parameter_file, stream);
   if (e->output_list_snapshots)
     output_list_struct_dump(e->output_list_snapshots, stream);
@@ -5301,11 +5321,6 @@ void engine_struct_restore(struct engine *e, FILE *stream) {
   chemistry_struct_restore(chemistry, stream);
   e->chemistry = chemistry;
 
-  struct sourceterms *sourceterms =
-      (struct sourceterms *)malloc(sizeof(struct sourceterms));
-  sourceterms_struct_restore(sourceterms, stream);
-  e->sourceterms = sourceterms;
-
   struct swift_params *parameter_file =
       (struct swift_params *)malloc(sizeof(struct swift_params));
   parser_struct_restore(parameter_file, stream);
diff --git a/src/engine.h b/src/engine.h
index 9c39ea15a85d19cf26dfd94a9c42897251c6b42a..d7d916c1bc25fc0d2cbac26c95de406accec5a67 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -46,7 +46,6 @@
 #include "potential.h"
 #include "runner.h"
 #include "scheduler.h"
-#include "sourceterms_struct.h"
 #include "space.h"
 #include "task.h"
 #include "units.h"
@@ -71,13 +70,12 @@ enum engine_policy {
   engine_policy_drift_all = (1 << 11),
   engine_policy_reconstruct_mpoles = (1 << 12),
   engine_policy_cooling = (1 << 13),
-  engine_policy_sourceterms = (1 << 14),
-  engine_policy_stars = (1 << 15),
-  engine_policy_structure_finding = (1 << 16),
-  engine_policy_star_formation = (1 << 17),
-  engine_policy_feedback = (1 << 18)
+  engine_policy_stars = (1 << 14),
+  engine_policy_structure_finding = (1 << 15),
+  engine_policy_star_formation = (1 << 16),
+  engine_policy_feedback = (1 << 17)
 };
-#define engine_maxpolicy 19
+#define engine_maxpolicy 18
 extern const char *engine_policy_names[engine_maxpolicy + 1];
 
 /**
@@ -360,9 +358,6 @@ struct engine {
   /* Properties of the chemistry model */
   const struct chemistry_global_data *chemistry;
 
-  /* Properties of source terms */
-  struct sourceterms *sourceterms;
-
   /* The (parsed) parameter file */
   struct swift_params *parameter_file;
 
@@ -417,8 +412,7 @@ void engine_init(struct engine *e, struct space *s, struct swift_params *params,
                  struct pm_mesh *mesh,
                  const struct external_potential *potential,
                  struct cooling_function_data *cooling_func,
-                 const struct chemistry_global_data *chemistry,
-                 struct sourceterms *sourceterms);
+                 const struct chemistry_global_data *chemistry);
 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);
diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c
index 68841aa5999441e6a2621f867038a44e9f52794c..2c9fe3cb70cfcf118cdb9d1f6fc36a6f854fd7b5 100644
--- a/src/engine_maketasks.c
+++ b/src/engine_maketasks.c
@@ -666,7 +666,6 @@ void engine_add_ghosts(struct engine *e, struct cell *c, struct task *ghost_in,
 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) {
@@ -696,12 +695,6 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) {
       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 */
diff --git a/src/exp10.h b/src/exp10.h
new file mode 100644
index 0000000000000000000000000000000000000000..48a950574693f81a9556b084d66278f348189a73
--- /dev/null
+++ b/src/exp10.h
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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_EXP10_H
+#define SWIFT_EXP10_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local headers. */
+#include "inline.h"
+
+#ifndef __GNUC__
+
+/**
+ * @brief Raises 10 to the power of the argument.
+ *
+ * This function is only used as a replacement for compilers that do
+ * not implement GNU extensions to the C language.
+ *
+ * @param x The input value.
+ */
+__attribute__((always_inline, const)) INLINE static double exp10(
+    const double x) {
+
+  return exp(x * M_LN10);
+}
+
+/**
+ * @brief Raises 10 to the power of the argument.
+ *
+ * This function is only used as a replacement for compilers that do
+ * not implement GNU extensions to the C language.
+ *
+ * @param x The input value.
+ */
+__attribute__((always_inline, const)) INLINE static float exp10f(
+    const float x) {
+
+  return expf(x * (float)M_LN10);
+}
+
+#endif /* __GNUC__ */
+
+#endif /* SWIFT_EXP10_H */
diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h
index c87a4f91d45167b76ee88a9a46329f3785165884..a3c5e21dbdf8df60b25b01c0326c33c3a10d1bce 100644
--- a/src/hydro/Gadget2/hydro_iact.h
+++ b/src/hydro/Gadget2/hydro_iact.h
@@ -56,9 +56,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
   float dv[3], curlvr[3];
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (pi->time_bin == time_bin_inhibited)
+  if (pi->time_bin >= time_bin_inhibited)
     error("Inhibited pi in interaction function!");
-  if (pj->time_bin == time_bin_inhibited)
+  if (pj->time_bin >= time_bin_inhibited)
     error("Inhibited pj in interaction function!");
 #endif
 
@@ -153,9 +153,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
   float dv[3], curlvr[3];
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (pi->time_bin == time_bin_inhibited)
+  if (pi->time_bin >= time_bin_inhibited)
     error("Inhibited pi in interaction function!");
-  if (pj->time_bin == time_bin_inhibited)
+  if (pj->time_bin >= time_bin_inhibited)
     error("Inhibited pj in interaction function!");
 #endif
 
@@ -451,9 +451,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   float wi, wj, wi_dx, wj_dx;
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (pi->time_bin == time_bin_inhibited)
+  if (pi->time_bin >= time_bin_inhibited)
     error("Inhibited pi in interaction function!");
-  if (pj->time_bin == time_bin_inhibited)
+  if (pj->time_bin >= time_bin_inhibited)
     error("Inhibited pj in interaction function!");
 #endif
 
@@ -580,9 +580,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   float wi, wj, wi_dx, wj_dx;
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (pi->time_bin == time_bin_inhibited)
+  if (pi->time_bin >= time_bin_inhibited)
     error("Inhibited pi in interaction function!");
-  if (pj->time_bin == time_bin_inhibited)
+  if (pj->time_bin >= time_bin_inhibited)
     error("Inhibited pj in interaction function!");
 #endif
 
diff --git a/src/hydro/GizmoMFM/hydro.h b/src/hydro/GizmoMFM/hydro.h
index 8e466daabb59482a1c2ebbaf80af30c64c4abdfe..1ce17d76f1814ec9b8d02ccbb73006748545e1e7 100644
--- a/src/hydro/GizmoMFM/hydro.h
+++ b/src/hydro/GizmoMFM/hydro.h
@@ -722,10 +722,12 @@ hydro_get_comoving_internal_energy(const struct part* restrict p) {
  * @brief Returns the physical internal energy of a particle
  *
  * @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 *
@@ -778,10 +780,12 @@ __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
  * @brief Returns the physical internal energy of a particle
  *
  * @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. */
diff --git a/src/hydro/GizmoMFV/hydro.h b/src/hydro/GizmoMFV/hydro.h
index 98a70aefed098243bbf2dfe08e752ee48a838d3e..77db9e3a01f1cfe6a4d3211726cfd9fe30e87cf4 100644
--- a/src/hydro/GizmoMFV/hydro.h
+++ b/src/hydro/GizmoMFV/hydro.h
@@ -808,10 +808,12 @@ hydro_get_comoving_internal_energy(const struct part* restrict p) {
  * @brief Returns the physical internal energy of a particle
  *
  * @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 *
@@ -828,7 +830,7 @@ __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);
+  return hydro_get_physical_internal_energy(p, /*xp=*/NULL, cosmo);
 }
 
 /**
@@ -850,10 +852,12 @@ __attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
  * @brief Returns the physical internal energy of a particle
  *
  * @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. */
diff --git a/src/hydro/Minimal/hydro_iact.h b/src/hydro/Minimal/hydro_iact.h
index e060cb3562f1b319c64d6f6523b18858662312e7..b29f44588c2e13bb5b7c5c9cd5297205557c3fc9 100644
--- a/src/hydro/Minimal/hydro_iact.h
+++ b/src/hydro/Minimal/hydro_iact.h
@@ -54,9 +54,9 @@ __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)
+  if (pi->time_bin >= time_bin_inhibited)
     error("Inhibited pi in interaction function!");
-  if (pj->time_bin == time_bin_inhibited)
+  if (pj->time_bin >= time_bin_inhibited)
     error("Inhibited pj in interaction function!");
 #endif
 
@@ -135,9 +135,9 @@ __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)
+  if (pi->time_bin >= time_bin_inhibited)
     error("Inhibited pi in interaction function!");
-  if (pj->time_bin == time_bin_inhibited)
+  if (pj->time_bin >= time_bin_inhibited)
     error("Inhibited pj in interaction function!");
 #endif
 
@@ -196,9 +196,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
     struct part *restrict pj, float a, float H) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (pi->time_bin == time_bin_inhibited)
+  if (pi->time_bin >= time_bin_inhibited)
     error("Inhibited pi in interaction function!");
-  if (pj->time_bin == time_bin_inhibited)
+  if (pj->time_bin >= time_bin_inhibited)
     error("Inhibited pj in interaction function!");
 #endif
 
@@ -323,9 +323,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
     const struct part *restrict pj, float a, float H) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (pi->time_bin == time_bin_inhibited)
+  if (pi->time_bin >= time_bin_inhibited)
     error("Inhibited pi in interaction function!");
-  if (pj->time_bin == time_bin_inhibited)
+  if (pj->time_bin >= time_bin_inhibited)
     error("Inhibited pj in interaction function!");
 #endif
 
diff --git a/src/hydro_properties.c b/src/hydro_properties.c
index 2b1cd42055c66768e943241c75298e53e0bf75a8..85f88d418bd46354f7a1cd3dd89b0e77b556b7d9 100644
--- a/src/hydro_properties.c
+++ b/src/hydro_properties.c
@@ -119,6 +119,12 @@ 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);
 
+  /* Mean molecular mass for neutral gas */
+  p->mu_neutral = 4. / (1. + 3. * p->hydrogen_mass_fraction);
+
+  /* Mean molecular mass for fully ionised gas */
+  p->mu_ionised = 4. / (8. - 5. * (1. - p->hydrogen_mass_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);
diff --git a/src/hydro_properties.h b/src/hydro_properties.h
index b45b93192e7db7b1bdca49557f8563322f09aae9..5ee6a22d2cf1c22f99e50a9254ef323d333d2a10 100644
--- a/src/hydro_properties.h
+++ b/src/hydro_properties.h
@@ -84,6 +84,12 @@ struct hydro_props {
   /*! Temperature of the neutral to ionized transition of Hydrogen */
   float hydrogen_ionization_temperature;
 
+  /*! Mean molecular weight below hydrogen ionization temperature */
+  float mu_neutral;
+
+  /*! Mean molecular weight above hydrogen ionization temperature */
+  float mu_ionised;
+
   /*! Artificial viscosity parameters */
   struct {
     /*! For the fixed, simple case. Also used to set the initial AV
diff --git a/src/parallel_io.c b/src/parallel_io.c
index febbb4a7db6796fd751cb1eacc174a42936d19a2..1c91b087169420f218b228b8ccdf6deca9925297 100644
--- a/src/parallel_io.c
+++ b/src/parallel_io.c
@@ -1136,6 +1136,8 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
       case swift_type_gas:
         hydro_write_particles(parts, xparts, list, &num_fields);
         num_fields += chemistry_write_particles(parts, list + num_fields);
+        num_fields += cooling_write_particles(parts, xparts, list + num_fields,
+                                              e->cooling_func);
         break;
 
       case swift_type_dark_matter:
@@ -1219,9 +1221,12 @@ void write_output_parallel(struct engine* e, const char* baseName,
   // 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 Ntot_written =
+      e->s->nr_gparts - e->s->nr_inhibited_gparts - e->s->nr_extra_gparts;
+  const size_t Ngas_written =
+      e->s->nr_parts - e->s->nr_inhibited_parts - e->s->nr_extra_parts;
+  const size_t Nstars_written =
+      e->s->nr_sparts - e->s->nr_inhibited_sparts - e->s->nr_extra_sparts;
   const size_t Nbaryons_written = Ngas_written + Nstars_written;
   const size_t Ndm_written =
       Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0;
@@ -1394,8 +1399,8 @@ void write_output_parallel(struct engine* e, const char* baseName,
           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);
+          num_fields += cooling_write_particles(
+              parts, xparts, list + num_fields, e->cooling_func);
         } else {
 
           /* Ok, we need to fish out the particles we want */
@@ -1418,8 +1423,9 @@ void write_output_parallel(struct engine* e, const char* baseName,
                                 &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);
+          num_fields +=
+              cooling_write_particles(parts_written, xparts_written,
+                                      list + num_fields, e->cooling_func);
         }
       } break;
 
diff --git a/src/part.c b/src/part.c
index 3a626e652cf28f0376cadc1d9a40ab85b752e6c1..ec3627d728f69f469cc7d75eb2beb9ae39ed107e 100644
--- a/src/part.c
+++ b/src/part.c
@@ -139,8 +139,9 @@ void part_verify_links(struct part *parts, struct gpart *gparts,
 
   for (size_t k = 0; k < nr_gparts; ++k) {
 
-    /* We have a DM particle */
-    if (gparts[k].type == swift_type_dark_matter) {
+    /* We have a real DM particle */
+    if (gparts[k].type == swift_type_dark_matter &&
+        gparts[k].time_bin != time_bin_not_created) {
 
       /* Check that it's not linked */
       if (gparts[k].id_or_neg_offset <= 0)
diff --git a/src/runner.c b/src/runner.c
index f14f8d8bc2721d0edfaca542b68af645bd5ac1a0..018e35c582efb30d4a0f090b478f8a2adb091d39 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -58,7 +58,6 @@
 #include "runner_doiact_vec.h"
 #include "scheduler.h"
 #include "sort_part.h"
-#include "sourceterms.h"
 #include "space.h"
 #include "space_getsid.h"
 #include "stars.h"
@@ -107,42 +106,6 @@
 #include "runner_doiact_stars.h"
 #undef FUNCTION
 
-/**
- * @brief Perform source terms
- *
- * @param r runner task
- * @param c cell
- * @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->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;
-  const int dimen = 3;
-
-  TIMER_TIC;
-
-  /* Recurse? */
-  if (c->split) {
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL) runner_do_sourceterms(r, c->progeny[k], 0);
-  } else {
-
-    if (count > 0) {
-
-      /* do sourceterms in this cell? */
-      const int incell =
-          sourceterms_test_cell(cell_min, cell_width, sourceterms, dimen);
-      if (incell == 1) {
-        sourceterms_apply(r, sourceterms, c);
-      }
-    }
-  }
-
-  if (timer) TIMER_TOC(timer_dosource);
-}
-
 /**
  * @brief Intermediate task after the density to check that the smoothing
  * lengths are correct.
@@ -499,7 +462,7 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) {
  */
 void runner_do_star_formation(struct runner *r, struct cell *c, int timer) {
 
-  const struct engine *e = r->e;
+  struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
   const int count = c->hydro.count;
   struct part *restrict parts = c->hydro.parts;
@@ -529,9 +492,16 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) {
 
         // MATTHIEU: Temporary star-formation law
         // Do not use this at home.
-        if (rho > 1.5e7 && e->step > 2) {
+        if (rho > 1.7e7 && e->step > 2) {
           message("Removing particle id=%lld rho=%e", p->id, rho);
-          cell_convert_part_to_gpart(e, c, p, xp);
+
+          struct spart *sp = cell_convert_part_to_spart(e, c, p, xp);
+
+          /* Did we run out of fresh particles? */
+          if (sp == NULL) continue;
+
+          /* Set everything to a valid state */
+          stars_init_spart(sp);
         }
       }
     }
@@ -623,9 +593,6 @@ void runner_do_sort_ascending(struct entry *sort, int N) {
  * @brief Recursively checks that the flags are consistent in a cell hierarchy.
  *
  * Debugging function. Exists in two flavours: hydro & stars.
- *
- * @param c The #cell to check.
- * @param flags The sorting flags to check.
  */
 #define RUNNER_CHECK_SORTS(TYPE)                                               \
   void runner_check_sorts_##TYPE(struct cell *c, int flags) {                  \
@@ -2913,9 +2880,6 @@ void *runner_main(void *data) {
         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;
         default:
           error("Unknown/invalid task type (%d).", t->type);
       }
diff --git a/src/serial_io.c b/src/serial_io.c
index 059318df180e0d06e446f9d3f839b16439dd1b34..8c3076461b9463f9cea373e528cd9fb390406981 100644
--- a/src/serial_io.c
+++ b/src/serial_io.c
@@ -792,9 +792,12 @@ void write_output_serial(struct engine* e, const char* baseName,
   // 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 Ntot_written =
+      e->s->nr_gparts - e->s->nr_inhibited_gparts - e->s->nr_extra_gparts;
+  const size_t Ngas_written =
+      e->s->nr_parts - e->s->nr_inhibited_parts - e->s->nr_extra_parts;
+  const size_t Nstars_written =
+      e->s->nr_sparts - e->s->nr_inhibited_sparts - e->s->nr_extra_sparts;
   const size_t Nbaryons_written = Ngas_written + Nstars_written;
   const size_t Ndm_written =
       Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0;
@@ -1071,8 +1074,8 @@ void write_output_serial(struct engine* e, const char* baseName,
               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);
+              num_fields += cooling_write_particles(
+                  parts, xparts, list + num_fields, e->cooling_func);
             } else {
 
               /* Ok, we need to fish out the particles we want */
@@ -1095,8 +1098,9 @@ void write_output_serial(struct engine* e, const char* baseName,
                                     &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);
+              num_fields +=
+                  cooling_write_particles(parts_written, xparts_written,
+                                          list + num_fields, e->cooling_func);
             }
           } break;
 
diff --git a/src/single_io.c b/src/single_io.c
index 833c4a80cb2f43455d34ad3cd694255b7b19038c..50f60b0a18a14f9d8ffbbbc261d38e80937d5fad 100644
--- a/src/single_io.c
+++ b/src/single_io.c
@@ -654,9 +654,12 @@ void write_output_single(struct engine* e, const char* baseName,
   // 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 Ntot_written =
+      e->s->nr_gparts - e->s->nr_inhibited_gparts - e->s->nr_extra_gparts;
+  const size_t Ngas_written =
+      e->s->nr_parts - e->s->nr_inhibited_parts - e->s->nr_extra_parts;
+  const size_t Nstars_written =
+      e->s->nr_sparts - e->s->nr_inhibited_sparts - e->s->nr_extra_sparts;
   const size_t Nbaryons_written = Ngas_written + Nstars_written;
   const size_t Ndm_written =
       Ntot_written > 0 ? Ntot_written - Nbaryons_written : 0;
@@ -890,8 +893,8 @@ void write_output_single(struct engine* e, const char* baseName,
           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);
+          num_fields += cooling_write_particles(
+              parts, xparts, list + num_fields, e->cooling_func);
         } else {
 
           /* Ok, we need to fish out the particles we want */
@@ -914,8 +917,9 @@ void write_output_single(struct engine* e, const char* baseName,
                                 &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);
+          num_fields +=
+              cooling_write_particles(parts_written, xparts_written,
+                                      list + num_fields, e->cooling_func);
         }
       } break;
 
diff --git a/src/sourceterms.c b/src/sourceterms.c
deleted file mode 100644
index 993045e61503e4e78b855816921bc057706b76d1..0000000000000000000000000000000000000000
--- a/src/sourceterms.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*******************************************************************************
- * This file is part of SWIFT.
- * Copyright (c) 2016 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"
-
-/* Local includes. */
-#include "const.h"
-#include "hydro.h"
-#include "parser.h"
-#include "units.h"
-
-/* This object's header. */
-#include "sourceterms.h"
-
-/**
- * @brief Initialises the sourceterms
- *
- * @param parameter_file The parsed parameter file
- * @param us The current internal system of units
- * @param source the structure that has all the source term properties
- */
-void sourceterms_init(struct swift_params *parameter_file,
-                      struct unit_system *us, struct sourceterms *source) {
-#ifdef SOURCETERMS_SN_FEEDBACK
-  supernova_init(parameter_file, us, source);
-#endif /* SOURCETERMS_SN_FEEDBACK */
-};
-
-/**
- * @brief Prints the properties of the source terms to stdout
- * @param source the structure that has all the source term properties
- */
-void sourceterms_print(struct sourceterms *source) {
-#ifdef SOURCETERMS_NONE
-  error(" no sourceterms defined yet you ran with -F");
-#ifdef SOURCETERMS_SN_FEEDBACK
-#error "can't have sourceterms when defined SOURCETERMS_NONE"
-#endif
-#endif
-#ifdef SOURCETERMS_SN_FEEDBACK
-  supernova_print(source);
-#endif /* SOURCETERMS_SN_FEEDBACK */
-};
-
-/**
- * @brief Write a sourceterms struct to the given FILE as a stream of bytes.
- *
- * @param sourceterms the struct
- * @param stream the file stream
- */
-void sourceterms_struct_dump(const struct sourceterms *sourceterms,
-                             FILE *stream) {
-  restart_write_blocks((void *)sourceterms, sizeof(struct sourceterms), 1,
-                       stream, "sourceterms", "sourceterms");
-}
-
-/**
- * @brief Restore a sourceterms struct from the given FILE as a stream of
- * bytes.
- *
- * @param sourceterms the struct
- * @param stream the file stream
- */
-void sourceterms_struct_restore(const struct sourceterms *sourceterms,
-                                FILE *stream) {
-  restart_read_blocks((void *)sourceterms, sizeof(struct sourceterms), 1,
-                      stream, NULL, "sourceterms");
-}
diff --git a/src/sourceterms.h b/src/sourceterms.h
deleted file mode 100644
index 407d2f19362531a3fd3537889593c484319919b5..0000000000000000000000000000000000000000
--- a/src/sourceterms.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*******************************************************************************
- * This file is part of SWIFT.
- * Coypright (c) 2015 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_SOURCETERMS_H
-#define SWIFT_SOURCETERMS_H
-
-/**
- * @file src/sourceterms.h
- * @brief Branches between the different sourceterms functions.
- */
-
-#include "./const.h"
-#include "runner.h"
-
-#ifdef SOURCETERMS_SN_FEEDBACK
-#include "sourceterms/sn_feedback/sn_feedback_struct.h"
-#endif
-
-/* So far only one model here */
-struct sourceterms {
-#ifdef SOURCETERMS_SN_FEEDBACK
-  struct supernova_struct supernova;
-#endif
-};
-#ifdef SOURCETERMS_SN_FEEDBACK
-#include "sourceterms/sn_feedback/sn_feedback.h"
-#endif
-
-void sourceterms_init(struct swift_params* parameter_file,
-                      struct unit_system* us, struct sourceterms* source);
-void sourceterms_print(struct sourceterms* source);
-
-/* Dump/restore. */
-void sourceterms_struct_dump(const struct sourceterms* source, FILE* stream);
-void sourceterms_struct_restore(const struct sourceterms* source, FILE* stream);
-
-/**
- * @brief Routines related to source terms
- * @param cell_min: corner of cell to test
- * @param cell_width: width of cell to test
- * @param sourceterms: properties of source terms to test
- * @param dimen: dimensionality of the problem
- *
- * This routine tests whether a source term should be applied to this cell
- * return: 1 if yes, return: 0 if no
- */
-
-__attribute__((always_inline)) INLINE static int sourceterms_test_cell(
-    const double cell_min[], const double cell_width[],
-    struct sourceterms* sourceterms, const int dimen) {
-#ifdef SOURCETERMS_SN_FEEDBACK
-  return supernova_feedback_test_cell(cell_min, cell_width, sourceterms, dimen);
-#endif
-  return 0;
-};
-
-__attribute__((always_inline)) INLINE static void sourceterms_apply(
-    struct runner* r, struct sourceterms* sourceterms, struct cell* c) {
-#ifdef SOURCETERMS_SN_FEEDBACK
-  supernova_feedback_apply(r, sourceterms, c);
-#endif
-};
-#endif /*  SWIFT_SOURCETERMS_H */
diff --git a/src/sourceterms/sn_feedback/sn_feedback.h b/src/sourceterms/sn_feedback/sn_feedback.h
deleted file mode 100644
index 411673c37e82ff89d906425d1cadaa135c46a38d..0000000000000000000000000000000000000000
--- a/src/sourceterms/sn_feedback/sn_feedback.h
+++ /dev/null
@@ -1,192 +0,0 @@
-/*******************************************************************************
- * This file is part of SWIFT.
- * Copyright (c) 2016 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/>.
- *
- ******************************************************************************/
-#ifndef SWIFT_SN_FEEDBACK_H
-#define SWIFT_SN_FEEDBACK_H
-#include <float.h>
-/* Config parameters. */
-#include "../config.h"
-
-#include "engine.h"
-#include "equation_of_state.h"
-#include "hydro.h"
-#include "runner.h"
-#include "timestep.h"
-
-/**
- * @file src/sourceterms/sn_feedback.h
- *
- * @brief Routines related to sourceterms (supernova feedback): determine if
- * feedback occurs in this cell
- *
- * @param cell_min: corner of cell to test
- * @param cell_width: width of cell to test
- * @param sourceterms: properties of source terms to test
- * @param dimen: dimensionality of the problem
- *
- * This routine tests whether a source term should be applied to this cell
- * return: 1 if yes, return: 0 if no
- */
-__attribute__((always_inline)) INLINE static int supernova_feedback_test_cell(
-    const double cell_min[], const double cell_width[],
-    struct sourceterms* sourceterms, const int dimen) {
-  if (sourceterms->supernova.status == supernova_is_done) return 0;
-
-  const double location[3] = {sourceterms->supernova.x,
-                              sourceterms->supernova.y,
-                              sourceterms->supernova.z};
-  for (int i = 0; i < dimen; i++) {
-    if (cell_min[i] > location[i]) return 0;
-    if ((cell_min[i] + cell_width[i]) <= location[i]) return 0;
-  };
-  return 1;
-};
-
-/**
- * @file src/sourceterms/sn_feedback.h
- *
- * @brief Routines related to source terms (supernova feedback): perform
- * feedback in this cell
- * @param r: the runner
- * @param sourceterms the structure describing the source terms properties
- * @param c the cell to apply feedback to
- *
- * This routine heats an individual particle (p), increasing its thermal energy
- * per unit mass
- *      by supernova energy / particle mass.
- */
-__attribute__((always_inline)) INLINE static void supernova_feedback_apply(
-    struct runner* restrict r, struct sourceterms* restrict sourceterms,
-    struct cell* restrict c) {
-
-  const int count = c->count;
-  struct part* restrict parts = c->parts;
-  struct xpart* restrict xparts = c->xparts;
-  const double timeBase = r->e->timeBase;
-  const int ti_current = r->e->ti_current;
-
-  /* inject SN energy into the particle with highest id in this cell if it is
-   * active */
-  int imax = 0;
-  struct part* restrict p_sn = NULL;
-  struct xpart* restrict xp_sn = NULL;
-
-  for (int i = 0; i < count; i++) {
-
-    /* Get a direct pointer on the part. */
-    struct part* restrict p = &parts[i];
-    if (p->id > imax) {
-      imax = p->id;
-      p_sn = p;
-      xp_sn = &xparts[i];
-    }
-  }
-
-  /* Is this part within the time step? */
-  if (p_sn->ti_begin == ti_current) {
-
-    /* Does this time step straddle the feedback injection time? */
-    const float t_begin = p_sn->ti_begin * timeBase;
-    const float t_end = p_sn->ti_end * timeBase;
-    if (t_begin <= sourceterms->supernova.time &&
-        t_end > sourceterms->supernova.time) {
-
-      /* store old time step */
-      const int dti_old = p_sn->ti_end - p_sn->ti_begin;
-
-      /* add supernova feedback */
-      const float u_old = hydro_get_internal_energy(p_sn, 0);
-      const float ent_old = hydro_get_entropy(p_sn, 0.0);
-      const float u_new =
-          u_old + sourceterms->supernova.energy / hydro_get_mass(p_sn);
-      hydro_set_internal_energy(p_sn, u_new);
-      const float u_set = hydro_get_internal_energy(p_sn, 0.0);
-      const float ent_set = hydro_get_entropy(p_sn, 0.0);
-      message(
-          " applied super nova, time = %e, location= %e %e %e velocity= %e %e "
-          "%e",
-          ti_current * timeBase, p_sn->x[0], p_sn->x[1], p_sn->x[2], p_sn->v[0],
-          p_sn->v[1], p_sn->v[2]);
-      message(
-          " injected SN energy in particle = %lld, increased energy from %e to "
-          "%e and is notw %e, entropy from %e to %e",
-          p_sn->id, u_old, u_new, u_set, ent_old, ent_set);
-
-      /* label supernova as done */
-      sourceterms->supernova.status = supernova_is_done;
-
-      /* update timestep if new time step shorter than old time step */
-      const int dti = get_part_timestep(p_sn, xp_sn, r->e);
-      if (dti < dti_old) {
-        p_sn->ti_end = p_sn->ti_begin + dti;
-        message(" changed timestep from %d to %d", dti_old, dti);
-
-        /* apply simple time-step limiter on all particles in same cell:
-         */
-        int i_limit = 0;
-        for (int i = 0; i < count; i++) {
-          struct part* restrict p = &parts[i];
-          const int dti_old = p->ti_end - p->ti_begin;
-          if (dti_old > 2 * dti) {
-            i_limit++;
-            const int dti_new = 2 * dti;
-            p->ti_end = p->ti_begin + dti_new;
-            message(" old step = %d new step = %d", dti_old, dti_new);
-          } else
-            message(" old step = %d", dti_old);
-        }
-        message(" count= %d limited timestep of %d particles ", count, i_limit);
-      } /* end of limiter */
-      error("end");
-    }
-  }
-};
-
-/**
- * @file src/sourceterms/sn_feedback.h
- *
- * @brief Routine to initialise supernova feedback
- * @param parameterfile: the parse parmeter file
- * @param us: the unit system in use
- * @param sourceterms the structure describing the source terms properties
- *
- * This routine heats an individual particle (p), increasing its thermal energy
- * per unit mass
- *      by supernova energy / particle mass.
- */
-
-__attribute__((always_inline)) INLINE static void supernova_init(
-    struct swift_params* parameter_file, struct unit_system* us,
-    struct sourceterms* source) {
-  source->supernova.time = parser_get_param_double(parameter_file, "SN:time");
-  source->supernova.energy =
-      parser_get_param_double(parameter_file, "SN:energy");
-  source->supernova.x = parser_get_param_double(parameter_file, "SN:x");
-  source->supernova.y = parser_get_param_double(parameter_file, "SN:y");
-  source->supernova.z = parser_get_param_double(parameter_file, "SN:z");
-  source->supernova.status = supernova_is_not_done;
-}
-__attribute__((always_inline)) INLINE static void supernova_print(
-    struct sourceterms* source) {
-  message(
-      " Single SNe of energy= %e will explode at time= %e at location "
-      "(%e,%e,%e)",
-      source->supernova.energy, source->supernova.time, source->supernova.x,
-      source->supernova.y, source->supernova.z);
-}
-#endif /* SWIFT_SN_FEEDBACK_H */
diff --git a/src/sourceterms/sn_feedback/sn_feedback_struct.h b/src/sourceterms/sn_feedback/sn_feedback_struct.h
deleted file mode 100644
index dd1842a6717c6c5a20352324cbe6b018c73e7b3e..0000000000000000000000000000000000000000
--- a/src/sourceterms/sn_feedback/sn_feedback_struct.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*******************************************************************************
- * This file is part of SWIFT.
- * Copyright (c) 2016 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/>.
- *
- ******************************************************************************/
-/**
- * @file src/sourceterms/sn_feedback_struct.h
- * @brief Routines related to source terms (feedback)
- *
- * enumeration type that sets if supernova explosion is done (is_done) or still
- * needs doing (is_not_done)
- */
-#ifndef SWIFT_SN_FEEDBACK_STRUCT_H
-#define SWIFT_SN_FEEDBACK_STRUCT_H
-enum supernova_status { supernova_is_done, supernova_is_not_done };
-
-/**
- * @file src/sourceterms/sn_feedback_struct.h
- * @brief Routines related to source terms (feedback)
- *
- * The structure that describes the source term (supernova feedback)
- * It specifies the time, energy and location of the desired supernova
- * explosion, and a status (supernova_is_done/supernova_is_not_done)
- * that records the status of the supernova
- */
-struct supernova_struct {
-  double time;
-  double energy;
-  double x, y, z;
-  enum supernova_status status;
-};
-#endif /* SWIFT_SN_FEEDBACK_STRUCT_H */
diff --git a/src/sourceterms_struct.h b/src/sourceterms_struct.h
deleted file mode 100644
index b3c38986db52d72df825fda97b36c985dff922b6..0000000000000000000000000000000000000000
--- a/src/sourceterms_struct.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*******************************************************************************
- * This file is part of SWIFT.
- * Coypright (c) 2015 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_SOURCETERMS_STRUCT_H
-#define SWIFT_SOURCETERMS_STRUCT_H
-#include "./const.h"
-#ifdef SOURCETERMS_SN_FEEDBACK
-#include "sourceterms/sn_feedback/sn_feedback_struct.h"
-#endif
-
-#endif /*  SWIFT_SOURCETERMS_STRUCT_H */
diff --git a/src/space.c b/src/space.c
index 82f369a501bc1be13d27d8096acc8a57a004a580..85e1147a909346829b6ff9df7e15619ecc0d15e5 100644
--- a/src/space.c
+++ b/src/space.c
@@ -70,6 +70,18 @@ int space_subsize_pair_stars = space_subsize_pair_stars_default;
 int space_subsize_self_stars = space_subsize_self_stars_default;
 int space_subdepth_diff_grav = space_subdepth_diff_grav_default;
 int space_maxsize = space_maxsize_default;
+
+/*! Number of extra #part we allocate memory for per top-level cell */
+int space_extra_parts = space_extra_parts_default;
+
+/*! Number of extra #spart we allocate memory for per top-level cell */
+int space_extra_sparts = space_extra_sparts_default;
+
+/*! Number of extra #gpart we allocate memory for per top-level cell */
+int space_extra_gparts = space_extra_gparts_default;
+
+/*! Expected maximal number of strays received at a rebuild */
+int space_expected_max_nr_strays = space_expected_max_nr_strays_default;
 #ifdef SWIFT_DEBUG_CHECKS
 int last_cell_id;
 #endif
@@ -104,9 +116,12 @@ struct index_data {
   struct space *s;
   int *ind;
   int *cell_counts;
-  int count_inhibited_part;
-  int count_inhibited_gpart;
-  int count_inhibited_spart;
+  size_t count_inhibited_part;
+  size_t count_inhibited_gpart;
+  size_t count_inhibited_spart;
+  size_t count_extra_part;
+  size_t count_extra_gpart;
+  size_t count_extra_spart;
 };
 
 /**
@@ -136,13 +151,13 @@ void space_rebuild_recycle_rec(struct space *s, struct cell *c,
         c->progeny[k]->next = *cell_rec_begin;
         *cell_rec_begin = c->progeny[k];
 
-        if (s->gravity) {
+        if (s->with_self_gravity) {
           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)
+        if (s->with_self_gravity && *multipole_rec_end == NULL)
           *multipole_rec_end = *multipole_rec_begin;
 
         c->progeny[k]->grav.multipole = NULL;
@@ -182,12 +197,15 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->hydro.sorted = 0;
     c->stars.sorted = 0;
     c->hydro.count = 0;
+    c->hydro.count_total = 0;
     c->hydro.updated = 0;
     c->hydro.inhibited = 0;
     c->grav.count = 0;
+    c->grav.count_total = 0;
     c->grav.updated = 0;
     c->grav.inhibited = 0;
     c->stars.count = 0;
+    c->stars.count_total = 0;
     c->stars.updated = 0;
     c->stars.inhibited = 0;
     c->grav.init = NULL;
@@ -232,7 +250,8 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
 #ifdef SWIFT_DEBUG_CHECKS
     c->cellID = 0;
 #endif
-    if (s->gravity) bzero(c->grav.multipole, sizeof(struct gravity_tensors));
+    if (s->with_self_gravity)
+      bzero(c->grav.multipole, sizeof(struct gravity_tensors));
     for (int i = 0; i < 13; i++) {
       if (c->hydro.sort[i] != NULL) {
         free(c->hydro.sort[i]);
@@ -445,7 +464,7 @@ void space_regrid(struct space *s, int verbose) {
     bzero(s->cells_top, s->nr_cells * sizeof(struct cell));
 
     /* Allocate the multipoles for the top-level cells. */
-    if (s->gravity) {
+    if (s->with_self_gravity) {
       if (posix_memalign((void **)&s->multipoles_top, multipole_align,
                          s->nr_cells * sizeof(struct gravity_tensors)) != 0)
         error("Failed to allocate top-level multipoles.");
@@ -525,7 +544,7 @@ void space_regrid(struct space *s, int verbose) {
           c->mpi.grav.recv = NULL;
           c->mpi.grav.send = NULL;
 #endif  // WITH_MPI
-          if (s->gravity) c->grav.multipole = &s->multipoles_top[cid];
+          if (s->with_self_gravity) c->grav.multipole = &s->multipoles_top[cid];
 #ifdef SWIFT_DEBUG_CHECKS
           c->cellID = -last_cell_id;
           last_cell_id++;
@@ -602,6 +621,341 @@ void space_regrid(struct space *s, int verbose) {
             clocks_getunit());
 }
 
+/**
+ * @brief Allocate memory for the extra particles used for on-the-fly creation.
+ *
+ * This rarely actually allocates memory. Most of the time, we convert
+ * pre-allocated memory inot extra particles.
+ *
+ * This function also sets the extra particles' location to their top-level
+ * cells. They can then be sorted into their correct memory position later on.
+ *
+ * @param s The current #space.
+ * @param verbose Are we talkative?
+ */
+void space_allocate_extras(struct space *s, int verbose) {
+
+  const int local_nodeID = s->e->nodeID;
+
+  /* Anything to do here? (Abort if we don't want extras)*/
+  if (space_extra_parts == 0 && space_extra_gparts == 0 &&
+      space_extra_sparts == 0)
+    return;
+
+  /* The top-level cells */
+  const struct cell *cells = s->cells_top;
+  const double half_cell_width[3] = {0.5 * cells[0].width[0],
+                                     0.5 * cells[0].width[1],
+                                     0.5 * cells[0].width[2]};
+
+  /* The current number of particles (including spare ones) */
+  size_t nr_parts = s->nr_parts;
+  size_t nr_gparts = s->nr_gparts;
+  size_t nr_sparts = s->nr_sparts;
+
+  /* The current number of actual particles */
+  size_t nr_actual_parts = nr_parts - s->nr_extra_parts;
+  size_t nr_actual_gparts = nr_gparts - s->nr_extra_gparts;
+  size_t nr_actual_sparts = nr_sparts - s->nr_extra_sparts;
+
+  /* The number of particles we allocated memory for (MPI overhead) */
+  size_t size_parts = s->size_parts;
+  size_t size_gparts = s->size_gparts;
+  size_t size_sparts = s->size_sparts;
+
+  int local_cells = 0;
+  for (int i = 0; i < s->nr_cells; ++i)
+    if (s->cells_top[i].nodeID == local_nodeID) local_cells++;
+
+  /* Number of extra particles we want for each type */
+  const size_t expected_num_extra_parts = local_cells * space_extra_parts;
+  const size_t expected_num_extra_gparts = local_cells * space_extra_gparts;
+  const size_t expected_num_extra_sparts = local_cells * space_extra_sparts;
+
+  if (verbose) {
+    message("Currently have %zd/%zd/%zd real particles.", nr_actual_parts,
+            nr_actual_gparts, nr_actual_sparts);
+    message("Currently have %zd/%zd/%zd spaces for extra particles.",
+            s->nr_extra_parts, s->nr_extra_gparts, s->nr_extra_sparts);
+    message("Requesting space for future %zd/%zd/%zd part/gpart/sparts.",
+            expected_num_extra_parts, expected_num_extra_gparts,
+            expected_num_extra_sparts);
+  }
+
+  if (expected_num_extra_parts < s->nr_extra_parts)
+    error("Reduction in top-level cells number not handled.");
+  if (expected_num_extra_gparts < s->nr_extra_gparts)
+    error("Reduction in top-level cells number not handled.");
+  if (expected_num_extra_sparts < s->nr_extra_sparts)
+    error("Reduction in top-level cells number not handled.");
+
+  /* Do we have enough space for the extra gparts (i.e. we haven't used up any)
+   * ? */
+  if (nr_gparts + expected_num_extra_gparts > size_gparts) {
+
+    /* Ok... need to put some more in the game */
+
+    /* Do we need to reallocate? */
+    if (nr_actual_gparts + expected_num_extra_gparts > size_gparts) {
+
+      size_gparts = (nr_actual_gparts + expected_num_extra_gparts) *
+                    engine_redistribute_alloc_margin;
+
+      if (verbose)
+        message("Re-allocating gparts array from %zd to %zd", s->size_gparts,
+                size_gparts);
+
+      /* Create more space for parts */
+      struct gpart *gparts_new = NULL;
+      if (posix_memalign((void **)&gparts_new, gpart_align,
+                         sizeof(struct gpart) * size_gparts) != 0)
+        error("Failed to allocate new gpart data");
+      const ptrdiff_t delta = gparts_new - s->gparts;
+      memcpy(gparts_new, s->gparts, sizeof(struct gpart) * s->size_gparts);
+      free(s->gparts);
+      s->gparts = gparts_new;
+
+      /* Update the counter */
+      s->size_gparts = size_gparts;
+
+      /* We now need to reset all the part and spart pointers */
+      for (size_t i = 0; i < nr_parts; ++i) {
+        if (s->parts[i].time_bin != time_bin_not_created)
+          s->parts[i].gpart += delta;
+      }
+      for (size_t i = 0; i < nr_sparts; ++i) {
+        if (s->sparts[i].time_bin != time_bin_not_created)
+          s->sparts[i].gpart += delta;
+      }
+    }
+
+    /* Turn some of the allocated spares into particles we can use */
+    for (size_t i = nr_gparts; i < nr_actual_gparts + expected_num_extra_gparts;
+         ++i) {
+      bzero(&s->gparts[i], sizeof(struct gpart));
+      s->gparts[i].time_bin = time_bin_not_created;
+      s->gparts[i].type = swift_type_dark_matter;
+      s->gparts[i].id_or_neg_offset = -1;
+    }
+
+      /* Put the spare particles in their correct cell */
+#ifdef WITH_MPI
+    error("Need to do this correctly over MPI for only the local cells.");
+#endif
+    int count_in_cell = 0, current_cell = 0;
+    size_t count_extra_gparts = 0;
+    for (size_t i = 0; i < nr_actual_gparts + expected_num_extra_gparts; ++i) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (current_cell == s->nr_cells)
+        error("Cell counter beyond the maximal nr. cells.");
+#endif
+
+      if (s->gparts[i].time_bin == time_bin_not_created) {
+
+        /* We want the extra particles to be at the centre of their cell */
+        s->gparts[i].x[0] = cells[current_cell].loc[0] + half_cell_width[0];
+        s->gparts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1];
+        s->gparts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2];
+        ++count_in_cell;
+        count_extra_gparts++;
+      }
+
+      /* Once we have reached the number of extra gpart per cell, we move to the
+       * next */
+      if (count_in_cell == space_extra_gparts) {
+        ++current_cell;
+        count_in_cell = 0;
+      }
+    }
+
+#ifdef SWIFT_DEBUG_CHECKS
+    if (count_extra_gparts != expected_num_extra_gparts)
+      error("Constructed the wrong number of extra gparts (%zd vs. %zd)",
+            count_extra_gparts, expected_num_extra_gparts);
+#endif
+
+    /* Update the counters */
+    s->nr_gparts = nr_actual_gparts + expected_num_extra_gparts;
+    s->nr_extra_gparts = expected_num_extra_gparts;
+  }
+
+  /* Do we have enough space for the extra parts (i.e. we haven't used up any) ?
+   */
+  if (expected_num_extra_parts > s->nr_extra_parts) {
+
+    /* Ok... need to put some more in the game */
+
+    /* Do we need to reallocate? */
+    if (nr_actual_parts + expected_num_extra_parts > size_parts) {
+
+      size_parts = (nr_actual_parts + expected_num_extra_parts) *
+                   engine_redistribute_alloc_margin;
+
+      if (verbose)
+        message("Re-allocating parts array from %zd to %zd", s->size_parts,
+                size_parts);
+
+      /* Create more space for parts */
+      struct part *parts_new = NULL;
+      if (posix_memalign((void **)&parts_new, part_align,
+                         sizeof(struct part) * size_parts) != 0)
+        error("Failed to allocate new part data");
+      memcpy(parts_new, s->parts, sizeof(struct part) * s->size_parts);
+      free(s->parts);
+      s->parts = parts_new;
+
+      /* Same for xparts */
+      struct xpart *xparts_new = NULL;
+      if (posix_memalign((void **)&xparts_new, xpart_align,
+                         sizeof(struct xpart) * size_parts) != 0)
+        error("Failed to allocate new xpart data");
+      memcpy(xparts_new, s->xparts, sizeof(struct xpart) * s->size_parts);
+      free(s->xparts);
+      s->xparts = xparts_new;
+
+      /* Update the counter */
+      s->size_parts = size_parts;
+    }
+
+    /* Turn some of the allocated spares into particles we can use */
+    for (size_t i = nr_parts; i < nr_actual_parts + expected_num_extra_parts;
+         ++i) {
+      bzero(&s->parts[i], sizeof(struct part));
+      bzero(&s->xparts[i], sizeof(struct xpart));
+      s->parts[i].time_bin = time_bin_not_created;
+      s->parts[i].id = -1;
+    }
+
+      /* Put the spare particles in their correct cell */
+#ifdef WITH_MPI
+    error("Need to do this correctly over MPI for only the local cells.");
+#endif
+    int count_in_cell = 0, current_cell = 0;
+    size_t count_extra_parts = 0;
+    for (size_t i = 0; i < nr_actual_parts + expected_num_extra_parts; ++i) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (current_cell == s->nr_cells)
+        error("Cell counter beyond the maximal nr. cells.");
+#endif
+
+      if (s->parts[i].time_bin == time_bin_not_created) {
+
+        /* We want the extra particles to be at the centre of their cell */
+        s->parts[i].x[0] = cells[current_cell].loc[0] + half_cell_width[0];
+        s->parts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1];
+        s->parts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2];
+        ++count_in_cell;
+        count_extra_parts++;
+      }
+
+      /* Once we have reached the number of extra part per cell, we move to the
+       * next */
+      if (count_in_cell == space_extra_parts) {
+        ++current_cell;
+        count_in_cell = 0;
+      }
+    }
+
+#ifdef SWIFT_DEBUG_CHECKS
+    if (count_extra_parts != expected_num_extra_parts)
+      error("Constructed the wrong number of extra parts (%zd vs. %zd)",
+            count_extra_parts, expected_num_extra_parts);
+#endif
+
+    /* Update the counters */
+    s->nr_parts = nr_actual_parts + expected_num_extra_parts;
+    s->nr_extra_parts = expected_num_extra_parts;
+  }
+
+  /* Do we have enough space for the extra sparts (i.e. we haven't used up any)
+   * ? */
+  if (nr_actual_sparts + expected_num_extra_sparts > nr_sparts) {
+
+    /* Ok... need to put some more in the game */
+
+    /* Do we need to reallocate? */
+    if (nr_actual_sparts + expected_num_extra_sparts > size_sparts) {
+
+      size_sparts = (nr_actual_sparts + expected_num_extra_sparts) *
+                    engine_redistribute_alloc_margin;
+
+      if (verbose)
+        message("Re-allocating sparts array from %zd to %zd", s->size_sparts,
+                size_sparts);
+
+      /* Create more space for parts */
+      struct spart *sparts_new = NULL;
+      if (posix_memalign((void **)&sparts_new, spart_align,
+                         sizeof(struct spart) * size_sparts) != 0)
+        error("Failed to allocate new spart data");
+      memcpy(sparts_new, s->sparts, sizeof(struct spart) * s->size_sparts);
+      free(s->sparts);
+      s->sparts = sparts_new;
+
+      /* Update the counter */
+      s->size_sparts = size_sparts;
+    }
+
+    /* Turn some of the allocated spares into particles we can use */
+    for (size_t i = nr_sparts; i < nr_actual_sparts + expected_num_extra_sparts;
+         ++i) {
+      bzero(&s->sparts[i], sizeof(struct spart));
+      s->sparts[i].time_bin = time_bin_not_created;
+      s->sparts[i].id = -42;
+    }
+
+      /* Put the spare particles in their correct cell */
+#ifdef WITH_MPI
+    error("Need to do this correctly over MPI for only the local cells.");
+#endif
+    int count_in_cell = 0, current_cell = 0;
+    size_t count_extra_sparts = 0;
+    for (size_t i = 0; i < nr_actual_sparts + expected_num_extra_sparts; ++i) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (current_cell == s->nr_cells)
+        error("Cell counter beyond the maximal nr. cells.");
+#endif
+
+      if (s->sparts[i].time_bin == time_bin_not_created) {
+
+        /* We want the extra particles to be at the centre of their cell */
+        s->sparts[i].x[0] = cells[current_cell].loc[0] + half_cell_width[0];
+        s->sparts[i].x[1] = cells[current_cell].loc[1] + half_cell_width[1];
+        s->sparts[i].x[2] = cells[current_cell].loc[2] + half_cell_width[2];
+        ++count_in_cell;
+        count_extra_sparts++;
+      }
+
+      /* Once we have reached the number of extra spart per cell, we move to the
+       * next */
+      if (count_in_cell == space_extra_sparts) {
+        ++current_cell;
+        count_in_cell = 0;
+      }
+    }
+
+#ifdef SWIFT_DEBUG_CHECKS
+    if (count_extra_sparts != expected_num_extra_sparts)
+      error("Constructed the wrong number of extra sparts (%zd vs. %zd)",
+            count_extra_sparts, expected_num_extra_sparts);
+#endif
+
+    /* Update the counters */
+    s->nr_sparts = nr_actual_sparts + expected_num_extra_sparts;
+    s->nr_extra_sparts = expected_num_extra_sparts;
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Verify that the links are correct */
+  if ((nr_gparts > 0 && nr_parts > 0) || (nr_gparts > 0 && nr_sparts > 0))
+    part_verify_links(s->parts, s->gparts, s->sparts, nr_parts, nr_gparts,
+                      nr_sparts, verbose);
+#endif
+}
+
 /**
  * @brief Re-build the cells as well as the tasks.
  *
@@ -622,67 +976,105 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
   /* Re-grid if necessary, or just re-set the cell data. */
   space_regrid(s, verbose);
 
+  /* Allocate extra space for particles that will be created */
+  if (s->with_star_formation) space_allocate_extras(s, verbose);
+
+  struct cell *cells_top = s->cells_top;
+  const integertime_t ti_current = (s->e != NULL) ? s->e->ti_current : 0;
+  const int local_nodeID = s->e->nodeID;
+
+  /* The current number of particles */
   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;
 
-  /* Run through the particles and get their cell index. Allocates
-     an index that is larger than the number of particles to avoid
-     re-allocating after shuffling. */
-  const size_t ind_size = s->size_parts + 100;
-  int *ind = (int *)malloc(sizeof(int) * ind_size);
-  if (ind == NULL) error("Failed to allocate temporary particle indices.");
-  int *cell_part_counts = (int *)calloc(sizeof(int), s->nr_cells);
-  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, &count_inhibited_parts,
-                               verbose);
+  /* The number of particles we allocated memory for */
+  size_t size_parts = s->size_parts;
+  size_t size_gparts = s->size_gparts;
+  size_t size_sparts = s->size_sparts;
+
+  /* Counter for the number of inhibited particles found on the node */
+  size_t count_inhibited_parts = 0;
+  size_t count_inhibited_gparts = 0;
+  size_t count_inhibited_sparts = 0;
+
+  /* Counter for the number of extra particles found on the node */
+  size_t count_extra_parts = 0;
+  size_t count_extra_gparts = 0;
+  size_t count_extra_sparts = 0;
+
+  /* Number of particles we expect to have after strays exchange */
+  const size_t h_index_size = size_parts + space_expected_max_nr_strays;
+  const size_t g_index_size = size_gparts + space_expected_max_nr_strays;
+  const size_t s_index_size = size_sparts + space_expected_max_nr_strays;
+
+  /* Allocate arrays to store the indices of the cells where particles
+     belong. We allocate extra space to allow for particles we may
+     receive from other nodes */
+  int *h_index = (int *)malloc(sizeof(int) * h_index_size);
+  int *g_index = (int *)malloc(sizeof(int) * g_index_size);
+  int *s_index = (int *)malloc(sizeof(int) * s_index_size);
+  if (h_index == NULL || g_index == NULL || s_index == NULL)
+    error("Failed to allocate temporary particle indices.");
+
+  /* Allocate counters of particles that will land in each cell */
+  int *cell_part_counts = (int *)malloc(sizeof(int) * s->nr_cells);
+  int *cell_gpart_counts = (int *)malloc(sizeof(int) * s->nr_cells);
+  int *cell_spart_counts = (int *)malloc(sizeof(int) * s->nr_cells);
+  if (cell_part_counts == NULL || cell_gpart_counts == NULL ||
+      cell_spart_counts == NULL)
+    error("Failed to allocate cell particle count buffer.");
+
+  /* Initialise the counters, including buffer space for future particles */
+  for (int i = 0; i < s->nr_cells; ++i) {
+    cell_part_counts[i] = 0;
+    cell_gpart_counts[i] = 0;
+    cell_spart_counts[i] = 0;
+  }
 
-  /* Run through the gravity particles and get their cell index. */
-  const size_t gind_size = s->size_gparts + 100;
-  int *gind = (int *)malloc(sizeof(int) * gind_size);
-  if (gind == NULL) error("Failed to allocate temporary g-particle indices.");
-  int *cell_gpart_counts = (int *)calloc(sizeof(int), s->nr_cells);
-  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,
-                                &count_inhibited_gparts, verbose);
-
-  /* Run through the star particles and get their cell index. */
-  const size_t sind_size = s->size_sparts + 100;
-  int *sind = (int *)malloc(sizeof(int) * sind_size);
-  if (sind == NULL) error("Failed to allocate temporary s-particle indices.");
-  int *cell_spart_counts = (int *)calloc(sizeof(int), s->nr_cells);
-  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,
-                                &count_inhibited_sparts, verbose);
+  /* Run through the particles and get their cell index. */
+  if (nr_parts > 0)
+    space_parts_get_cell_index(s, h_index, cell_part_counts,
+                               &count_inhibited_parts, &count_extra_parts,
+                               verbose);
+  if (nr_gparts > 0)
+    space_gparts_get_cell_index(s, g_index, cell_gpart_counts,
+                                &count_inhibited_gparts, &count_extra_gparts,
+                                verbose);
+  if (nr_sparts > 0)
+    space_sparts_get_cell_index(s, s_index, cell_spart_counts,
+                                &count_inhibited_sparts, &count_extra_sparts,
+                                verbose);
 
 #ifdef SWIFT_DEBUG_CHECKS
+  /* Some safety 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
 
-  const int local_nodeID = s->e->nodeID;
+  if (count_extra_parts != s->nr_extra_parts)
+    error(
+        "Number of extra parts in the part array not matching the space "
+        "counter.");
+  if (count_extra_gparts != s->nr_extra_gparts)
+    error(
+        "Number of extra gparts in the gpart array not matching the space "
+        "counter.");
+  if (count_extra_sparts != s->nr_extra_sparts)
+    error(
+        "Number of extra sparts in the spart array not matching the space "
+        "counter.");
+#endif
 
   /* 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) {
+      if (h_index[k] == -1 || cells_top[h_index[k]].nodeID != local_nodeID) {
 
         /* One fewer particle */
         nr_parts -= 1;
@@ -701,7 +1093,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
         /* 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));
+        memswap(&h_index[k], &h_index[nr_parts], sizeof(int));
 
       } else {
         /* Increment when not exchanging otherwise we need to retest "k".*/
@@ -712,17 +1104,17 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that all parts are in the correct places. */
-  int check_count_inhibited_part = 0;
+  size_t check_count_inhibited_part = 0;
   for (size_t k = 0; k < nr_parts; k++) {
-    if (ind[k] == -1 || cells_top[ind[k]].nodeID != local_nodeID) {
+    if (h_index[k] == -1 || cells_top[h_index[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 (ind[k] != -1 && cells_top[ind[k]].nodeID == local_nodeID) {
+    if (h_index[k] != -1 && cells_top[h_index[k]].nodeID == local_nodeID) {
       error("Failed to remove local parts from send list");
     }
-    if (ind[k] == -1) ++check_count_inhibited_part;
+    if (h_index[k] == -1) ++check_count_inhibited_part;
   }
   if (check_count_inhibited_part != count_inhibited_parts)
     error("Counts of inhibited particles do not match!");
@@ -733,7 +1125,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
     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) {
+      if (s_index[k] == -1 || cells_top[s_index[k]].nodeID != local_nodeID) {
 
         /* One fewer particle */
         nr_sparts -= 1;
@@ -750,7 +1142,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
         }
 
         /* Swap the index */
-        memswap(&sind[k], &sind[nr_sparts], sizeof(int));
+        memswap(&s_index[k], &s_index[nr_sparts], sizeof(int));
 
       } else {
         /* Increment when not exchanging otherwise we need to retest "k".*/
@@ -761,17 +1153,17 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that all sparts are in the correct place. */
-  int check_count_inhibited_spart = 0;
+  size_t check_count_inhibited_spart = 0;
   for (size_t k = 0; k < nr_sparts; k++) {
-    if (sind[k] == -1 || cells_top[sind[k]].nodeID != local_nodeID) {
+    if (s_index[k] == -1 || cells_top[s_index[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 (sind[k] != -1 && cells_top[sind[k]].nodeID == local_nodeID) {
+    if (s_index[k] != -1 && cells_top[s_index[k]].nodeID == local_nodeID) {
       error("Failed to remove local sparts from send list");
     }
-    if (sind[k] == -1) ++check_count_inhibited_spart;
+    if (s_index[k] == -1) ++check_count_inhibited_spart;
   }
   if (check_count_inhibited_spart != count_inhibited_sparts)
     error("Counts of inhibited s-particles do not match!");
@@ -782,7 +1174,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
     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) {
+      if (g_index[k] == -1 || cells_top[g_index[k]].nodeID != local_nodeID) {
 
         /* One fewer particle */
         nr_gparts -= 1;
@@ -805,7 +1197,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
         }
 
         /* Swap the index */
-        memswap(&gind[k], &gind[nr_gparts], sizeof(int));
+        memswap(&g_index[k], &g_index[nr_gparts], sizeof(int));
       } else {
         /* Increment when not exchanging otherwise we need to retest "k".*/
         k++;
@@ -815,17 +1207,17 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that all gparts are in the correct place. */
-  int check_count_inhibited_gpart = 0;
+  size_t check_count_inhibited_gpart = 0;
   for (size_t k = 0; k < nr_gparts; k++) {
-    if (gind[k] == -1 || cells_top[gind[k]].nodeID != local_nodeID) {
+    if (g_index[k] == -1 || cells_top[g_index[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 (gind[k] != -1 && cells_top[gind[k]].nodeID == local_nodeID) {
+    if (g_index[k] != -1 && cells_top[g_index[k]].nodeID == local_nodeID) {
       error("Failed to remove local gparts from send list");
     }
-    if (gind[k] == -1) ++check_count_inhibited_gpart;
+    if (g_index[k] == -1) ++check_count_inhibited_gpart;
   }
   if (check_count_inhibited_gpart != count_inhibited_gparts)
     error("Counts of inhibited g-particles do not match!");
@@ -834,21 +1226,23 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
 #ifdef WITH_MPI
 
   /* Exchange the strays, note that this potentially re-allocates
-     the parts arrays. This can be skipped if we just repartitioned aspace
-     there should be no strays */
+     the parts arrays. This can be skipped if we just repartitioned space
+     as there should be no strays in that case */
   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);
+    engine_exchange_strays(s->e, nr_parts, &h_index[nr_parts],
+                           &nr_parts_exchanged, nr_gparts, &g_index[nr_gparts],
+                           &nr_gparts_exchanged, nr_sparts, &s_index[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)
@@ -870,23 +1264,23 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
   }
 
   /* Re-allocate the index array for the parts if needed.. */
-  if (s->nr_parts + 1 > ind_size) {
+  if (s->nr_parts + 1 > h_index_size) {
     int *ind_new;
     if ((ind_new = (int *)malloc(sizeof(int) * (s->nr_parts + 1))) == NULL)
       error("Failed to allocate temporary particle indices.");
-    memcpy(ind_new, ind, sizeof(int) * nr_parts);
-    free(ind);
-    ind = ind_new;
+    memcpy(ind_new, h_index, sizeof(int) * nr_parts);
+    free(h_index);
+    h_index = ind_new;
   }
 
   /* Re-allocate the index array for the sparts if needed.. */
-  if (s->nr_sparts + 1 > sind_size) {
+  if (s->nr_sparts + 1 > s_index_size) {
     int *sind_new;
     if ((sind_new = (int *)malloc(sizeof(int) * (s->nr_sparts + 1))) == NULL)
       error("Failed to allocate temporary s-particle indices.");
-    memcpy(sind_new, sind, sizeof(int) * nr_sparts);
-    free(sind);
-    sind = sind_new;
+    memcpy(sind_new, s_index, sizeof(int) * nr_sparts);
+    free(s_index);
+    s_index = sind_new;
   }
 
   const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]};
@@ -895,13 +1289,13 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
   /* Assign each received part to its cell. */
   for (size_t k = nr_parts; k < s->nr_parts; k++) {
     const struct part *const p = &s->parts[k];
-    ind[k] =
+    h_index[k] =
         cell_getid(cdim, p->x[0] * ih[0], p->x[1] * ih[1], p->x[2] * ih[2]);
-    cell_part_counts[ind[k]]++;
+    cell_part_counts[h_index[k]]++;
 #ifdef SWIFT_DEBUG_CHECKS
-    if (cells_top[ind[k]].nodeID != local_nodeID)
+    if (cells_top[h_index[k]].nodeID != local_nodeID)
       error("Received part that does not belong to me (nodeID=%i).",
-            cells_top[ind[k]].nodeID);
+            cells_top[h_index[k]].nodeID);
 #endif
   }
   nr_parts = s->nr_parts;
@@ -909,13 +1303,13 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
   /* Assign each received spart to its cell. */
   for (size_t k = nr_sparts; k < s->nr_sparts; k++) {
     const struct spart *const sp = &s->sparts[k];
-    sind[k] =
+    s_index[k] =
         cell_getid(cdim, sp->x[0] * ih[0], sp->x[1] * ih[1], sp->x[2] * ih[2]);
-    cell_spart_counts[sind[k]]++;
+    cell_spart_counts[s_index[k]]++;
 #ifdef SWIFT_DEBUG_CHECKS
-    if (cells_top[sind[k]].nodeID != local_nodeID)
+    if (cells_top[s_index[k]].nodeID != local_nodeID)
       error("Received s-part that does not belong to me (nodeID=%i).",
-            cells_top[sind[k]].nodeID);
+            cells_top[s_index[k]].nodeID);
 #endif
   }
   nr_sparts = s->nr_sparts;
@@ -930,8 +1324,8 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
 
   /* Sort the parts according to their cells. */
   if (nr_parts > 0)
-    space_parts_sort(s->parts, s->xparts, ind, cell_part_counts, s->nr_cells,
-                     0);
+    space_parts_sort(s->parts, s->xparts, h_index, cell_part_counts,
+                     s->nr_cells, 0);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Verify that the part have been sorted correctly. */
@@ -949,7 +1343,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
     /* New cell of this part */
     const struct cell *c = &s->cells_top[new_ind];
 
-    if (ind[k] != new_ind)
+    if (h_index[k] != new_ind)
       error("part's new cell index not matching sorted index.");
 
     if (p->x[0] < c->loc[0] || p->x[0] > c->loc[0] + c->width[0] ||
@@ -961,7 +1355,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
 
   /* Sort the sparts according to their cells. */
   if (nr_sparts > 0)
-    space_sparts_sort(s->sparts, sind, cell_spart_counts, s->nr_cells, 0);
+    space_sparts_sort(s->sparts, s_index, cell_spart_counts, s->nr_cells, 0);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Verify that the spart have been sorted correctly. */
@@ -979,7 +1373,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
     /* New cell of this spart */
     const struct cell *c = &s->cells_top[new_sind];
 
-    if (sind[k] != new_sind)
+    if (s_index[k] != new_sind)
       error("spart's new cell index not matching sorted index.");
 
     if (sp->x[0] < c->loc[0] || sp->x[0] > c->loc[0] + c->width[0] ||
@@ -989,54 +1383,58 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
   }
 #endif /* SWIFT_DEBUG_CHECKS */
 
-  /* Extract the cell counts from the sorted indices. */
+  /* Extract the cell counts from the sorted indices. Deduct the extra
+   * particles. */
   size_t last_index = 0;
-  ind[nr_parts] = s->nr_cells;  // sentinel.
+  h_index[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]].hydro.count = k - last_index + 1;
+    if (h_index[k] < h_index[k + 1]) {
+      cells_top[h_index[k]].hydro.count =
+          k - last_index + 1 - space_extra_parts;
       last_index = k + 1;
     }
   }
 
-  /* Extract the cell counts from the sorted indices. */
+  /* Extract the cell counts from the sorted indices. Deduct the extra
+   * particles. */
   size_t last_sindex = 0;
-  sind[nr_sparts] = s->nr_cells;  // sentinel.
+  s_index[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]].stars.count = k - last_sindex + 1;
+    if (s_index[k] < s_index[k + 1]) {
+      cells_top[s_index[k]].stars.count =
+          k - last_sindex + 1 - space_extra_sparts;
       last_sindex = k + 1;
     }
   }
 
   /* We no longer need the indices as of here. */
-  free(ind);
+  free(h_index);
   free(cell_part_counts);
-  free(sind);
+  free(s_index);
   free(cell_spart_counts);
 
 #ifdef WITH_MPI
 
   /* Re-allocate the index array for the gparts if needed.. */
-  if (s->nr_gparts + 1 > gind_size) {
+  if (s->nr_gparts + 1 > g_index_size) {
     int *gind_new;
     if ((gind_new = (int *)malloc(sizeof(int) * (s->nr_gparts + 1))) == NULL)
       error("Failed to allocate temporary g-particle indices.");
-    memcpy(gind_new, gind, sizeof(int) * nr_gparts);
-    free(gind);
-    gind = gind_new;
+    memcpy(gind_new, g_index, sizeof(int) * nr_gparts);
+    free(g_index);
+    g_index = gind_new;
   }
 
   /* Assign each received gpart to its cell. */
   for (size_t k = nr_gparts; k < s->nr_gparts; k++) {
     const struct gpart *const p = &s->gparts[k];
-    gind[k] =
+    g_index[k] =
         cell_getid(cdim, p->x[0] * ih[0], p->x[1] * ih[1], p->x[2] * ih[2]);
-    cell_gpart_counts[gind[k]]++;
+    cell_gpart_counts[g_index[k]]++;
 #ifdef SWIFT_DEBUG_CHECKS
-    if (cells_top[gind[k]].nodeID != s->e->nodeID)
+    if (cells_top[g_index[k]].nodeID != s->e->nodeID)
       error("Received g-part that does not belong to me (nodeID=%i).",
-            cells_top[gind[k]].nodeID);
+            cells_top[g_index[k]].nodeID);
 #endif
   }
   nr_gparts = s->nr_gparts;
@@ -1055,8 +1453,8 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
 
   /* Sort the gparts according to their cells. */
   if (nr_gparts > 0)
-    space_gparts_sort(s->gparts, s->parts, s->sparts, gind, cell_gpart_counts,
-                      s->nr_cells);
+    space_gparts_sort(s->gparts, s->parts, s->sparts, g_index,
+                      cell_gpart_counts, s->nr_cells);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Verify that the gpart have been sorted correctly. */
@@ -1074,7 +1472,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
     /* New cell of this gpart */
     const struct cell *c = &s->cells_top[new_gind];
 
-    if (gind[k] != new_gind)
+    if (g_index[k] != new_gind)
       error("gpart's new cell index not matching sorted index.");
 
     if (gp->x[0] < c->loc[0] || gp->x[0] > c->loc[0] + c->width[0] ||
@@ -1084,18 +1482,20 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
   }
 #endif /* SWIFT_DEBUG_CHECKS */
 
-  /* Extract the cell counts from the sorted indices. */
+  /* Extract the cell counts from the sorted indices. Deduct the extra
+   * particles. */
   size_t last_gindex = 0;
-  gind[nr_gparts] = s->nr_cells;
+  g_index[nr_gparts] = s->nr_cells;
   for (size_t k = 0; k < nr_gparts; k++) {
-    if (gind[k] < gind[k + 1]) {
-      cells_top[gind[k]].grav.count = k - last_gindex + 1;
+    if (g_index[k] < g_index[k + 1]) {
+      cells_top[g_index[k]].grav.count =
+          k - last_gindex + 1 - space_extra_gparts;
       last_gindex = k + 1;
     }
   }
 
   /* We no longer need the indices as of here. */
-  free(gind);
+  free(g_index);
   free(cell_gpart_counts);
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -1134,10 +1534,15 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
       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];
+
+      c->hydro.count_total = c->hydro.count + space_extra_parts;
+      c->grav.count_total = c->grav.count + space_extra_gparts;
+      c->stars.count_total = c->stars.count + space_extra_sparts;
+
+      finger = &finger[c->hydro.count_total];
+      xfinger = &xfinger[c->hydro.count_total];
+      gfinger = &gfinger[c->grav.count_total];
+      sfinger = &sfinger[c->stars.count_total];
 
       /* Add this cell to the list of local cells */
       s->local_cells_top[s->nr_local_cells] = k;
@@ -1160,13 +1565,17 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
             clocks_from_ticks(getticks() - tic2), 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. */
+  /* Re-order the extra particles such that they are at the end of their cell's
+     memory pool. */
+  if (s->with_star_formation) space_reorder_extras(s, verbose);
+
+  /* At this point, we have the upper-level cells. Now recursively split each
+     cell to get the full AMR grid. */
   space_split(s, verbose);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that the multipole construction went OK */
-  if (s->gravity)
+  if (s->with_self_gravity)
     for (int k = 0; k < s->nr_cells; k++)
       cell_check_multipole(&s->cells_top[k]);
 #endif
@@ -1201,6 +1610,75 @@ void space_split(struct space *s, int verbose) {
             clocks_getunit());
 }
 
+void space_reorder_extra_parts_mapper(void *map_data, int num_cells,
+                                      void *extra_data) {
+
+  struct cell *cells_top = (struct cell *)map_data;
+  struct space *s = (struct space *)extra_data;
+
+  for (int ind = 0; ind < num_cells; ind++) {
+    struct cell *c = &cells_top[ind];
+    cell_reorder_extra_parts(c, c->hydro.parts - s->parts);
+  }
+}
+
+void space_reorder_extra_gparts_mapper(void *map_data, int num_cells,
+                                       void *extra_data) {
+
+  struct cell *cells_top = (struct cell *)map_data;
+  struct space *s = (struct space *)extra_data;
+
+  for (int ind = 0; ind < num_cells; ind++) {
+    struct cell *c = &cells_top[ind];
+    cell_reorder_extra_gparts(c, s->parts, s->sparts);
+  }
+}
+
+void space_reorder_extra_sparts_mapper(void *map_data, int num_cells,
+                                       void *extra_data) {
+
+  struct cell *cells_top = (struct cell *)map_data;
+  struct space *s = (struct space *)extra_data;
+
+  for (int ind = 0; ind < num_cells; ind++) {
+    struct cell *c = &cells_top[ind];
+    cell_reorder_extra_sparts(c, c->stars.parts - s->sparts);
+  }
+}
+
+/**
+ * @brief Re-orders the particles in each cell such that the extra particles
+ * for on-the-fly creation are located at the end of their respective cells.
+ *
+ * This assumes that all the particles (real and extra) have already been sorted
+ * in their correct top-level cell.
+ *
+ * @param s The #space to act upon.
+ * @param verbose Are we talkative?
+ */
+void space_reorder_extras(struct space *s, int verbose) {
+
+#ifdef WITH_MPI
+  if (space_extra_parts || space_extra_gparts || space_extra_sparts)
+    error("Need an MPI-proof version of this.");
+#endif
+
+  /* Re-order the gas particles */
+  if (space_extra_parts)
+    threadpool_map(&s->e->threadpool, space_reorder_extra_parts_mapper,
+                   s->cells_top, s->nr_cells, sizeof(struct cell), 0, s);
+
+  /* Re-order the gravity particles */
+  if (space_extra_gparts)
+    threadpool_map(&s->e->threadpool, space_reorder_extra_gparts_mapper,
+                   s->cells_top, s->nr_cells, sizeof(struct cell), 0, s);
+
+  /* Re-order the star particles */
+  if (space_extra_sparts)
+    threadpool_map(&s->e->threadpool, space_reorder_extra_sparts_mapper,
+                   s->cells_top, s->nr_cells, sizeof(struct cell), 0, s);
+}
+
 /**
  * @brief #threadpool mapper function to sanitize the cells
  *
@@ -1264,7 +1742,8 @@ 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;
+  size_t count_inhibited_part = 0;
+  size_t count_extra_part = 0;
 
   /* Loop over the parts. */
   for (int k = 0; k < nr_parts; k++) {
@@ -1296,26 +1775,31 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts,
             pos_z);
 #endif
 
-    /* Is this particle to be removed? */
     if (p->time_bin == time_bin_inhibited) {
+      /* Is this particle to be removed? */
       ind[k] = -1;
       ++count_inhibited_part;
+    } else if (p->time_bin == time_bin_not_created) {
+      /* Is this a place-holder for on-the-fly creation? */
+      ind[k] = index;
+      cell_counts[index]++;
+      ++count_extra_part;
     } else {
-      /* List its top-level cell index */
+      /* Normal case: list its top-level cell index */
       ind[k] = index;
       cell_counts[index]++;
-    }
 
-    /* Compute minimal mass */
-    min_mass = min(min_mass, hydro_get_mass(p));
+      /* Compute minimal mass */
+      min_mass = min(min_mass, hydro_get_mass(p));
 
-    /* Compute sum of velocity norm */
-    sum_vel_norm += p->v[0] * p->v[0] + p->v[1] * p->v[1] + p->v[2] * p->v[2];
+      /* Compute sum of velocity norm */
+      sum_vel_norm += p->v[0] * p->v[0] + p->v[1] * p->v[1] + p->v[2] * p->v[2];
 
-    /* Update the position */
-    p->x[0] = pos_x;
-    p->x[1] = pos_y;
-    p->x[2] = pos_z;
+      /* Update the position */
+      p->x[0] = pos_x;
+      p->x[1] = pos_y;
+      p->x[2] = pos_z;
+    }
   }
 
   /* Write the counts back to the global array. */
@@ -1323,8 +1807,10 @@ 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 the count of inhibited and extra parts */
+  if (count_inhibited_part)
+    atomic_add(&data->count_inhibited_part, count_inhibited_part);
+  if (count_extra_part) atomic_add(&data->count_extra_part, count_extra_part);
 
   /* Write back the minimal part mass and velocity sum */
   atomic_min_f(&s->min_part_mass, min_mass);
@@ -1364,7 +1850,8 @@ 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;
+  size_t count_inhibited_gpart = 0;
+  size_t count_extra_gpart = 0;
 
   for (int k = 0; k < nr_gparts; k++) {
 
@@ -1395,31 +1882,36 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts,
             pos_z);
 #endif
 
-    /* Is this particle to be removed? */
     if (gp->time_bin == time_bin_inhibited) {
+      /* Is this particle to be removed? */
       ind[k] = -1;
       ++count_inhibited_gpart;
+    } else if (gp->time_bin == time_bin_not_created) {
+      /* Is this a place-holder for on-the-fly creation? */
+      ind[k] = index;
+      cell_counts[index]++;
+      ++count_extra_gpart;
     } else {
       /* List its top-level cell index */
       ind[k] = index;
       cell_counts[index]++;
-    }
 
-    if (gp->type == swift_type_dark_matter) {
+      if (gp->type == swift_type_dark_matter) {
 
-      /* Compute minimal mass */
-      min_mass = min(min_mass, gp->mass);
+        /* 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];
-    }
+        /* 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];
+      }
 
-    /* Update the position */
-    gp->x[0] = pos_x;
-    gp->x[1] = pos_y;
-    gp->x[2] = pos_z;
+      /* Update the position */
+      gp->x[0] = pos_x;
+      gp->x[1] = pos_y;
+      gp->x[2] = pos_z;
+    }
   }
 
   /* Write the counts back to the global array. */
@@ -1427,8 +1919,11 @@ 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 the count of inhibited and extra gparts */
+  if (count_inhibited_gpart)
+    atomic_add(&data->count_inhibited_gpart, count_inhibited_gpart);
+  if (count_extra_gpart)
+    atomic_add(&data->count_extra_gpart, count_extra_gpart);
 
   /* Write back the minimal part mass and velocity sum */
   atomic_min_f(&s->min_gpart_mass, min_mass);
@@ -1468,7 +1963,8 @@ 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;
+  size_t count_inhibited_spart = 0;
+  size_t count_extra_spart = 0;
 
   for (int k = 0; k < nr_sparts; k++) {
 
@@ -1503,23 +1999,28 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
     if (sp->time_bin == time_bin_inhibited) {
       ind[k] = -1;
       ++count_inhibited_spart;
+    } else if (sp->time_bin == time_bin_not_created) {
+      /* Is this a place-holder for on-the-fly creation? */
+      ind[k] = index;
+      cell_counts[index]++;
+      ++count_extra_spart;
     } else {
       /* List its top-level cell index */
       ind[k] = index;
       cell_counts[index]++;
-    }
 
-    /* Compute minimal mass */
-    min_mass = min(min_mass, sp->mass);
+      /* Compute minimal mass */
+      min_mass = min(min_mass, sp->mass);
 
-    /* Compute sum of velocity norm */
-    sum_vel_norm +=
-        sp->v[0] * sp->v[0] + sp->v[1] * sp->v[1] + sp->v[2] * sp->v[2];
+      /* Compute sum of velocity norm */
+      sum_vel_norm +=
+          sp->v[0] * sp->v[0] + sp->v[1] * sp->v[1] + sp->v[2] * sp->v[2];
 
-    /* Update the position */
-    sp->x[0] = pos_x;
-    sp->x[1] = pos_y;
-    sp->x[2] = pos_z;
+      /* Update the position */
+      sp->x[0] = pos_x;
+      sp->x[1] = pos_y;
+      sp->x[2] = pos_z;
+    }
   }
 
   /* Write the counts back to the global array. */
@@ -1527,8 +2028,11 @@ 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 the count of inhibited and extra sparts */
+  if (count_inhibited_spart)
+    atomic_add(&data->count_inhibited_spart, count_inhibited_spart);
+  if (count_extra_spart)
+    atomic_add(&data->count_extra_spart, count_extra_spart);
 
   /* Write back the minimal part mass and velocity sum */
   atomic_min_f(&s->min_spart_mass, min_mass);
@@ -1544,10 +2048,13 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
  * @param ind The array of indices to fill.
  * @param cell_counts The cell counters to update.
  * @param count_inhibited_parts (return) The number of #part to remove.
+ * @param count_extra_parts (return) The number of #part for on-the-fly
+ * creation.
  * @param verbose Are we talkative ?
  */
 void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts,
-                                int *count_inhibited_parts, int verbose) {
+                                size_t *count_inhibited_parts,
+                                size_t *count_extra_parts, int verbose) {
 
   const ticks tic = getticks();
 
@@ -1563,11 +2070,15 @@ void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts,
   data.count_inhibited_part = 0;
   data.count_inhibited_gpart = 0;
   data.count_inhibited_spart = 0;
+  data.count_extra_part = 0;
+  data.count_extra_gpart = 0;
+  data.count_extra_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;
+  *count_extra_parts = data.count_extra_part;
 
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
@@ -1583,10 +2094,13 @@ void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts,
  * @param gind The array of indices to fill.
  * @param cell_counts The cell counters to update.
  * @param count_inhibited_gparts (return) The number of #gpart to remove.
+ * @param count_extra_gparts (return) The number of #gpart for on-the-fly
+ * creation.
  * @param verbose Are we talkative ?
  */
 void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
-                                 int *count_inhibited_gparts, int verbose) {
+                                 size_t *count_inhibited_gparts,
+                                 size_t *count_extra_gparts, int verbose) {
 
   const ticks tic = getticks();
 
@@ -1602,11 +2116,15 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
   data.count_inhibited_part = 0;
   data.count_inhibited_gpart = 0;
   data.count_inhibited_spart = 0;
+  data.count_extra_part = 0;
+  data.count_extra_gpart = 0;
+  data.count_extra_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;
+  *count_extra_gparts = data.count_extra_gpart;
 
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
@@ -1622,10 +2140,13 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
  * @param sind The array of indices to fill.
  * @param cell_counts The cell counters to update.
  * @param count_inhibited_sparts (return) The number of #spart to remove.
+ * @param count_extra_sparts (return) The number of #spart for on-the-fly
+ * creation.
  * @param verbose Are we talkative ?
  */
 void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
-                                 int *count_inhibited_sparts, int verbose) {
+                                 size_t *count_inhibited_sparts,
+                                 size_t *count_extra_sparts, int verbose) {
 
   const ticks tic = getticks();
 
@@ -1641,11 +2162,15 @@ void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
   data.count_inhibited_part = 0;
   data.count_inhibited_gpart = 0;
   data.count_inhibited_spart = 0;
+  data.count_extra_part = 0;
+  data.count_extra_gpart = 0;
+  data.count_extra_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;
+  *count_extra_sparts = data.count_extra_spart;
 
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
@@ -2018,7 +2543,7 @@ void space_split_recursive(struct space *s, struct cell *c,
   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 with_self_gravity = s->with_self_gravity;
   const int depth = c->depth;
   int maxdepth = 0;
   float h_max = 0.0f;
@@ -2046,6 +2571,8 @@ void space_split_recursive(struct space *s, struct cell *c,
 #ifdef SWIFT_DEBUG_CHECKS
         if (parts[k].time_bin == time_bin_inhibited)
           error("Inhibited particle present in space_split()");
+        if (parts[k].time_bin == time_bin_not_created)
+          error("Extra particle present in space_split()");
 #endif
         buff[k].x[0] = parts[k].x[0];
         buff[k].x[1] = parts[k].x[1];
@@ -2060,6 +2587,8 @@ void space_split_recursive(struct space *s, struct cell *c,
 #ifdef SWIFT_DEBUG_CHECKS
         if (gparts[k].time_bin == time_bin_inhibited)
           error("Inhibited particle present in space_split()");
+        if (gparts[k].time_bin == time_bin_not_created)
+          error("Extra particle present in space_split()");
 #endif
         gbuff[k].x[0] = gparts[k].x[0];
         gbuff[k].x[1] = gparts[k].x[1];
@@ -2074,6 +2603,8 @@ void space_split_recursive(struct space *s, struct cell *c,
 #ifdef SWIFT_DEBUG_CHECKS
         if (sparts[k].time_bin == time_bin_inhibited)
           error("Inhibited particle present in space_split()");
+        if (sparts[k].time_bin == time_bin_not_created)
+          error("Extra particle present in space_split()");
 #endif
         sbuff[k].x[0] = sparts[k].x[0];
         sbuff[k].x[1] = sparts[k].x[1];
@@ -2094,8 +2625,8 @@ void space_split_recursive(struct space *s, struct cell *c,
   }
 
   /* Split or let it be? */
-  if ((with_gravity && gcount > space_splitsize) ||
-      (!with_gravity &&
+  if ((with_self_gravity && gcount > space_splitsize) ||
+      (!with_self_gravity &&
        (count > space_splitsize || scount > space_splitsize))) {
 
     /* No longer just a leaf. */
@@ -2108,6 +2639,9 @@ void space_split_recursive(struct space *s, struct cell *c,
       cp->hydro.count = 0;
       cp->grav.count = 0;
       cp->stars.count = 0;
+      cp->hydro.count_total = 0;
+      cp->grav.count_total = 0;
+      cp->stars.count_total = 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;
@@ -2193,7 +2727,7 @@ void space_split_recursive(struct space *s, struct cell *c,
     }
 
     /* Deal with the multipole */
-    if (s->gravity) {
+    if (s->with_self_gravity) {
 
       /* Reset everything */
       gravity_reset(c->grav.multipole);
@@ -2317,6 +2851,8 @@ void space_split_recursive(struct space *s, struct cell *c,
     /* parts: Get dt_min/dt_max and h_max. */
     for (int k = 0; k < count; k++) {
 #ifdef SWIFT_DEBUG_CHECKS
+      if (parts[k].time_bin == time_bin_not_created)
+        error("Extra particle present in space_split()");
       if (parts[k].time_bin == time_bin_inhibited)
         error("Inhibited particle present in space_split()");
 #endif
@@ -2335,6 +2871,8 @@ void space_split_recursive(struct space *s, struct cell *c,
     /* gparts: Get dt_min/dt_max. */
     for (int k = 0; k < gcount; k++) {
 #ifdef SWIFT_DEBUG_CHECKS
+      if (gparts[k].time_bin == time_bin_not_created)
+        error("Extra g-particle present in space_split()");
       if (gparts[k].time_bin == time_bin_inhibited)
         error("Inhibited g-particle present in space_split()");
 #endif
@@ -2345,6 +2883,8 @@ void space_split_recursive(struct space *s, struct cell *c,
     /* sparts: Get dt_min/dt_max */
     for (int k = 0; k < scount; k++) {
 #ifdef SWIFT_DEBUG_CHECKS
+      if (sparts[k].time_bin == time_bin_not_created)
+        error("Extra s-particle present in space_split()");
       if (sparts[k].time_bin == time_bin_inhibited)
         error("Inhibited s-particle present in space_split()");
 #endif
@@ -2372,7 +2912,7 @@ void space_split_recursive(struct space *s, struct cell *c,
     ti_stars_end_min = get_integer_time_end(ti_current, stars_time_bin_min);
 
     /* Construct the multipole and the centre of mass*/
-    if (s->gravity) {
+    if (s->with_self_gravity) {
       if (gcount > 0) {
 
         gravity_P2M(c->grav.multipole, c->grav.parts, c->grav.count);
@@ -2480,7 +3020,7 @@ void space_recycle(struct space *s, struct cell *c) {
   lock_lock(&s->lock);
 
   /* Hook the multipole back in the buffer */
-  if (s->gravity) {
+  if (s->with_self_gravity) {
     c->grav.multipole->next = s->multipoles_sub;
     s->multipoles_sub = c->grav.multipole;
   }
@@ -2538,7 +3078,7 @@ void space_recycle_list(struct space *s, struct cell *cell_list_begin,
   s->tot_cells -= count;
 
   /* Hook the multipoles into the buffer. */
-  if (s->gravity) {
+  if (s->with_self_gravity) {
     multipole_list_end->next = s->multipoles_sub;
     s->multipoles_sub = multipole_list_begin;
   }
@@ -2582,7 +3122,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) {
     }
 
     /* Is the multipole buffer empty? */
-    if (s->gravity && s->multipoles_sub == NULL) {
+    if (s->with_self_gravity && s->multipoles_sub == NULL) {
       if (posix_memalign(
               (void **)&s->multipoles_sub, multipole_align,
               space_cellallocchunk * sizeof(struct gravity_tensors)) != 0)
@@ -2600,7 +3140,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) {
     s->tot_cells += 1;
 
     /* Hook the multipole */
-    if (s->gravity) {
+    if (s->with_self_gravity) {
       cells[j]->grav.multipole = s->multipoles_sub;
       s->multipoles_sub = cells[j]->grav.multipole->next;
     }
@@ -2773,6 +3313,13 @@ void space_first_init_parts_mapper(void *restrict map_data, int count,
   const struct chemistry_global_data *chemistry = e->chemistry;
   const struct cooling_function_data *cool_func = e->cooling_func;
 
+  /* Check that the smoothing lengths are non-zero */
+  for (int k = 0; k < count; k++) {
+    if (p[k].h <= 0.)
+      error("Invalid value of smoothing length for part %lld h=%e", p[k].id,
+            p[k].h);
+  }
+
   /* Convert velocities to internal units */
   for (int k = 0; k < count; k++) {
     p[k].v[0] *= a_factor_vel;
@@ -2900,12 +3447,15 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count,
 
   struct spart *restrict sp = (struct spart *)map_data;
   const struct space *restrict s = (struct space *)extra_data;
+  const struct engine *e = s->e;
 
 #ifdef SWIFT_DEBUG_CHECKS
   const ptrdiff_t delta = sp - s->sparts;
 #endif
 
-  const struct cosmology *cosmo = s->e->cosmology;
+  const int with_feedback = (e->policy & engine_policy_feedback);
+
+  const struct cosmology *cosmo = e->cosmology;
   const float a_factor_vel = cosmo->a;
 
   /* Convert velocities to internal units */
@@ -2926,6 +3476,13 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count,
 #endif
   }
 
+  /* Check that the smoothing lengths are non-zero */
+  for (int k = 0; k < count; k++) {
+    if (with_feedback && sp[k].h <= 0.)
+      error("Invalid value of smoothing length for spart %lld h=%e", sp[k].id,
+            sp[k].h);
+  }
+
   /* Initialise the rest */
   for (int k = 0; k < count; k++) {
 
@@ -3045,8 +3602,11 @@ void space_convert_quantities_mapper(void *restrict map_data, int count,
   const ptrdiff_t index = parts - s->parts;
   struct xpart *restrict xparts = s->xparts + index;
 
+  /* Loop over all the particles ignoring the extra buffer ones for on-the-fly
+   * creation */
   for (int k = 0; k < count; k++)
-    hydro_convert_quantities(&parts[k], &xparts[k], cosmo, hydro_props);
+    if (parts[k].time_bin <= num_time_bins)
+      hydro_convert_quantities(&parts[k], &xparts[k], cosmo, hydro_props);
 }
 
 /**
@@ -3087,6 +3647,7 @@ void space_convert_quantities(struct space *s, int verbose) {
  * @param generate_gas_in_ics Are we generating gas particles from the gparts?
  * @param hydro flag whether we are doing hydro or not?
  * @param self_gravity flag whether we are doing gravity or not?
+ * @param star_formation flag whether we are doing star formation or not?
  * @param verbose Print messages to stdout or not.
  * @param dry_run If 1, just initialise stuff, don't do anything with the parts.
  *
@@ -3100,7 +3661,8 @@ void space_init(struct space *s, struct swift_params *params,
                 struct part *parts, struct gpart *gparts, struct spart *sparts,
                 size_t Npart, size_t Ngpart, size_t Nspart, int periodic,
                 int replicate, int generate_gas_in_ics, int hydro,
-                int self_gravity, int verbose, int dry_run) {
+                int self_gravity, int star_formation, int verbose,
+                int dry_run) {
 
   /* Clean-up everything */
   bzero(s, sizeof(struct space));
@@ -3110,16 +3672,23 @@ void space_init(struct space *s, struct swift_params *params,
   s->dim[1] = dim[1];
   s->dim[2] = dim[2];
   s->periodic = periodic;
-  s->gravity = self_gravity;
-  s->hydro = hydro;
+  s->with_self_gravity = self_gravity;
+  s->with_hydro = hydro;
+  s->with_star_formation = star_formation;
   s->nr_parts = Npart;
-  s->size_parts = Npart;
-  s->parts = parts;
   s->nr_gparts = Ngpart;
-  s->size_gparts = Ngpart;
-  s->gparts = gparts;
   s->nr_sparts = Nspart;
+  s->size_parts = Npart;
+  s->size_gparts = Ngpart;
   s->size_sparts = Nspart;
+  s->nr_inhibited_parts = 0;
+  s->nr_inhibited_gparts = 0;
+  s->nr_inhibited_sparts = 0;
+  s->nr_extra_parts = 0;
+  s->nr_extra_gparts = 0;
+  s->nr_extra_sparts = 0;
+  s->parts = parts;
+  s->gparts = gparts;
   s->sparts = sparts;
   s->min_part_mass = FLT_MAX;
   s->min_gpart_mass = FLT_MAX;
@@ -3202,6 +3771,12 @@ void space_init(struct space *s, struct swift_params *params,
   space_subdepth_diff_grav =
       parser_get_opt_param_int(params, "Scheduler:cell_subdepth_diff_grav",
                                space_subdepth_diff_grav_default);
+  space_extra_parts = parser_get_opt_param_int(
+      params, "Scheduler:cell_extra_parts", space_extra_parts_default);
+  space_extra_sparts = parser_get_opt_param_int(
+      params, "Scheduler:cell_extra_sparts", space_extra_sparts_default);
+  space_extra_gparts = parser_get_opt_param_int(
+      params, "Scheduler:cell_extra_gparts", space_extra_gparts_default);
 
   if (verbose) {
     message("max_size set to %d split_size set to %d", space_maxsize,
@@ -3308,6 +3883,9 @@ void space_init(struct space *s, struct swift_params *params,
   last_cell_id = 1;
 #endif
 
+  /* Do we want any spare particles for on the fly cration? */
+  if (!star_formation) space_extra_sparts = 0;
+
   /* Build the cells recursively. */
   if (!dry_run) space_regrid(s, verbose);
 }
@@ -3453,7 +4031,7 @@ void space_generate_gas(struct space *s, const struct cosmology *cosmo,
                         int periodic, const double dim[3], int verbose) {
 
   /* Check that this is a sensible ting to do */
-  if (!s->hydro)
+  if (!s->with_hydro)
     error(
         "Cannot generate gas from ICs if we are running without "
         "hydrodynamics. Need to run with -s and the corresponding "
diff --git a/src/space.h b/src/space.h
index e6d774200be1a31d622419dceafb16b3826ce177..a1280945d2aa232cbb5e5b519266bc7058e5dc57 100644
--- a/src/space.h
+++ b/src/space.h
@@ -44,6 +44,10 @@ struct cosmology;
 #define space_cellallocchunk 1000
 #define space_splitsize_default 400
 #define space_maxsize_default 8000000
+#define space_extra_parts_default 0
+#define space_extra_gparts_default 0
+#define space_extra_sparts_default 100
+#define space_expected_max_nr_strays_default 100
 #define space_subsize_pair_hydro_default 256000000
 #define space_subsize_self_hydro_default 32000
 #define space_subsize_pair_grav_default 256000000
@@ -68,6 +72,9 @@ extern int space_subsize_self_grav;
 extern int space_subsize_pair_stars;
 extern int space_subsize_self_stars;
 extern int space_subdepth_diff_grav;
+extern int space_extra_parts;
+extern int space_extra_gparts;
+extern int space_extra_sparts;
 
 /**
  * @brief The space in which the cells and particles reside.
@@ -84,10 +91,13 @@ struct space {
   struct hydro_space hs;
 
   /*! Are we doing hydrodynamics? */
-  int hydro;
+  int with_hydro;
 
   /*! Are we doing gravity? */
-  int gravity;
+  int with_self_gravity;
+
+  /*! Are we doing star formation? */
+  int with_star_formation;
 
   /*! Width of the top-level cells. */
   double width[3];
@@ -149,14 +159,23 @@ struct space {
   /*! 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;
+  /*! The total number of #part in the space. */
+  size_t nr_parts;
+
+  /*! The total number of #gpart in the space. */
+  size_t nr_gparts;
+
+  /*! The total number of #spart in the space. */
+  size_t nr_sparts;
+
+  /*! The total number of #part we allocated memory for */
+  size_t size_parts;
 
-  /*! The total number of g-parts in the space. */
-  size_t nr_gparts, size_gparts;
+  /*! The total number of #gpart we allocated memory for */
+  size_t size_gparts;
 
-  /*! The total number of g-parts in the space. */
-  size_t nr_sparts, size_sparts;
+  /*! The total number of #spart we allocated memory for */
+  size_t size_sparts;
 
   /*! Number of inhibted gas particles in the space */
   size_t nr_inhibited_parts;
@@ -167,6 +186,15 @@ struct space {
   /*! Number of inhibted star particles in the space */
   size_t nr_inhibited_sparts;
 
+  /*! Number of extra #part we allocated (for on-the-fly creation) */
+  size_t nr_extra_parts;
+
+  /*! Number of extra #gpart we allocated (for on-the-fly creation) */
+  size_t nr_extra_gparts;
+
+  /*! Number of extra #spart we allocated (for on-the-fly creation) */
+  size_t nr_extra_sparts;
+
   /*! The particle data (cells have pointers to this). */
   struct part *parts;
 
@@ -241,7 +269,7 @@ void space_init(struct space *s, struct swift_params *params,
                 struct part *parts, struct gpart *gparts, struct spart *sparts,
                 size_t Npart, size_t Ngpart, size_t Nspart, int periodic,
                 int replicate, int generate_gas_in_ics, int hydro, int gravity,
-                int verbose, int dry_run);
+                int star_formation, int verbose, int dry_run);
 void space_sanitize(struct space *s);
 void space_map_cells_pre(struct space *s, int full,
                          void (*fun)(struct cell *c, void *data), void *data);
@@ -260,14 +288,18 @@ void space_recycle_list(struct space *s, struct cell *cell_list_begin,
                         struct gravity_tensors *multipole_list_begin,
                         struct gravity_tensors *multipole_list_end);
 void space_split(struct space *s, int verbose);
+void space_reorder_extras(struct space *s, int verbose);
 void space_split_mapper(void *map_data, int num_elements, void *extra_data);
 void space_list_useful_top_level_cells(struct space *s);
 void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts,
-                                int *count_inibibited_parts, int verbose);
+                                size_t *count_inhibited_parts,
+                                size_t *count_extra_parts, int verbose);
 void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
-                                 int *count_inibibited_gparts, int verbose);
+                                 size_t *count_inhibited_gparts,
+                                 size_t *count_extra_gparts, int verbose);
 void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
-                                 int *count_inibibited_sparts, int verbose);
+                                 size_t *count_inhibited_sparts,
+                                 size_t *count_extra_sparts, int verbose);
 void space_synchronize_particle_positions(struct space *s);
 void space_do_parts_sort(void);
 void space_do_gparts_sort(void);
diff --git a/src/swift.h b/src/swift.h
index 153c4ae0d4440d083f1b0c9850e1f2649c0df6fb..7279fc58b3b4ac0227c11b8a835c829df2ceb49c 100644
--- a/src/swift.h
+++ b/src/swift.h
@@ -65,7 +65,6 @@
 #include "scheduler.h"
 #include "serial_io.h"
 #include "single_io.h"
-#include "sourceterms.h"
 #include "space.h"
 #include "stars.h"
 #include "stars_io.h"
diff --git a/src/task.c b/src/task.c
index 3918dad3b713c6c226e5dacf3e38756910c1dd27..f68d10f7cfbcfd512151e5bd4c21089128a6b804 100644
--- a/src/task.c
+++ b/src/task.c
@@ -75,7 +75,6 @@ const char *taskID_names[task_type_count] = {"none",
                                              "grav_mesh",
                                              "cooling",
                                              "star_formation",
-                                             "sourceterms",
                                              "logger",
                                              "stars_ghost_in",
                                              "stars_ghost",
@@ -141,7 +140,6 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
     case task_type_ghost:
     case task_type_extra_ghost:
     case task_type_cooling:
-    case task_type_sourceterms:
       return task_action_part;
       break;
 
@@ -391,6 +389,12 @@ void task_unlock(struct task *t) {
       cell_munlocktree(cj);
       break;
 
+    case task_type_star_formation:
+      cell_unlocktree(ci);
+      cell_sunlocktree(ci);
+      cell_gunlocktree(ci);
+      break;
+
     default:
       break;
   }
@@ -545,6 +549,21 @@ int task_lock(struct task *t) {
         cell_munlocktree(ci);
         return 0;
       }
+      break;
+
+    case task_type_star_formation:
+      /* Lock the gas, gravity and star particles */
+      if (ci->hydro.hold || ci->stars.hold || ci->grav.phold) return 0;
+      if (cell_locktree(ci) != 0) return 0;
+      if (cell_slocktree(ci) != 0) {
+        cell_unlocktree(ci);
+        return 0;
+      }
+      if (cell_glocktree(ci) != 0) {
+        cell_unlocktree(ci);
+        cell_sunlocktree(ci);
+        return 0;
+      }
 
     default:
       break;
diff --git a/src/task.h b/src/task.h
index 994b2b14c05965b71e877feac5cb9827a1d1b4bb..5bce55d6a28fae7ad99f966267b14e926b7fd924 100644
--- a/src/task.h
+++ b/src/task.h
@@ -67,7 +67,6 @@ enum task_types {
   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,
diff --git a/src/timeline.h b/src/timeline.h
index 38727def50b5a81c073ab23375f0c548ca096b66..09161b3ec68e9f7c19bf812103384257473f5719 100644
--- a/src/timeline.h
+++ b/src/timeline.h
@@ -40,6 +40,9 @@ typedef char timebin_t;
 /*! Fictious time-bin to hold inhibited particles */
 #define time_bin_inhibited (num_time_bins + 2)
 
+/*! Fictious time-bin to hold particles not yet created */
+#define time_bin_not_created (num_time_bins + 3)
+
 /*! Fictitious time-bin for particles not awaken */
 #define time_bin_not_awake (0)
 
diff --git a/src/timers.c b/src/timers.c
index d7523edaa42570f8e5f3c01516075267988dfd9c..9ede0320e49c70b2488cd5ceb3e4b6965659aa74 100644
--- a/src/timers.c
+++ b/src/timers.c
@@ -61,7 +61,6 @@ const char* timers_names[timer_count] = {
     "dograv_mesh",
     "dograv_top_level",
     "dograv_long_range",
-    "dosource",
     "dosub_self_density",
     "dosub_self_gradient",
     "dosub_self_force",
diff --git a/src/timers.h b/src/timers.h
index 412f36d7de547040e29efdede9cc6826358929bc..3a2a939339e6d08b43836d4f5ca213af0822c2b2 100644
--- a/src/timers.h
+++ b/src/timers.h
@@ -62,7 +62,6 @@ enum {
   timer_dograv_mesh,
   timer_dograv_top_level,
   timer_dograv_long_range,
-  timer_dosource,
   timer_dosub_self_density,
   timer_dosub_self_gradient,
   timer_dosub_self_force,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7a0242437b8496d8c8756b1bccd2abb4d991262f..41bffaef3e1c61d5de2216d5e10c4841a0125929 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -15,9 +15,9 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # 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) $(NUMA_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)
+AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_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 \
diff --git a/theory/Cosmology/operators.tex b/theory/Cosmology/operators.tex
index 89aa32bae554dceba8f1525cb209728a17154f5b..4ec4ea4b5fa49082295675420f562fa9e45e3e18 100644
--- a/theory/Cosmology/operators.tex
+++ b/theory/Cosmology/operators.tex
@@ -37,19 +37,21 @@ time using $\Delta t_{\rm kick,A} = \Delta t_{\rm
   operator. They then use $\int H dt$ as the operator, which
   integrates out trivially. This slight inconsistency with the rest of
   the time-integration operators is unlikely to lead to any practical
-  difference.}, whilst the change in energy due to the expansion of
-the Universe (first term in eq.~\ref{eq:cosmo_eom_u}) can be computed
-using
-\begin{equation}
-  \int_{a_n}^{a_{n+1}} H dt = \int_{a_n}^{a_{n+1}} \frac{da}{a} =
-  \log{a_{n+1}} - \log{a_n}.
-\end{equation}
+  difference.}. We additionally compute a few other terms
+appearing in some viscosity terms and subgrid models. There are the
+difference in cosmic time between the start and the end of the step
+and the corresponding change in redshift:
+\begin{align}
+  \Delta t_{\rm cosmic} &= \int_{a_n}^{a_{n+1}} dt = \frac{1}{H_0}
+  \int_{a_n}^{a_{n+1}} \frac{da}{a E(a)},\\
+  \Delta z &= \frac{1}{a_n} - \frac{1}{a_{n+1}} \approx -\frac{H}{a} \Delta t_{\rm cosmic}.
+\end{align}
 Following the same method as for the age of the Universe
-(sec. \ref{ssec:flrw}), the three non-trivial integrals are evaluated
-numerically at the start of the simulation for a series $10^4$ values
-of $a$ placed at regular intervals between $\log a_{\rm begin}$ and
-$\log a_{\rm end}$. The values for a specific pair of scale-factors
-$a_n$ and $a_{n+1}$ are then obtained by interpolating that table
-linearly.
+(sec. \ref{ssec:flrw}), these three non-trivial integrals are
+evaluated numerically at the start of the simulation for a series
+$10^4$ values of $a$ placed at regular intervals between $\log a_{\rm
+  begin}$ and $\log a_{\rm end}$. The values for a specific pair of
+scale-factors $a_n$ and $a_{n+1}$ are then obtained by interpolating
+that table linearly.
 
 
diff --git a/theory/Cosmology/timesteps.tex b/theory/Cosmology/timesteps.tex
index 4a1c2ef534d32c667f1b5b655e9b93ae618b8c99..a9856d03a2d04cc0c917176a828fc135a83fb745 100644
--- a/theory/Cosmology/timesteps.tex
+++ b/theory/Cosmology/timesteps.tex
@@ -40,3 +40,7 @@ dominates the overall time-step size calculation.
 
 \subsubsection{Conversion from time to integer time-line} 
 
+\begin{equation}
+  \int_{a_n}^{a_{n+1}} H dt = \int_{a_n}^{a_{n+1}} \frac{da}{a} =
+  \log{a_{n+1}} - \log{a_n}.
+\end{equation}
diff --git a/tools/task_plots/analyse_tasks.py b/tools/task_plots/analyse_tasks.py
index ca41970c683a1680e9d1054c9d70d6370992a05e..f79a0090b04e013c9e10c53e6a4796e46579e1b9 100755
--- a/tools/task_plots/analyse_tasks.py
+++ b/tools/task_plots/analyse_tasks.py
@@ -91,7 +91,6 @@ TASKTYPES = [
     "grav_mesh",
     "cooling",
     "star_formation",
-    "sourceterms",
     "logger",
     "stars_ghost_in",
     "stars_ghost",
diff --git a/tools/task_plots/plot_tasks.py b/tools/task_plots/plot_tasks.py
index 1fe7bcbd11f30ff17051bc9a7ae789439df8b9e9..1ff722a6079883a043109e26dea8d6b3fb405000 100755
--- a/tools/task_plots/plot_tasks.py
+++ b/tools/task_plots/plot_tasks.py
@@ -176,7 +176,6 @@ TASKTYPES = [
     "grav_mesh",
     "cooling",
     "star_formation",
-    "sourceterms",
     "logger",
     "stars_ghost_in",
     "stars_ghost",