diff --git a/.gitignore b/.gitignore
index 3521b353ec4b1c4dea7192047e197596f083fe99..db06575cf9f291d1fb9fa253fb5146c065523dd9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,12 +29,14 @@ examples/used_parameters.yml
 examples/energy.txt
 examples/*/*.xmf
 examples/*/*.hdf5
+examples/*/*.png
 examples/*/*.txt
 examples/*/used_parameters.yml
 examples/*/*/*.xmf
 examples/*/*/*.hdf5
 examples/*/*/*.txt
 examples/*/*/used_parameters.yml
+examples/*/*.png
 
 tests/testPair
 tests/brute_force_standard.dat
@@ -61,6 +63,7 @@ tests/testFFT
 tests/testInteractions
 tests/testSymmetry
 tests/testMaths
+tests/testThreadpool
 tests/testParser
 tests/parser_output.yml
 tests/test27cells.sh
@@ -70,7 +73,11 @@ tests/testPair.sh
 tests/testPairPerturbed.sh
 tests/testParser.sh
 tests/testReading.sh
-
+tests/testAdiabaticIndex
+tests/testRiemannExact
+tests/testRiemannTRRS
+tests/testRiemannHLLC
+tests/testMatrixInversion
 
 theory/latex/swift.pdf
 theory/kernel/kernels.pdf
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 883a63c034401ec1fceb477fe33c8342f74a87c1..1248883c0aea4d1ecc3cfeaa219b739ee7712de6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1,12 @@
 The SWIFT source code is using a variation of the 'Google' formatting style. 
 The script 'format.sh' in the root directory applies the clang-format-3.8
 tool with our style choices to all the SWIFT C source file. Please apply 
-the formatting script to the files before submitting a merge request.
\ No newline at end of file
+the formatting script to the files before submitting a merge request.
+
+The SWIFT code comes with a series of unit tests that are run automatically 
+when a push to the master branch occurs. The suite can be run by doing a `make 
+check` in the root directory. Please check that the test suite still
+runs with your changes applied before submitting a merge request and add 
+relevant unit tests probing the correctness of new modules. An example of how
+to add a test to the suite can be found by considering the tests/testGreeting 
+case.
\ No newline at end of file
diff --git a/README b/README
index cd2a397a18e872e7914b24fd58cc588ec1d6c8c0..2fc16c28f5b14904e566cf734f2c0e34235f93ae 100644
--- a/README
+++ b/README
@@ -22,12 +22,12 @@ Valid options are:
   -d          Dry run. Read the parameter file, allocate memory but does not read 
               the particles from ICs and exit before the start of time integration.
               Allows user to check validy of parameter and IC files as well as memory limits.
+  -D          Always drift all particles even the ones far from active particles.
   -e          Enable floating-point exceptions (debugging mode)
   -f    {int} Overwrite the CPU frequency (Hz) to be used for time measurements
   -g          Run with an external gravitational potential
   -G          Run with self-gravity
-  -n    {int} Execute a fixed number of time steps. When unset use the time_end
-              parameter to stop. 
+  -n    {int} Execute a fixed number of time steps. When unset use the time_end parameter to stop. 
   -s          Run with SPH
   -t    {int} The number of threads to use on each MPI rank. Defaults to 1 if not specified.
   -v     [12] Increase the level of verbosity 1: MPI-rank 0 writes 
diff --git a/configure.ac b/configure.ac
index 483937a9ce4b166410ee42e312b24b13551b5d6a..82382447fde7c411f61dbd62f7db388a6a8d9cf9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,7 +16,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # Init the project.
-AC_INIT([SWIFT],[0.3.0])
+AC_INIT([SWIFT],[0.4.0])
 AC_CONFIG_SRCDIR([src/space.c])
 AC_CONFIG_AUX_DIR([.])
 AM_INIT_AUTOMAKE
@@ -466,10 +466,10 @@ if test "$enable_warn" != "no"; then
     # We will do this by hand instead and only default to the macro for unknown compilers
     case "$ax_cv_c_compiler_vendor" in
           gnu | clang)
-             CFLAGS="$CFLAGS -Wall"
+             CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter"
           ;;
 	  intel)
-             CFLAGS="$CFLAGS -w2"
+             CFLAGS="$CFLAGS -w2 -Wunused-variable"
           ;;
 	  *)
 	     AX_CFLAGS_WARN_ALL
diff --git a/examples/BigCosmoVolume/makeIC.py b/examples/BigCosmoVolume/makeIC.py
index 411ac54b41fadc4209b314b5b9976e5ac95d8000..c141337c06fb28aa4049e2823fcc7cd3e9d5513c 100644
--- a/examples/BigCosmoVolume/makeIC.py
+++ b/examples/BigCosmoVolume/makeIC.py
@@ -133,6 +133,7 @@ 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
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
diff --git a/examples/CosmoVolume/run.sh b/examples/CosmoVolume/run.sh
index 412456c3cb1ae9b869afa52b1046747e32c2eefe..e128aa8f88cc30e13c420bda2088c9958c5bfc31 100755
--- a/examples/CosmoVolume/run.sh
+++ b/examples/CosmoVolume/run.sh
@@ -7,4 +7,4 @@ then
     ./getIC.sh
 fi
 
-../swift -s -t 16 cosmoVolume.yml
+../swift -s -t 16 cosmoVolume.yml 2>&1 | tee output.log
diff --git a/examples/Disk-Patch/GravityOnly/README b/examples/Disk-Patch/GravityOnly/README
new file mode 100644
index 0000000000000000000000000000000000000000..5bf2638fc5ed48ebe248773223dde888af0c3bc8
--- /dev/null
+++ b/examples/Disk-Patch/GravityOnly/README
@@ -0,0 +1,10 @@
+Setup for a potential of a patch disk, see Creasey, Theuns &
+Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948
+
+The density is given by
+rho(z) = (Sigma/2b) / cosh^2(z/b)
+where Sigma is the surface density, and b the scale height.
+
+The corresponding force is
+dphi/dz = 2 pi G Sigma tanh(z/b),
+which satifies d^2phi/dz^2 = 4 pi G rho.
diff --git a/examples/Disk-Patch/GravityOnly/disk-patch.yml b/examples/Disk-Patch/GravityOnly/disk-patch.yml
new file mode 100644
index 0000000000000000000000000000000000000000..78b42e78356f83e80eee8e7f5f91ad7dcf90c37f
--- /dev/null
+++ b/examples/Disk-Patch/GravityOnly/disk-patch.yml
@@ -0,0 +1,43 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.9885e33     # Grams
+  UnitLength_in_cgs:   3.0856776e18  # Centimeters
+  UnitVelocity_in_cgs: 1e5           # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   480.  # The end time of the simulation (in internal units).
+  dt_min:     1e-3  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1     # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1.0 # Time between statistics output
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            Disk-Patch # Common part of the name of output files
+  time_first:          0.         # Time of the first output (in internal units)
+  delta_time:          8.         # Time difference between consecutive outputs (in internal units)
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2349   # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel).
+  delta_neighbours:      1.       # The tolerance for the targetted number of neighbours.
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  max_ghost_iterations:  30       # Maximal number of iterations allowed to converge towards the smoothing length.
+  max_smoothing_length:  40.      # Maximal smoothing length allowed (in internal units).
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  Disk-Patch.hdf5       # The file to read
+
+# External potential parameters
+Disk-PatchPotential:
+  surface_density: 10.
+  scale_height:    100.
+  z_disk:          300.
+  timestep_mult:   0.03
diff --git a/examples/Disk-Patch/GravityOnly/makeIC.py b/examples/Disk-Patch/GravityOnly/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..702a50ff53b73d004ff36be8049823515675cccf
--- /dev/null
+++ b/examples/Disk-Patch/GravityOnly/makeIC.py
@@ -0,0 +1,161 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 John A. Regan (john.a.regan@durham.ac.uk)
+ #                    Tom Theuns (tom.theuns@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+import h5py
+import sys
+import numpy
+import math
+import random
+
+# Generates N particles in a box of [0:BoxSize,0:BoxSize,-2scale_height:2scale_height]
+# see Creasey, Theuns & Bower, 2013, for the equations:
+# disk parameters are: surface density sigma
+#                      scale height b
+# density: rho(z) = (sigma/2b) sech^2(z/b)
+# isothermal velocity dispersion = <v_z^2? = b pi G sigma
+# grad potential  = 2 pi G sigma tanh(z/b)
+# potential       = ln(cosh(z/b)) + const
+# Dynamical time  = sqrt(b / (G sigma))
+# to obtain the 1/ch^2(z/b) profile from a uniform profile (a glass, say, or a uniform random variable), note that, when integrating in z
+# \int 0^z dz/ch^2(z) = tanh(z)-tanh(0) = \int_0^x dx = x (where the last integral refers to a uniform density distribution), so that z = atanh(x)
+# usage: python makeIC.py 1000 
+
+# physical constants in cgs
+NEWTON_GRAVITY_CGS  = 6.672e-8
+SOLAR_MASS_IN_CGS   = 1.9885e33
+PARSEC_IN_CGS       = 3.0856776e18
+PROTON_MASS_IN_CGS  = 1.6726231e24
+YEAR_IN_CGS         = 3.154e+7
+
+# choice of units
+const_unit_length_in_cgs   =   (PARSEC_IN_CGS)
+const_unit_mass_in_cgs     =   (SOLAR_MASS_IN_CGS)
+const_unit_velocity_in_cgs =   (1e5)
+
+print "UnitMass_in_cgs:     ", const_unit_mass_in_cgs 
+print "UnitLength_in_cgs:   ", const_unit_length_in_cgs
+print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs
+
+
+# parameters of potential
+surface_density = 10.
+scale_height    = 100.
+
+# derived units
+const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs)
+const_G                = ((NEWTON_GRAVITY_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs)))
+print 'G=', const_G
+v_disp                 = numpy.sqrt(scale_height * math.pi * const_G * surface_density)
+t_dyn                  = numpy.sqrt(scale_height / (const_G * surface_density))
+print 'dynamical time = ',t_dyn
+print ' velocity dispersion = ',v_disp
+
+# Parameters
+periodic= 1             # 1 For periodic box
+boxSize = 600.          #  
+Radius  = 100.          # maximum radius of particles [kpc]
+G       = const_G 
+
+N       = int(sys.argv[1])  # Number of particles
+
+# these are not used but necessary for I/O
+rho = 2.              # Density
+P = 1.                # Pressure
+gamma = 5./3.         # Gas adiabatic index
+fileName = "Disk-Patch.hdf5" 
+
+
+#---------------------------------------------------
+numPart        = N
+mass           = 1
+internalEnergy = P / ((gamma - 1.)*rho)
+
+#--------------------------------------------------
+
+#File
+file = h5py.File(fileName, 'w')
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs 
+grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] =  [0, numPart, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [0, numPart, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = periodic
+
+# set seed for random number
+numpy.random.seed(1234)
+
+#Particle group
+#grp0 = file.create_group("/PartType0")
+grp1 = file.create_group("/PartType1")
+
+#generate particle positions
+r      = numpy.zeros((numPart, 3))
+r[:,0] = numpy.random.rand(N) * boxSize
+r[:,1] = numpy.random.rand(N) * boxSize
+z      = scale_height * numpy.arctanh(numpy.random.rand(2*N))
+gd     = z < boxSize / 2
+r[:,2] = z[gd][0:N]
+random = numpy.random.rand(N) > 0.5
+r[random,2] *= -1
+r[:,2] += 0.5 * boxSize
+
+#generate particle velocities
+v      = numpy.zeros((numPart, 3))
+v      = numpy.zeros(1)
+#v[:,2] = 
+
+
+ds = grp1.create_dataset('Velocities', (numPart, 3), 'f')
+ds[()] = v
+
+
+m = numpy.ones((numPart, ), dtype=numpy.float32) * mass
+ds = grp1.create_dataset('Masses', (numPart,), 'f')
+ds[()] = m
+m = numpy.zeros(1)
+
+
+ids = 1 + numpy.linspace(0, numPart, numPart, endpoint=False, dtype='L')
+ds = grp1.create_dataset('ParticleIDs', (numPart, ), 'L')
+ds[()] = ids
+
+ds = grp1.create_dataset('Coordinates', (numPart, 3), 'd')
+ds[()] = r
+
+
+file.close()
diff --git a/examples/Disk-Patch/GravityOnly/run.sh b/examples/Disk-Patch/GravityOnly/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a123ad24d7ca34105c22f5f31e75c688c681288f
--- /dev/null
+++ b/examples/Disk-Patch/GravityOnly/run.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+if [ ! -e Isothermal.hdf5 ]
+then
+    echo "Generating initial conditions for the disk-patch example..."
+    python makeIC.py 1000
+fi
+
+../../swift -g -t 2 disk-patch.yml
diff --git a/examples/Disk-Patch/GravityOnly/test.pro b/examples/Disk-Patch/GravityOnly/test.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4bd0d001975d80b6729cf2ef7b95f81da5bc4fe8
--- /dev/null
+++ b/examples/Disk-Patch/GravityOnly/test.pro
@@ -0,0 +1,158 @@
+;
+;  test energy / angular momentum conservation of test problem
+;
+
+iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare final and initial energy
+
+; set physical constants
+@physunits
+
+indir    = './'
+basefile = 'Disk-Patch_'
+
+; set properties of potential
+uL   = phys.pc                  ; unit of length
+uM   = phys.msun                ; unit of mass
+uV   = 1d5                      ; unit of velocity
+
+; properties of patch
+surface_density = 10.
+scale_height    = 100.
+
+; derived units
+constG   = 10.^(alog10(phys.g)+alog10(uM)-2d0*alog10(uV)-alog10(uL)) ;
+pcentre  = [0.,0.,300.] * pc / uL
+
+;
+infile = indir + basefile + '*'
+spawn,'ls -1 '+infile,res
+nfiles = n_elements(res)
+
+
+; choose: calculate change of energy and Lz, comparing first and last
+; snapshots for all particles, or do so for a subset
+
+; compare all
+ifile   = 0
+inf     = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5'
+id      = h5rd(inf,'PartType1/ParticleIDs')
+nfollow = n_elements(id)
+
+; follow a subset
+nfollow  = 500                    ; number of particles to follow
+
+;
+if (iplot eq 1) then begin
+   nskip = 1
+   nsave = nfiles
+endif else begin
+   nskip = nfiles - 2
+   nsave = 2
+endelse
+
+;
+lout     = fltarr(nfollow, nsave) ; Lz
+xout     = fltarr(nfollow, nsave) ; x
+yout     = fltarr(nfollow, nsave) ; y
+zout     = fltarr(nfollow, nsave) ; z
+eout     = fltarr(nfollow, nsave) ; energies
+ekin     = fltarr(nfollow, nsave)
+epot     = fltarr(nfollow, nsave) ; 2 pi G Sigma b ln(cosh(z/b)) + const
+tout     = fltarr(nsave)
+
+
+
+ifile  = 0
+isave = 0
+for ifile=0,nfiles-1,nskip do begin
+   inf    = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5'
+   time   = h5ra(inf, 'Header','Time')
+   p      = h5rd(inf,'PartType1/Coordinates')
+   v      = h5rd(inf,'PartType1/Velocities')
+   id     = h5rd(inf,'PartType1/ParticleIDs')
+   indx   = sort(id)
+
+;; ;  if you want to sort particles by ID
+;;    id     = id[indx]
+;;    for ic=0,2 do begin
+;;       tmp = reform(p[ic,*]) & p[ic,*] = tmp[indx]
+;;       tmp = reform(v[ic,*]) & v[ic,*] = tmp[indx]
+;;    endfor
+   
+
+; calculate energy
+   dd  = size(p,/dimen) & npart = dd[1]
+   ener = fltarr(npart)
+   dr   = fltarr(npart) & dv = dr
+   for ic=0,2 do dr[*] = dr[*] + (p[ic,*]-pcentre[ic])^2
+   for ic=0,2 do dv[*] = dv[*] + v[ic,*]^2
+   xout[*,isave] = p[0,0:nfollow-1]-pcentre[0]
+   yout[*,isave] = p[1,0:nfollow-1]-pcentre[1]
+   zout[*,isave] = p[2,0:nfollow-1]-pcentre[2]
+   Lz  = (p[0,*]-pcentre[0]) * v[1,*] - (p[1,*]-pcentre[1]) * v[0,*]
+   dz  = reform(p[2,0:nfollow-1]-pcentre[2])
+;   print,'time = ',time,p[0,0],v[0,0],id[0]
+   ek   = 0.5 * dv
+   ep   = fltarr(nfollow)
+   ep   = 2 * !pi * constG * surface_density * scale_height * alog(cosh(abs(dz)/scale_height))
+   ener = ek + ep
+   tout(isave) = time
+   lout[*,isave] = lz[0:nfollow-1]
+   eout(*,isave) = ener[0:nfollow-1]
+   ekin(*,isave) = ek[0:nfollow-1]
+   epot(*,isave) = ep[0:nfollow-1]
+   print,format='('' time= '',f7.1,'' E= '',f9.2,'' Lz= '',e9.2)', time,eout[0],lz[0]
+   isave = isave + 1
+   
+endfor
+
+x0 = reform(xout[0,*])
+y0 = reform(xout[1,*])
+z0 = reform(xout[2,*])
+
+; calculate relative energy change
+de    = 0.0 * eout
+dl    = 0.0 * lout
+nsave = isave
+for ifile=1, nsave-1 do de[*,ifile] = (eout[*,ifile]-eout[*,0])/eout[*,0]
+for ifile=1, nsave-1 do dl[*,ifile] = (lout[*,ifile] - lout[*,0])/lout[*,0]
+
+
+; calculate statistics of energy changes
+print,' relatve energy change: (per cent) ',minmax(de) * 100.
+print,' relative Lz    change: (per cent) ',minmax(dl) * 100.
+
+; plot enery and Lz conservation for some particles
+if(iplot eq 1) then begin
+; plot results on energy conservation for some particles
+   nplot = min(10, nfollow)
+   win,0
+   xr = [min(tout), max(tout)]
+   yr = [-2,2]*1d-2             ; in percent
+   plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/nodata,xtitle='time',ytitle='dE/E, dL/L (%)'
+   for i=0,nplot-1 do oplot,tout,de[i,*]
+   for i=0,nplot-1 do oplot,tout,dl[i,*],color=red
+   legend,['dE/E','dL/L'],linestyle=[0,0],color=[black,red],box=0,/bottom,/left
+   screen_to_png,'e-time.png'
+
+;  plot vertical oscillation
+   win,2
+   xr = [min(tout), max(tout)]
+   yr = [-3,3]*scale_height
+   plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/iso,/nodata,xtitle='x',ytitle='y'
+   color = floor(findgen(nplot)*255/float(nplot))
+   for i=0,nplot-1 do oplot,tout,zout[i,*],color=color(i)
+   screen_to_png,'orbit.png'
+
+; make histogram of energy changes at end
+   win,6
+   ohist,de,x,y,-0.05,0.05,0.001
+   plot,x,y,psym=10,xtitle='de (%)'
+   screen_to_png,'de-hist.png'
+
+
+endif
+
+end
+
+
diff --git a/examples/Disk-Patch/HydroStatic/README b/examples/Disk-Patch/HydroStatic/README
new file mode 100644
index 0000000000000000000000000000000000000000..56578731eb16a27febb3627524956b4e38b6428f
--- /dev/null
+++ b/examples/Disk-Patch/HydroStatic/README
@@ -0,0 +1,20 @@
+Generates and evolves a disk-patch, where gas is in hydrostatic
+equilibrium with an imposed external gravitational force, using the
+equations from Creasey, Theuns & Bower, 2013, MNRAS, Volume 429,
+Issue 3, p.1922-1948.
+
+To generate ICs ready for a scientific run:
+
+1) Recover a uniform glass file by running 'getGlass.sh'.
+
+2) Generate pre-ICs by running the 'makeIC.py' script.
+
+3) Run SWIFT with an isothermal EoS, no cooling nor feedback, and the
+disk-patch potential switched on and using the parameters from
+'disk-patch-icc.yml'
+
+4) The ICs are then ready to be run for a science problem. Rename the last 
+output to 'Disk-Patch-dynamic.hdf5'. These are now the ICs for the actual test.
+
+When running SWIFT with the parameters from 'disk-patch.yml' and an
+ideal gas EoS on these ICs the disk should stay in equilibrium.
diff --git a/examples/Disk-Patch/HydroStatic/disk-patch-icc.yml b/examples/Disk-Patch/HydroStatic/disk-patch-icc.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ebf04675852a7663119ed1ecfd33a05da6b7bb15
--- /dev/null
+++ b/examples/Disk-Patch/HydroStatic/disk-patch-icc.yml
@@ -0,0 +1,44 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.9885e33     # Grams
+  UnitLength_in_cgs:   3.0856776e18  # Centimeters
+  UnitVelocity_in_cgs: 1e5           # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0     # The starting time of the simulation (in internal units).
+  time_end:   968.  # The end time of the simulation (in internal units).
+  dt_min:     1e-4  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1.    # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1 # Time between statistics output
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            Disk-Patch   # Common part of the name of output files
+  time_first:          0.           # Time of the first output (in internal units)
+  delta_time:          12.          # Time difference between consecutive outputs (in internal units)
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2349   # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  max_ghost_iterations:  30       # Maximal number of iterations allowed to converge towards the smoothing length.
+  max_smoothing_length:  70.      # Maximal smoothing length allowed (in internal units).
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  Disk-Patch.hdf5       # The file to read
+
+# External potential parameters
+Disk-PatchPotential:
+  surface_density: 10.
+  scale_height:    100.
+  z_disk:          200.
+  timestep_mult:   0.03
+  growth_time:     5.
diff --git a/examples/Disk-Patch/HydroStatic/disk-patch.yml b/examples/Disk-Patch/HydroStatic/disk-patch.yml
new file mode 100644
index 0000000000000000000000000000000000000000..55df81a08d16c6a4f39aa5e9e9205101dedaa3a9
--- /dev/null
+++ b/examples/Disk-Patch/HydroStatic/disk-patch.yml
@@ -0,0 +1,43 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.9885e33     # Grams
+  UnitLength_in_cgs:   3.0856776e18  # Centimeters
+  UnitVelocity_in_cgs: 1e5           # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 968   # The starting time of the simulation (in internal units).
+  time_end:   12000.  # The end time of the simulation (in internal units).
+  dt_min:     1e-4  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1.    # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1 # Time between statistics output
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:           Disk-Patch-dynamic # Common part of the name of output files
+  time_first:         968.               # Time of the first output (in internal units)
+  delta_time:         24.                 # Time difference between consecutive outputs (in internal units)
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2349   # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  max_ghost_iterations:  30       # Maximal number of iterations allowed to converge towards the smoothing length.
+  max_smoothing_length:  70.      # Maximal smoothing length allowed (in internal units).
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  Disk-Patch-dynamic.hdf5       # The file to read
+
+# External potential parameters
+Disk-PatchPotential:
+  surface_density: 10.
+  scale_height:    100.
+  z_disk:          200.
+  timestep_mult:   0.03
diff --git a/examples/Disk-Patch/HydroStatic/dynamic.pro b/examples/Disk-Patch/HydroStatic/dynamic.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c02c65fe418e84cdd62978dbddcf5a641fa4c156
--- /dev/null
+++ b/examples/Disk-Patch/HydroStatic/dynamic.pro
@@ -0,0 +1,129 @@
+;
+;  test energy / angular momentum conservation of test problem
+;
+
+iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare final and initial energy
+
+; set physical constants
+@physunits
+
+indir    = './'
+basefile = 'Disk-Patch-dynamic_'
+
+; set properties of potential
+uL   = phys.pc                  ; unit of length
+uM   = phys.msun                ; unit of mass
+uV   = 1d5                      ; unit of velocity
+
+; properties of patch
+surface_density = 10.
+scale_height    = 100.
+z_disk          = 200.;
+gamma           = 5./3.
+
+; derived units
+constG   = 10.^(alog10(phys.g)+alog10(uM)-2d0*alog10(uV)-alog10(uL)) ;
+pcentre  = [0.,0.,z_disk] * pc / uL
+utherm     = !pi * constG * surface_density * scale_height / (gamma-1.)
+soundspeed = sqrt(gamma * (gamma-1.) * utherm)
+t_dyn      = sqrt(scale_height / (constG * surface_density))
+
+;
+infile = indir + basefile + '*'
+spawn,'ls -1 '+infile,res
+nfiles = n_elements(res)
+
+
+; choose: calculate change of energy and Lz, comparing first and last
+; snapshots for all particles, or do so for a subset
+
+; compare all
+ifile   = 0
+inf     = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5'
+id      = h5rd(inf,'PartType0/ParticleIDs')
+nfollow = n_elements(id)
+
+
+; compute anlytic profile
+nbins = 100
+zbins = findgen(nbins)/float(nbins-1) * 2 * scale_height
+rbins = (surface_density/(2.*scale_height)) / cosh(abs(zbins)/scale_height)^2
+
+
+; plot analytic profile
+wset,0
+plot,[0],[0],xr=[0,2*scale_height],yr=[0,max(rbins)],/nodata,xtitle='|z|',ytitle=textoidl('\rho')
+oplot,zbins,rbins,color=blue
+
+ifile  = 0
+nskip   = nfiles - 1
+isave  = 0
+nplot  = 8192 ; randomly plot particles
+color = floor(findgen(nfiles)/float(nfiles-1)*255)
+;for ifile=0,nfiles-1,nskip do begin
+tsave  = [0.]
+toplot = [1,nfiles-1]
+for iplot=0,1 do begin
+   ifile  = toplot[iplot]
+   inf    = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5'
+   time   = h5ra(inf, 'Header','Time')
+   tsave  = [tsave, time]
+   print,' time= ',time
+   p      = h5rd(inf,'PartType0/Coordinates')
+   v      = h5rd(inf,'PartType0/Velocities')
+   id     = h5rd(inf,'PartType0/ParticleIDs')
+   rho    = h5rd(inf,'PartType0/Density')
+   h      = h5rd(inf,'PartType0/SmoothingLength')
+   utherm = h5rd(inf,'PartType0/InternalEnergy')
+   indx   = sort(id)
+
+; substract disk centre
+   for ic=0,2 do p[ic,*]=p[ic,*] - pcentre[ic]
+
+
+;; ;  if you want to sort particles by ID
+;;    id     = id[indx]
+;;    rho    = rho[indx]
+;;    utherm = utherm[indx]
+;;    h      = h[indx]
+;;    for ic=0,2 do begin
+;;       tmp = reform(p[ic,*]) & p[ic,*] = tmp[indx]
+;;       tmp = reform(v[ic,*]) & v[ic,*] = tmp[indx]
+;;    endfor
+   
+   ip = floor(randomu(ifile+1,nplot)*n_elements(rho))
+   color = red
+   if(ifile eq 1) then begin
+      color=black
+   endif else begin
+      color=red
+   endelse
+   oplot,abs(p[2,ip]), rho[ip], psym=3, color=color
+
+   isave = isave + 1
+   
+endfor
+
+; time in units of dynamical time
+tsave = tsave[1:*] / t_dyn
+
+label = ['']
+for i=0,n_elements(tsave)-1 do label=[label,'time/t_dynamic='+string(tsave[i],format='(f8.0)')]
+label = label[1:*]
+legend,['analytic',label[0],label[1]],linestyle=[0,0,0],color=[blue,black,red],box=0,/top,/right
+
+; make histograms of particle velocities
+xr    = 1d-3 * [-1,1]
+bsize = 1.d-5
+ohist,v[0,*]/soundspeed,x,vx,xr[0],xr[1],bsize
+ohist,v[1,*]/soundspeed,y,vy,xr[0],xr[1],bsize
+ohist,v[2,*]/soundspeed,z,vz,xr[0],xr[1],bsize
+wset,2
+plot,x,vx,psym=10,xtitle='velocity/soundspeed',ytitle='pdf',/nodata,xr=xr,/xs
+oplot,x,vx,psym=10,color=black
+oplot,y,vy,psym=10,color=blue
+oplot,z,vz,psym=10,color=red
+legend,['vx/c','vy/c','vz/c'],linestyle=[0,0,0],color=[black,blue,red],box=0,/top,/right
+end
+
+
diff --git a/examples/Disk-Patch/HydroStatic/getGlass.sh b/examples/Disk-Patch/HydroStatic/getGlass.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ffd92e88deae6e91237059adac2a6c2067caee46
--- /dev/null
+++ b/examples/Disk-Patch/HydroStatic/getGlass.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_32.hdf5
diff --git a/examples/Disk-Patch/HydroStatic/makeIC.py b/examples/Disk-Patch/HydroStatic/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..40b1d1f1ff9e08dae0c4b0b1539ca773c93009b4
--- /dev/null
+++ b/examples/Disk-Patch/HydroStatic/makeIC.py
@@ -0,0 +1,255 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 John A. Regan (john.a.regan@durham.ac.uk)
+ #                    Tom Theuns (tom.theuns@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+import h5py
+import sys
+import numpy
+import math
+import random
+import matplotlib.pyplot as plt
+
+# Generates a disk-patch in hydrostatic equilibrium
+# see Creasey, Theuns & Bower, 2013, for the equations:
+# disk parameters are: surface density sigma
+#                      scale height b
+# density: rho(z) = (sigma/2b) sech^2(z/b)
+# isothermal velocity dispersion = <v_z^2? = b pi G sigma
+# grad potential  = 2 pi G sigma tanh(z/b)
+# potential       = ln(cosh(z/b)) + const
+# Dynamical time  = sqrt(b / (G sigma))
+# to obtain the 1/ch^2(z/b) profile from a uniform profile (a glass, say, or a uniform random variable), note that, when integrating in z
+# \int 0^z dz/ch^2(z) = tanh(z)-tanh(0) = \int_0^x dx = x (where the last integral refers to a uniform density distribution), so that z = atanh(x)
+# usage: python makeIC.py 1000 
+
+# physical constants in cgs
+NEWTON_GRAVITY_CGS  = 6.672e-8
+SOLAR_MASS_IN_CGS   = 1.9885e33
+PARSEC_IN_CGS       = 3.0856776e18
+PROTON_MASS_IN_CGS  = 1.6726231e24
+YEAR_IN_CGS         = 3.154e+7
+
+# choice of units
+const_unit_length_in_cgs   =   (PARSEC_IN_CGS)
+const_unit_mass_in_cgs     =   (SOLAR_MASS_IN_CGS)
+const_unit_velocity_in_cgs =   (1e5)
+
+print "UnitMass_in_cgs:     ", const_unit_mass_in_cgs 
+print "UnitLength_in_cgs:   ", const_unit_length_in_cgs
+print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs
+
+
+# parameters of potential
+surface_density = 10.
+scale_height    = 100.
+gamma           = 5./3.
+
+# derived units
+const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs)
+const_G                = ((NEWTON_GRAVITY_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs)))
+print 'G=', const_G
+utherm                 = math.pi * const_G * surface_density * scale_height / (gamma-1)
+v_disp                 = numpy.sqrt(2 * utherm)
+soundspeed             = numpy.sqrt(utherm / (gamma * (gamma-1.)))
+t_dyn                  = numpy.sqrt(scale_height / (const_G * surface_density))
+t_cross                = scale_height / soundspeed
+print 'dynamical time = ',t_dyn,' sound crossing time = ',t_cross,' sound speed= ',soundspeed,' 3D velocity dispersion = ',v_disp,' thermal_energy= ',utherm
+
+
+# Parameters
+periodic= 1            # 1 For periodic box
+boxSize = 400.         #  [kpc]
+Radius  = 100.         # maximum radius of particles [kpc]
+G       = const_G 
+
+# File
+fileName = "Disk-Patch.hdf5" 
+
+#---------------------------------------------------
+mass           = 1
+
+#--------------------------------------------------
+
+
+# using glass ICs
+# read glass file and generate gas positions and tile it ntile times in each dimension
+ntile   = 1
+inglass = 'glassCube_32.hdf5'
+infile  = h5py.File(inglass, "r")
+one_glass_p = infile["/PartType0/Coordinates"][:,:]
+one_glass_h = infile["/PartType0/SmoothingLength"][:]
+
+# scale in [-0.5,0.5]*BoxSize / ntile
+one_glass_p[:,:] -= 0.5
+one_glass_p      *= boxSize / ntile
+one_glass_h      *= boxSize / ntile
+ndens_glass       = (one_glass_h.shape[0]) / (boxSize/ntile)**3
+h_glass           = numpy.amin(one_glass_h) * (boxSize/ntile)
+
+glass_p = []
+glass_h = []
+for ix in range(0,ntile):
+    for iy in range(0,ntile):
+        for iz in range(0,ntile):
+            shift = one_glass_p.copy()
+            shift[:,0] += (ix-(ntile-1)/2.) * boxSize / ntile
+            shift[:,1] += (iy-(ntile-1)/2.) * boxSize / ntile
+            shift[:,2] += (iz-(ntile-1)/2.) * boxSize / ntile
+            glass_p.append(shift)
+            glass_h.append(one_glass_h.copy())
+
+glass_p = numpy.concatenate(glass_p, axis=0)
+glass_h = numpy.concatenate(glass_h, axis=0)
+
+# random shuffle of glas ICs
+numpy.random.seed(12345)
+indx   = numpy.random.rand(numpy.shape(glass_h)[0])
+indx   = numpy.argsort(indx)
+glass_p = glass_p[indx, :]
+glass_h = glass_h[indx]
+
+# select numGas of them
+numGas = 8192
+pos    = glass_p[0:numGas,:]
+h      = glass_h[0:numGas]
+numGas = numpy.shape(pos)[0]
+
+# compute furthe properties of ICs
+column_density = surface_density * numpy.tanh(boxSize/2./scale_height)
+enclosed_mass  = column_density * boxSize * boxSize
+pmass          = enclosed_mass / numGas
+meanrho        = enclosed_mass / boxSize**3
+print 'pmass= ',pmass,' mean(rho) = ', meanrho,' entropy= ', (gamma-1) * utherm / meanrho**(gamma-1)
+
+# desired density
+rho            = surface_density / (2. * scale_height) / numpy.cosh(abs(pos[:,2])/scale_height)**2
+u              = (1. + 0 * h) * utherm 
+entropy        = (gamma-1) * u / rho**(gamma-1)
+mass           = 0.*h + pmass
+entropy_flag   = 0
+vel            = 0 + 0 * pos
+
+# move centre of disk to middle of box
+pos[:,:]     += boxSize/2
+
+
+# create numPart dm particles
+numPart = 0
+
+# Create and write output file
+
+#File
+file = h5py.File(fileName, 'w')
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs 
+grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] =  [numGas, numPart, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [numGas, numPart, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [entropy_flag]
+grp.attrs["Dimension"] = 3
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = periodic
+
+
+# write gas particles
+grp0   = file.create_group("/PartType0")
+
+ds     = grp0.create_dataset('Coordinates', (numGas, 3), 'f')
+ds[()] = pos
+
+ds     = grp0.create_dataset('Velocities', (numGas, 3), 'f')
+ds[()] = vel
+
+ds     = grp0.create_dataset('Masses', (numGas,), 'f')
+ds[()] = mass
+
+ds     = grp0.create_dataset('SmoothingLength', (numGas,), 'f')
+ds[()] = h
+
+ds = grp0.create_dataset('InternalEnergy', (numGas,), 'f')
+u = numpy.full((numGas, ), utherm)
+if (entropy_flag == 1):
+    ds[()] = entropy
+else:
+    ds[()] = u    
+
+ids = 1 + numpy.linspace(0, numGas, numGas, endpoint=False, dtype='L')
+ds = grp0.create_dataset('ParticleIDs', (numGas, ), 'L')
+ds[()] = ids
+
+print "Internal energy:", u[0]
+
+# generate dark matter particles if needed
+if(numPart > 0):
+    
+    # set seed for random number
+    numpy.random.seed(1234)
+    
+    grp1 = file.create_group("/PartType1")
+    
+    radius = Radius * (numpy.random.rand(N))**(1./3.) 
+    ctheta = -1. + 2 * numpy.random.rand(N)
+    stheta = numpy.sqrt(1.-ctheta**2)
+    phi    =  2 * math.pi * numpy.random.rand(N)
+    r      = numpy.zeros((numPart, 3))
+
+    speed  = vrot
+    v      = numpy.zeros((numPart, 3))
+    omega  = speed / radius
+    period = 2.*math.pi/omega
+    print 'period = minimum = ',min(period), ' maximum = ',max(period)
+    
+    v[:,0] = -omega * r[:,1]
+    v[:,1] =  omega * r[:,0]
+    
+    ds = grp1.create_dataset('Coordinates', (numPart, 3), 'd')
+    ds[()] = r
+    
+    ds = grp1.create_dataset('Velocities', (numPart, 3), 'f')
+    ds[()] = v
+    v = numpy.zeros(1)
+    
+    m = numpy.full((numPart, ),10)
+    ds = grp1.create_dataset('Masses', (numPart,), 'f')
+    ds[()] = m
+    m = numpy.zeros(1)
+        
+    ids = 1 + numpy.linspace(0, numPart, numPart, endpoint=False, dtype='L')
+    ds = grp1.create_dataset('ParticleIDs', (numPart, ), 'L')
+    ds[()] = ids
+
+
+file.close()
+
+sys.exit()
diff --git a/examples/Disk-Patch/HydroStatic/test.pro b/examples/Disk-Patch/HydroStatic/test.pro
new file mode 100644
index 0000000000000000000000000000000000000000..31e027e3a308b04f1c59222e1a339786857061ac
--- /dev/null
+++ b/examples/Disk-Patch/HydroStatic/test.pro
@@ -0,0 +1,142 @@
+;
+;  test energy / angular momentum conservation of test problem
+;
+
+iplot = 1 ; if iplot = 1, make plot of E/Lz conservation, else, simply compare final and initial energy
+
+; set physical constants
+@physunits
+
+indir    = './'
+basefile = 'Disk-Patch_'
+
+; set properties of potential
+uL   = phys.pc                  ; unit of length
+uM   = phys.msun                ; unit of mass
+uV   = 1d5                      ; unit of velocity
+
+; properties of patch
+surface_density = 10.
+scale_height    = 100.
+
+; derived units
+constG   = 10.^(alog10(phys.g)+alog10(uM)-2d0*alog10(uV)-alog10(uL)) ;
+pcentre  = [0.,0.,200.] * pc / uL
+
+;
+infile = indir + basefile + '*'
+spawn,'ls -1 '+infile,res
+nfiles = n_elements(res)
+
+
+; choose: calculate change of energy and Lz, comparing first and last
+; snapshots for all particles, or do so for a subset
+
+; compare all
+ifile   = 0
+inf     = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5'
+id      = h5rd(inf,'PartType0/ParticleIDs')
+nfollow = n_elements(id)
+
+; follow a subset
+; nfollow  = min(4000, nfollow)   ; number of particles to follow
+
+;
+if (iplot eq 1) then begin
+   nskip = 1
+   nsave = nfiles
+endif else begin
+   nskip = nfiles - 2
+   nsave = 2
+endelse
+
+;
+lout     = fltarr(nfollow, nsave) ; Lz
+xout     = fltarr(nfollow, nsave) ; x
+yout     = fltarr(nfollow, nsave) ; y
+zout     = fltarr(nfollow, nsave) ; z
+vzout    = fltarr(nfollow, nsave) ; z
+rout     = fltarr(nfollow, nsave) ; rho
+hout     = fltarr(nfollow, nsave) ; h
+uout     = fltarr(nfollow, nsave) ; thermal energy
+eout     = fltarr(nfollow, nsave) ; energies
+ekin     = fltarr(nfollow, nsave)
+epot     = fltarr(nfollow, nsave) ; 2 pi G Sigma b ln(cosh(z/b)) + const
+tout     = fltarr(nsave)
+
+ifile  = 0
+isave = 0
+for ifile=0,nfiles-1,nskip do begin
+   inf    = indir + basefile + strtrim(string(ifile,'(i3.3)'),1) + '.hdf5'
+   time   = h5ra(inf, 'Header','Time')
+   p      = h5rd(inf,'PartType0/Coordinates')
+   v      = h5rd(inf,'PartType0/Velocities')
+   id     = h5rd(inf,'PartType0/ParticleIDs')
+   rho    = h5rd(inf,'PartType0/Density')
+   h      = h5rd(inf,'PartType0/SmoothingLength')
+   utherm = h5rd(inf,'PartType0/InternalEnergy')
+   indx   = sort(id)
+
+;  if you want to sort particles by ID
+   id     = id[indx]
+   rho    = rho[indx]
+   utherm = utherm[indx]
+   h      = h[indx]
+   for ic=0,2 do begin
+      tmp = reform(p[ic,*]) & p[ic,*] = tmp[indx]
+      tmp = reform(v[ic,*]) & v[ic,*] = tmp[indx]
+   endfor
+
+; calculate energy
+   dd  = size(p,/dimen) & npart = dd[1]
+   ener = fltarr(npart)
+   dr   = fltarr(npart) & dv = dr
+   for ic=0,2 do dr[*] = dr[*] + (p[ic,*]-pcentre[ic])^2
+   for ic=0,2 do dv[*] = dv[*] + v[ic,*]^2
+   xout[*,isave] = p[0,0:nfollow-1]-pcentre[0]
+   yout[*,isave] = p[1,0:nfollow-1]-pcentre[1]
+   zout[*,isave] = p[2,0:nfollow-1]-pcentre[2]
+   vzout[*,isave]= v[2,0:nfollow-1]
+   rout[*,isave] = rho[0:nfollow-1]
+   hout[*,isave] = h[0:nfollow-1]
+   uout[*,isave] = utherm[0:nfollow-1]
+   Lz  = (p[0,*]-pcentre[0]) * v[1,*] - (p[1,*]-pcentre[1]) * v[0,*]
+   dz  = reform(p[2,0:nfollow-1]-pcentre[2])
+;   print,'time = ',time,p[0,0],v[0,0],id[0]
+   ek   = 0.5 * dv
+   ep   = fltarr(nfollow)
+   ep   = 2 * !pi * constG * surface_density * scale_height * alog(cosh(abs(dz)/scale_height))
+   ener = ek + ep
+   tout(isave) = time
+   lout[*,isave] = lz[0:nfollow-1]
+   eout(*,isave) = ener[0:nfollow-1]
+   ekin(*,isave) = ek[0:nfollow-1]
+   epot(*,isave) = ep[0:nfollow-1]
+   print,format='('' time= '',f7.1,'' E= '',f9.2,'' Lz= '',e9.2)', time,eout[0],lz[0]
+   isave = isave + 1
+   
+endfor
+
+x0 = reform(xout[0,*])
+y0 = reform(xout[1,*])
+z0 = reform(xout[2,*])
+
+
+; plot density profile and compare to analytic profile
+nplot = nfollow
+
+                                ; plot density profile
+wset,0
+xr   = [0, 3*scale_height]
+nbins = 100
+zpos  = findgen(nbins)/float(nbins-1) * max(xr)
+dens  = (surface_density/(2.d0*scale_height)) * 1./cosh(zpos/scale_height)^2
+plot,[0],[0],xr=xr,/xs,yr=[0,max(dens)*1.4],/ys,/nodata,xtitle='|z|',ytitle='density'
+oplot,zpos,dens,color=black,thick=3
+;oplot,abs(zout[*,1]),rout[*,1],psym=3 ; initial profile
+oplot,abs(zout[*,nsave-1]),rout[*,nsave-1],psym=3,color=red
+
+
+end
+
+
diff --git a/examples/EAGLE_12/run.sh b/examples/EAGLE_12/run.sh
index 58c92adc0a1afadf0d15f81613d749f6e736f211..21b965050ab1c786fd08c0dfd28b1612ac09e9e5 100755
--- a/examples/EAGLE_12/run.sh
+++ b/examples/EAGLE_12/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../swift -s -t 16 eagle_12.yml
+../swift -s -t 16 eagle_12.yml 2>&1 | tee output.log
 
diff --git a/examples/EAGLE_25/run.sh b/examples/EAGLE_25/run.sh
index f232cf98f772ab9900bd22b635090e3c103749b5..21d0c7baf4482bdc6df83c552253a02d47432ba2 100755
--- a/examples/EAGLE_25/run.sh
+++ b/examples/EAGLE_25/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../swift -s -t 16 eagle_25.yml
+../swift -s -t 16 eagle_25.yml 2>&1 | tee output.log
 
diff --git a/examples/EAGLE_50/run.sh b/examples/EAGLE_50/run.sh
index 87d522be6bd95fa503d437af861a68ce8bf85b4c..253e7e6eb11a04e1731c605e9ea067ba2bd13221 100755
--- a/examples/EAGLE_50/run.sh
+++ b/examples/EAGLE_50/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../swift -s -t 16 eagle_50.yml
+../swift -s -t 16 eagle_50.yml 2>&1 | tee output.log
 
diff --git a/examples/ExternalPointMass/makeIC.py b/examples/ExternalPointMass/makeIC.py
index 37fc46a9243b2a4c42029de4587082f9efb11f43..326183398933c88d7348e72e00343064b3e3a64c 100644
--- a/examples/ExternalPointMass/makeIC.py
+++ b/examples/ExternalPointMass/makeIC.py
@@ -83,7 +83,7 @@ grp.attrs["Time"] = 0.0
 grp.attrs["NumFilesPerSnapshot"] = 1
 grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
-
+grp.attrs["Dimension"] = 3
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
diff --git a/examples/ExternalPointMass/run.sh b/examples/ExternalPointMass/run.sh
index 9bbe03738b472f48715ae1875dfd462ef577b3d9..9f90ca395a5c8cf83e67928b3fdbd4d8529ac254 100755
--- a/examples/ExternalPointMass/run.sh
+++ b/examples/ExternalPointMass/run.sh
@@ -7,4 +7,4 @@ then
     python makeIC.py 10000
 fi
 
-../swift -g -t 2 externalPointMass.yml
+../swift -g -t 2 externalPointMass.yml 2>&1 | tee output.log
diff --git a/examples/Glass/glass_50000.hdf5 b/examples/Glass/glass_50000.hdf5
deleted file mode 100644
index ad209b851f04ee26e527fe925b71322a2740bb55..0000000000000000000000000000000000000000
Binary files a/examples/Glass/glass_50000.hdf5 and /dev/null differ
diff --git a/examples/Gradients/gradientsCartesian.yml b/examples/Gradients/gradientsCartesian.yml
new file mode 100644
index 0000000000000000000000000000000000000000..917a4803004c2ce89984beb857cb1691d9a1ec1b
--- /dev/null
+++ b/examples/Gradients/gradientsCartesian.yml
@@ -0,0 +1,36 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   1e-6    # The end time of the simulation (in internal units).
+  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-6  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            gradients_cartesian # Common part of the name of output files
+  time_first:          0.  # Time of the first output (in internal units)
+  delta_time:          5e-7 # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-6 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.01     # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./Gradients_cartesian.hdf5       # The file to read
+
diff --git a/examples/Gradients/gradientsRandom.yml b/examples/Gradients/gradientsRandom.yml
new file mode 100644
index 0000000000000000000000000000000000000000..209f30060f031f7d50a15ffbf8ad0e7fe5b013b8
--- /dev/null
+++ b/examples/Gradients/gradientsRandom.yml
@@ -0,0 +1,36 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   1e-6    # The end time of the simulation (in internal units).
+  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-6  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            gradients_random # Common part of the name of output files
+  time_first:          0.  # Time of the first output (in internal units)
+  delta_time:          5e-7 # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-6 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.01     # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./Gradients_random.hdf5       # The file to read
+
diff --git a/examples/Gradients/gradientsStretched.yml b/examples/Gradients/gradientsStretched.yml
new file mode 100644
index 0000000000000000000000000000000000000000..592a70762988fca764c3ec7dcbc9bfcc9a8f2751
--- /dev/null
+++ b/examples/Gradients/gradientsStretched.yml
@@ -0,0 +1,36 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   1e-6    # The end time of the simulation (in internal units).
+  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-6  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            gradients_stretched # Common part of the name of output files
+  time_first:          0.  # Time of the first output (in internal units)
+  delta_time:          5e-7 # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-6 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.01     # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./Gradients_stretched.hdf5       # The file to read
+
diff --git a/examples/Gradients/makeICs.py b/examples/Gradients/makeICs.py
new file mode 100644
index 0000000000000000000000000000000000000000..38d035d2ad2dd3dd6daacfd6f58d824e9daf6742
--- /dev/null
+++ b/examples/Gradients/makeICs.py
@@ -0,0 +1,177 @@
+################################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+
+import h5py
+import random
+import numpy as np
+import sys
+
+# Generates a swift IC file with some density gradients, to check the gradient
+# reconstruction
+
+# Parameters
+periodic= 1      # 1 For periodic box
+gamma = 5./3.     # Gas adiabatic index
+gridtype = "cartesian"
+if len(sys.argv) > 1:
+    gridtype = sys.argv[1]
+
+# stretched cartesian box ######################################################
+if gridtype == "stretched":
+    fileName = "Gradients_stretched.hdf5"
+    factor = 8
+    boxSize = [ 1.0 , 1.0/factor , 1.0/factor ]
+    L = 20
+    nx1 = factor*L/2
+    ny1 = L
+    nz1 = L
+    numfac = 2.
+    nx2 = int(nx1/numfac)
+    ny2 = int(ny1/numfac)
+    nz2 = int(nz1/numfac)
+    npart = nx1*ny1*nz1 + nx2*ny2*nz2
+    vol = boxSize[0] * boxSize[1] * boxSize[2]
+    partVol1 = 0.5*vol/(nx1*ny1*nz1)
+    partVol2 = 0.5*vol/(nx2*ny2*nz2)
+
+    coords = np.zeros((npart,3))
+    h = np.zeros((npart,1))
+    ids = np.zeros((npart,1), dtype='L')
+    idx = 0
+    dcell = 0.5/nx1
+    for i in range(nx1):
+        for j in range(ny1):
+            for k in range(nz1):
+                coords[idx,0] = (i+0.5)*dcell
+                coords[idx,1] = (j+0.5)*dcell
+                coords[idx,2] = (k+0.5)*dcell
+                h[idx] = 0.56/nx1
+                ids[idx] = idx
+                idx += 1
+    dcell = 0.5/nx2
+    for i in range(nx2):
+        for j in range(ny2):
+            for k in range(nz2):
+                coords[idx,0] = 0.5+(i+0.5)*dcell
+                coords[idx,1] = (j+0.5)*dcell
+                coords[idx,2] = (k+0.5)*dcell
+                h[idx] = 0.56/nx2
+                ids[idx] = idx
+                idx += 1
+
+# cartesian box ################################################################
+if gridtype == "cartesian":
+    fileName = "Gradients_cartesian.hdf5"
+    boxSize = [ 1.0 , 1.0 , 1.0 ]
+    nx = 20
+    npart = nx**3
+    partVol = 1./npart
+    coords = np.zeros((npart,3))
+    h = np.zeros((npart,1))
+    ids = np.zeros((npart,1), dtype='L')
+    idx = 0
+    dcell = 1./nx
+    for i in range(nx):
+        for j in range(nx):
+            for k in range(nx):
+                coords[idx,0] = (i+0.5)*dcell
+                coords[idx,1] = (j+0.5)*dcell
+                coords[idx,2] = (k+0.5)*dcell
+                h[idx] = 1.12/nx
+                ids[idx] = idx
+                idx += 1
+
+# random box ###################################################################
+if gridtype == "random":
+    fileName = "Gradients_random.hdf5"
+    boxSize = [ 1.0 , 1.0 , 1.0 ]
+    glass = h5py.File("../Glass/glass_50000.hdf5", "r")
+    coords = np.array(glass["/PartType0/Coordinates"])
+    npart = len(coords)
+    partVol = 1./npart
+    h = np.zeros((npart,1))
+    ids = np.zeros((npart,1), dtype='L')
+    for i in range(npart):
+        h[i] = 0.019
+        ids[i] = i
+
+v = np.zeros((npart,3))
+m = np.zeros((npart,1))
+rho = np.zeros((npart,1))
+u = np.zeros((npart,1))
+
+for i in range(npart):
+    rhox = coords[i,0]
+    if coords[i,0] < 0.75:
+        rhox = 0.75
+    if coords[i,0] < 0.25:
+        rhox = 1.-coords[i,0]
+    rhoy = 1.+boxSize[1]-coords[i,1]
+    if coords[i,1] < 0.75*boxSize[1]:
+        rhoy = 1. + 0.25*boxSize[1]
+    if coords[i,1] < 0.25*boxSize[1]:
+        rhoy = 1.+coords[i,1]
+    rhoz = 1.
+    rho[i] = rhox + rhoy + rhoz
+    P = 1.
+    u[i] = P / ((gamma-1.)*rho[i])
+    if gridtype == "stretched":
+        if coords[i,0] < 0.5:
+            m[i] = rho[i] * partVol1
+        else:
+            m[i] = rho[i] * partVol2
+    else:
+        m[i] = rho[i] * partVol
+
+#File
+file = h5py.File(fileName, 'w')
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] =  [npart, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [npart, 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, 0, 0, 0, 0, 0]
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = periodic
+
+#Particle group
+grp = file.create_group("/PartType0")
+ds = grp.create_dataset('Coordinates', (npart, 3), 'd')
+ds[()] = coords
+ds = grp.create_dataset('Velocities', (npart, 3), 'f')
+ds[()] = v
+ds = grp.create_dataset('Masses', (npart,1), 'f')
+ds[()] = m
+ds = grp.create_dataset('Density', (npart,1), 'd')
+ds[()] = rho
+ds = grp.create_dataset('SmoothingLength', (npart,1), 'f')
+ds[()] = h
+ds = grp.create_dataset('InternalEnergy', (npart,1), 'd')
+ds[()] = u
+ds = grp.create_dataset('ParticleIDs', (npart,1), 'L')
+ds[()] = ids[:]
+
+file.close()
diff --git a/examples/Gradients/plot.py b/examples/Gradients/plot.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6750ffc581437ebbf402ec44bcb1d14cb82a698
--- /dev/null
+++ b/examples/Gradients/plot.py
@@ -0,0 +1,50 @@
+################################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
+#
+# 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 scipy as sp
+import pylab as pl
+import numpy as np
+import h5py
+import sys
+
+# this file plots the gradients of the density in the x and y direction for
+# the given input file and saves the result as gradiens_NAME.png
+
+inputfile = sys.argv[1]
+outputfile = "gradients_{0}.png".format(sys.argv[2])
+
+f = h5py.File(inputfile, "r")
+rho = np.array(f["/PartType0/Density"])
+gradrho = np.array(f["/PartType0/GradDensity"])
+coords = np.array(f["/PartType0/Coordinates"])
+
+fig, ax = pl.subplots(1,2, sharey=True)
+
+ax[0].plot(coords[:,0], rho, "r.", label="density")
+ax[0].plot(coords[:,0], gradrho[:,0], "b.", label="grad density x")
+ax[0].set_xlabel("x")
+ax[0].legend(loc="best")
+
+ax[1].plot(coords[:,1], rho, "r.", label="density")
+ax[1].plot(coords[:,1], gradrho[:,1], "b.", label="grad density y")
+ax[1].set_xlabel("y")
+ax[1].legend(loc="best")
+
+pl.tight_layout()
+pl.savefig(outputfile)
diff --git a/examples/Gradients/run.sh b/examples/Gradients/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..cc1adc676427b257445f64a011ed8ebee87285ab
--- /dev/null
+++ b/examples/Gradients/run.sh
@@ -0,0 +1,13 @@
+#! /bin/bash
+
+python makeICs.py stretched
+../swift -s -t 2 gradientsStretched.yml
+python plot.py gradients_stretched_001.hdf5 stretched
+
+python makeICs.py cartesian
+../swift -s -t 2 gradientsCartesian.yml
+python plot.py gradients_cartesian_001.hdf5 cartesian
+
+python makeICs.py random
+../swift -s -t 2 gradientsRandom.yml
+python plot.py gradients_random_001.hdf5 random
diff --git a/examples/GreshoVortex/solution.py b/examples/GreshoVortex/solution.py
deleted file mode 100644
index 5b282caf2d7f3311ddb595781fed74c51bb4819f..0000000000000000000000000000000000000000
--- a/examples/GreshoVortex/solution.py
+++ /dev/null
@@ -1,69 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import random
-from numpy import *
-
-# Computes the analytical solution of the Gresho-Chan vortex
-# The script works for a given initial box and background pressure and computes the solution for any time t (The solution is constant over time).
-# The code writes five files rho.dat, P.dat, v.dat, u.dat and s.dat with the density, pressure, internal energy and
-# entropic function on N radial points between r=0 and r=R_max.
-
-# Parameters
-rho0 = 1.         # Background Density
-P0 = 0.           # Background Pressure
-gamma = 5./3.     # Gas polytropic index
-N = 1000          # Number of radial points
-R_max = 1.        # Maximal radius
-
-# ---------------------------------------------------------------
-# Don't touch anything after this.
-# ---------------------------------------------------------------
-
-r = arange(0, R_max, R_max / N)
-rho = ones(N)
-P = zeros(N)
-v = zeros(N)
-u = zeros(N)
-s = zeros(N)
-
-
-for i in range(N):
-    if r[i] < 0.2:
-        P[i] = P0 + 5. + 12.5*r[i]**2
-        v[i] = 5.*r[i]
-    elif r[i] < 0.4:
-        P[i] = P0 + 9. + 12.5*r[i]**2 - 20.*r[i] + 4.*log(r[i]/0.2)
-        v[i] = 2. -5.*r[i]
-    else:
-        P[i] = P0 + 3. + 4.*log(2.)
-        v[i] = 0.
-    rho[i] = rho0
-    s[i] = P[i] / rho[i]**gamma
-    u[i] = P[i] /((gamma - 1.)*rho[i])
-
-savetxt("rho.dat", column_stack((r, rho)))
-savetxt("P.dat", column_stack((r, P)))
-savetxt("v.dat", column_stack((r, v)))
-savetxt("u.dat", column_stack((r, u)))
-savetxt("s.dat", column_stack((r, s)))
-
-
-
diff --git a/examples/GreshoVortex_2D/getGlass.sh b/examples/GreshoVortex_2D/getGlass.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ae3c977064f5e7a408aa249c5fd9089b3c52ecb1
--- /dev/null
+++ b/examples/GreshoVortex_2D/getGlass.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5
diff --git a/examples/GreshoVortex_2D/gresho.yml b/examples/GreshoVortex_2D/gresho.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b4de5bde517556cacb94d996f5a11cbe05188bf9
--- /dev/null
+++ b/examples/GreshoVortex_2D/gresho.yml
@@ -0,0 +1,35 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   1.    # The end time of the simulation (in internal units).
+  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            gresho # Common part of the name of output files
+  time_first:          0.     # Time of the first output (in internal units)
+  delta_time:          1e-1   # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-2 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.02     # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./greshoVortex.hdf5     # The file to read
diff --git a/examples/GreshoVortex_2D/makeIC.py b/examples/GreshoVortex_2D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f4ec3407b04971882fbf3d7d7479e74bf56c762
--- /dev/null
+++ b/examples/GreshoVortex_2D/makeIC.py
@@ -0,0 +1,121 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 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/>.
+ # 
+ ##############################################################################
+
+import h5py
+from numpy import *
+
+# Generates a swift IC file for the Gresho-Chan vortex in a periodic box
+
+# Parameters
+gamma = 5./3.     # Gas adiabatic index
+rho0 = 1           # Gas density
+P0 = 0.           # Constant additional pressure (should have no impact on the dynamics)
+fileOutputName = "greshoVortex.hdf5" 
+fileGlass = "glassPlane_128.hdf5"
+#---------------------------------------------------
+
+# Get position and smoothing lengths from the glass
+fileInput = h5py.File(fileGlass, 'r')
+coords = fileInput["/PartType0/Coordinates"][:,:]
+h = fileInput["/PartType0/SmoothingLength"][:]
+ids = fileInput["/PartType0/ParticleIDs"][:]
+boxSize = fileInput["/Header"].attrs["BoxSize"][0]
+numPart = size(h)
+fileInput.close()
+
+# Now generate the rest
+m = ones(numPart) * rho0 * boxSize**2 / numPart
+u = zeros(numPart)
+v = zeros((numPart, 3))
+
+for i in range(numPart):
+    
+    x = coords[i,0]
+    y = coords[i,1]
+
+    r2 = (x - boxSize / 2)**2 + (y - boxSize / 2)**2
+    r = sqrt(r2)
+
+    v_phi = 0.
+    if r < 0.2:
+        v_phi = 5.*r
+    elif r < 0.4:
+        v_phi = 2. - 5.*r
+    else:
+        v_phi = 0.
+    v[i,0] = -v_phi * (y - boxSize / 2) / r
+    v[i,1] =  v_phi * (x - boxSize / 2) / r
+    v[i,2] = 0.
+
+    P = P0
+    if r < 0.2:
+        P = P + 5. + 12.5*r2
+    elif r < 0.4:
+        P = P + 9. + 12.5*r2 - 20.*r + 4.*log(r/0.2)
+    else:
+        P = P + 3. + 4.*log(2.)
+    u[i] = P / ((gamma - 1.)*rho0)
+    
+
+
+#File
+fileOutput = h5py.File(fileOutputName, 'w')
+
+# Header
+grp = fileOutput.create_group("/Header")
+grp.attrs["BoxSize"] = [boxSize, boxSize, 0.2]
+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["NumFileOutputsPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 2
+
+#Runtime parameters
+grp = fileOutput.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+#Units
+grp = fileOutput.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 1.
+grp.attrs["Unit mass in cgs (U_M)"] = 1.
+grp.attrs["Unit time in cgs (U_t)"] = 1.
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+#Particle group
+grp = fileOutput.create_group("/PartType0")
+ds = grp.create_dataset('Coordinates', (numPart, 3), 'd')
+ds[()] = coords
+ds = grp.create_dataset('Velocities', (numPart, 3), 'f')
+ds[()] = v
+ds = grp.create_dataset('Masses', (numPart, 1), 'f')
+ds[()] = m.reshape((numPart,1))
+ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f')
+ds[()] = h.reshape((numPart,1))
+ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f')
+ds[()] = u.reshape((numPart,1))
+ds = grp.create_dataset('ParticleIDs', (numPart,1), 'L')
+ds[()] = ids.reshape((numPart,1))
+
+fileOutput.close()
+
+
diff --git a/examples/GreshoVortex_2D/plotSolution.py b/examples/GreshoVortex_2D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..050bca39a6b7d6f985d630e057a475945471086a
--- /dev/null
+++ b/examples/GreshoVortex_2D/plotSolution.py
@@ -0,0 +1,198 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016  Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Computes the analytical solution of the Gresho-Chan vortex and plots the SPH answer
+
+# Parameters
+gas_gamma = 5./3.     # Gas adiabatic index
+rho0 = 1          # Gas density
+P0 = 0.           # Constant additional pressure (should have no impact on the dynamics)
+
+# ---------------------------------------------------------------
+# Don't touch anything after this.
+# ---------------------------------------------------------------
+
+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])
+
+# Generate the analytic solution at this time
+N = 200
+R_max = 0.8
+solution_r = arange(0, R_max, R_max / N)
+solution_P = zeros(N)
+solution_v_phi = zeros(N)
+solution_v_r = zeros(N)
+
+for i in range(N):
+    if solution_r[i] < 0.2:
+        solution_P[i] = P0 + 5. + 12.5*solution_r[i]**2
+        solution_v_phi[i] = 5.*solution_r[i]
+    elif solution_r[i] < 0.4:
+        solution_P[i] = P0 + 9. + 12.5*solution_r[i]**2 - 20.*solution_r[i] + 4.*log(solution_r[i]/0.2)
+        solution_v_phi[i] = 2. -5.*solution_r[i]
+    else:
+        solution_P[i] = P0 + 3. + 4.*log(2.)
+        solution_v_phi[i] = 0.
+
+solution_rho = ones(N) * rho0
+solution_s = solution_P / solution_rho**gas_gamma
+solution_u = solution_P /((gas_gamma - 1.)*solution_rho)
+
+# Read the simulation data
+sim = h5py.File("gresho_%03d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][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"]
+
+pos = sim["/PartType0/Coordinates"][:,:]
+x = pos[:,0] - boxSize / 2
+y = pos[:,1] - boxSize / 2
+vel = sim["/PartType0/Velocities"][:,:]
+r = sqrt(x**2 + y**2)
+v_r = (x * vel[:,0] + y * vel[:,1]) / r
+v_phi = (-y * vel[:,0] + x * vel[:,1]) / r
+v_norm = sqrt(vel[:,0]**2 + vel[:,1]**2)
+rho = sim["/PartType0/Density"][:]
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+
+# Plot the interesting quantities
+figure()
+
+
+# Azimuthal velocity profile -----------------------------
+subplot(231)
+
+plot(r, v_phi, '.', color='r', ms=0.5)
+plot(solution_r, solution_v_phi, '--', color='k', alpha=0.8, lw=1.2)
+plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Azimuthal~velocity}}~v_\\phi$", labelpad=0)
+xlim(0,R_max)
+ylim(-0.1, 1.2)
+
+# Radial density profile --------------------------------
+subplot(232)
+
+plot(r, rho, '.', color='r', ms=0.5)
+plot(solution_r, solution_rho, '--', color='k', alpha=0.8, lw=1.2)
+plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+xlim(0,R_max)
+ylim(rho0-0.3, rho0 + 0.3)
+#yticks([-0.2, -0.1, 0., 0.1, 0.2])
+
+# Radial pressure profile --------------------------------
+subplot(233)
+
+plot(r, P, '.', color='r', ms=0.5)
+plot(solution_r, solution_P, '--', color='k', alpha=0.8, lw=1.2)
+plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+xlim(0, R_max)
+ylim(4.9 + P0, P0 + 6.1)
+
+# Internal energy profile --------------------------------
+subplot(234)
+
+plot(r, u, '.', color='r', ms=0.5)
+plot(solution_r, solution_u, '--', color='k', alpha=0.8, lw=1.2)
+plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+xlim(0,R_max)
+ylim(7.3, 9.1)
+
+
+# Radial entropy profile --------------------------------
+subplot(235)
+
+plot(r, S, '.', color='r', ms=0.5)
+plot(solution_r, solution_s, '--', color='k', alpha=0.8, lw=1.2)
+plot([0.2, 0.2], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+plot([0.4, 0.4], [-100, 100], ':', color='k', alpha=0.4, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+xlim(0, R_max)
+ylim(4.9 + P0, P0 + 6.1)
+
+# Image --------------------------------------------------
+#subplot(234)
+#scatter(pos[:,0], pos[:,1], c=v_norm, cmap="PuBu", edgecolors='face', s=4, vmin=0, vmax=1)
+#text(0.95, 0.95, "$|v|$", ha="right", va="top")
+#xlim(0,1)
+#ylim(0,1)
+#xlabel("$x$", labelpad=0)
+#ylabel("$y$", labelpad=0)
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+text(-0.49, 0.9, "Gresho-Chan vortex with  $\\gamma=%.3f$ at $t=%.2f$"%(gas_gamma,time), fontsize=10)
+text(-0.49, 0.8, "Background $\\rho_0=%.3f$"%rho0, fontsize=10)
+text(-0.49, 0.7, "Background $P_0=%.3f$"%P0, fontsize=10)
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+savefig("GreshoVortex.png", dpi=200)
diff --git a/examples/GreshoVortex_2D/run.sh b/examples/GreshoVortex_2D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6d537bcc96c68385434f685bd551a2d423f469e0
--- /dev/null
+++ b/examples/GreshoVortex_2D/run.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e glassPlane_128.hdf5 ]
+then
+    echo "Fetching initial glass file for the Gresho-Chan vortex example..."
+    ./getGlass.sh
+fi
+if [ ! -e greshoVortex.hdf5 ]
+then
+    echo "Generating initial conditions for the Gresho-Chan vortex example..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift -s -t 1 gresho.yml 2>&1 | tee output.log
+
+# Plot the solution
+python plotSolution.py 11
diff --git a/examples/IsothermalPotential/GravityOnly/legend.pro b/examples/IsothermalPotential/GravityOnly/legend.pro
deleted file mode 100755
index e510bdfcd7dcabed796cb090c6fa7d54536608b6..0000000000000000000000000000000000000000
--- a/examples/IsothermalPotential/GravityOnly/legend.pro
+++ /dev/null
@@ -1,459 +0,0 @@
-;+
-; NAME:
-;       LEGEND
-; PURPOSE:
-;       Create an annotation legend for a plot.
-; EXPLANATION:
-;       This procedure makes a legend for a plot.  The legend can contain
-;       a mixture of symbols, linestyles, Hershey characters (vectorfont),
-;       and filled polygons (usersym).  A test procedure, legendtest.pro,
-;       shows legend's capabilities.  Placement of the legend is controlled
-;       with keywords like /right, /top, and /center or by using a position
-;       keyword for exact placement (position=[x,y]) or via mouse (/position).
-; CALLING SEQUENCE:
-;       LEGEND [,items][,keyword options]
-; EXAMPLES:
-;       The call:
-;               legend,['Plus sign','Asterisk','Period'],psym=[1,2,3]
-;         produces:
-;               -----------------
-;               |               |
-;               |  + Plus sign  |
-;               |  * Asterisk   |
-;               |  . Period     |
-;               |               |
-;               -----------------
-;         Each symbol is drawn with a plots command, so they look OK.
-;         Other examples are given in optional output keywords.
-;
-;       lines = indgen(6)                       ; for line styles
-;       items = 'linestyle '+strtrim(lines,2)   ; annotations
-;       legend,items,linestyle=lines            ; vertical legend---upper left
-;       items = ['Plus sign','Asterisk','Period']
-;       sym = [1,2,3]
-;       legend,items,psym=sym                   ; ditto except using symbols
-;       legend,items,psym=sym,/horizontal       ; horizontal format
-;       legend,items,psym=sym,box=0             ; sans border
-;       legend,items,psym=sym,delimiter='='     ; embed '=' betw psym & text
-;       legend,items,psym=sym,margin=2          ; 2-character margin
-;       legend,items,psym=sym,position=[x,y]    ; upper left in data coords
-;       legend,items,psym=sym,pos=[x,y],/norm   ; upper left in normal coords
-;       legend,items,psym=sym,pos=[x,y],/device ; upper left in device coords
-;       legend,items,psym=sym,/position         ; interactive position
-;       legend,items,psym=sym,/right            ; at upper right
-;       legend,items,psym=sym,/bottom           ; at lower left
-;       legend,items,psym=sym,/center           ; approximately near center
-;       legend,items,psym=sym,number=2          ; plot two symbols, not one
-;       legend,items,/fill,psym=[8,8,8],colors=[10,20,30]; 3 filled squares
-; INPUTS:
-;       items = text for the items in the legend, a string array.
-;               For example, items = ['diamond','asterisk','square'].
-;               You can omit items if you don't want any text labels.
-; OPTIONAL INPUT KEYWORDS:
-;
-;       linestyle = array of linestyle numbers  If linestyle[i] < 0, then omit
-;               ith symbol or line to allow a multi-line entry.     If 
-;               linestyle = -99 then text will be left-justified.  
-;       psym = array of plot symbol numbers.  If psym[i] is negative, then a
-;               line connects pts for ith item.  If psym[i] = 8, then the
-;               procedure usersym is called with vertices define in the
-;               keyword usersym.   If psym[i] = 88, then use the previously
-;               defined user symbol
-;       vectorfont = vector-drawn characters for the sym/line column, e.g.,
-;               ['!9B!3','!9C!3','!9D!3'] produces an open square, a checkmark,
-;               and a partial derivative, which might have accompanying items
-;               ['BOX','CHECK','PARTIAL DERIVATIVE'].
-;               There is no check that !p.font is set properly, e.g., -1 for
-;               X and 0 for PostScript.  This can produce an error, e.g., use
-;               !20 with PostScript and !p.font=0, but allows use of Hershey
-;               *AND* PostScript fonts together.
-;       N. B.: Choose any of linestyle, psym, and/or vectorfont.  If none is
-;               present, only the text is output.  If more than one
-;               is present, all need the same number of elements, and normal
-;               plot behaviour occurs.
-;               By default, if psym is positive, you get one point so there is
-;               no connecting line.  If vectorfont[i] = '',
-;               then plots is called to make a symbol or a line, but if
-;               vectorfont[i] is a non-null string, then xyouts is called.
-;       /help = flag to print header
-;       /horizontal = flag to make the legend horizontal
-;       /vertical = flag to make the legend vertical (D=vertical)
-;       box = flag to include/omit box around the legend (D=include)
-;       clear = flag to clear the box area before drawing the legend
-;       delimiter = embedded character(s) between symbol and text (D=none)
-;       colors = array of colors for plot symbols/lines (D=!P.color)
-;       textcolors = array of colors for text (D=!P.color)
-;       margin = margin around text measured in characters and lines
-;       spacing = line spacing (D=bit more than character height)
-;       pspacing = psym spacing (D=3 characters) (when number of symbols is
-;             greater than 1)
-;       charsize = just like !p.charsize for plot labels
-;       charthick = just like !p.charthick for plot labels
-;       thick = array of line thickness numbers (D = !P.thick), if used, then 
-;               linestyle must also be specified
-;       position = data coordinates of the /top (D) /left (D) of the legend
-;       normal = use normal coordinates for position, not data
-;       device = use device coordinates for position, not data
-;       number = number of plot symbols to plot or length of line (D=1)
-;       usersym = 2-D array of vertices, cf. usersym in IDL manual. 
-;             (/USERSYM =square, default is to use existing USERSYM definition)
-;       /fill = flag to fill the usersym
-;       /left_legend = flag to place legend snug against left side of plot
-;                 window (D)
-;       /right_legend = flag to place legend snug against right side of plot
-;               window.    If /right,pos=[x,y], then x is position of RHS and
-;               text runs right-to-left.
-;       /top_legend = flag to place legend snug against top of plot window (D)
-;       /bottom = flag to place legend snug against bottom of plot window
-;               /top,pos=[x,y] and /bottom,pos=[x,y] produce same positions.
-;
-;       If LINESTYLE, PSYM, VECTORFONT, THICK, COLORS, or TEXTCOLORS are
-;       supplied as scalars, then the scalar value is set for every line or
-;       symbol in the legend.
-; Outputs:
-;       legend to current plot device
-; OPTIONAL OUTPUT KEYWORDS:
-;       corners = 4-element array, like !p.position, of the normalized
-;         coords for the box (even if box=0): [llx,lly,urx,ury].
-;         Useful for multi-column or multi-line legends, for example,
-;         to make a 2-column legend, you might do the following:
-;           c1_items = ['diamond','asterisk','square']
-;           c1_psym = [4,2,6]
-;           c2_items = ['solid','dashed','dotted']
-;           c2_line = [0,2,1]
-;           legend,c1_items,psym=c1_psym,corners=c1,box=0
-;           legend,c2_items,line=c2_line,corners=c2,box=0,pos=[c1[2],c1[3]]
-;           c = [c1[0]<c2[0],c1[1]<c2[1],c1[2]>c2[2],c1[3]>c2[3]]
-;           plots,[c[0],c[0],c[2],c[2],c[0]],[c[1],c[3],c[3],c[1],c[1]],/norm
-;         Useful also to place the legend.  Here's an automatic way to place
-;         the legend in the lower right corner.  The difficulty is that the
-;         legend's width is unknown until it is plotted.  In this example,
-;         the legend is plotted twice: the first time in the upper left, the
-;         second time in the lower right.
-;           legend,['1','22','333','4444'],linestyle=indgen(4),corners=corners
-;                       ; BOGUS LEGEND---FIRST TIME TO REPORT CORNERS
-;           xydims = [corners[2]-corners[0],corners[3]-corners[1]]
-;                       ; SAVE WIDTH AND HEIGHT
-;           chdim=[!d.x_ch_size/float(!d.x_size),!d.y_ch_size/float(!d.y_size)]
-;                       ; DIMENSIONS OF ONE CHARACTER IN NORMALIZED COORDS
-;           pos = [!x.window[1]-chdim[0]-xydims[0] $
-;                       ,!y.window[0]+chdim[1]+xydims[1]]
-;                       ; CALCULATE POSITION FOR LOWER RIGHT
-;           plot,findgen(10)    ; SIMPLE PLOT; YOU DO WHATEVER YOU WANT HERE.
-;           legend,['1','22','333','4444'],linestyle=indgen(4),pos=pos
-;                       ; REDO THE LEGEND IN LOWER RIGHT CORNER
-;         You can modify the pos calculation to place the legend where you
-;         want.  For example to place it in the upper right:
-;           pos = [!x.window[1]-chdim[0]-xydims[0],!y.window[1]-xydims[1]]
-; Common blocks:
-;       none
-; Procedure:
-;       If keyword help is set, call doc_library to print header.
-;       See notes in the code.  Much of the code deals with placement of the
-;       legend.  The main problem with placement is not being
-;       able to sense the length of a string before it is output.  Some crude
-;       approximations are used for centering.
-; Restrictions:
-;       Here are some things that aren't implemented.
-;       - An orientation keyword would allow lines at angles in the legend.
-;       - An array of usersyms would be nice---simple change.
-;       - An order option to interchange symbols and text might be nice.
-;       - Somebody might like double boxes, e.g., with box = 2.
-;       - Another feature might be a continuous bar with ticks and text.
-;       - There are no guards to avoid writing outside the plot area.
-;       - There is no provision for multi-line text, e.g., '1st line!c2nd line'
-;         Sensing !c would be easy, but !c isn't implemented for PostScript.
-;         A better way might be to simply output the 2nd line as another item
-;         but without any accompanying symbol or linestyle.  A flag to omit
-;         the symbol and linestyle is linestyle[i] = -1.
-;       - There is no ability to make a title line containing any of titles
-;         for the legend, for the symbols, or for the text.
-; Side Effects:
-; Modification history:
-;       write, 24-25 Aug 92, F K Knight (knight@ll.mit.edu)
-;       allow omission of items or omission of both psym and linestyle, add
-;         corners keyword to facilitate multi-column legends, improve place-
-;         ment of symbols and text, add guards for unequal size, 26 Aug 92, FKK
-;       add linestyle(i)=-1 to suppress a single symbol/line, 27 Aug 92, FKK
-;       add keyword vectorfont to allow characters in the sym/line column,
-;         28 Aug 92, FKK
-;       add /top, /bottom, /left, /right keywords for automatic placement at
-;         the four corners of the plot window.  The /right keyword forces
-;         right-to-left printing of menu. 18 Jun 93, FKK
-;       change default position to data coords and add normal, data, and
-;         device keywords, 17 Jan 94, FKK
-;       add /center keyword for positioning, but it is not precise because
-;         text string lengths cannot be known in advance, 17 Jan 94, FKK
-;       add interactive positioning with /position keyword, 17 Jan 94, FKK
-;       allow a legend with just text, no plotting symbols.  This helps in
-;         simply describing a plot or writing assumptions done, 4 Feb 94, FKK
-;       added thick, symsize, and clear keyword Feb 96, W. Landsman HSTX
-;               David Seed, HR Wallingford, d.seed@hrwallingford.co.uk
-;       allow scalar specification of keywords, Mar 96, W. Landsman HSTX
-;       added charthick keyword, June 96, W. Landsman HSTX
-;       Made keyword names  left,right,top,bottom,center longer,
-;                                 Aug 16, 2000, Kim Tolbert
-;       Added ability to have regular text lines in addition to plot legend 
-;       lines in legend.  If linestyle is -99 that item is left-justified.
-;       Previously, only option for no sym/line was linestyle=-1, but then text
-;       was lined up after sym/line column.    10 Oct 2000, Kim Tolbert
-;       Make default value of thick = !P.thick  W. Landsman  Jan. 2001
-;       Don't overwrite existing USERSYM definition  W. Landsman Mar. 2002
-;-
-pro legend, items, BOTTOM_LEGEND=bottom, BOX = box, CENTER_LEGEND=center, $
-    CHARTHICK=charthick, CHARSIZE = charsize, CLEAR = clear, COLORS = colorsi, $
-    CORNERS = corners, DATA=data, DELIMITER=delimiter, DEVICE=device, $
-    FILL=fill, HELP = help, HORIZONTAL=horizontal,LEFT_LEGEND=left, $
-    LINESTYLE=linestylei, MARGIN=margin, NORMAL=normal, NUMBER=number, $
-    POSITION=position,PSPACING=pspacing, PSYM=psymi, RIGHT_LEGEND=right, $
-    SPACING=spacing, SYMSIZE=symsize, TEXTCOLORS=textcolorsi, THICK=thicki, $
-    TOP_LEGEND=top, USERSYM=usersym,  VECTORFONT=vectorfonti, VERTICAL=vertical
-;
-;       =====>> HELP
-;
-on_error,2
-if keyword_set(help) then begin & doc_library,'legend' & return & endif
-;
-;       =====>> SET DEFAULTS FOR SYMBOLS, LINESTYLES, AND ITEMS.
-;
- ni = n_elements(items)
- np = n_elements(psymi)
- nl = n_elements(linestylei)
- nth = n_elements(thicki)
- nv = n_elements(vectorfonti)
- nlpv = max([np,nl,nv])
- n = max([ni,np,nl,nv])                                  ; NUMBER OF ENTRIES
-strn = strtrim(n,2)                                     ; FOR ERROR MESSAGES
-if n eq 0 then message,'No inputs!  For help, type legend,/help.'
-if ni eq 0 then begin
-  items = replicate('',n)                               ; DEFAULT BLANK ARRAY
-endif else begin
-  if size(items,/TNAME) NE 'STRING' then message, $
-      'First parameter must be a string array.  For help, type legend,/help.'
-  if ni ne n then message,'Must have number of items equal to '+strn
-endelse
-symline = (np ne 0) or (nl ne 0)                        ; FLAG TO PLOT SYM/LINE
- if (np ne 0) and (np ne n) and (np NE 1) then message, $
-        'Must have 0, 1 or '+strn+' elements in PSYM array.'
- if (nl ne 0) and (nl ne n) and (nl NE 1) then message, $
-         'Must have 0, 1 or '+strn+' elements in LINESTYLE array.'
- if (nth ne 0) and (nth ne n) and (nth NE 1) then message, $
-         'Must have 0, 1 or '+strn+' elements in THICK array.'
-
- case nl of 
- 0: linestyle = intarr(n)              ;Default = solid
- 1: linestyle = intarr(n)  + linestylei
- else: linestyle = linestylei
- endcase 
- 
- case nth of 
- 0: thick = replicate(!p.thick,n)      ;Default = !P.THICK
- 1: thick = intarr(n) + thicki
- else: thick = thicki
- endcase 
-
- case np of             ;Get symbols
- 0: psym = intarr(n)    ;Default = solid
- 1: psym = intarr(n) + psymi
- else: psym = psymi
- endcase 
-
- case nv of 
- 0: vectorfont = replicate('',n)
- 1: vectorfont = replicate(vectorfonti,n)
- else: vectorfont = vectorfonti
- endcase 
-;
-;       =====>> CHOOSE VERTICAL OR HORIZONTAL ORIENTATION.
-;
-if n_elements(horizontal) eq 0 then begin               ; D=VERTICAL
-  if n_elements(vertical) eq 0 then vertical = 1
-endif else begin
-  if n_elements(vertical) eq 0 then vertical = not horizontal
-endelse
-;
-;       =====>> SET DEFAULTS FOR OTHER OPTIONS.
-;
-if n_elements(box) eq 0 then box = 1
-if n_elements(clear) eq 0 then clear = 0
-
-if n_elements(margin) eq 0 then margin = 0.5
-if n_elements(delimiter) eq 0 then delimiter = ''
-if n_elements(charsize) eq 0 then charsize = !p.charsize
-if n_elements(charthick) eq 0 then charthick = !p.charthick
-if charsize eq 0 then charsize = 1
-if (n_elements (symsize) eq 0) then symsize= charsize + intarr(n)
-if n_elements(number) eq 0 then number = 1
- case N_elements(colorsi) of 
- 0: colors = replicate(!P.color,n)     ;Default is !P.COLOR
- 1: colors = replicate(colorsi,n)
- else: colors = colorsi
- endcase 
-
- case N_elements(textcolorsi) of 
- 0: textcolors = replicate(!P.color,n)      ;Default is !P.COLOR
- 1: textcolors = replicate(textcolorsi,n)
- else: textcolors = textcolorsi
- endcase 
- fill = keyword_set(fill)
-if n_elements(usersym) eq 1 then usersym = 2*[[0,0],[0,1],[1,1],[1,0],[0,0]]-1
-;
-;       =====>> INITIALIZE SPACING
-;
-if n_elements(spacing) eq 0 then spacing = 1.2
-if n_elements(pspacing) eq 0 then pspacing = 3
-xspacing = !d.x_ch_size/float(!d.x_size) * (spacing > charsize)
-yspacing = !d.y_ch_size/float(!d.y_size) * (spacing > charsize)
-ltor = 1                                        ; flag for left-to-right
-if n_elements(left) eq 1 then ltor = left eq 1
-if n_elements(right) eq 1 then ltor = right ne 1
-ttob = 1                                        ; flag for top-to-bottom
-if n_elements(top) eq 1 then ttob = top eq 1
-if n_elements(bottom) eq 1 then ttob = bottom ne 1
-xalign = ltor ne 1                              ; x alignment: 1 or 0
-yalign = -0.5*ttob + 1                          ; y alignment: 0.5 or 1
-xsign = 2*ltor - 1                              ; xspacing direction: 1 or -1
-ysign = 2*ttob - 1                              ; yspacing direction: 1 or -1
-if not ttob then yspacing = -yspacing
-if not ltor then xspacing = -xspacing
-;
-;       =====>> INITIALIZE POSITIONS: FIRST CALCULATE X OFFSET FOR TEXT
-;
-xt = 0
-if nlpv gt 0 then begin                         ; SKIP IF TEXT ITEMS ONLY.
-if vertical then begin                          ; CALC OFFSET FOR TEXT START
-  for i = 0,n-1 do begin
-    if (psym[i] eq 0) and (vectorfont[i] eq '') then num = (number + 1) > 3 else num = number
-    if psym[i] lt 0 then num = number > 2       ; TO SHOW CONNECTING LINE
-    if psym[i] eq 0 then expand = 1 else expand = 2
-    thisxt = (expand*pspacing*(num-1)*xspacing)
-    if ltor then xt = thisxt > xt else xt = thisxt < xt
-    endfor
-endif   ; NOW xt IS AN X OFFSET TO ALIGN ALL TEXT ENTRIES.
-endif
-;
-;       =====>> INITIALIZE POSITIONS: SECOND LOCATE BORDER
-;
-if !x.window[0] eq !x.window[1] then begin
-  plot,/nodata,xstyle=4,ystyle=4,[0],/noerase
-endif
-;       next line takes care of weirdness with small windows
-pos = [min(!x.window),min(!y.window),max(!x.window),max(!y.window)]
-case n_elements(position) of
- 0: begin
-  if ltor then px = pos[0] else px = pos[2]
-  if ttob then py = pos[3] else py = pos[1]
-  if keyword_set(center) then begin
-    if not keyword_set(right) and not keyword_set(left) then $
-      px = (pos[0] + pos[2])/2. - xt
-    if not keyword_set(top) and not keyword_set(bottom) then $
-      py = (pos[1] + pos[3])/2. + n*yspacing
-    endif
-  position = [px,py] + [xspacing,-yspacing]
-  end
- 1: begin       ; interactive
-  message,/inform,'Place mouse at upper left corner and click any mouse button.'
-  cursor,x,y,/normal
-  position = [x,y]
-  end
- 2: begin       ; convert upper left corner to normal coordinates
-  if keyword_set(data) then $
-    position = convert_coord(position,/to_norm) $
-  else if keyword_set(device) then $
-    position = convert_coord(position,/to_norm,/device) $
-  else if not keyword_set(normal) then $
-    position = convert_coord(position,/to_norm)
-  end
- else: message,'Position keyword can have 0, 1, or 2 elements only. Try legend,/help.'
-endcase
-
-yoff = 0.25*yspacing*ysign                      ; VERT. OFFSET FOR SYM/LINE.
-
-x0 = position[0] + (margin)*xspacing            ; INITIAL X & Y POSITIONS
-y0 = position[1] - margin*yspacing + yalign*yspacing    ; WELL, THIS WORKS!
-;
-;       =====>> OUTPUT TEXT FOR LEGEND, ITEM BY ITEM.
-;       =====>> FOR EACH ITEM, PLACE SYM/LINE, THEN DELIMITER,
-;       =====>> THEN TEXT---UPDATING X & Y POSITIONS EACH TIME.
-;       =====>> THERE ARE A NUMBER OF EXCEPTIONS DONE WITH IF STATEMENTS.
-;
-for iclr = 0,clear do begin
-  y = y0                                                ; STARTING X & Y POSITIONS
-  x = x0
-  if ltor then xend = 0 else xend = 1           ; SAVED WIDTH FOR DRAWING BOX
-
- if ttob then ii = [0,n-1,1] else ii = [n-1,0,-1]
- for i = ii[0],ii[1],ii[2] do begin
-  if vertical then x = x0 else y = y0           ; RESET EITHER X OR Y
-  x = x + xspacing                              ; UPDATE X & Y POSITIONS
-  y = y - yspacing
-  if nlpv eq 0 then goto,TEXT_ONLY              ; FLAG FOR TEXT ONLY
-  if (psym[i] eq 0) and (vectorfont[i] eq '') then num = (number + 1) > 3 else num = number
-  if psym[i] lt 0 then num = number > 2         ; TO SHOW CONNECTING LINE
-  if psym[i] eq 0 then expand = 1 else expand = 2
-  xp = x + expand*pspacing*indgen(num)*xspacing
-  if (psym[i] gt 0) and (num eq 1) and vertical then xp = x + xt/2.
-  yp = y + intarr(num)
-  if vectorfont[i] eq '' then yp = yp + yoff
-  if psym[i] eq 0 then begin
-    xp = [min(xp),max(xp)]                      ; TO EXPOSE LINESTYLES
-    yp = [min(yp),max(yp)]                      ; DITTO
-    endif
-  if (psym[i] eq 8) and (N_elements(usersym) GT 1) then $
-                usersym,usersym,fill=fill,color=colors[i]
-;; extra by djseed .. psym=88 means use the already defined usersymbol
- if psym[i] eq 88 then psym[i] =8
-  if vectorfont[i] ne '' then begin
-;    if (num eq 1) and vertical then xp = x + xt/2      ; IF 1, CENTERED.
-    xyouts,xp,yp,vectorfont[i],width=width,color=colors[i] $
-      ,size=charsize,align=xalign,charthick = charthick,/norm
-    xt = xt > width
-    xp = xp + width/2.
-  endif else begin
-    if symline and (linestyle[i] ge 0) then plots,xp,yp,color=colors[i] $
-      ,/normal,linestyle=linestyle[i],psym=psym[i],symsize=symsize[i], $
-      thick=thick[i]
-  endelse
-
-  if vertical then x = x + xt else if ltor then x = max(xp) else x = min(xp)
-  if symline then x = x + xspacing
-  TEXT_ONLY:
-  if vertical and (vectorfont[i] eq '') and symline and (linestyle[i] eq -99) then x=x0 + xspacing
-  xyouts,x,y,delimiter,width=width,/norm,color=textcolors[i], $
-         size=charsize,align=xalign,charthick = charthick
-  x = x + width*xsign
-  if width ne 0 then x = x + 0.5*xspacing
-  xyouts,x,y,items[i],width=width,/norm,color=textcolors[i],size=charsize, $
-             align=xalign,charthick=charthick
-  x = x + width*xsign
-  if not vertical and (i lt (n-1)) then x = x+2*xspacing; ADD INTER-ITEM SPACE
-  xfinal = (x + xspacing*margin)
-  if ltor then xend = xfinal > xend else xend = xfinal < xend   ; UPDATE END X
- endfor
-
- if (iclr lt clear ) then begin
-;       =====>> CLEAR AREA
-        x = position[0]
-        y = position[1]
-        if vertical then bottom = n else bottom = 1
-        ywidth = - (2*margin+bottom-0.5)*yspacing
-        corners = [x,y+ywidth,xend,y]
-        polyfill,[x,xend,xend,x,x],y + [0,0,ywidth,ywidth,0],/norm,color=-1
-;       plots,[x,xend,xend,x,x],y + [0,0,ywidth,ywidth,0],thick=2
- endif else begin
-
-;
-;       =====>> OUTPUT BORDER
-;
-        x = position[0]
-        y = position[1]
-        if vertical then bottom = n else bottom = 1
-        ywidth = - (2*margin+bottom-0.5)*yspacing
-        corners = [x,y+ywidth,xend,y]
-        if box then plots,[x,xend,xend,x,x],y + [0,0,ywidth,ywidth,0],/norm
-        return
- endelse
-endfor
-
-end
-
diff --git a/examples/IsothermalPotential/GravityOnly/makeIC.py b/examples/IsothermalPotential/GravityOnly/makeIC.py
index a8d325a00aaf2b048ed1743c2bc877a3f4fbf82c..e564a5aaba5249c134a263e902d169a34cc6ae0b 100644
--- a/examples/IsothermalPotential/GravityOnly/makeIC.py
+++ b/examples/IsothermalPotential/GravityOnly/makeIC.py
@@ -102,7 +102,7 @@ grp.attrs["Time"] = 0.0
 grp.attrs["NumFilesPerSnapshot"] = 1
 grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
-
+grp.attrs["Dimension"] = 3
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
diff --git a/examples/IsothermalPotential/GravityOnly/run.sh b/examples/IsothermalPotential/GravityOnly/run.sh
index 49f32149679561479ee17265192bbdd547cc7b24..47b9556af9b4dad2c63ea87cf2410468893e4b3c 100755
--- a/examples/IsothermalPotential/GravityOnly/run.sh
+++ b/examples/IsothermalPotential/GravityOnly/run.sh
@@ -7,4 +7,4 @@ then
     python makeIC.py 10000 1 1
 fi
 
-../../swift -g -t 2 isothermal.yml
+../../swift -g -t 2 isothermal.yml 2>&1 | tee output.log
diff --git a/examples/IsothermalPotential/GravityOnly/test.pro b/examples/IsothermalPotential/GravityOnly/test.pro
index ac1f5e915c38c107408ffed9579c52944295f079..edfa50121d2e5adb7e039f3c38d6d4c0b4d5e34f 100644
--- a/examples/IsothermalPotential/GravityOnly/test.pro
+++ b/examples/IsothermalPotential/GravityOnly/test.pro
@@ -127,7 +127,7 @@ print,' relative Lz    change: (per cent) ',minmax(dl) * 100.
 if(iplot eq 1) then begin
 ; plot results on energy conservation for some particles
    nplot = min(10, nfollow)
-   wset,0
+   win,0
    xr = [min(tout), max(tout)]
    yr = [-2,2]*1d-2             ; in percent
    plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/nodata,xtitle='time',ytitle='dE/E, dL/L (%)'
@@ -137,7 +137,7 @@ if(iplot eq 1) then begin
    screen_to_png,'e-time.png'
 
 ;  plot orbits of those particles
-   wset,2
+   win,2
    xr = [-100,100]
    yr = xr
    plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/iso,/nodata,xtitle='x',ytitle='y'
@@ -146,7 +146,7 @@ if(iplot eq 1) then begin
    screen_to_png,'orbit.png'
 
 ; plot radial position of these particles
-   wset,4
+   win,4
    xr = [min(tout), max(tout)]
    yr = [0,80]
    plot,[0],[0],xr=xr,yr=yr,/xs,/ys,/nodata,xtitle='t',ytitle='r'
@@ -155,7 +155,7 @@ for i=0,nplot-1 do begin dr = sqrt(reform(xout[i,*])^2 + reform(yout[i,*])^2) &
    screen_to_png,'r-time.png'
 
 ; make histogram of energy changes at end
-   wset,6
+   win,6
    ohist,de,x,y,-0.05,0.05,0.001
    plot,x,y,psym=10,xtitle='de (%)'
    screen_to_png,'de-hist.png'
diff --git a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
new file mode 100644
index 0000000000000000000000000000000000000000..38dd16880a209b885f7ad9c30c024988f4d8228f
--- /dev/null
+++ b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
@@ -0,0 +1,35 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   1.5   # The end time of the simulation (in internal units).
+  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            kelvinHelmholtz  # Common part of the name of output files
+  time_first:          0.               # Time of the first output (in internal units)
+  delta_time:          0.25      # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-2 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.01      # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./kelvinHelmholtz.hdf5     # The file to read
diff --git a/examples/KelvinHelmholtz_2D/makeIC.py b/examples/KelvinHelmholtz_2D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd0f39ed90faf0d67ff4a508bff83067bf748d43
--- /dev/null
+++ b/examples/KelvinHelmholtz_2D/makeIC.py
@@ -0,0 +1,154 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 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/>.
+ # 
+ ##############################################################################
+
+import h5py
+from numpy import *
+import sys
+
+# Generates a swift IC file for the Kelvin-Helmholtz vortex in a periodic box
+
+# Parameters
+L2    = 128       # Particles along one edge in the low-density region
+gamma = 5./3.     # Gas adiabatic index
+P1    = 2.5       # Central region pressure
+P2    = 2.5       # Outskirts pressure
+v1    = 0.5       # Central region velocity
+v2    = -0.5      # Outskirts vlocity
+rho1  = 2         # Central density
+rho2  = 1         # Outskirts density
+omega0 = 0.1
+sigma = 0.05 / sqrt(2)
+fileOutputName = "kelvinHelmholtz.hdf5" 
+#---------------------------------------------------
+
+# Start by generating grids of particles at the two densities
+numPart2 = L2 * L2
+L1 = int(sqrt(numPart2 / rho2 * rho1))
+numPart1 = L1 * L1
+
+#print "N2 =", numPart2, "N1 =", numPart1
+#print "L2 =", L2, "L1 = ", L1
+#print "rho2 =", rho2, "rho1 =", (float(L1*L1)) / (float(L2*L2))
+
+coords1 = zeros((numPart1, 3))
+coords2 = zeros((numPart2, 3))
+h1 = ones(numPart1) * 1.2348 / L1
+h2 = ones(numPart2) * 1.2348 / L2
+m1 = zeros(numPart1)
+m2 = zeros(numPart2)
+u1 = zeros(numPart1)
+u2 = zeros(numPart2)
+vel1 = zeros((numPart1, 3))
+vel2 = zeros((numPart2, 3))
+
+# Particles in the central region
+for i in range(L1):
+    for j in range(L1):
+
+        index = i * L1 + j
+        
+        x = i / float(L1) + 1. / (2. * L1)
+        y = j / float(L1) + 1. / (2. * L1)
+
+        coords1[index, 0] = x
+        coords1[index, 1] = y
+        u1[index] = P1 / (rho1 * (gamma-1.))
+        vel1[index, 0] = v1
+        
+# Particles in the outskirts
+for i in range(L2):
+    for j in range(L2):
+
+        index = i * L2 + j
+        
+        x = i / float(L2) + 1. / (2. * L2)
+        y = j / float(L2) + 1. / (2. * L2)
+
+        coords2[index, 0] = x
+        coords2[index, 1] = y
+        u2[index] = P2 / (rho2 * (gamma-1.))
+        vel2[index, 0] = v2
+
+
+# Now concatenate arrays
+where1 = abs(coords1[:,1]-0.5) < 0.25
+where2 = abs(coords2[:,1]-0.5) > 0.25
+
+coords = append(coords1[where1, :], coords2[where2, :], axis=0)
+
+#print L2*(L2/2), L1*(L1/2)
+#print shape(coords), shape(coords1[where1,:]), shape(coords2[where2,:])
+#print shape(coords), shape(logical_not(coords1[where1,:])), shape(logical_not(coords2[where2,:]))
+
+vel = append(vel1[where1, :], vel2[where2, :], axis=0)
+h = append(h1[where1], h2[where2], axis=0)
+m = append(m1[where1], m2[where2], axis=0)
+u = append(u1[where1], u2[where2], axis=0)
+numPart = size(h)
+ids = linspace(1, numPart, numPart)
+m[:] = (0.5 * rho1 + 0.5 * rho2) / float(numPart)
+
+# Velocity perturbation
+vel[:,1] = omega0 * sin(4*pi*coords[:,0]) * (exp(-(coords[:,1]-0.25)**2 / (2 * sigma**2)) + exp(-(coords[:,1]-0.75)**2 / (2 * sigma**2)))
+            
+#File
+fileOutput = h5py.File(fileOutputName, 'w')
+
+# Header
+grp = fileOutput.create_group("/Header")
+grp.attrs["BoxSize"] = [1., 1., 0.1]
+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["NumFileOutputsPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 2
+
+#Runtime parameters
+grp = fileOutput.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+#Units
+grp = fileOutput.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 1.
+grp.attrs["Unit mass in cgs (U_M)"] = 1.
+grp.attrs["Unit time in cgs (U_t)"] = 1.
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+#Particle group
+grp = fileOutput.create_group("/PartType0")
+ds = grp.create_dataset('Coordinates', (numPart, 3), 'd')
+ds[()] = coords
+ds = grp.create_dataset('Velocities', (numPart, 3), 'f')
+ds[()] = vel
+ds = grp.create_dataset('Masses', (numPart, 1), 'f')
+ds[()] = m.reshape((numPart,1))
+ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f')
+ds[()] = h.reshape((numPart,1))
+ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f')
+ds[()] = u.reshape((numPart,1))
+ds = grp.create_dataset('ParticleIDs', (numPart,1), 'L')
+ds[()] = ids.reshape((numPart,1))
+
+fileOutput.close()
+
+
diff --git a/examples/KelvinHelmholtz_2D/plotSolution.py b/examples/KelvinHelmholtz_2D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..9191f3ac7ec75c61d5fdab5d347c86222f787fab
--- /dev/null
+++ b/examples/KelvinHelmholtz_2D/plotSolution.py
@@ -0,0 +1,159 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016  Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Computes the analytical solution of the Gresho-Chan vortex and plots the SPH answer
+
+# Parameters
+gas_gamma = 5./3.     # Gas adiabatic index
+P1    = 2.5       # Central region pressure
+P2    = 2.5       # Outskirts pressure
+v1    = 0.5       # Central region velocity
+v2    = -0.5      # Outskirts vlocity
+rho1  = 2         # Central density
+rho2  = 1         # Outskirts density
+
+# ---------------------------------------------------------------
+# Don't touch anything after this.
+# ---------------------------------------------------------------
+
+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("kelvinHelmholtz_%03d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][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"]
+
+pos = sim["/PartType0/Coordinates"][:,:]
+x = pos[:,0] - boxSize / 2
+y = pos[:,1] - boxSize / 2
+vel = sim["/PartType0/Velocities"][:,:]
+v_norm = sqrt(vel[:,0]**2 + vel[:,1]**2)
+rho = sim["/PartType0/Density"][:]
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+
+# Plot the interesting quantities
+figure()
+
+
+# Azimuthal velocity profile -----------------------------
+subplot(231)
+scatter(pos[:,0], pos[:,1], c=vel[:,0], cmap="PuBu", edgecolors='face', s=4, vmin=-1., vmax=1.)
+text(0.97, 0.97, "${\\rm{Velocity~along}}~x$", ha="right", va="top", backgroundcolor="w")
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=0)
+xlim(0, 1)
+ylim(0, 1)
+
+# Radial density profile --------------------------------
+subplot(232)
+scatter(pos[:,0], pos[:,1], c=rho, cmap="PuBu", edgecolors='face', s=4, vmin=0.8, vmax=2.2)
+text(0.97, 0.97, "${\\rm{Density}}$", ha="right", va="top", backgroundcolor="w")
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=0)
+xlim(0, 1)
+ylim(0, 1)
+
+# Radial pressure profile --------------------------------
+subplot(233)
+scatter(pos[:,0], pos[:,1], c=P, cmap="PuBu", edgecolors='face', s=4, vmin=1, vmax=4)
+text(0.97, 0.97, "${\\rm{Pressure}}$", ha="right", va="top", backgroundcolor="w")
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=0)
+xlim(0, 1)
+ylim(0, 1)
+
+# Internal energy profile --------------------------------
+subplot(234)
+scatter(pos[:,0], pos[:,1], c=u, cmap="PuBu", edgecolors='face', s=4, vmin=1.5, vmax=5.)
+text(0.97, 0.97, "${\\rm{Internal~energy}}$", ha="right", va="top", backgroundcolor="w")
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=0)
+xlim(0, 1)
+ylim(0, 1)
+
+# Radial entropy profile --------------------------------
+subplot(235)
+scatter(pos[:,0], pos[:,1], c=S, cmap="PuBu", edgecolors='face', s=4, vmin=0.5, vmax=3.)
+text(0.97, 0.97, "${\\rm{Entropy}}$", ha="right", va="top", backgroundcolor="w")
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=0)
+xlim(0, 1)
+ylim(0, 1)
+
+# Image --------------------------------------------------
+#subplot(234)
+#scatter(pos[:,0], pos[:,1], c=v_norm, cmap="PuBu", edgecolors='face', s=4, vmin=0, vmax=1)
+#text(0.95, 0.95, "$|v|$", ha="right", va="top")
+#xlim(0,1)
+#ylim(0,1)
+#xlabel("$x$", labelpad=0)
+#ylabel("$y$", labelpad=0)
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+text(-0.49, 0.9, "Kelvin-Helmholtz instability at $t=%.2f$"%(time), fontsize=10)
+text(-0.49, 0.8, "Centre:~~~ $(P, \\rho, v) = (%.3f, %.3f, %.3f)$"%(P1, rho1, v1), fontsize=10)
+text(-0.49, 0.7, "Outskirts: $(P, \\rho, v) = (%.3f, %.3f, %.3f)$"%(P2, rho2, v2), fontsize=10)
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+savefig("KelvinHelmholtz.png", dpi=200)
diff --git a/examples/KelvinHelmholtz_2D/run.sh b/examples/KelvinHelmholtz_2D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3d83bcc6dd7c226885ed83c3188f3177c4807154
--- /dev/null
+++ b/examples/KelvinHelmholtz_2D/run.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e kelvinHelmholtz.hdf5 ]
+then
+    echo "Generating initial conditions for the Kelvin-Helmholtz example..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift -s -t 1 kelvinHelmholtz.yml 2>&1 | tee output.log
+
+# Plot the solution
+python plotSolution.py 6
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 187abcf9898b8024b4e4f5089de7ca51e6dd2e3c..38ddcf61935b2099a785fa9a7f427807177b7cc0 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -65,19 +65,36 @@ swift_fixdt_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="e
 swift_fixdt_mpi_LDADD =  ../src/.libs/libswiftsim_mpi.a $(MPI_LIBS) $(EXTRA_LIBS)
 
 # Scripts to generate ICs
-EXTRA_DIST = UniformBox/makeIC.py UniformBox/run.sh UniformBox/uniformBox.yml \
-	     UniformDMBox/makeIC.py \
-	     PerturbedBox/makeIC.py \
-	     SedovBlast/makeIC.py SedovBlast/makeIC_fcc.py SedovBlast/solution.py SedovBlast/run.sh SedovBlast/sedov.yml \
-	     SodShock/makeIC.py SodShock/solution.py SodShock/glass_001.hdf5 SodShock/glass_002.hdf5 SodShock/rhox.py SodShock/run.sh SodShock/sodShock.yml \
-	     CosmoVolume/getIC.sh CosmoVolume/run.sh CosmoVolume/cosmoVolume.yml \
-	     BigCosmoVolume/makeIC.py \
+EXTRA_DIST = BigCosmoVolume/makeIC.py \
 	     BigPerturbedBox/makeIC_fcc.py \
-             GreshoVortex/makeIC.py GreshoVortex/solution.py \
-             MultiTypes/makeIC.py \
-             parameter_example.yml
+	     CosmoVolume/cosmoVolume.yml CosmoVolume/getIC.sh CosmoVolume/run.sh \
+	     EAGLE_12/eagle_12.yml EAGLE_12/getIC.sh EAGLE_12/README EAGLE_12/run.sh \
+	     EAGLE_25/eagle_25.yml EAGLE_25/getIC.sh EAGLE_25/README EAGLE_25/run.sh \
+	     EAGLE_50/eagle_50.yml EAGLE_50/getIC.sh EAGLE_50/README EAGLE_50/run.sh \
+	     ExternalPointMass/externalPointMass.yml ExternalPointMass/makeIC.py ExternalPointMass/run.sh ExternalPointMass/test.pro \
+	     GreshoVortex_2D/getGlass.sh GreshoVortex_2D/gresho.yml GreshoVortex_2D/makeIC.py GreshoVortex_2D/plotSolution.py GreshoVortex_2D/run.sh \
+	     KelvinHelmholtz_2D/kelvinHelmholtz.yml KelvinHelmholtz_2D/makeIC.py KelvinHelmholtz_2D/plotSolution.py KelvinHelmholtz_2D/run.sh \
+	     MultiTypes/makeIC.py  MultiTypes/multiTypes.yml MultiTypes/run.sh \
+	     PerturbedBox_2D/makeIC.py PerturbedBox_2D/perturbedPlane.yml \
+	     PerturbedBox_3D/makeIC.py PerturbedBox_3D/perturbedBox.yml PerturbedBox_3D/run.sh \
+	     SedovBlast_1D/makeIC.py SedovBlast_1D/plotSolution.py SedovBlast_1D/run.sh SedovBlast_1D/sedov.yml \
+	     SedovBlast_2D/getGlass.sh SedovBlast_2D/makeIC.py SedovBlast_2D/plotSolution.py SedovBlast_2D/run.sh SedovBlast_2D/sedov.yml \
+	     SedovBlast_3D/getGlass.sh SedovBlast_3D/makeIC.py SedovBlast_3D/plotSolution.py SedovBlast_3D/run.sh SedovBlast_3D/sedov.yml \
+	     SodShock_1D/makeIC.py SodShock_1D/plotSolution.py SodShock_1D/run.sh SodShock_1D/sodShock.yml \
+	     SodShock_2D/getGlass.sh SodShock_2D/makeIC.py SodShock_2D/plotSolution.py SodShock_2D/run.sh SodShock_2D/sodShock.yml \
+	     SodShock_3D/getGlass.sh SodShock_3D/makeIC.py SodShock_3D/plotSolution.py SodShock_3D/run.sh SodShock_3D/sodShock.yml \
+	     SquareTest_2D/makeIC.py SquareTest_2D/plotSolution.py SquareTest_2D/run.sh SquareTest_2D/square.yml \
+	     UniformBox_2D/makeIC.py UniformBox_2D/run.sh UniformBox_2D/uniformPlane.yml \
+	     UniformBox_3D/makeICbig.py UniformBox_3D/makeIC.py UniformBox_3D/run.sh UniformBox_3D/uniformBox.yml \
+	     UniformDMBox/makeIC.py
+
+# Default parameter file
+EXTRA_DIST += parameter_example.yml
 
 # Scripts to plot task graphs
 EXTRA_DIST += plot_tasks_MPI.py plot_tasks.py \
 	      process_plot_tasks_MPI process_plot_tasks
 
+# Script for scaling plot
+EXTRA_DIST += plot_scaling_results.py
+
diff --git a/examples/MultiTypes/makeIC.py b/examples/MultiTypes/makeIC.py
index cf889f9b6eab502f692cd6c8b4506c31664ecdcb..229450b67c02258553b588483d7cbd4fef887817 100644
--- a/examples/MultiTypes/makeIC.py
+++ b/examples/MultiTypes/makeIC.py
@@ -61,6 +61,7 @@ grp.attrs["Time"] = 0.0
 grp.attrs["NumFilesPerSnapshot"] = 1
 grp.attrs["MassTable"] = [0.0, massDM, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
+grp.attrs["Dimension"] = 3
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
diff --git a/examples/MultiTypes/run.sh b/examples/MultiTypes/run.sh
index 9782a27fe18a7fac94cc2a12353f81abb9efb06b..57465ce0ba6dde3988359df990f2a93323dbc617 100755
--- a/examples/MultiTypes/run.sh
+++ b/examples/MultiTypes/run.sh
@@ -7,4 +7,4 @@ then
     python makeIC.py 50 60
 fi
 
-../swift -s -g -t 16 multiTypes.yml
+../swift -s -g -t 16 multiTypes.yml 2>&1 | tee output.log
diff --git a/examples/GreshoVortex/makeIC.py b/examples/PerturbedBox_2D/makeIC.py
similarity index 55%
rename from examples/GreshoVortex/makeIC.py
rename to examples/PerturbedBox_2D/makeIC.py
index 12edcb6e8154ec6f865d28a6daeb02d385d14bbf..87a41517772570870e04c79d3694c115a909e214 100644
--- a/examples/GreshoVortex/makeIC.py
+++ b/examples/PerturbedBox_2D/makeIC.py
@@ -1,7 +1,6 @@
 ###############################################################################
  # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # Copyright (c) 2016 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
@@ -19,28 +18,28 @@
  ##############################################################################
 
 import h5py
+import sys
 import random
 from numpy import *
 
-# Generates a swift IC file for the Gresho Vortex in a periodic box
+# Generates a swift IC file containing a perturbed cartesian distribution of particles
+# at a constant density and pressure in a cubic box
 
 # Parameters
-periodic= 1      # 1 For periodic box
-factor = 3
-boxSize = [ 1.0 , 1.0, 1.0/factor ]
-L = 120           # Number of particles along one axis
-gamma = 5./3.     # Gas adiabatic index
-eta = 1.2349      # 48 ngbs with cubic spline kernel
-rho = 1           # Gas density
-P0 = 0.           # Constant additional pressure (should have no impact on the dynamics)
-fileName = "greshoVortex.hdf5" 
-vol = boxSize[0] * boxSize[1] * boxSize[2]
+periodic= 1          # 1 For periodic box
+boxSize = 1.
+L = int(sys.argv[1]) # Number of particles along one axis
+rho = 1.             # Density
+P = 1.               # Pressure
+gamma = 5./3.        # Gas adiabatic index
+pert = 0.1          # Perturbation scale (in units of the interparticle separation)
+fileName = "perturbedPlane.hdf5" 
 
 
 #---------------------------------------------------
-
-numPart = L**3 / factor
-mass = boxSize[0]*boxSize[1]*boxSize[2] * rho / numPart
+numPart = L**2
+mass = boxSize**2 * rho / numPart
+internalEnergy = P / ((gamma - 1.)*rho)
 
 #Generate particles
 coords = zeros((numPart, 3))
@@ -50,43 +49,26 @@ h      = zeros((numPart, 1))
 u      = zeros((numPart, 1))
 ids    = zeros((numPart, 1), dtype='L')
 
-partId=0
 for i in range(L):
     for j in range(L):
-        for k in range(L/factor):
-            index = i*L*L/factor + j*L/factor + k
-            x = i * boxSize[0] / L + boxSize[0] / (2*L)
-            y = j * boxSize[0] / L + boxSize[0] / (2*L)
-            z = k * boxSize[0] / L + boxSize[0] / (2*L)
-            r2 = (x - boxSize[0] / 2)**2 + (y - boxSize[1] / 2)**2
-            r = sqrt(r2)
-            coords[index,0] = x
-            coords[index,1] = y
-            coords[index,2] = z
-            v_phi = 0.
-            if r < 0.2:
-                v_phi = 5.*r
-            elif r < 0.4:
-                v_phi = 2. - 5.*r
-            else:
-                v_phi = 0.
-            v[index,0] = -v_phi * (y - boxSize[0] / 2) / r
-            v[index,1] =  v_phi * (x - boxSize[0] / 2) / r
-            v[index,2] = 0.
-            m[index] = mass
-            h[index] = eta * boxSize[0] / L
-            P = P0
-            if r < 0.2:
-                P = P + 5. + 12.5*r2
-            elif r < 0.4:
-                P = P + 9. + 12.5*r2 - 20.*r + 4.*log(r/0.2)
-            else:
-                P = P + 3. + 4.*log(2.)
-            u[index] = P / ((gamma - 1.)*rho)
-            ids[index] = partId + 1
-            partId = partId + 1
-
-
+        index = i*L + j
+        x = i * boxSize / L + boxSize / (2*L) + random.random() * pert * boxSize/(2.*L)
+        y = j * boxSize / L + boxSize / (2*L) + random.random() * pert * boxSize/(2.*L)
+        z = 0
+        coords[index,0] = x
+        coords[index,1] = y
+        coords[index,2] = z
+        v[index,0] = 0.
+        v[index,1] = 0.
+        v[index,2] = 0.
+        m[index] = mass
+        h[index] = 1.23485 * boxSize / L
+        u[index] = internalEnergy
+        ids[index] = index
+            
+            
+
+#--------------------------------------------------
 
 #File
 file = h5py.File(fileName, 'w')
@@ -101,6 +83,8 @@ grp.attrs["Time"] = 0.0
 grp.attrs["NumFilesPerSnapshot"] = 1
 grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total"] = numPart
+grp.attrs["Dimension"] = 2
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
@@ -126,9 +110,7 @@ ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f')
 ds[()] = h
 ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f')
 ds[()] = u
-ds = grp.create_dataset('ParticleIDs', (numPart,1), 'L')
-ds[()] = ids[:]
+ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L')
+ds[()] = ids + 1
 
 file.close()
-
-
diff --git a/examples/PerturbedBox_2D/perturbedPlane.yml b/examples/PerturbedBox_2D/perturbedPlane.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e810131cc4b1c66b46b483b1605f9d84bcf203b3
--- /dev/null
+++ b/examples/PerturbedBox_2D/perturbedPlane.yml
@@ -0,0 +1,35 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   10.   # The end time of the simulation (in internal units).
+  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            perturbedPlane # Common part of the name of output files
+  time_first:          0.             # Time of the first output (in internal units)
+  delta_time:          1e-1           # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-3 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.1      # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./perturbedPlane.hdf5     # The file to read
diff --git a/examples/PerturbedBox/makeIC.py b/examples/PerturbedBox_3D/makeIC.py
similarity index 99%
rename from examples/PerturbedBox/makeIC.py
rename to examples/PerturbedBox_3D/makeIC.py
index cc7fffe14d4f361153a07101ddcec20a3c979b4a..1b0fc284e4c40b51fca45f117b92175a0ea45f31 100644
--- a/examples/PerturbedBox/makeIC.py
+++ b/examples/PerturbedBox_3D/makeIC.py
@@ -86,6 +86,7 @@ grp.attrs["NumFilesPerSnapshot"] = 1
 grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["NumPart_Total"] = numPart
+grp.attrs["Dimension"] = 3
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
diff --git a/examples/PerturbedBox/perturbedBox.yml b/examples/PerturbedBox_3D/perturbedBox.yml
similarity index 100%
rename from examples/PerturbedBox/perturbedBox.yml
rename to examples/PerturbedBox_3D/perturbedBox.yml
diff --git a/examples/PerturbedBox/run.sh b/examples/PerturbedBox_3D/run.sh
similarity index 78%
rename from examples/PerturbedBox/run.sh
rename to examples/PerturbedBox_3D/run.sh
index c15011ca526efb7250e93dc8cb002e49647b82b6..e20bff52d18322ce377fb589900fd9e13eefe64d 100755
--- a/examples/PerturbedBox/run.sh
+++ b/examples/PerturbedBox_3D/run.sh
@@ -7,4 +7,4 @@ then
     python makeIC.py 50
 fi
 
-../swift -s -t 16 perturbedBox.yml
+../swift -s -t 16 perturbedBox.yml 2>&1 | tee output.log
diff --git a/examples/SedovBlast/makeIC_fcc.py b/examples/SedovBlast/makeIC_fcc.py
deleted file mode 100644
index 0d3a017a9b7f3b30b61e723e3d1646d7797b40a4..0000000000000000000000000000000000000000
--- a/examples/SedovBlast/makeIC_fcc.py
+++ /dev/null
@@ -1,122 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import h5py
-import random
-from numpy import *
-
-# Generates a swift IC file for the Sedov blast test in a periodic cubic box
-
-# Parameters
-periodic= 1      # 1 For periodic box
-boxSize = 5.
-L = 64            # Number of particles boxes along one axis
-rho = 1.          # Density
-P = 1.e-5         # Pressure
-E0= 1.e2          # Energy of the explosion
-pert = 0.025
-gamma = 5./3.     # Gas adiabatic index
-eta = 1.2349          # 48 ngbs with cubic spline kernel
-fileName = "sedov.hdf5" 
-
-
-#---------------------------------------------------
-numPart = 4*(L**3) 
-mass = boxSize**3 * rho / numPart
-internalEnergy = P / ((gamma - 1.)*rho)
-off = array( [ [ 0.0 , 0.0 , 0.0 ] , [ 0.0 , 0.5 , 0.5 ] , [ 0.5 , 0.0 , 0.5 ] , [ 0.5 , 0.5 , 0.0 ] ] );
-hbox = boxSize / L
-
-# if L%2 == 0:
-#     print "Number of particles along each dimension must be odd."
-#     exit()
-
-#Generate particles
-coords = zeros((numPart, 3))
-v      = zeros((numPart, 3))
-m      = zeros(numPart)
-h      = zeros(numPart)
-u      = zeros(numPart)
-ids    = zeros(numPart, dtype='L')
-
-for i in range(L):
-    for j in range(L):
-        for k in range(L):
-            x = (i + 0.25) * hbox
-            y = (j + 0.25) * hbox
-            z = (k + 0.25) * hbox
-            for ell in range(4): 
-                index = 4*(i*L*L + j*L + k) + ell
-                coords[index,0] = x + off[ell,0] * hbox
-                coords[index,1] = y + off[ell,1] * hbox
-                coords[index,2] = z + off[ell,2] * hbox
-                v[index,0] = 0.
-                v[index,1] = 0.
-                v[index,2] = 0.
-                m[index] = mass
-                h[index] = eta * hbox
-                u[index] = internalEnergy
-                ids[index] = index + 1
-                if sqrt((x - boxSize/2.)**2 + (y - boxSize/2.)**2 + (z - boxSize/2.)**2) < 1.2 * hbox:
-                    u[index] = u[index] + E0 / (28. * mass)
-                    print "Particle " , index , " set to detonate."
-                coords[index,0] += random.random() * pert * hbox
-                coords[index,1] += random.random() * pert * hbox
-                coords[index,2] += random.random() * pert * hbox
-
-
-#--------------------------------------------------
-
-#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
-
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
-#Units
-grp = file.create_group("/Units")
-grp.attrs["Unit length in cgs (U_L)"] = 1.
-grp.attrs["Unit mass in cgs (U_M)"] = 1.
-grp.attrs["Unit time in cgs (U_t)"] = 1.
-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/SedovBlast/profile.py b/examples/SedovBlast/profile.py
deleted file mode 100644
index 1b1dfb389b2c9f6d0e5af02a17a07068f03bfd92..0000000000000000000000000000000000000000
--- a/examples/SedovBlast/profile.py
+++ /dev/null
@@ -1,46 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import numpy as np
-import h5py
-import matplotlib
-matplotlib.use('Agg')
-import pylab as pl
-import glob
-import sedov
-
-# plot the radial density profile for all snapshots in the current directory,
-# as well as the analytical solution of Sedov
-
-for f in sorted(glob.glob("output*.hdf5")):
-    file = h5py.File(f, "r")
-    t = file["/Header"].attrs["Time"]
-    coords = np.array(file["/PartType0/Coordinates"])
-    rho = np.array(file["/PartType0/Density"])
-
-    radius = np.sqrt( (coords[:,0]-5.)**2 + (coords[:,1]-5.)**2 + \
-                      (coords[:,2]-5.)**2 )
-    
-    r_theory, rho_theory = sedov.get_analytical_solution(100., 5./3., 3, t,
-                                                         max(radius))
-    
-    pl.plot(r_theory, rho_theory, "r-")
-    pl.plot(radius, rho, "k.")
-    pl.savefig("{name}.png".format(name = f[:-5]))
-    pl.close()
diff --git a/examples/SedovBlast/rdf.py b/examples/SedovBlast/rdf.py
deleted file mode 100644
index 7f932cc814dc36e14e1bef52e33cf5ed1f527dfd..0000000000000000000000000000000000000000
--- a/examples/SedovBlast/rdf.py
+++ /dev/null
@@ -1,121 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import h5py
-import random
-import sys
-import math
-from numpy import *
-
-# Reads the HDF5 output of SWIFT and generates a radial density profile
-# of the different physical values.
-
-# Input values?
-if len(sys.argv) < 3 :
-    print "Usage: " , sys.argv[0] , " <filename> <nr. bins>"
-    exit()
-    
-# Get the input arguments
-fileName = sys.argv[1];
-nr_bins = int( sys.argv[2] );
-
-
-# Open the file
-fileName = sys.argv[1];
-file = h5py.File( fileName , 'r' )
-
-# Get the space dimensions.
-grp = file[ "/Header" ]
-boxsize = grp.attrs[ 'BoxSize' ]
-if len( boxsize ) > 0:
-    boxsize = boxsize[0]
-
-# Get the particle data
-grp = file.get( '/PartType0' )
-ds = grp.get( 'Coordinates' )
-coords = ds[...]
-ds = grp.get( 'Velocities' )
-v = ds[...]
-ds = grp.get( 'Masses' )
-m = ds[...]
-ds = grp.get( 'SmoothingLength' )
-h = ds[...]
-ds = grp.get( 'InternalEnergy' )
-u = ds[...]
-ds = grp.get( 'ParticleIDs' )
-ids = ds[...]
-ds = grp.get( 'Density' )
-rho = ds[...]
-
-# Set the origin to be the middle of the box
-origin = [ boxsize / 2 , boxsize / 2 , boxsize / 2 ]
-
-# Get the maximum radius
-r_max = boxsize / 2
-
-# Init the bins
-nr_parts = coords.shape[0]
-bins_v = zeros( nr_bins )
-bins_m = zeros( nr_bins )
-bins_h = zeros( nr_bins )
-bins_u = zeros( nr_bins )
-bins_rho = zeros( nr_bins )
-bins_count = zeros( nr_bins )
-bins_P = zeros( nr_bins )
-
-# Loop over the particles and fill the bins.
-for i in range( nr_parts ):
-
-    # Get the box index.
-    r = coords[i] - origin
-    r = sqrt( r[0]*r[0] + r[1]*r[1] + r[2]*r[2] )
-    ind = floor( r / r_max * nr_bins )
-    
-    # Update the bins
-    if ( ind < nr_bins ):
-        bins_count[ind] += 1
-        bins_v[ind] += sqrt( v[i,0]*v[i,0] + v[i,1]*v[i,1] + v[i,2]*v[i,2] )
-        bins_m[ind] += m[i]
-        bins_h[ind] += h[i]
-        bins_u[ind] += u[i]
-        bins_rho[ind] += rho[i]
-        bins_P[ind] += (2.0/3)*u[i]*rho[i]
-    
-# Loop over the bins and dump them
-print "# bucket left right count v m h u rho"
-for i in range( nr_bins ):
-
-    # Normalize by the bin volume.
-    r_left = r_max * i / nr_bins
-    r_right = r_max * (i+1) / nr_bins
-    vol = 4/3*math.pi*(r_right*r_right*r_right - r_left*r_left*r_left)
-    ivol = 1.0 / vol
-
-    print "%i %.3e %.3e %.3e %.3e %.3e %.3e %.3e %.3e %.3e" % \
-        ( i , r_left , r_right , \
-          bins_count[i] * ivol , \
-          bins_v[i] / bins_count[i] , \
-          bins_m[i] * ivol , \
-          bins_h[i] / bins_count[i] , \
-          bins_u[i] / bins_count[i] , \
-          bins_rho[i] / bins_count[i] ,
-          bins_P[i] / bins_count[i] )
-    
-    
diff --git a/examples/SedovBlast/run.sh b/examples/SedovBlast/run.sh
deleted file mode 100755
index f71830eb6a66a9ea84e93fd1bed1261b1cb42b7b..0000000000000000000000000000000000000000
--- a/examples/SedovBlast/run.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-# Generate the initial conditions if they are not present.
-if [ ! -e sedov.hdf5 ]
-then
-    echo "Generating initial conditions for the SedovBlast example..."
-    python makeIC_fcc.py
-fi
-
-../swift -s -t 16 sedov.yml
diff --git a/examples/SedovBlast/sedov.py b/examples/SedovBlast/sedov.py
deleted file mode 100755
index ca908fe65cd15ddd2b0e4fb41a402492432a0690..0000000000000000000000000000000000000000
--- a/examples/SedovBlast/sedov.py
+++ /dev/null
@@ -1,212 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import numpy as np
-import scipy.integrate as integrate
-import scipy.optimize as optimize
-import os
-
-# Calculate the analytical solution of the Sedov-Taylor shock wave for a given
-# number of dimensions, gamma and time. We assume dimensionless units and a
-# setup with constant density 1, pressure and velocity 0. An energy 1 is
-# inserted in the center at t 0.
-#
-# The solution is a self-similar shock wave, which was described in detail by
-# Sedov (1959). We follow his notations and solution method.
-#
-# The position of the shock at time t is given by
-#  r2 = (E/rho1)^(1/(2+nu)) * t^(2/(2+nu))
-# the energy E is related to the inserted energy E0 by E0 = alpha*E, with alpha
-# a constant which has to be calculated by imposing energy conservation.
-#
-# The density for a given radius at a certain time is determined by introducing
-# the dimensionless position coordinate lambda = r/r2. The density profile as
-# a function of lambda is constant in time and given by
-#  rho = rho1 * R(V, gamma, nu)
-# and
-#  V = V(lambda, gamma, nu)
-#
-# The function V(lambda, gamma, nu) is found by solving a differential equation
-# described in detail in Sedov (1959) chapter 4, section 5. Alpha is calculated
-# from the integrals in section 11 of the same chapter.
-#
-# Numerically, the complete solution requires the use of 3 quadratures and 1
-# root solver, which are implemented using the GNU Scientific Library (GSL).
-# Since some quadratures call functions that themselves contain quadratures,
-# the problem is highly non-linear and complex and takes a while to converge.
-# Therefore, we tabulate the alpha values and profile values the first time
-# a given set of gamma and nu values is requested and reuse these tabulated
-# values.
-#
-# Reference:
-#  Sedov (1959): Sedov, L., Similitude et dimensions en mecanique (7th ed.;
-#                Moscou: Editions Mir) - french translation of the original
-#                book from 1959.
-
-# dimensionless variable z = gamma*P/R as a function of V (and gamma and nu)
-# R is a dimensionless density, while P is a dimensionless pressure
-# z is hence a sort of dimensionless sound speed
-# The expression below corresponds to eq. 11.9 in Sedov (1959), chapter 4
-def _z(V, gamma, nu):
-    if V == 2./(nu+2.)/gamma:
-        return 0.
-    else:
-        return (gamma-1.)*V*V*(V-2./(2.+nu))/2./(2./(2.+nu)/gamma-V)
-
-# differential equation that needs to be solved to obtain lambda for a given V
-# corresponds to eq. 5.11 in Sedov (1959), chapter 4 (omega = 0)
-def _dlnlambda_dV(V, gamma, nu):
-    nom = _z(V, gamma, nu) - (V-2./(nu+2.))*(V-2./(nu+2.))
-    denom = V*(V-1.)*(V-2./(nu+2.))+nu*(2./(nu+2.)/gamma-V)*_z(V, gamma, nu)
-    return nom/denom
-
-# dimensionless variable lambda = r/r2 as a function of V (and gamma and nu)
-# found by solving differential equation 5.11 in Sedov (1959), chapter 4
-# (omega = 0)
-def _lambda(V, gamma, nu):
-    if V == 2./(nu+2.)/gamma:
-        return 0.
-    else:
-        V0 = 4./(nu+2.)/(gamma+1.)
-        integral, err = integrate.quad(_dlnlambda_dV, V, V0, (gamma, nu),
-                                       limit = 8000)
-        return np.exp(-integral)
-
-# dimensionless variable R = rho/rho1 as a function of V (and gamma and nu)
-# found by inverting eq. 5.12 in Sedov (1959), chapter 4 (omega = 0)
-# the integration constant C is found by inserting the R, V and z values
-# at the shock wave, where lambda is 1. These correspond to eq. 11.8 in Sedov
-# (1959), chapter 4.
-def _R(V, gamma, nu):
-    if V == 2./(nu+2.)/gamma:
-        return 0.
-    else:
-        C = 8.*gamma*(gamma-1.)/(nu+2.)/(nu+2.)/(gamma+1.)/(gamma+1.) \
-                *((gamma-1.)/(gamma+1.))**(gamma-2.) \
-                *(4./(nu+2.)/(gamma+1.)-2./(nu+2.))
-        lambda1 = _lambda(V, gamma, nu)
-        lambda5 = lambda1**(nu+2)
-        return (_z(V, gamma, nu)*(V-2./(nu+2.))*lambda5/C)**(1./(gamma-2.))
-
-# function of which we need to find the zero point to invert lambda(V)
-def _lambda_min_lambda(V, lambdax, gamma, nu):
-    return _lambda(V, gamma, nu) - lambdax
-
-# dimensionless variable V = v*t/r as a function of lambda (and gamma and nu)
-# found by inverting the function lamdba(V) which is found by solving
-# differential equation 5.11 in Sedov (1959), chapter 4 (omega = 0)
-# the invertion is done by searching the zero point of the function
-# lambda_min_lambda defined above
-def _V_inv(lambdax, gamma, nu):
-    if lambdax == 0.:
-        return 2./(2.+nu)/gamma;
-    else:
-        return optimize.brentq(_lambda_min_lambda, 2./(nu+2.)/gamma, 
-                               4./(nu+2.)/(gamma+1.), (lambdax, gamma, nu))
-
-# integrand of the first integral in eq. 11.24 in Sedov (1959), chapter 4
-def _integrandum1(lambdax, gamma, nu):
-    V = _V_inv(lambdax, gamma, nu)
-    if nu == 2:
-        return _R(V, gamma, nu)*V**2*lambdax**3
-    else:
-        return _R(V, gamma, nu)*V**2*lambdax**4
-
-# integrand of the second integral in eq. 11.24 in Sedov (1959), chapter 4
-def _integrandum2(lambdax, gamma, nu):
-    V = _V_inv(lambdax, gamma, nu)
-    if V == 2./(nu+2.)/gamma:
-        P = 0.
-    else:
-        P = _z(V, gamma, nu)*_R(V, gamma, nu)/gamma
-    if nu == 2:
-        return P*lambdax**3
-    else:
-        return P*lambdax**4
-
-# calculate alpha = E0/E
-# this corresponds to eq. 11.24 in Sedov (1959), chapter 4
-def get_alpha(gamma, nu):
-  integral1, err1 = integrate.quad(_integrandum1, 0., 1., (gamma, nu))
-  integral2, err2 = integrate.quad(_integrandum2, 0., 1., (gamma, nu))
-  
-  if nu == 2:
-    return np.pi*integral1+2.*np.pi/(gamma-1.)*integral2
-  else:
-    return 2.*np.pi*integral1+4.*np.pi/(gamma-1.)*integral2
-
-# get the analytical solution for the Sedov-Taylor blastwave given an input
-# energy E, adiabatic index gamma, and number of dimensions nu, at time t and
-# with a maximal outer region radius maxr
-def get_analytical_solution(E, gamma, nu, t, maxr = 1.):
-    # we check for the existance of a datafile with precomputed alpha and
-    # profile values
-    # if it does not exist, we calculate it here and write it out
-    # calculation of alpha and the profile takes a while...
-    lvec = np.zeros(1000)
-    Rvec = np.zeros(1000)
-    fname = "sedov_profile_gamma_{gamma}_nu_{nu}.dat".format(gamma = gamma,
-                                                             nu = nu)
-    if os.path.exists(fname):
-        file = open(fname, "r")
-        lines = file.readlines()
-        alpha = float(lines[0])
-        for i in range(len(lines)-1):
-            data = lines[i+1].split()
-            lvec[i] = float(data[0])
-            Rvec[i] = float(data[1])
-    else:
-        alpha = get_alpha(gamma, nu)
-        for i in range(1000):
-            lvec[i] = (i+1)*0.001
-            V = _V_inv(lvec[i], gamma, nu)
-            Rvec[i] = _R(V, gamma, nu)
-        file = open(fname, "w")
-        file.write("{alpha}\n".format(alpha = alpha))
-        for i in range(1000):
-            file.write("{l}\t{R}\n".format(l = lvec[i], R = Rvec[i]))
-
-    xvec = np.zeros(1002)
-    rhovec = np.zeros(1002)
-    if nu == 2:
-        r2 = (E/alpha)**0.25*np.sqrt(t)
-    else:
-        r2 = (E/alpha)**0.2*t**0.4
-
-    for i in range(1000):
-        xvec[i] = lvec[i]*r2
-        rhovec[i] = Rvec[i]
-    xvec[1000] = 1.001*r2
-    xvec[1001] = maxr
-    rhovec[1000] = 1.
-    rhovec[1001] = 1.
-    
-    return xvec, rhovec
-
-def main():
-    E = 1.
-    gamma = 1.66667
-    nu = 3
-    t = 0.001
-    x, rho = get_analytical_solution(E, gamma, nu, t)
-    for i in range(len(x)):
-        print x[i], rho[i]
-
-if __name__ == "__main__":
-    main()
diff --git a/examples/SedovBlast/solution.py b/examples/SedovBlast/solution.py
deleted file mode 100644
index 9335e22bdd76585e8d53d3dba9f9a435197f92fc..0000000000000000000000000000000000000000
--- a/examples/SedovBlast/solution.py
+++ /dev/null
@@ -1,114 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import random
-from numpy import *
-
-# Computes the analytical solution of the 3D Sedov blast wave.
-# The script works for a given initial box and dumped energy and computes the solution at a later time t.
-# The code writes five files rho.dat, P.dat, v.dat, u.dat and s.dat with the density, pressure, internal energy and
-# entropic function on N radial points between r=0 and r=R_max.
-# Follows the solution in Landau & Lifschitz
-
-# Parameters
-rho_0 = 1.          # Background Density
-P_0 = 1.e-5         # Background Pressure
-E_0 = 1.e2          # Energy of the explosion
-gamma = 5./3.     # Gas polytropic index
-
-t = 0.275           # Time of the solution
-
-N = 1000          # Number of radial points
-R_max = 3.        # Maximal radius
-
-# ---------------------------------------------------------------
-# Don't touch anything after this.
-# ---------------------------------------------------------------
-
-if gamma == 5./3.:
-    alpha = 0.49 
-else:
-    print "Unknown value for alpha"
-    exit
-
-# Position and velocity of the shock
-r_shock = (E_0  / (alpha * rho_0))**(1./5.) * t**(2./5.)
-v_shock = (1./5.) * (1./alpha)**(1./5.) * ((E_0 * t**2. / rho_0)**(-4./5.)) * E_0 * (t / rho_0)
-
-# Prepare arrays
-delta_r = R_max / N
-r_s = arange(0, R_max, delta_r) 
-rho_s = ones(N) * rho_0
-P_s = ones(N) * P_0
-u_s = ones(N)
-v_s = zeros(N)
-
-# State on the shock
-rho_shock = rho_0 * (gamma+1.)/(gamma-1.)
-P_shock = 2./(gamma+1.) * rho_shock * v_shock**2
-
-# Integer position of the shock 
-i_shock = min(floor(r_shock /delta_r), N)
-
-# Dimensionless velocity and its spatial derivative
-v_bar0 = (gamma+1.)/(2.*gamma)
-deltaV_bar = (1.0 - v_bar0) / (i_shock - 1)
-
-def rho_dimensionless(v_bar):
-    power1 = (2./(gamma-2.))
-    power2 = -(12.-7.*gamma+13.*gamma**2)/(2.-3.*gamma-11.*gamma**2+6.*gamma**3)
-    power3 = 3./(1.+2.*gamma)
-    term1 = ((1.+ gamma - 2.*v_bar)/(gamma-1.))**power1
-    term2 = ((5.+5.*gamma+2.*v_bar-6.*gamma*v_bar)/(7.-gamma))**power2
-    term3 = ((2.*gamma*v_bar - gamma -1.)/(gamma-1.))**power3
-    return term1 * term2 * term3
-
-def P_dimensionless(v_bar):
-    return (gamma+1. - 2.*v_bar)/(2.*gamma*v_bar - gamma - 1.)*v_bar**2 * rho_dimensionless(v_bar)
-
-def r_dimensionless(v_bar):
-    power1 = (-12.+7.*gamma-13.*gamma**2)/(5.*(-1.+gamma+6.*gamma**2))
-    power2 = (gamma - 1.)/(1.+2.*gamma)
-    term1 = ((5.+5.*gamma+2.*v_bar-6.*gamma*v_bar)/(7.-gamma))**power1
-    term2 = ((2.*gamma*v_bar-gamma-1.)/(gamma-1.))**power2
-    return v_bar**(-2./5.)*term1*term2
-
-# Generate solution
-for i in range(1,int(i_shock)):
-    v_bar = v_bar0 + (i-1)*deltaV_bar    
-    r_s[i] = r_dimensionless(v_bar) * r_shock
-    rho_s[i] = rho_dimensionless(v_bar) * rho_shock
-    P_s[i] = P_shock * (r_s[i]/r_shock)**2 * P_dimensionless(v_bar)
-    v_s[i] = (4./(5.*(gamma+1.)) * r_s[i] / t * v_bar)
-
-u_s = P_s / ((gamma - 1.)*rho_s) # internal energy
-s_s = P_s / rho_s**gamma # entropic function
-rho_s[0] = 0.
-P_s[0] = P_s[1] # dirty...
-
-
-savetxt("rho.dat", column_stack((r_s, rho_s)))
-savetxt("P.dat", column_stack((r_s, P_s)))
-savetxt("v.dat", column_stack((r_s, v_s)))
-savetxt("u.dat", column_stack((r_s, u_s)))
-savetxt("s.dat", column_stack((r_s, s_s)))
-
-
-
diff --git a/examples/SedovBlast/makeIC.py b/examples/SedovBlast_1D/makeIC.py
similarity index 54%
rename from examples/SedovBlast/makeIC.py
rename to examples/SedovBlast_1D/makeIC.py
index e64942e8e92ee6fe67142f841f566019b1a668be..0c3a311703651003dbf17da099e53bf8a607b881 100644
--- a/examples/SedovBlast/makeIC.py
+++ b/examples/SedovBlast_1D/makeIC.py
@@ -1,7 +1,6 @@
 ###############################################################################
  # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # Copyright (c) 2016 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
@@ -19,65 +18,42 @@
  ##############################################################################
 
 import h5py
-import random
 from numpy import *
 
 # Generates a swift IC file for the Sedov blast test in a periodic cubic box
 
 # Parameters
-periodic= 1      # 1 For periodic box
-boxSize = 10.
-L = 101           # Number of particles along one axis
-rho = 1.          # Density
-P = 1.e-5         # Pressure
-E0= 1.e2          # Energy of the explosion
-pert = 0.1
-gamma = 5./3.     # Gas adiabatic index
-eta = 1.2349      # 48 ngbs with cubic spline kernel
+numPart = 1000   
+gamma = 5./3.      # Gas adiabatic index
+rho0 = 1.          # Background density
+P0 = 1.e-6         # Background pressure
+E0= 1.             # Energy of the explosion
+N_inject = 3     # Number of particles in which to inject energy
 fileName = "sedov.hdf5" 
 
-
 #---------------------------------------------------
-numPart = L**3
-mass = boxSize**3 * rho / numPart
-internalEnergy = P / ((gamma - 1.)*rho)
+coords = zeros((numPart, 3))
+h = zeros(numPart)
+vol = 1.
 
-if L%2 == 0:
-    print "Number of particles along each dimension must be odd."
-    exit()
+for i in range(numPart):
+    coords[i,0] = i * vol/numPart + vol/(2.*numPart)
+    h[i] = 1.2348 * vol / numPart
 
-#Generate particles
-coords = zeros((numPart, 3))
-v      = zeros((numPart, 3))
-m      = zeros(numPart)
-h      = zeros(numPart)
-u      = zeros(numPart)
-ids    = zeros(numPart, dtype='L')
+# Generate extra arrays
+v = zeros((numPart, 3))
+ids = linspace(1, numPart, numPart)
+m = zeros(numPart)
+u = zeros(numPart)
+r = zeros(numPart)
 
-for i in range(L):
-    for j in range(L):
-        for k in range(L):
-            index = i*L*L + j*L + k
-            x = i * boxSize / L + boxSize / (2*L)
-            y = j * boxSize / L + boxSize / (2*L)
-            z = k * boxSize / L + boxSize / (2*L)
-            coords[index,0] = x
-            coords[index,1] = y
-            coords[index,2] = z
-            v[index,0] = 0.
-            v[index,1] = 0.
-            v[index,2] = 0.
-            m[index] = mass
-            h[index] = eta * boxSize / L
-            u[index] = internalEnergy
-            ids[index] = index + 1
-            if sqrt((x - boxSize/2.)**2 + (y - boxSize/2.)**2 + (z - boxSize/2.)**2) < 2.01 * boxSize/L:
-                u[index] = u[index] + E0 / (33. * mass)
-                print "Particle " , index , " set to detonate."
-            coords[index,0] = x + random.random() * pert * boxSize/(2.*L)
-            coords[index,1] = y + random.random() * pert * boxSize/(2.*L)
-            coords[index,2] = z + random.random() * pert * boxSize/(2.*L)
+r = abs(coords[:,0] - 0.5)
+m[:] = rho0 * vol / numPart    
+u[:] = P0 / (rho0 * (gamma - 1))
 
+# Make the central particle detonate
+index = argsort(r)
+u[index[0:N_inject]] = E0 / (N_inject * m[0])
 
 #--------------------------------------------------
 
@@ -86,7 +62,7 @@ file = h5py.File(fileName, 'w')
 
 # Header
 grp = file.create_group("/Header")
-grp.attrs["BoxSize"] = boxSize
+grp.attrs["BoxSize"] = [1., 1., 1.]
 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]
@@ -94,10 +70,11 @@ 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
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
+grp.attrs["PeriodicBoundariesOn"] = 1
 
 #Units
 grp = file.create_group("/Units")
diff --git a/examples/SedovBlast_1D/plotSolution.py b/examples/SedovBlast_1D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..a62775b012edda3217558031c266ed6e9b48f423
--- /dev/null
+++ b/examples/SedovBlast_1D/plotSolution.py
@@ -0,0 +1,279 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
+ #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Computes the analytical solution of the 2D Sedov blast wave.
+# The script works for a given initial box and dumped energy and computes the solution at a later time t.
+
+# Parameters
+rho_0 = 1.          # Background Density
+P_0 = 1.e-6         # Background Pressure
+E_0 = 1.            # Energy of the explosion
+gas_gamma = 5./3.   # Gas polytropic index
+
+
+# ---------------------------------------------------------------
+# Don't touch anything after this.
+# ---------------------------------------------------------------
+
+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("sedov_%03d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][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"]
+
+pos = sim["/PartType0/Coordinates"][:,:]
+x = pos[:,0] - boxSize / 2
+vel = sim["/PartType0/Velocities"][:,:]
+r = abs(x)
+v_r = x * vel[:,0] / r
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+rho = sim["/PartType0/Density"][:]
+
+
+# Now, work our the solution....
+
+from scipy.special import gamma as Gamma
+from numpy import *
+
+def calc_a(g,nu=3):
+    """ 
+    exponents of the polynomials of the sedov solution
+    g - the polytropic gamma
+    nu - the dimension
+    """
+    a = [0]*8
+   
+    a[0] = 2.0 / (nu + 2)
+    a[2] = (1-g) / (2*(g-1) + nu)
+    a[3] = nu / (2*(g-1) + nu)
+    a[5] = 2 / (g-2)
+    a[6] = g / (2*(g-1) + nu)
+   
+    a[1] = (((nu+2)*g)/(2.0+nu*(g-1.0)) ) * ( (2.0*nu*(2.0-g))/(g*(nu+2.0)**2) - a[2])
+    a[4] = a[1]*(nu+2) / (2-g)
+    a[7] = (2 + nu*(g-1))*a[1]/(nu*(2-g))
+    return a
+
+def calc_beta(v, g, nu=3):
+    """ 
+    beta values for the sedov solution (coefficients of the polynomials of the similarity variables) 
+    v - the similarity variable
+    g - the polytropic gamma
+    nu- the dimension
+    """
+
+    beta = (nu+2) * (g+1) * array((0.25, (g/(g-1))*0.5,
+            -(2 + nu*(g-1))/2.0 / ((nu+2)*(g+1) -2*(2 + nu*(g-1))),
+     -0.5/(g-1)), dtype=float64)
+
+    beta = outer(beta, v)
+
+    beta += (g+1) * array((0.0,  -1.0/(g-1),
+                           (nu+2) / ((nu+2)*(g+1) -2.0*(2 + nu*(g-1))),
+                           1.0/(g-1)), dtype=float64).reshape((4,1))
+
+    return beta
+
+
+def sedov(t, E0, rho0, g, n=1000, nu=3):
+    """ 
+    solve the sedov problem
+    t - the time
+    E0 - the initial energy
+    rho0 - the initial density
+    n - number of points (10000)
+    nu - the dimension
+    g - the polytropic gas gamma
+    """
+    # the similarity variable
+    v_min = 2.0 / ((nu + 2) * g)
+    v_max = 4.0 / ((nu + 2) * (g + 1))
+
+    v = v_min + arange(n) * (v_max - v_min) / (n - 1.0)
+
+    a = calc_a(g, nu)
+    beta = calc_beta(v, g=g, nu=nu)
+    lbeta = log(beta)
+    
+    r = exp(-a[0] * lbeta[0] - a[2] * lbeta[1] - a[1] * lbeta[2])
+    rho = ((g + 1.0) / (g - 1.0)) * exp(a[3] * lbeta[1] + a[5] * lbeta[3] + a[4] * lbeta[2])
+    p = exp(nu * a[0] * lbeta[0] + (a[5] + 1) * lbeta[3] + (a[4] - 2 * a[1]) * lbeta[2])
+    u = beta[0] * r * 4.0 / ((g + 1) * (nu + 2))
+    p *= 8.0 / ((g + 1) * (nu + 2) * (nu + 2))
+
+    # we have to take extra care at v=v_min, since this can be a special point.
+    # It is not a singularity, however, the gradients of our variables (wrt v) are.
+    # r -> 0, u -> 0, rho -> 0, p-> constant
+
+    u[0] = 0.0; rho[0] = 0.0; r[0] = 0.0; p[0] = p[1]
+
+    # volume of an n-sphere
+    vol = (pi ** (nu / 2.0) / Gamma(nu / 2.0 + 1)) * power(r, nu)
+
+    # note we choose to evaluate the integral in this way because the
+    # volumes of the first few elements (i.e near v=vmin) are shrinking 
+    # very slowly, so we dramatically improve the error convergence by 
+    # finding the volumes exactly. This is most important for the
+    # pressure integral, as this is on the order of the volume.
+
+    # (dimensionless) energy of the model solution
+    de = rho * u * u * 0.5 + p / (g - 1)
+    # integrate (trapezium rule)
+    q = inner(de[1:] + de[:-1], diff(vol)) * 0.5
+
+    # the factor to convert to this particular problem
+    fac = (q * (t ** nu) * rho0 / E0) ** (-1.0 / (nu + 2))
+
+    # shock speed
+    shock_speed = fac * (2.0 / (nu + 2))
+    rho_s = ((g + 1) / (g - 1)) * rho0                                                                            
+    r_s = shock_speed * t * (nu + 2) / 2.0
+    p_s = (2.0 * rho0 * shock_speed * shock_speed) / (g + 1)
+    u_s = (2.0 * shock_speed) / (g + 1)
+    
+    r *= fac * t
+    u *= fac
+    p *= fac * fac * rho0
+    rho *= rho0
+    return r, p, rho, u, r_s, p_s, rho_s, u_s, shock_speed
+
+
+# The main properties of the solution
+r_s, P_s, rho_s, v_s, r_shock, _, _, _, _ = sedov(time, E_0, rho_0, gas_gamma, 1000, 1)
+
+# Append points for after the shock
+r_s = np.insert(r_s, np.size(r_s), [r_shock, r_shock*1.5])
+rho_s = np.insert(rho_s, np.size(rho_s), [rho_0, rho_0])
+P_s = np.insert(P_s, np.size(P_s), [P_0, P_0])
+v_s = np.insert(v_s, np.size(v_s), [0, 0])
+
+# 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(r, v_r, '.', color='r', ms=2.)
+plot(r_s, v_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Radial~velocity}}~v_r$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-0.2, 3.8)
+
+# Density profile --------------------------------
+subplot(232)
+plot(r, rho, '.', color='r', ms=2.)
+plot(r_s, rho_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Density}}~\\rho$", labelpad=2)
+xlim(0, 1.3 * r_shock)
+ylim(-0.2, 5.2)
+
+# Pressure profile --------------------------------
+subplot(233)
+plot(r, P, '.', color='r', ms=2.)
+plot(r_s, P_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-1, 12.5)
+
+# Internal energy profile -------------------------
+subplot(234)
+plot(r, u, '.', color='r', ms=2.)
+plot(r_s, u_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-2, 22)
+
+# Entropy profile ---------------------------------
+subplot(235)
+plot(r, S, '.', color='r', ms=2.)
+plot(r_s, s_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-5, 50)
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+text(-0.49, 0.9, "Sedov blast with  $\\gamma=%.3f$ in 1D at $t=%.2f$"%(gas_gamma,time), fontsize=10)
+text(-0.49, 0.8, "Background $\\rho_0=%.2f$"%(rho_0), fontsize=10)
+text(-0.49, 0.7, "Energy injected $E_0=%.2f$"%(E_0), fontsize=10)
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+
+savefig("Sedov.png", dpi=200)
+
+
+
+
diff --git a/examples/SedovBlast_1D/run.sh b/examples/SedovBlast_1D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4b9a84f069673bd6def3b96faec71b9d4fdd0dda
--- /dev/null
+++ b/examples/SedovBlast_1D/run.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e sedov.hdf5 ]
+then
+    echo "Generating initial conditions for the Sedov blast example..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift -s -t 1 sedov.yml 2>&1 | tee output.log
+
+# Plot the solution
+python plotSolution.py 5
diff --git a/examples/SedovBlast/sedov.yml b/examples/SedovBlast_1D/sedov.yml
similarity index 83%
rename from examples/SedovBlast/sedov.yml
rename to examples/SedovBlast_1D/sedov.yml
index eb95fd85d5599145b2ff037256dcde72fc306209..6f519835d26ff5aa851ffb8999e650815c522cd3 100644
--- a/examples/SedovBlast/sedov.yml
+++ b/examples/SedovBlast_1D/sedov.yml
@@ -9,15 +9,15 @@ InternalUnitSystem:
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   1.    # The end time of the simulation (in internal units).
+  time_end:   5e-2  # The end time of the simulation (in internal units).
   dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
 
 # Parameters governing the snapshots
 Snapshots:
   basename:            sedov # Common part of the name of output files
   time_first:          0.    # Time of the first output (in internal units)
-  delta_time:          0.1   # Time difference between consecutive outputs (in internal units)
+  delta_time:          1e-2  # Time difference between consecutive outputs (in internal units)
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -27,7 +27,7 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  1.       # Maximal smoothing length allowed (in internal units).
+  max_smoothing_length:  0.1       # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/SedovBlast_2D/getGlass.sh b/examples/SedovBlast_2D/getGlass.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ae3c977064f5e7a408aa249c5fd9089b3c52ecb1
--- /dev/null
+++ b/examples/SedovBlast_2D/getGlass.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassPlane_128.hdf5
diff --git a/examples/SedovBlast_2D/makeIC.py b/examples/SedovBlast_2D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e83c7b19b9ac9bd69e20950a64e8a49dd8d0df9
--- /dev/null
+++ b/examples/SedovBlast_2D/makeIC.py
@@ -0,0 +1,96 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 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/>.
+ # 
+ ##############################################################################
+
+import h5py
+from numpy import *
+
+# Generates a swift IC file for the Sedov blast test in a periodic cubic box
+
+# Parameters
+gamma = 5./3.      # Gas adiabatic index
+rho0 = 1.          # Background density
+P0 = 1.e-6         # Background pressure
+E0= 1.             # Energy of the explosion
+N_inject = 21      # Number of particles in which to inject energy
+fileName = "sedov.hdf5" 
+
+#---------------------------------------------------
+glass = h5py.File("glassPlane_128.hdf5", "r")
+
+# Read particle positions and h from the glass
+pos = glass["/PartType0/Coordinates"][:,:]
+h = glass["/PartType0/SmoothingLength"][:] * 0.3
+
+numPart = size(h)
+vol = 1.
+
+# Generate extra arrays
+v = zeros((numPart, 3))
+ids = linspace(1, numPart, numPart)
+m = zeros(numPart)
+u = zeros(numPart)
+r = zeros(numPart)
+
+r = sqrt((pos[:,0] - 0.5)**2 + (pos[:,1] - 0.5)**2)
+m[:] = rho0 * vol / numPart    
+u[:] = P0 / (rho0 * (gamma - 1))
+
+# Make the central particle detonate
+index = argsort(r)
+u[index[0:N_inject]] = E0 / (N_inject * m[0])
+
+#--------------------------------------------------
+
+#File
+file = h5py.File(fileName, 'w')
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = [1., 1., 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
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 1.
+grp.attrs["Unit mass in cgs (U_M)"] = 1.
+grp.attrs["Unit time in cgs (U_t)"] = 1.
+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/SedovBlast_2D/plotSolution.py b/examples/SedovBlast_2D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..69e4e1232dd5c14b06e8a705f4add391f1f597f0
--- /dev/null
+++ b/examples/SedovBlast_2D/plotSolution.py
@@ -0,0 +1,280 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
+ #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Computes the analytical solution of the 2D Sedov blast wave.
+# The script works for a given initial box and dumped energy and computes the solution at a later time t.
+
+# Parameters
+rho_0 = 1.          # Background Density
+P_0 = 1.e-6         # Background Pressure
+E_0 = 1.            # Energy of the explosion
+gas_gamma = 5./3.   # Gas polytropic index
+
+
+# ---------------------------------------------------------------
+# Don't touch anything after this.
+# ---------------------------------------------------------------
+
+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("sedov_%03d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][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"]
+
+pos = sim["/PartType0/Coordinates"][:,:]
+x = pos[:,0] - boxSize / 2
+y = pos[:,1] - boxSize / 2
+vel = sim["/PartType0/Velocities"][:,:]
+r = sqrt(x**2 + y**2)
+v_r = (x * vel[:,0] + y * vel[:,1]) / r
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+rho = sim["/PartType0/Density"][:]
+
+
+# Now, work our the solution....
+
+from scipy.special import gamma as Gamma
+from numpy import *
+
+def calc_a(g,nu=3):
+    """ 
+    exponents of the polynomials of the sedov solution
+    g - the polytropic gamma
+    nu - the dimension
+    """
+    a = [0]*8
+   
+    a[0] = 2.0 / (nu + 2)
+    a[2] = (1-g) / (2*(g-1) + nu)
+    a[3] = nu / (2*(g-1) + nu)
+    a[5] = 2 / (g-2)
+    a[6] = g / (2*(g-1) + nu)
+   
+    a[1] = (((nu+2)*g)/(2.0+nu*(g-1.0)) ) * ( (2.0*nu*(2.0-g))/(g*(nu+2.0)**2) - a[2])
+    a[4] = a[1]*(nu+2) / (2-g)
+    a[7] = (2 + nu*(g-1))*a[1]/(nu*(2-g))
+    return a
+
+def calc_beta(v, g, nu=3):
+    """ 
+    beta values for the sedov solution (coefficients of the polynomials of the similarity variables) 
+    v - the similarity variable
+    g - the polytropic gamma
+    nu- the dimension
+    """
+
+    beta = (nu+2) * (g+1) * array((0.25, (g/(g-1))*0.5,
+            -(2 + nu*(g-1))/2.0 / ((nu+2)*(g+1) -2*(2 + nu*(g-1))),
+     -0.5/(g-1)), dtype=float64)
+
+    beta = outer(beta, v)
+
+    beta += (g+1) * array((0.0,  -1.0/(g-1),
+                           (nu+2) / ((nu+2)*(g+1) -2.0*(2 + nu*(g-1))),
+                           1.0/(g-1)), dtype=float64).reshape((4,1))
+
+    return beta
+
+
+def sedov(t, E0, rho0, g, n=1000, nu=3):
+    """ 
+    solve the sedov problem
+    t - the time
+    E0 - the initial energy
+    rho0 - the initial density
+    n - number of points (10000)
+    nu - the dimension
+    g - the polytropic gas gamma
+    """
+    # the similarity variable
+    v_min = 2.0 / ((nu + 2) * g)
+    v_max = 4.0 / ((nu + 2) * (g + 1))
+
+    v = v_min + arange(n) * (v_max - v_min) / (n - 1.0)
+
+    a = calc_a(g, nu)
+    beta = calc_beta(v, g=g, nu=nu)
+    lbeta = log(beta)
+    
+    r = exp(-a[0] * lbeta[0] - a[2] * lbeta[1] - a[1] * lbeta[2])
+    rho = ((g + 1.0) / (g - 1.0)) * exp(a[3] * lbeta[1] + a[5] * lbeta[3] + a[4] * lbeta[2])
+    p = exp(nu * a[0] * lbeta[0] + (a[5] + 1) * lbeta[3] + (a[4] - 2 * a[1]) * lbeta[2])
+    u = beta[0] * r * 4.0 / ((g + 1) * (nu + 2))
+    p *= 8.0 / ((g + 1) * (nu + 2) * (nu + 2))
+
+    # we have to take extra care at v=v_min, since this can be a special point.
+    # It is not a singularity, however, the gradients of our variables (wrt v) are.
+    # r -> 0, u -> 0, rho -> 0, p-> constant
+
+    u[0] = 0.0; rho[0] = 0.0; r[0] = 0.0; p[0] = p[1]
+
+    # volume of an n-sphere
+    vol = (pi ** (nu / 2.0) / Gamma(nu / 2.0 + 1)) * power(r, nu)
+
+    # note we choose to evaluate the integral in this way because the
+    # volumes of the first few elements (i.e near v=vmin) are shrinking 
+    # very slowly, so we dramatically improve the error convergence by 
+    # finding the volumes exactly. This is most important for the
+    # pressure integral, as this is on the order of the volume.
+
+    # (dimensionless) energy of the model solution
+    de = rho * u * u * 0.5 + p / (g - 1)
+    # integrate (trapezium rule)
+    q = inner(de[1:] + de[:-1], diff(vol)) * 0.5
+
+    # the factor to convert to this particular problem
+    fac = (q * (t ** nu) * rho0 / E0) ** (-1.0 / (nu + 2))
+
+    # shock speed
+    shock_speed = fac * (2.0 / (nu + 2))
+    rho_s = ((g + 1) / (g - 1)) * rho0                                                                            
+    r_s = shock_speed * t * (nu + 2) / 2.0
+    p_s = (2.0 * rho0 * shock_speed * shock_speed) / (g + 1)
+    u_s = (2.0 * shock_speed) / (g + 1)
+    
+    r *= fac * t
+    u *= fac
+    p *= fac * fac * rho0
+    rho *= rho0
+    return r, p, rho, u, r_s, p_s, rho_s, u_s, shock_speed
+
+
+# The main properties of the solution
+r_s, P_s, rho_s, v_s, r_shock, _, _, _, _ = sedov(time, E_0, rho_0, gas_gamma, 1000, 2)
+
+# Append points for after the shock
+r_s = np.insert(r_s, np.size(r_s), [r_shock, r_shock*1.5])
+rho_s = np.insert(rho_s, np.size(rho_s), [rho_0, rho_0])
+P_s = np.insert(P_s, np.size(P_s), [P_0, P_0])
+v_s = np.insert(v_s, np.size(v_s), [0, 0])
+
+# 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(r, v_r, '.', color='r', ms=1.)
+plot(r_s, v_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Radial~velocity}}~v_r$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-0.2, 3.8)
+
+# Density profile --------------------------------
+subplot(232)
+plot(r, rho, '.', color='r', ms=1.)
+plot(r_s, rho_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Density}}~\\rho$", labelpad=2)
+xlim(0, 1.3 * r_shock)
+ylim(-0.2, 5.2)
+
+# Pressure profile --------------------------------
+subplot(233)
+plot(r, P, '.', color='r', ms=1.)
+plot(r_s, P_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-1, 12.5)
+
+# Internal energy profile -------------------------
+subplot(234)
+plot(r, u, '.', color='r', ms=1.)
+plot(r_s, u_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-2, 22)
+
+# Entropy profile ---------------------------------
+subplot(235)
+plot(r, S, '.', color='r', ms=1.)
+plot(r_s, s_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-5, 50)
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+text(-0.49, 0.9, "Sedov blast with  $\\gamma=%.3f$ in 2D at $t=%.2f$"%(gas_gamma,time), fontsize=10)
+text(-0.49, 0.8, "Background $\\rho_0=%.2f$"%(rho_0), fontsize=10)
+text(-0.49, 0.7, "Energy injected $E_0=%.2f$"%(E_0), fontsize=10)
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+
+savefig("Sedov.png", dpi=200)
+
+
+
+
diff --git a/examples/SedovBlast_2D/run.sh b/examples/SedovBlast_2D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a32c8f0d6f3116d5486fe1bd086bf8df49d06020
--- /dev/null
+++ b/examples/SedovBlast_2D/run.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e glassPlane_128.hdf5 ]
+then
+    echo "Fetching initial glass file for the Sedov blast example..."
+    ./getGlass.sh
+fi
+if [ ! -e sedov.hdf5 ]
+then
+    echo "Generating initial conditions for the Sedov blast example..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift -s -t 1 sedov.yml 2>&1 | tee output.log
+
+# Plot the solution
+python plotSolution.py 5
diff --git a/examples/SedovBlast_2D/sedov.yml b/examples/SedovBlast_2D/sedov.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6f519835d26ff5aa851ffb8999e650815c522cd3
--- /dev/null
+++ b/examples/SedovBlast_2D/sedov.yml
@@ -0,0 +1,36 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   5e-2  # The end time of the simulation (in internal units).
+  dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            sedov # Common part of the name of output files
+  time_first:          0.    # Time of the first output (in internal units)
+  delta_time:          1e-2  # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-3 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.1       # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./sedov.hdf5          # The file to read
+
diff --git a/examples/SedovBlast_3D/getGlass.sh b/examples/SedovBlast_3D/getGlass.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d5c5f590ac37c9c9431d626a2ea61b0c12c1513c
--- /dev/null
+++ b/examples/SedovBlast_3D/getGlass.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5
diff --git a/examples/SedovBlast_3D/makeIC.py b/examples/SedovBlast_3D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..e1b743c6cdcd8dcc2f8da14d1d5589fb9ed111f0
--- /dev/null
+++ b/examples/SedovBlast_3D/makeIC.py
@@ -0,0 +1,96 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 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/>.
+ # 
+ ##############################################################################
+
+import h5py
+from numpy import *
+
+# Generates a swift IC file for the Sedov blast test in a periodic cubic box
+
+# Parameters
+gamma = 5./3.      # Gas adiabatic index
+rho0 = 1.          # Background density
+P0 = 1.e-6         # Background pressure
+E0= 1.             # Energy of the explosion
+N_inject = 15      # Number of particles in which to inject energy
+fileName = "sedov.hdf5" 
+
+#---------------------------------------------------
+glass = h5py.File("glassCube_64.hdf5", "r")
+
+# Read particle positions and h from the glass
+pos = glass["/PartType0/Coordinates"][:,:]
+h = glass["/PartType0/SmoothingLength"][:] * 0.3
+
+numPart = size(h)
+vol = 1.
+
+# Generate extra arrays
+v = zeros((numPart, 3))
+ids = linspace(1, numPart, numPart)
+m = zeros(numPart)
+u = zeros(numPart)
+r = zeros(numPart)
+
+r = sqrt((pos[:,0] - 0.5)**2 + (pos[:,1] - 0.5)**2 + (pos[:,2] - 0.5)**2)
+m[:] = rho0 * vol / numPart    
+u[:] = P0 / (rho0 * (gamma - 1))
+
+# Make the central particles detonate
+index = argsort(r)
+u[index[0:N_inject]] = E0 / (N_inject * m[0])
+
+#--------------------------------------------------
+
+#File
+file = h5py.File(fileName, 'w')
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = [1., 1., 1.]
+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
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 1.
+grp.attrs["Unit mass in cgs (U_M)"] = 1.
+grp.attrs["Unit time in cgs (U_t)"] = 1.
+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/SedovBlast_3D/plotSolution.py b/examples/SedovBlast_3D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..1eea372b08e084a37c001f9c53a61667277c765b
--- /dev/null
+++ b/examples/SedovBlast_3D/plotSolution.py
@@ -0,0 +1,281 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
+ #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Computes the analytical solution of the 2D Sedov blast wave.
+# The script works for a given initial box and dumped energy and computes the solution at a later time t.
+
+# Parameters
+rho_0 = 1.          # Background Density
+P_0 = 1.e-6         # Background Pressure
+E_0 = 1.            # Energy of the explosion
+gas_gamma = 5./3.   # Gas polytropic index
+
+
+# ---------------------------------------------------------------
+# Don't touch anything after this.
+# ---------------------------------------------------------------
+
+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("sedov_%03d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][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"]
+
+pos = sim["/PartType0/Coordinates"][:,:]
+x = pos[:,0] - boxSize / 2
+y = pos[:,1] - boxSize / 2
+z = pos[:,2] - boxSize / 2
+vel = sim["/PartType0/Velocities"][:,:]
+r = sqrt(x**2 + y**2 + z**2)
+v_r = (x * vel[:,0] + y * vel[:,1] + z * vel[:,2]) / r
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+rho = sim["/PartType0/Density"][:]
+
+
+# Now, work our the solution....
+
+from scipy.special import gamma as Gamma
+from numpy import *
+
+def calc_a(g,nu=3):
+    """ 
+    exponents of the polynomials of the sedov solution
+    g - the polytropic gamma
+    nu - the dimension
+    """
+    a = [0]*8
+   
+    a[0] = 2.0 / (nu + 2)
+    a[2] = (1-g) / (2*(g-1) + nu)
+    a[3] = nu / (2*(g-1) + nu)
+    a[5] = 2 / (g-2)
+    a[6] = g / (2*(g-1) + nu)
+   
+    a[1] = (((nu+2)*g)/(2.0+nu*(g-1.0)) ) * ( (2.0*nu*(2.0-g))/(g*(nu+2.0)**2) - a[2])
+    a[4] = a[1]*(nu+2) / (2-g)
+    a[7] = (2 + nu*(g-1))*a[1]/(nu*(2-g))
+    return a
+
+def calc_beta(v, g, nu=3):
+    """ 
+    beta values for the sedov solution (coefficients of the polynomials of the similarity variables) 
+    v - the similarity variable
+    g - the polytropic gamma
+    nu- the dimension
+    """
+
+    beta = (nu+2) * (g+1) * array((0.25, (g/(g-1))*0.5,
+            -(2 + nu*(g-1))/2.0 / ((nu+2)*(g+1) -2*(2 + nu*(g-1))),
+     -0.5/(g-1)), dtype=float64)
+
+    beta = outer(beta, v)
+
+    beta += (g+1) * array((0.0,  -1.0/(g-1),
+                           (nu+2) / ((nu+2)*(g+1) -2.0*(2 + nu*(g-1))),
+                           1.0/(g-1)), dtype=float64).reshape((4,1))
+
+    return beta
+
+
+def sedov(t, E0, rho0, g, n=1000, nu=3):
+    """ 
+    solve the sedov problem
+    t - the time
+    E0 - the initial energy
+    rho0 - the initial density
+    n - number of points (10000)
+    nu - the dimension
+    g - the polytropic gas gamma
+    """
+    # the similarity variable
+    v_min = 2.0 / ((nu + 2) * g)
+    v_max = 4.0 / ((nu + 2) * (g + 1))
+
+    v = v_min + arange(n) * (v_max - v_min) / (n - 1.0)
+
+    a = calc_a(g, nu)
+    beta = calc_beta(v, g=g, nu=nu)
+    lbeta = log(beta)
+    
+    r = exp(-a[0] * lbeta[0] - a[2] * lbeta[1] - a[1] * lbeta[2])
+    rho = ((g + 1.0) / (g - 1.0)) * exp(a[3] * lbeta[1] + a[5] * lbeta[3] + a[4] * lbeta[2])
+    p = exp(nu * a[0] * lbeta[0] + (a[5] + 1) * lbeta[3] + (a[4] - 2 * a[1]) * lbeta[2])
+    u = beta[0] * r * 4.0 / ((g + 1) * (nu + 2))
+    p *= 8.0 / ((g + 1) * (nu + 2) * (nu + 2))
+
+    # we have to take extra care at v=v_min, since this can be a special point.
+    # It is not a singularity, however, the gradients of our variables (wrt v) are.
+    # r -> 0, u -> 0, rho -> 0, p-> constant
+
+    u[0] = 0.0; rho[0] = 0.0; r[0] = 0.0; p[0] = p[1]
+
+    # volume of an n-sphere
+    vol = (pi ** (nu / 2.0) / Gamma(nu / 2.0 + 1)) * power(r, nu)
+
+    # note we choose to evaluate the integral in this way because the
+    # volumes of the first few elements (i.e near v=vmin) are shrinking 
+    # very slowly, so we dramatically improve the error convergence by 
+    # finding the volumes exactly. This is most important for the
+    # pressure integral, as this is on the order of the volume.
+
+    # (dimensionless) energy of the model solution
+    de = rho * u * u * 0.5 + p / (g - 1)
+    # integrate (trapezium rule)
+    q = inner(de[1:] + de[:-1], diff(vol)) * 0.5
+
+    # the factor to convert to this particular problem
+    fac = (q * (t ** nu) * rho0 / E0) ** (-1.0 / (nu + 2))
+
+    # shock speed
+    shock_speed = fac * (2.0 / (nu + 2))
+    rho_s = ((g + 1) / (g - 1)) * rho0                                                                            
+    r_s = shock_speed * t * (nu + 2) / 2.0
+    p_s = (2.0 * rho0 * shock_speed * shock_speed) / (g + 1)
+    u_s = (2.0 * shock_speed) / (g + 1)
+    
+    r *= fac * t
+    u *= fac
+    p *= fac * fac * rho0
+    rho *= rho0
+    return r, p, rho, u, r_s, p_s, rho_s, u_s, shock_speed
+
+
+# The main properties of the solution
+r_s, P_s, rho_s, v_s, r_shock, _, _, _, _ = sedov(time, E_0, rho_0, gas_gamma, 1000, 3)
+
+# Append points for after the shock
+r_s = np.insert(r_s, np.size(r_s), [r_shock, r_shock*1.5])
+rho_s = np.insert(rho_s, np.size(rho_s), [rho_0, rho_0])
+P_s = np.insert(P_s, np.size(P_s), [P_0, P_0])
+v_s = np.insert(v_s, np.size(v_s), [0, 0])
+
+# 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(r, v_r, '.', color='r', ms=1.)
+plot(r_s, v_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Radial~velocity}}~v_r$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-0.2, 3.8)
+
+# Density profile --------------------------------
+subplot(232)
+plot(r, rho, '.', color='r', ms=1.)
+plot(r_s, rho_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Density}}~\\rho$", labelpad=2)
+xlim(0, 1.3 * r_shock)
+ylim(-0.2, 5.2)
+
+# Pressure profile --------------------------------
+subplot(233)
+plot(r, P, '.', color='r', ms=1.)
+plot(r_s, P_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-1, 12.5)
+
+# Internal energy profile -------------------------
+subplot(234)
+plot(r, u, '.', color='r', ms=1.)
+plot(r_s, u_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-2, 22)
+
+# Entropy profile ---------------------------------
+subplot(235)
+plot(r, S, '.', color='r', ms=1.)
+plot(r_s, s_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Radius}}~r$", labelpad=0)
+ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+xlim(0, 1.3 * r_shock)
+ylim(-5, 50)
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+text(-0.49, 0.9, "Sedov blast with  $\\gamma=%.3f$ in 3D at $t=%.2f$"%(gas_gamma,time), fontsize=10)
+text(-0.49, 0.8, "Background $\\rho_0=%.2f$"%(rho_0), fontsize=10)
+text(-0.49, 0.7, "Energy injected $E_0=%.2f$"%(E_0), fontsize=10)
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+
+savefig("Sedov.png", dpi=200)
+
+
+
+
diff --git a/examples/SedovBlast_3D/run.sh b/examples/SedovBlast_3D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..00d5e5b91c31e64f824a3d2a28c8e1a126684a74
--- /dev/null
+++ b/examples/SedovBlast_3D/run.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e glassCube_64.hdf5 ]
+then
+    echo "Fetching initial glass file for the Sedov blast example..."
+    ./getGlass.sh
+fi
+if [ ! -e sedov.hdf5 ]
+then
+    echo "Generating initial conditions for the Sedov blast example..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift -s -t 4 sedov.yml 2>&1 | tee output.log
+
+# Plot the solution
+python plotSolution.py 5
diff --git a/examples/SedovBlast_3D/sedov.yml b/examples/SedovBlast_3D/sedov.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6f519835d26ff5aa851ffb8999e650815c522cd3
--- /dev/null
+++ b/examples/SedovBlast_3D/sedov.yml
@@ -0,0 +1,36 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   5e-2  # The end time of the simulation (in internal units).
+  dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            sedov # Common part of the name of output files
+  time_first:          0.    # Time of the first output (in internal units)
+  delta_time:          1e-2  # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-3 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.1       # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./sedov.hdf5          # The file to read
+
diff --git a/examples/SodShock/glass_001.hdf5 b/examples/SodShock/glass_001.hdf5
deleted file mode 100644
index a371826c2ef4c1d53ad5f50a6bc7eb590017220e..0000000000000000000000000000000000000000
Binary files a/examples/SodShock/glass_001.hdf5 and /dev/null differ
diff --git a/examples/SodShock/glass_002.hdf5 b/examples/SodShock/glass_002.hdf5
deleted file mode 100644
index dffb8d343157a9ae8318e9572fc752eecd8955fb..0000000000000000000000000000000000000000
Binary files a/examples/SodShock/glass_002.hdf5 and /dev/null differ
diff --git a/examples/SodShock/makeIC.py b/examples/SodShock/makeIC.py
deleted file mode 100644
index 8ae19050c11c0712579b44646c8870d7574d113b..0000000000000000000000000000000000000000
--- a/examples/SodShock/makeIC.py
+++ /dev/null
@@ -1,135 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import h5py
-import random
-from numpy import *
-
-# Generates a swift IC file for the Sod Shock in a periodic box
-
-# Parameters
-periodic= 1      # 1 For periodic box
-factor = 8
-boxSize = [ 1.0 , 1.0/factor , 1.0/factor ]
-L = 100           # Number of particles along one axis
-P1 = 1.           # Pressure left state
-P2 = 0.1795       # Pressure right state
-gamma = 5./3.     # Gas adiabatic index
-fileName = "sodShock.hdf5" 
-vol = boxSize[0] * boxSize[1] * boxSize[2]
-
-
-#---------------------------------------------------
-
-#Read in high density glass
-# glass1 = h5py.File("../Glass/glass_200000.hdf5")
-glass1 = h5py.File("glass_001.hdf5")
-pos1 = glass1["/PartType0/Coordinates"][:,:]
-pos1 = pos1 / factor # Particles are in [0:0.25, 0:0.25, 0:0.25]
-glass_h1 = glass1["/PartType0/SmoothingLength"][:] / factor
-
-#Read in high density glass
-# glass2 = h5py.File("../Glass/glass_50000.hdf5")
-glass2 = h5py.File("glass_002.hdf5")
-pos2 = glass2["/PartType0/Coordinates"][:,:]
-pos2 = pos2 / factor # Particles are in [0:0.25, 0:0.25, 0:0.25]
-glass_h2 = glass2["/PartType0/SmoothingLength"][:] / factor
-
-#Generate high density region
-rho1 = 1.
-coord1 = append(pos1, pos1 + [0.125, 0, 0], 0)
-coord1 = append(coord1, coord1 + [0.25, 0, 0], 0)
-# coord1 = append(pos1, pos1 + [0, 0.5, 0], 0)
-# coord1 = append(coord1, pos1 + [0, 0, 0.5], 0)
-# coord1 = append(coord1, pos1 + [0, 0.5, 0.5], 0)
-N1 = size(coord1)/3
-v1 = zeros((N1, 3))
-u1 = ones(N1) * P1 / ((gamma - 1.) * rho1)
-m1 = ones(N1) * vol * 0.5 * rho1 / N1
-h1 = append(glass_h1, glass_h1, 0)
-h1 = append(h1, h1, 0)
-
-#Generate low density region
-rho2 = 0.25
-coord2 = append(pos2, pos2 + [0.125, 0, 0], 0)
-coord2 = append(coord2, coord2 + [0.25, 0, 0], 0)
-# coord2 = append(pos2, pos2 + [0, 0.5, 0], 0)
-# coord2 = append(coord2, pos2 + [0, 0, 0.5], 0)
-# coord2 = append(coord2, pos2 + [0, 0.5, 0.5], 0)
-N2 = size(coord2)/3
-v2 = zeros((N2, 3))
-u2 = ones(N2) * P2 / ((gamma - 1.) * rho2)
-m2 = ones(N2) * vol * 0.5 * rho2 / N2
-h2 = append(glass_h2, glass_h2, 0)
-h2 = append(h2, h2, 0)
-
-#Merge arrays
-numPart = N1 + N2
-coords = append(coord1, coord2+[0.5, 0., 0.], 0)
-v = append(v1, v2,0)
-h = append(h1, h2,0)
-u = append(u1, u2,0)
-m = append(m1, m2,0)
-ids = zeros(numPart, dtype='L')
-for i in range(1, numPart+1):
-    ids[i-1] = i
-
-#Final operation since we come from Gadget-2 cubic spline ICs
-h /= 1.825752
-
-#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
-
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
-#Units
-grp = file.create_group("/Units")
-grp.attrs["Unit length in cgs (U_L)"] = 1.
-grp.attrs["Unit mass in cgs (U_M)"] = 1.
-grp.attrs["Unit time in cgs (U_t)"] = 1.
-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/SodShock/rhox.py b/examples/SodShock/rhox.py
deleted file mode 100644
index 70493be3728cdeb27409a79f616fa3ec5bb9cdfd..0000000000000000000000000000000000000000
--- a/examples/SodShock/rhox.py
+++ /dev/null
@@ -1,115 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import h5py
-import random
-import sys
-import math
-from numpy import *
-
-# Reads the HDF5 output of SWIFT and generates a radial density profile
-# of the different physical values.
-
-# Input values?
-if len(sys.argv) < 3 :
-    print "Usage: " , sys.argv[0] , " <filename> <nr. bins>"
-    exit()
-    
-# Get the input arguments
-fileName = sys.argv[1];
-nr_bins = int( sys.argv[2] );
-
-
-# Open the file
-fileName = sys.argv[1];
-file = h5py.File( fileName , 'r' )
-
-# Get the space dimensions.
-grp = file[ "/Header" ]
-boxsize = grp.attrs[ 'BoxSize' ]
-boxsize = boxsize[0]
-
-# Get the particle data
-grp = file.get( '/PartType0' )
-ds = grp.get( 'Coordinates' )
-coords = ds[...]
-ds = grp.get( 'Velocities' )
-v = ds[...]
-# ds = grp.get( 'Mass' )
-# m = ds[...]
-ds = grp.get( 'SmoothingLength' )
-h = ds[...]
-ds = grp.get( 'InternalEnergy' )
-u = ds[...]
-ds = grp.get( 'ParticleIDs' )
-ids = ds[...]
-ds = grp.get( 'Density' )
-rho = ds[...]
-
-# Get the maximum radius
-r_max = boxsize
-
-# Init the bins
-nr_parts = coords.shape[0]
-bins_v = zeros( nr_bins )
-bins_m = zeros( nr_bins )
-bins_h = zeros( nr_bins )
-bins_u = zeros( nr_bins )
-bins_rho = zeros( nr_bins )
-bins_count = zeros( nr_bins )
-bins_P = zeros( nr_bins )
-
-# Loop over the particles and fill the bins.
-for i in range( nr_parts ):
-
-    # Get the box index.
-    r = coords[i,0]
-    ind = floor( r / r_max * nr_bins )
-    
-    # Update the bins
-    bins_count[ind] += 1
-    bins_v[ind] += v[i,0] # sqrt( v[i,0]*v[i,0] + v[i,1]*v[i,1] + v[i,2]*v[i,2] )
-    # bins_m[ind] += m[i]
-    bins_h[ind] += h[i]
-    bins_u[ind] += u[i]
-    bins_rho[ind] += rho[i]
-    bins_P[ind] += (2.0/3)*u[i]*rho[i]
-    
-# Loop over the bins and dump them
-print "# bucket left right count v m h u rho"
-for i in range( nr_bins ):
-
-    # Normalize by the bin volume.
-    r_left = r_max * i / nr_bins
-    r_right = r_max * (i+1) / nr_bins
-    vol = 4/3*math.pi*(r_right*r_right*r_right - r_left*r_left*r_left)
-    ivol = 1.0 / vol
-
-    print "%i %.3e %.3e %.3e %.3e %.3e %.3e %.3e %.3e %.3e" % \
-        ( i , r_left , r_right , \
-          bins_count[i] * ivol , \
-          bins_v[i] / bins_count[i] , \
-          bins_m[i] * ivol , \
-          bins_h[i] / bins_count[i] , \
-          bins_u[i] / bins_count[i] , \
-          bins_rho[i] / bins_count[i] ,
-          bins_P[i] / bins_count[i] )
-    
-    
diff --git a/examples/SodShock/run.sh b/examples/SodShock/run.sh
deleted file mode 100755
index b8141e51543f348d6ec6be505d136aed7d803b2e..0000000000000000000000000000000000000000
--- a/examples/SodShock/run.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-# Generate the initial conditions if they are not present.
-if [ ! -e sodShock.hdf5 ]
-then
-    echo "Generating initial conditions for the SodShock example..."
-    python makeIC.py
-fi
-
-../swift -s -t 16 sodShock.yml
diff --git a/examples/SodShock/solution.py b/examples/SodShock/solution.py
deleted file mode 100644
index 39f25c625232eee9bae0300339955f775f3b46ed..0000000000000000000000000000000000000000
--- a/examples/SodShock/solution.py
+++ /dev/null
@@ -1,186 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import random
-from numpy import *
-
-# 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.
-# The code writes five files rho.dat, P.dat, v.dat, u.dat and s.dat with the density, pressure, internal energy and
-# entropic function on N points between x_min and x_max.
-# This follows the solution given in (Toro, 2009)
-
-
-# Parameters
-rho_L = 1
-P_L = 1
-v_L = 0.
-
-rho_R = 0.25
-P_R = 0.1795
-v_R = 0.
-
-gamma = 5./3. # Polytropic index
-
-t = 0.12  # Time of the evolution
-
-N = 1000  # Number of points
-x_min = -0.25
-x_max = 0.25
-
-
-# ---------------------------------------------------------------
-# Don't touch anything after this.
-# ---------------------------------------------------------------
-
-c_L = sqrt(gamma * P_L / rho_L)   # Speed of the rarefaction wave
-c_R = sqrt(gamma * P_R / rho_R)   # Speed of the shock front
-
-# Helpful variable
-Gama = (gamma - 1.) / (gamma + 1.)
-beta = (gamma - 1.) / (2. * gamma)
-
-# Characteristic function and its derivative, following Toro (2009)
-def compute_f(P_3, P, c):
-    u = P_3 / P
-    if u > 1:
-        term1 = gamma*((gamma+1.)*u + gamma-1.)
-        term2 = sqrt(2./term1)
-        fp = (u - 1.)*c*term2
-        dfdp = c*term2/P + (u - 1.)*c/term2*(-1./term1**2)*gamma*(gamma+1.)/P
-    else:
-        fp = (u**beta - 1.)*(2.*c/(gamma-1.))
-        dfdp = 2.*c/(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*(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.)/(gamma*(v_3-v_R))
-else:
-    v_right = c_R + 0.5*(gamma+1.)*v_3 - 0.5*(gamma-1.)*v_R
-
-if shock_L:
-    v_left = v_L + c_L**2*(P_3/p_L - 1.)/(gamma*(v_3-v_L))
-else:
-    v_left = c_L - 0.5*(gamma+1.)*v_3 + 0.5*(gamma-1.)*v_L
-
-# Compute position of the transitions
-x_23 = -fabs(v_left) * t
-if shock_L :
-    x_12 = -fabs(v_left) * t
-else:
-    x_12 = -(c_L - v_L) * t
-
-x_34 = v_3 * t
-
-x_45 = fabs(v_right) * t
-if shock_R:
-    x_56 = fabs(v_right) * t
-else:
-    x_56 = (c_R + v_R) * t 
-
-
-# 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 * t) + Gama * v_L/c_L + (1.-Gama))**(2./(gamma-1.))
-            P_s[i] = P_L*(rho_s[i] / rho_L)**gamma
-            v_s[i] = (1.-Gama)*(c_L -(0. - x_s[i]) / t) + 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./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./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*t) - Gama*v_R/c_R + (1.-Gama))**(2./(gamma-1.))
-            P_s[i] = p_R*(rho_s[i]/rho_R)**gamma
-            v_s[i] = (1.-Gama)*(-c_R - (-x_s[i])/t) + 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 * (gamma - 1.))  #internal energy
-s_s = P_s / rho_s**gamma # entropic function
-        
-#---------------------------------------------------------------
-# Print arrays
-
-savetxt("rho.dat", column_stack((x_s, rho_s)))
-savetxt("P.dat", column_stack((x_s, P_s)))
-savetxt("v.dat", column_stack((x_s, v_s)))
-savetxt("u.dat", column_stack((x_s, u_s)))
-savetxt("s.dat", column_stack((x_s, s_s)))
diff --git a/examples/SodShock_1D/makeIC.py b/examples/SodShock_1D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..a5c7f03b24d10e81057dbe25855f33f795218f19
--- /dev/null
+++ b/examples/SodShock_1D/makeIC.py
@@ -0,0 +1,119 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 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/>.
+ # 
+ ##############################################################################
+
+import h5py
+from numpy import *
+
+# Generates a swift IC file for the 1D Sod Shock in a periodic box
+
+# 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
+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
+    
+#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
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 1.
+grp.attrs["Unit mass in cgs (U_M)"] = 1.
+grp.attrs["Unit time in cgs (U_t)"] = 1.
+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/SodShock_1D/plotSolution.py b/examples/SodShock_1D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a7720f4a6cf26e5a8acda1101bd438850d8d553
--- /dev/null
+++ b/examples/SodShock_1D/plotSolution.py
@@ -0,0 +1,288 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Computes the 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_%03d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][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]
+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
+
+# ---------------------------------------------------------------
+# 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 profile ---------------------------------
+subplot(235)
+plot(x, S, '.', color='r', ms=4.0)
+plot(x_s, s_s, '--', color='k', alpha=0.8, 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)
+
+text(-0.49, 0.9, "Sod shock with  $\\gamma=%.3f$ in 1D at $t=%.2f$"%(gas_gamma,time), 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)
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+
+savefig("SodShock.png", dpi=200)
diff --git a/examples/SodShock_1D/run.sh b/examples/SodShock_1D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4be4254baa4a87b105a5f3c1bfbf9059348a1e9e
--- /dev/null
+++ b/examples/SodShock_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 -s -t 1 sodShock.yml 2>&1 | tee output.log
+
+# Plot the result
+python plotSolution.py 1 
diff --git a/examples/SodShock/sodShock.yml b/examples/SodShock_1D/sodShock.yml
similarity index 77%
rename from examples/SodShock/sodShock.yml
rename to examples/SodShock_1D/sodShock.yml
index 19bacb57f3bfb173063dbac5d752929763dede29..d5c4d0b034ff5351222d2162e37e3e40ceab834f 100644
--- a/examples/SodShock/sodShock.yml
+++ b/examples/SodShock_1D/sodShock.yml
@@ -9,15 +9,15 @@ InternalUnitSystem:
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   1.    # The end time of the simulation (in internal units).
+  time_end:   0.2   # The end time of the simulation (in internal units).
   dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
   dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
 
 # Parameters governing the snapshots
 Snapshots:
-  basename:            sod # Common part of the name of output files
-  time_first:          0.  # Time of the first output (in internal units)
-  delta_time:          0.1 # Time difference between consecutive outputs (in internal units)
+  basename:            sodShock # Common part of the name of output files
+  time_first:          0.       # Time of the first output (in internal units)
+  delta_time:          0.2     # Time difference between consecutive outputs (in internal units)
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -27,7 +27,7 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.01     # Maximal smoothing length allowed (in internal units).
+  max_smoothing_length:  0.4      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/SodShock_2D/getGlass.sh b/examples/SodShock_2D/getGlass.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f4cb4ebcb4b452b2b123462bc97eed532f43ba25
--- /dev/null
+++ b/examples/SodShock_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/SodShock_2D/makeIC.py b/examples/SodShock_2D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..fdc1610df8cb87b3057323b1330e4c3044f36241
--- /dev/null
+++ b/examples/SodShock_2D/makeIC.py
@@ -0,0 +1,123 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 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/>.
+ # 
+ ##############################################################################
+
+import h5py
+from numpy import *
+
+# Generates a swift IC file for the 2D Sod Shock in a periodic box
+
+# 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
+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
+
+#File
+file = h5py.File(fileName, 'w')
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = [boxSize, 0.5, 0.1]
+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
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 1.
+grp.attrs["Unit mass in cgs (U_M)"] = 1.
+grp.attrs["Unit time in cgs (U_t)"] = 1.
+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/SodShock_2D/plotSolution.py b/examples/SodShock_2D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..99ba7e9a6e9ae4b6d50688a1428f07e9a08b3b85
--- /dev/null
+++ b/examples/SodShock_2D/plotSolution.py
@@ -0,0 +1,288 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Computes the 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 *
+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_%03d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][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]
+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
+
+# ---------------------------------------------------------------
+# 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=0.5)
+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=0.5)
+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=0.5)
+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=0.5)
+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 profile ---------------------------------
+subplot(235)
+plot(x, S, '.', color='r', ms=0.5)
+plot(x_s, s_s, '--', color='k', alpha=0.8, 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)
+
+text(-0.49, 0.9, "Sod shock with  $\\gamma=%.3f$ in 2D at $t=%.2f$"%(gas_gamma,time), 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)
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+
+savefig("SodShock.png", dpi=200)
diff --git a/examples/SodShock_2D/run.sh b/examples/SodShock_2D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9e6bbfdf1c0a7c206ce6966fdca7b20a28047dd8
--- /dev/null
+++ b/examples/SodShock_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 -s -t 1 sodShock.yml 2>&1 | tee output.log
+
+python plotSolution.py 1
diff --git a/examples/SodShock_2D/sodShock.yml b/examples/SodShock_2D/sodShock.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6805724ff58defebc41f3fb5b636d0003b0d6680
--- /dev/null
+++ b/examples/SodShock_2D/sodShock.yml
@@ -0,0 +1,36 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   0.2   # The end time of the simulation (in internal units).
+  dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the 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:          0.2      # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-2 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.02     # Maximal smoothing length allowed (in internal units).
+  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
+
diff --git a/examples/SodShock_3D/getGlass.sh b/examples/SodShock_3D/getGlass.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f61b61d4e6c51b44576fd7cdd6242cb9f0133039
--- /dev/null
+++ b/examples/SodShock_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/SodShock_3D/makeIC.py b/examples/SodShock_3D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..c71c07c6c97bb715c580f747cf8d39ddf08445c3
--- /dev/null
+++ b/examples/SodShock_3D/makeIC.py
@@ -0,0 +1,123 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 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/>.
+ # 
+ ##############################################################################
+
+import h5py
+from numpy import *
+
+# Generates a swift IC file for the 3D Sod Shock in a periodic box
+
+# 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
+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
+vol_R = 0.25
+
+# 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
+
+#File
+file = h5py.File(fileName, 'w')
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = [boxSize, 0.5, 0.5]
+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
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 1.
+grp.attrs["Unit mass in cgs (U_M)"] = 1.
+grp.attrs["Unit time in cgs (U_t)"] = 1.
+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/SodShock_3D/plotSolution.py b/examples/SodShock_3D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..23a16e6aed73a7281cf78a215940ccdcff722a79
--- /dev/null
+++ b/examples/SodShock_3D/plotSolution.py
@@ -0,0 +1,288 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Computes the 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_%03d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][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]
+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
+
+# ---------------------------------------------------------------
+# 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=0.5)
+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=0.5)
+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=0.5)
+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=0.5)
+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 profile ---------------------------------
+subplot(235)
+plot(x, S, '.', color='r', ms=0.5)
+plot(x_s, s_s, '--', color='k', alpha=0.8, 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)
+
+text(-0.49, 0.9, "Sod shock with  $\\gamma=%.3f$ in 3D at $t=%.2f$"%(gas_gamma,time), 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)
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+
+savefig("SodShock.png", dpi=200)
diff --git a/examples/SodShock_3D/run.sh b/examples/SodShock_3D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..8ed85baf73425b75f402c491a3c66785f6c6fce0
--- /dev/null
+++ b/examples/SodShock_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 -s -t 4 sodShock.yml 2>&1 | tee output.log
+
+python plotSolution.py 1
diff --git a/examples/SodShock_3D/sodShock.yml b/examples/SodShock_3D/sodShock.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1ab6eb626db09678f66322e8f0e8674c0931ddb6
--- /dev/null
+++ b/examples/SodShock_3D/sodShock.yml
@@ -0,0 +1,36 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   0.2   # The end time of the simulation (in internal units).
+  dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the 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:          0.2      # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-2 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.05     # Maximal smoothing length allowed (in internal units).
+  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
+
diff --git a/examples/SquareTest_2D/makeIC.py b/examples/SquareTest_2D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..186e653124a6ff62a964c37cf0fb2220f1152a0e
--- /dev/null
+++ b/examples/SquareTest_2D/makeIC.py
@@ -0,0 +1,128 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 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/>.
+ # 
+ ##############################################################################
+
+import h5py
+from numpy import *
+
+# Generates a swift IC file for the Square test in a periodic box
+
+# Parameters
+L = 64            # Number of particles on the side 
+gamma = 5./3.     # Gas adiabatic index
+rho0 = 4          # Gas central density
+rho1 = 1          # Gas outskirt density
+P0 = 2.5          # Gas central pressure
+P1 = 2.5          # Gas central pressure
+vx = 142.3        # Random velocity for all particles 
+vy = -31.4
+fileOutputName = "square.hdf5"
+#---------------------------------------------------
+
+vol = 1.
+
+numPart_out = L * L
+numPart_in = L * L * rho0 / rho1 / 4
+
+L_out = int(sqrt(numPart_out))
+L_in = int(sqrt(numPart_in))
+
+pos_out = zeros((numPart_out, 3))
+for i in range(L_out):
+    for j in range(L_out):
+        index = i * L_out + j
+        pos_out[index, 0] =  i / (float(L_out)) + 1./(2. * L_out)
+        pos_out[index, 1] =  j / (float(L_out)) + 1./(2. * L_out)
+h_out = ones(numPart_out) * (1. / L_out) * 1.2348
+m_out = ones(numPart_out) * vol  * rho1 / numPart_out
+u_out = ones(numPart_out) * P1 / (rho1 * (gamma - 1.))
+
+pos_in = zeros((numPart_in, 3))
+for i in range(L_in):
+    for j in range(L_in):
+        index = i * L_in + j
+        pos_in[index, 0] =  0.25 + i / float(2. * L_in) + 1./(2. * 2. * L_in)
+        pos_in[index, 1] =  0.25 + j / float(2. * L_in) + 1./(2. * 2. * L_in)
+h_in = ones(numPart_in) * (1. / L_in) * 1.2348
+m_in = ones(numPart_in) * 0.25 * vol * rho0 / numPart_in
+u_in = ones(numPart_in) * P0 / (rho0 * (gamma - 1.))
+
+# Remove the central particles 
+select_out = logical_or(logical_or(pos_out[:,0] < 0.25 , pos_out[:,0] > 0.75), logical_or(pos_out[:,1] < 0.25, pos_out[:,1] > 0.75))
+pos_out = pos_out[select_out, :]
+h_out = h_out[select_out]
+u_out = u_out[select_out]
+m_out = m_out[select_out]
+
+# Add the central region
+pos = append(pos_out, pos_in, axis=0)
+h = append(h_out, h_in, axis=0)
+u = append(u_out, u_in)
+m = append(m_out, m_in)
+numPart = size(h)
+ids = linspace(1, numPart, numPart)
+vel = zeros((numPart, 3))
+vel[:,0] = vx
+vel[:,1] = vy
+        
+
+#File
+fileOutput = h5py.File(fileOutputName, 'w')
+
+# Header
+grp = fileOutput.create_group("/Header")
+grp.attrs["BoxSize"] = [vol, vol, 0.2]
+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, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 2
+
+#Runtime parameters
+grp = fileOutput.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+#Units
+grp = fileOutput.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 1.
+grp.attrs["Unit mass in cgs (U_M)"] = 1.
+grp.attrs["Unit time in cgs (U_t)"] = 1.
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+#Particle group
+grp = fileOutput.create_group("/PartType0")
+ds = grp.create_dataset('Coordinates', (numPart, 3), 'd')
+ds[()] = pos
+ds = grp.create_dataset('Velocities', (numPart, 3), 'f')
+ds[()] = vel
+ds = grp.create_dataset('Masses', (numPart, 1), 'f')
+ds[()] = m.reshape((numPart,1))
+ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f')
+ds[()] = h.reshape((numPart,1))
+ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f')
+ds[()] = u.reshape((numPart,1))
+ds = grp.create_dataset('ParticleIDs', (numPart,1), 'L')
+ds[()] = ids.reshape((numPart,1))
+
+fileOutput.close()
+
+
diff --git a/examples/SquareTest_2D/plotSolution.py b/examples/SquareTest_2D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9efe76de1e6c5993fa5333be76a13ba95bdab0f
--- /dev/null
+++ b/examples/SquareTest_2D/plotSolution.py
@@ -0,0 +1,186 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016  Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Computes the analytical solution of the square test
+
+# Parameters
+gas_gamma = 5./3.     # Gas adiabatic index
+gamma = 5./3.     # Gas adiabatic index
+rho0 = 4          # Gas central density
+rho1 = 1          # Gas outskirt density
+P0 = 2.5          # Gas central pressure
+P1 = 2.5          # Gas central pressure
+vx = 142.3        # Random velocity for all particles 
+vy = -31.4
+
+# ---------------------------------------------------------------
+# Don't touch anything after this.
+# ---------------------------------------------------------------
+
+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("square_%03d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][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"]
+
+# Analytical soltion
+centre_x = 0.5 + time * vx
+centre_y = 0.5 + time * vy
+
+while centre_x > 1.:
+    centre_x -= 1.
+while centre_x < 0.:
+    centre_x += 1.
+while centre_y > 1.:
+    centre_y -= 1.
+while centre_y < 0.:
+    centre_y += 1.
+
+pos = sim["/PartType0/Coordinates"][:,:]
+vel = sim["/PartType0/Velocities"][:,:]
+v_norm = sqrt(vel[:,0]**2 + vel[:,1]**2)
+rho = sim["/PartType0/Density"][:]
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+x = pos[:,0] - centre_x
+y = pos[:,1] - centre_y
+
+# Box wrapping
+x[x>0.5] -= 1.
+x[x<-0.5] += 1.
+y[y>0.5] -= 1.
+y[y<-0.5] += 1.
+
+# Azimuthal velocity profile -----------------------------
+subplot(231)
+scatter(x, y, c=v_norm, cmap="PuBu", edgecolors='face', s=4, vmin=-1., vmax=1.)
+text(0.47, 0.47, "${\\rm{Velocity~norm}}$", ha="right", va="top", backgroundcolor="w")
+plot([-0.25, 0.25], [0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, 0.25], [-0.25, -0.25], '--', color='k', alpha=0.8, lw=2)
+plot([0.25, 0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, -0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=-7)
+xlim(-0.5, 0.5)
+ylim(-0.5, 0.5)
+
+# Radial density profile --------------------------------
+subplot(232)
+scatter(x, y, c=rho, cmap="PuBu", edgecolors='face', s=4, vmin=0., vmax=4.)
+text(0.47, 0.47, "${\\rm{Density}}$", ha="right", va="top", backgroundcolor="w")
+plot([-0.25, 0.25], [0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, 0.25], [-0.25, -0.25], '--', color='k', alpha=0.8, lw=2)
+plot([0.25, 0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, -0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=-7)
+xlim(-0.5, 0.5)
+ylim(-0.5, 0.5)
+
+# Radial pressure profile --------------------------------
+subplot(233)
+scatter(x, y, c=P, cmap="PuBu", edgecolors='face', s=4, vmin=2, vmax=4)
+text(0.47, 0.47, "${\\rm{Pressure}}$", ha="right", va="top", backgroundcolor="w")
+plot([-0.25, 0.25], [0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, 0.25], [-0.25, -0.25], '--', color='k', alpha=0.8, lw=2)
+plot([0.25, 0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, -0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=-7)
+xlim(-0.5, 0.5)
+ylim(-0.5, 0.5)
+
+# Internal energy profile --------------------------------
+subplot(234)
+scatter(x, y, c=u, cmap="PuBu", edgecolors='face', s=4, vmin=0.5, vmax=4.)
+text(0.47, 0.47, "${\\rm{Internal~energy}}$", ha="right", va="top", backgroundcolor="w")
+plot([-0.25, 0.25], [0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, 0.25], [-0.25, -0.25], '--', color='k', alpha=0.8, lw=2)
+plot([0.25, 0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, -0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=-7)
+xlim(-0.5, 0.5)
+ylim(-0.5, 0.5)
+
+# Radial entropy profile --------------------------------
+subplot(235)
+scatter(x, y, c=S, cmap="PuBu", edgecolors='face', s=4, vmin=0., vmax=3.)
+text(0.47, 0.47, "${\\rm{Entropy}}$", ha="right", va="top", backgroundcolor="w")
+plot([-0.25, 0.25], [0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, 0.25], [-0.25, -0.25], '--', color='k', alpha=0.8, lw=2)
+plot([0.25, 0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+plot([-0.25, -0.25], [-0.25, 0.25], '--', color='k', alpha=0.8, lw=2)
+xlabel("${\\rm{Position}}~x$", labelpad=0)
+ylabel("${\\rm{Position}}~y$", labelpad=-7)
+xlim(-0.5, 0.5)
+ylim(-0.5, 0.5)
+
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+text(-0.49, 0.9, "Square test with $\\gamma=%.3f$ at $t=%.2f$"%(gas_gamma,time), fontsize=10)
+text(-0.49, 0.8, "Centre:~~~ $(P, \\rho) = (%.3f, %.3f)$"%(P0, rho0), fontsize=10)
+text(-0.49, 0.7, "Outskirts: $(P, \\rho) = (%.3f, %.3f)$"%(P1, rho1), fontsize=10)
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+savefig("SquareTest.png", dpi=200)
diff --git a/examples/SquareTest_2D/run.sh b/examples/SquareTest_2D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..242f1b8b729979b026cfe31002e84cd9ef741129
--- /dev/null
+++ b/examples/SquareTest_2D/run.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e square.hdf5 ]
+then
+    echo "Generating initial conditions for the square test ..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift -s -t 1 square.yml 2>&1 | tee output.log
+
+# Plot the solution
+python plotSolution.py 40
diff --git a/examples/SquareTest_2D/square.yml b/examples/SquareTest_2D/square.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4f39c6490899cfaafeda17fb0c28281cbadcbbea
--- /dev/null
+++ b/examples/SquareTest_2D/square.yml
@@ -0,0 +1,35 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   4.   # The end time of the simulation (in internal units).
+  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            square # Common part of the name of output files
+  time_first:          0.     # Time of the first output (in internal units)
+  delta_time:          1e-1   # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-2 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.02     # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./square.hdf5     # The file to read
diff --git a/examples/UniformBox_2D/makeIC.py b/examples/UniformBox_2D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..642896c6ec406a5a75127e024d19775ea4a8e09b
--- /dev/null
+++ b/examples/UniformBox_2D/makeIC.py
@@ -0,0 +1,115 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 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/>.
+ # 
+ ##############################################################################
+
+import h5py
+import sys
+import random
+from numpy import *
+
+# Generates a swift IC file containing a perturbed cartesian distribution of particles
+# at a constant density and pressure in a cubic box
+
+# Parameters
+periodic= 1          # 1 For periodic box
+boxSize = 1.
+L = int(sys.argv[1]) # Number of particles along one axis
+rho = 1.             # Density
+P = 1.               # Pressure
+gamma = 5./3.        # Gas adiabatic index
+fileName = "uniformPlane.hdf5" 
+
+
+#---------------------------------------------------
+numPart = L**2
+mass = boxSize**2 * rho / numPart
+internalEnergy = P / ((gamma - 1.)*rho)
+
+#Generate particles
+coords = zeros((numPart, 3))
+v      = zeros((numPart, 3))
+m      = zeros((numPart, 1))
+h      = zeros((numPart, 1))
+u      = zeros((numPart, 1))
+ids    = zeros((numPart, 1), dtype='L')
+
+for i in range(L):
+    for j in range(L):
+        index = i*L + j
+        x = i * boxSize / L + boxSize / (2*L) + boxSize/(2.*L)
+        y = j * boxSize / L + boxSize / (2*L) + boxSize/(2.*L)
+        z = 0
+        coords[index,0] = x
+        coords[index,1] = y
+        coords[index,2] = z
+        v[index,0] = 0.
+        v[index,1] = 0.
+        v[index,2] = 0.
+        m[index] = mass
+        h[index] = 1.23485 * boxSize / L
+        u[index] = internalEnergy
+        ids[index] = index
+            
+            
+
+#--------------------------------------------------
+
+#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, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total"] = numPart
+grp.attrs["Dimension"] = 2
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = periodic
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 1.
+grp.attrs["Unit mass in cgs (U_M)"] = 1.
+grp.attrs["Unit time in cgs (U_t)"] = 1.
+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")
+ds = grp.create_dataset('Coordinates', (numPart, 3), 'd')
+ds[()] = coords
+ds = grp.create_dataset('Velocities', (numPart, 3), 'f')
+ds[()] = v
+ds = grp.create_dataset('Masses', (numPart,1), 'f')
+ds[()] = m
+ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f')
+ds[()] = h
+ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f')
+ds[()] = u
+ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L')
+ds[()] = ids + 1
+
+file.close()
diff --git a/examples/UniformBox_2D/run.sh b/examples/UniformBox_2D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ee3ef109968a65e2437ea17b42013266195d3314
--- /dev/null
+++ b/examples/UniformBox_2D/run.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+if [ ! -e uniformPlane.hdf5 ]
+then
+    echo "Generating initial conditions for the uniform box example..."
+    python makeIC.py 100
+fi
+
+../swift -s -t 16 uniformPlane.yml 2>&1 | tee output.log
diff --git a/examples/UniformBox_2D/uniformPlane.yml b/examples/UniformBox_2D/uniformPlane.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a3e2d275e50fb20f66ea6494c1202319e462dbed
--- /dev/null
+++ b/examples/UniformBox_2D/uniformPlane.yml
@@ -0,0 +1,35 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1   # Grams
+  UnitLength_in_cgs:   1   # Centimeters
+  UnitVelocity_in_cgs: 1   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   10.   # The end time of the simulation (in internal units).
+  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            uniformPlane   # Common part of the name of output files
+  time_first:          0.             # Time of the first output (in internal units)
+  delta_time:          1e-1           # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-3 # 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).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  max_smoothing_length:  0.1      # Maximal smoothing length allowed (in internal units).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./uniformPlane.hdf5     # The file to read
diff --git a/examples/UniformBox/makeIC.py b/examples/UniformBox_3D/makeIC.py
similarity index 99%
rename from examples/UniformBox/makeIC.py
rename to examples/UniformBox_3D/makeIC.py
index 1484f60596e68734f0f98685ab2ab845f2e0b407..01e37c67b6e2eec2984d62f4ffd503b23b5bd9ec 100644
--- a/examples/UniformBox/makeIC.py
+++ b/examples/UniformBox_3D/makeIC.py
@@ -55,6 +55,7 @@ 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
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
diff --git a/examples/UniformBox/makeICbig.py b/examples/UniformBox_3D/makeICbig.py
similarity index 100%
rename from examples/UniformBox/makeICbig.py
rename to examples/UniformBox_3D/makeICbig.py
diff --git a/examples/UniformBox/run.sh b/examples/UniformBox_3D/run.sh
similarity index 78%
rename from examples/UniformBox/run.sh
rename to examples/UniformBox_3D/run.sh
index 0cb0a505915be47bafb99ed7531685bfeb3dc829..08891cdd08fccf8f43089951e94dddb33e162030 100755
--- a/examples/UniformBox/run.sh
+++ b/examples/UniformBox_3D/run.sh
@@ -7,4 +7,4 @@ then
     python makeIC.py 100
 fi
 
-../swift -s -t 16 uniformBox.yml
+../swift -s -t 16 uniformBox.yml 2>&1 | tee output.log
diff --git a/examples/UniformBox/uniformBox.yml b/examples/UniformBox_3D/uniformBox.yml
similarity index 100%
rename from examples/UniformBox/uniformBox.yml
rename to examples/UniformBox_3D/uniformBox.yml
diff --git a/examples/UniformDMBox/makeIC.py b/examples/UniformDMBox/makeIC.py
index 2aee89798a5b8bbd425a6b73528779fb1aa7db23..8e032500016eb6cc8e0decc54968bb5b841d7f93 100644
--- a/examples/UniformDMBox/makeIC.py
+++ b/examples/UniformDMBox/makeIC.py
@@ -51,6 +51,7 @@ grp.attrs["Time"] = 0.0
 grp.attrs["NumFilesPerSnapshot"] = 1
 grp.attrs["MassTable"] = [0.0, mass, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
+grp.attrs["Dimension"] = 3
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
diff --git a/examples/main.c b/examples/main.c
index 85d2f885c0e99c23fee83e2a212f135452c080f2..af4375a5f137ed012a8a12d24716ab430b3c4ec7 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -68,6 +68,8 @@ void print_help_message() {
   printf("  %2s %8s %s\n", "", "",
          "Allows user to check validy of parameter and IC files as well as "
          "memory limits.");
+  printf("  %2s %8s %s\n", "-D", "",
+         "Always drift all particles even the ones far from active particles.");
   printf("  %2s %8s %s\n", "-e", "",
          "Enable floating-point exceptions (debugging mode)");
   printf("  %2s %8s %s\n", "-f", "{int}",
@@ -76,7 +78,8 @@ void print_help_message() {
          "Run with an external gravitational potential");
   printf("  %2s %8s %s\n", "-G", "", "Run with self-gravity");
   printf("  %2s %8s %s\n", "-n", "{int}",
-         "Execute a fixed number of time steps");
+         "Execute a fixed number of time steps. When unset use the time_end "
+         "parameter to stop.");
   printf("  %2s %8s %s\n", "-s", "", "Run with SPH");
   printf("  %2s %8s %s\n", "-t", "{int}",
          "The number of threads to use on each MPI rank. Defaults to 1 if not "
@@ -148,6 +151,7 @@ int main(int argc, char *argv[]) {
   int with_self_gravity = 0;
   int with_hydro = 0;
   int with_fp_exceptions = 0;
+  int with_drift_all = 0;
   int verbose = 0;
   int nr_threads = 1;
   char paramFileName[200] = "";
@@ -155,7 +159,7 @@ int main(int argc, char *argv[]) {
 
   /* Parse the parameters */
   int c;
-  while ((c = getopt(argc, argv, "acCdef:gGhn:st:v:y:")) != -1) switch (c) {
+  while ((c = getopt(argc, argv, "acCdDef:gGhn:st:v:y:")) != -1) switch (c) {
       case 'a':
         with_aff = 1;
         break;
@@ -168,6 +172,9 @@ int main(int argc, char *argv[]) {
       case 'd':
         dry_run = 1;
         break;
+      case 'D':
+        with_drift_all = 1;
+        break;
       case 'e':
         with_fp_exceptions = 1;
         break;
@@ -258,7 +265,7 @@ int main(int argc, char *argv[]) {
 
   /* Do we choke on FP-exceptions ? */
   if (with_fp_exceptions) {
-    feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+    feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW);
     if (myrank == 0) message("Floating point exceptions will be reported.");
   }
 
@@ -271,18 +278,6 @@ int main(int argc, char *argv[]) {
     message("sizeof(struct cell)  is %4zi bytes.", sizeof(struct cell));
   }
 
-/* Temporary abort to handle absence of vectorized functions */
-#ifdef WITH_VECTORIZATION
-
-#ifdef MINIMAL_SPH
-  error(
-      "Vectorized version of Minimal SPH routines not implemented yet. "
-      "Reconfigure with --disable-vec and recompile or use DEFAULT_SPH.");
-#endif
-
-#endif
-  /* End temporary fix */
-
   /* How vocal are we ? */
   const int talking = (verbose == 1 && myrank == 0) || (verbose == 2);
 
@@ -337,10 +332,11 @@ int main(int argc, char *argv[]) {
 
   /* Initialise the external potential properties */
   struct external_potential potential;
-  if (with_external_gravity) potential_init(params, &us, &potential);
+  if (with_external_gravity)
+    potential_init(params, &prog_const, &us, &potential);
   if (with_external_gravity && myrank == 0) potential_print(&potential);
 
-  /* Initialise the external potential properties */
+  /* Initialise the cooling function properties */
   struct cooling_data cooling;
   if (with_cooling) cooling_init(params, &us, &prog_const, &cooling);
   if (with_cooling && myrank == 0) cooling_print(&cooling);
@@ -349,6 +345,8 @@ int main(int argc, char *argv[]) {
   char ICfileName[200] = "";
   parser_get_param_string(params, "InitialConditions:file_name", ICfileName);
   if (myrank == 0) message("Reading ICs from file '%s'", ICfileName);
+  fflush(stdout);
+
   struct part *parts = NULL;
   struct gpart *gparts = NULL;
   size_t Ngas = 0, Ngpart = 0;
@@ -447,6 +445,7 @@ int main(int argc, char *argv[]) {
 
   /* Construct the engine policy */
   int engine_policies = ENGINE_POLICY | engine_policy_steal;
+  if (with_drift_all) engine_policies |= engine_policy_drift_all;
   if (with_hydro) engine_policies |= engine_policy_hydro;
   if (with_self_gravity) engine_policies |= engine_policy_self_gravity;
   if (with_external_gravity) engine_policies |= engine_policy_external_gravity;
@@ -466,9 +465,6 @@ int main(int argc, char *argv[]) {
     fflush(stdout);
   }
 
-  /* Write the state of the system before starting time integration. */
-  if (!dry_run) engine_dump_snapshot(&e);
-
 /* Init the runner history. */
 #ifdef HIST
   for (k = 0; k < runner_hist_N; k++) runner_hist_bins[k] = 0;
@@ -492,6 +488,8 @@ int main(int argc, char *argv[]) {
 #endif
     if (myrank == 0)
       message("Time integration ready to start. End of dry-run.");
+    engine_clean(&e);
+    free(params);
     return 0;
   }
 
@@ -504,6 +502,9 @@ int main(int argc, char *argv[]) {
   /* Initialise the particles */
   engine_init_particles(&e, flag_entropy_ICs);
 
+  /* Write the state of the system before starting time integration. */
+  engine_dump_snapshot(&e);
+
   /* Legend */
   if (myrank == 0)
     printf("# %6s %14s %14s %10s %10s %16s [%s]\n", "Step", "Time", "Time-step",
@@ -555,7 +556,7 @@ int main(int argc, char *argv[]) {
             if (!e.sched.tasks[l].skip && !e.sched.tasks[l].implicit) {
               fprintf(
                   file_thread, " %03i %i %i %i %i %lli %lli %i %i %i %i %i\n",
-                  myrank, e.sched.tasks[l].last_rid, e.sched.tasks[l].type,
+                  myrank, e.sched.tasks[l].rid, e.sched.tasks[l].type,
                   e.sched.tasks[l].subtype, (e.sched.tasks[l].cj == NULL),
                   e.sched.tasks[l].tic, e.sched.tasks[l].toc,
                   (e.sched.tasks[l].ci != NULL) ? e.sched.tasks[l].ci->count
@@ -591,7 +592,7 @@ int main(int argc, char *argv[]) {
         if (!e.sched.tasks[l].skip && !e.sched.tasks[l].implicit)
           fprintf(
               file_thread, " %i %i %i %i %lli %lli %i %i %i %i\n",
-              e.sched.tasks[l].last_rid, e.sched.tasks[l].type,
+              e.sched.tasks[l].rid, e.sched.tasks[l].type,
               e.sched.tasks[l].subtype, (e.sched.tasks[l].cj == NULL),
               e.sched.tasks[l].tic, e.sched.tasks[l].toc,
               (e.sched.tasks[l].ci == NULL) ? 0 : e.sched.tasks[l].ci->count,
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index edb0885d621975850a09e4298b8e035ebb45a3cd..b1e106b94d4b42e51836ce1c36d733062746be6d 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -25,6 +25,7 @@ Snapshots:
   basename:   output      # Common part of the name of output files
   time_first: 0.          # Time of the first output (in internal units)
   delta_time: 0.01        # Time difference between consecutive outputs (in internal units)
+  compression: 0          # (Optional) Set the level of compression of the HDF5 datasets [0-9]. 0 does no compression.
   UnitMass_in_cgs:     1  # (Optional) Unit system for the outputs (Grams)
   UnitLength_in_cgs:   1  # (Optional) Unit system for the outputs (Centimeters)
   UnitVelocity_in_cgs: 1  # (Optional) Unit system for the outputs (Centimeters per second)
@@ -66,15 +67,22 @@ DomainDecomposition:
   
 # Point mass external potentials
 PointMass:
-  position_x:      50.     # location of external point mass in internal units
+  position_x:      50.      # location of external point mass (internal units)
   position_y:      50.
   position_z:      50.
-  mass:            1e10     # mass of external point mass in internal units
-  timestep_mult:   0.03     # Pre-factor for the time-step condition
+  mass:            1e10     # mass of external point mass (internal units)
+  timestep_mult:   0.03     # Dimensionless pre-factor for the time-step condition
 
 IsothermalPotential:
-  position_x:      100.     # Location of centre of isothermal potential in internal units
+  position_x:      100.     # Location of centre of isothermal potential (internal units)
   position_y:      100.
   position_z:      100.
-  vrot:            200.     # Rotation speed of isothermal potential in internal units
-  timestep_mult:   0.03     # Pre-factor for the time-step condition
+  vrot:            200.     # Rotation speed of isothermal potential (internal units)
+  timestep_mult:   0.03     # Dimensionless pre-factor for the time-step condition
+
+Disk-PatchPotential:
+  surface_density: 10.      # Surface density of the disk (internal units)
+  scale_height:    100.     # Scale height of the disk (internal units)
+  z_disk:          200.     # Disk height (internal units)
+  timestep_mult:   0.03     # Dimensionless pre-factor for the time-step condition
+  growth_time:     5.       # (Optional) Time for the disk to grow to its final size (multiple of the dynamical time)
diff --git a/examples/plot_scaling_results.py b/examples/plot_scaling_results.py
index 66365bfad2edb38efa4b90c0c1602fd38bd750fd..cf9c96d41fe8f0897acb6be1f9af2269abee6174 100755
--- a/examples/plot_scaling_results.py
+++ b/examples/plot_scaling_results.py
@@ -17,6 +17,26 @@ import re
 import numpy as np
 import matplotlib.pyplot as plt
 
+params = {'axes.labelsize': 14,
+'axes.titlesize': 18,
+'font.size': 12,
+'legend.fontsize': 12,
+'xtick.labelsize': 14,
+'ytick.labelsize': 14,
+'text.usetex': True,
+'figure.subplot.left'    : 0.055,
+'figure.subplot.right'   : 0.98  ,
+'figure.subplot.bottom'  : 0.05  ,
+'figure.subplot.top'     : 0.95  ,
+'figure.subplot.wspace'  : 0.14  ,
+'figure.subplot.hspace'  : 0.12  ,
+'lines.markersize' : 6,
+'lines.linewidth' : 3.,
+'text.latex.unicode': True
+}
+plt.rcParams.update(params)
+plt.rc('font',**{'family':'sans-serif','sans-serif':['Times']})
+
 version = []
 branch = []
 revision = []
@@ -25,7 +45,10 @@ hydro_kernel = []
 hydro_neighbours = []
 hydro_eta = []
 threadList = []
-linestyle = ('ro-','bo-','go-','yo-','mo-')
+hexcols = ['#332288', '#88CCEE', '#44AA99', '#117733', '#999933', '#DDCC77',
+           '#CC6677', '#882255', '#AA4499', '#661100', '#6699CC', '#AA4466',
+           '#4477AA']
+linestyle = (hexcols[0],hexcols[1],hexcols[3],hexcols[5],hexcols[6])
 #cmdLine = './swift_fixdt -s -t 16 cosmoVolume.yml'
 #platform = 'KNL'
 
@@ -106,8 +129,8 @@ def parse_files():
     parse_header(file_list[0])
     
     version.append(branch[i] + " " + revision[i] + "\n" + hydro_scheme[i] + 
-                   "\n" + hydro_kernel[i] + r", $N_{neigh}$=" + hydro_neighbours[i] + 
-                   r", $\eta$=" + hydro_eta[i] + "\n")                  
+                   "\n" + hydro_kernel[i] + r", $N_{ngb}$=" + hydro_neighbours[i] + 
+                   r", $\eta$=" + hydro_eta[i])                  
     times.append([])
     totalTime.append([])
     speedUp.append([])
@@ -116,7 +139,7 @@ def parse_files():
     # Loop over all files for a given series and load the times
     for j in range(0,len(file_list)):
       times[i].append([])
-      times[i][j].append(np.loadtxt(file_list[j],usecols=(5,)))
+      times[i][j].append(np.loadtxt(file_list[j],usecols=(5,), skiprows=11))
       totalTime[i].append(np.sum(times[i][j]))
 
     serialTime.append(totalTime[i][0])
@@ -153,47 +176,59 @@ def print_results(times,totalTime,parallelEff,version):
 
 def plot_results(times,totalTime,speedUp,parallelEff):
   
-  fig, axarr = plt.subplots(2, 2,figsize=(15,15))
+  fig, axarr = plt.subplots(2, 2, figsize=(10,10), frameon=False)
   speedUpPlot = axarr[0, 0]
   parallelEffPlot = axarr[0, 1]
   totalTimePlot = axarr[1, 0]
   emptyPlot = axarr[1, 1]
   
   # Plot speed up
+  speedUpPlot.plot(threadList[0],threadList[0], linestyle='--', lw=1.5, color='0.2')
   for i in range(0,numOfSeries):
     speedUpPlot.plot(threadList[i],speedUp[i],linestyle[i],label=version[i])
   
-  speedUpPlot.plot(threadList[i],threadList[i],color='k',linestyle='--')
-  speedUpPlot.set_ylabel("Speed Up")
-  speedUpPlot.set_xlabel("No. of Threads")
+  speedUpPlot.set_ylabel("${\\rm Speed\\textendash up}$", labelpad=0.)
+  speedUpPlot.set_xlabel("${\\rm Threads}$", labelpad=0.)
+  speedUpPlot.set_xlim([0.7,threadList[i][-1]+1])
+  speedUpPlot.set_ylim([0.7,threadList[i][-1]+1])
 
   # Plot parallel efficiency
+  parallelEffPlot.plot([threadList[0][0], 10**np.floor(np.log10(threadList[0][-1])+1)], [1,1], 'k--', lw=1.5, color='0.2')
+  parallelEffPlot.plot([threadList[0][0], 10**np.floor(np.log10(threadList[0][-1])+1)], [0.9,0.9], 'k--', lw=1.5, color='0.2')
+  parallelEffPlot.plot([threadList[0][0], 10**np.floor(np.log10(threadList[0][-1])+1)], [0.75,0.75], 'k--', lw=1.5, color='0.2')
+  parallelEffPlot.plot([threadList[0][0], 10**np.floor(np.log10(threadList[0][-1])+1)], [0.5,0.5], 'k--', lw=1.5, color='0.2')
   for i in range(0,numOfSeries):
     parallelEffPlot.plot(threadList[i],parallelEff[i],linestyle[i])
-  
+
   parallelEffPlot.set_xscale('log')
-  parallelEffPlot.set_ylabel("Parallel Efficiency")
-  parallelEffPlot.set_xlabel("No. of Threads")
+  parallelEffPlot.set_ylabel("${\\rm Parallel~efficiency}$", labelpad=0.)
+  parallelEffPlot.set_xlabel("${\\rm Threads}$", labelpad=0.)
   parallelEffPlot.set_ylim([0,1.1])
+  parallelEffPlot.set_xlim([0.9,10**(np.floor(np.log10(threadList[i][-1]))+0.5)])
 
   # Plot time to solution     
   for i in range(0,numOfSeries):
+    pts = [1, 10**np.floor(np.log10(threadList[i][-1])+1)]
+    totalTimePlot.loglog(pts,totalTime[i][0]/pts, 'k--', lw=1., color='0.2')
     totalTimePlot.loglog(threadList[i],totalTime[i],linestyle[i],label=version[i])
   
   totalTimePlot.set_xscale('log')
-  totalTimePlot.set_xlabel("No. of Threads")
-  totalTimePlot.set_ylabel("Time to Solution (ms)")
+  totalTimePlot.set_xlabel("${\\rm Threads}$", labelpad=0.)
+  totalTimePlot.set_ylabel("${\\rm Time~to~solution}~[{\\rm ms}]$", labelpad=0.)
+  totalTimePlot.set_xlim([0.9, 10**(np.floor(np.log10(threadList[i][-1]))+0.5)])
+  totalTimePlot.set_ylim([10**np.floor(np.log10(np.min(totalTime)*0.6)), 1.2*10**np.floor(np.log10(np.max(totalTime) * 1.5)+1)])
   
-  totalTimePlot.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,prop={'size':14})
+  totalTimePlot.legend(bbox_to_anchor=(1.14, 1), loc=2, borderaxespad=0.,prop={'size':12})
   emptyPlot.axis('off')
   
   for i, txt in enumerate(threadList[0]):
-    speedUpPlot.annotate(txt, (threadList[0][i],speedUp[0][i]))
-    parallelEffPlot.annotate(txt, (threadList[0][i],parallelEff[0][i]))
-    totalTimePlot.annotate(txt, (threadList[0][i],totalTime[0][i]))
+    if 2**np.floor(np.log2(threadList[0][i])) == threadList[0][i]: # only powers of 2
+      speedUpPlot.annotate("$%s$"%txt, (threadList[0][i],speedUp[0][i]), (threadList[0][i],speedUp[0][i] + 0.3), color=hexcols[0])
+      parallelEffPlot.annotate("$%s$"%txt, (threadList[0][i],parallelEff[0][i]), (threadList[0][i], parallelEff[0][i]+0.02), color=hexcols[0])
+      totalTimePlot.annotate("$%s$"%txt, (threadList[0][i],totalTime[0][i]), (threadList[0][i], totalTime[0][i]*1.1), color=hexcols[0])
 
   #fig.suptitle("Thread Speed Up, Parallel Efficiency and Time To Solution for {} Time Steps of Cosmo Volume\n Cmd Line: {}, Platform: {}".format(len(times[0][0][0]),cmdLine,platform))
-  fig.suptitle("Thread Speed Up, Parallel Efficiency and Time To Solution for {} Time Steps".format(len(times[0][0][0])))
+  fig.suptitle("${\\rm Speed\\textendash up,~parallel~efficiency~and~time~to~solution~for}~%d~{\\rm time\\textendash steps}$"%len(times[0][0][0]), fontsize=16)
 
   return
 
@@ -204,4 +239,5 @@ plot_results(times,totalTime,speedUp,parallelEff)
 
 print_results(times,totalTime,parallelEff,version)
 
+# And plot
 plt.show()
diff --git a/examples/plot_tasks.py b/examples/plot_tasks.py
index f2d0aa95d1f35f30476e1989349a07be8d9e5b0a..fb8b2ce57a47b4d397284bba9960b098c1e3ce62 100755
--- a/examples/plot_tasks.py
+++ b/examples/plot_tasks.py
@@ -56,9 +56,8 @@ pl.rcParams.update(PLOT_PARAMS)
 
 #  Tasks and subtypes. Indexed as in tasks.h.
 TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", "ghost",
-             "drift", "kick", "kick_fixdt", "send", "recv", "grav_pp", "grav_mm",
-             "grav_up", "grav_down", "grav_external", "part_sort", "gpart_sort",
-             "split_cell", "rewait", "count"]
+             "kick", "kick_fixdt", "send", "recv", "grav_gather_m", "grav_fft",
+             "grav_mm", "grav_up", "grav_external", "count"]
 
 TASKCOLOURS = {"none": "black",
                "sort": "lightblue",
@@ -68,29 +67,26 @@ TASKCOLOURS = {"none": "black",
                "sub_pair": "navy",
                "init": "indigo",
                "ghost": "cyan",
-               "drift": "maroon",
                "kick": "green",
                "kick_fixdt": "green",
                "send": "yellow",
                "recv": "magenta",
-               "grav_pp": "mediumorchid",
+               "grav_gather_m": "mediumorchid",
+               "grav_fft": "mediumnightblue",
                "grav_mm": "mediumturquoise",
                "grav_up": "mediumvioletred",
-               "grav_down": "mediumnightblue",
                "grav_external": "darkred",
-               "part_sort": "steelblue",
-               "gpart_sort": "teal" ,
-               "split_cell": "seagreen",
-               "rewait": "olive",
                "count": "powerblue"}
 
-SUBTYPES = ["none", "density", "force", "grav", "count"]
+SUBTYPES = ["none", "density", "gradient", "force", "grav", "tend", "count"]
 
 SUBCOLOURS = {"none": "black",
               "density": "red",
+              "gradient": "powerblue",
               "force": "blue",
               "grav": "indigo",
-              "count": "purple"}
+              "tend": "grey",
+              "count": "black"}
 
 #  Show docs if help is requested.
 if len( sys.argv ) == 2 and ( sys.argv[1][0:2] == "-h" or sys.argv[1][0:3] == "--h" ):
diff --git a/examples/plot_tasks_MPI.py b/examples/plot_tasks_MPI.py
index 9a92faf9417c9a302831eb8cb2f4471eb672d59c..398324cdc773d1dc4b7f26c58866c9df6469cc0b 100755
--- a/examples/plot_tasks_MPI.py
+++ b/examples/plot_tasks_MPI.py
@@ -62,9 +62,8 @@ pl.rcParams.update(PLOT_PARAMS)
 
 #  Tasks and subtypes. Indexed as in tasks.h.
 TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", "ghost",
-             "drift", "kick", "kick_fixdt", "send", "recv", "grav_pp", "grav_mm",
-             "grav_up", "grav_down", "grav_external", "part_sort", "gpart_sort",
-             "split_cell", "rewait", "count"]
+             "kick", "kick_fixdt", "send", "recv", "grav_gather_m", "grav_fft",
+             "grav_mm", "grav_up", "grav_external", "count"]
 
 TASKCOLOURS = {"none": "black",
                "sort": "lightblue",
@@ -74,29 +73,26 @@ TASKCOLOURS = {"none": "black",
                "sub_pair": "navy",
                "init": "indigo",
                "ghost": "cyan",
-               "drift": "maroon",
                "kick": "green",
                "kick_fixdt": "green",
                "send": "yellow",
                "recv": "magenta",
-               "grav_pp": "mediumorchid",
+               "grav_gather_m": "mediumorchid",
+               "grav_fft": "mediumnightblue",
                "grav_mm": "mediumturquoise",
                "grav_up": "mediumvioletred",
-               "grav_down": "mediumnightblue",
                "grav_external": "darkred",
-               "part_sort": "steelblue",
-               "gpart_sort": "teal" ,
-               "split_cell": "seagreen",
-               "rewait": "olive",
                "count": "powerblue"}
 
-SUBTYPES = ["none", "density", "force", "grav", "count"]
+SUBTYPES = ["none", "density", "gradient", "force", "grav", "tend", "count"]
 
 SUBCOLOURS = {"none": "black",
               "density": "red",
+              "gradient": "powerblue",
               "force": "blue",
               "grav": "indigo",
-              "count": "purple"}
+              "tend": "grey",
+              "count": "black"}
 
 #  Show docs if help is requested.
 if len( sys.argv ) == 2 and ( sys.argv[1][0:2] == "-h" or sys.argv[1][0:3] == "--h" ):
diff --git a/src/Makefile.am b/src/Makefile.am
index 21f46ab4abe046bdac189bd7aae1cb1fa87a429a..a69d59ba4f1fbd541c335e9f208609582c50251b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -42,8 +42,8 @@ endif
 include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \
     engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.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 potentials.h version.h hydro_properties.h \
-    cooling.h
+    physical_constants.h physical_constants_cgs.h potentials.h version.h \
+    hydro_properties.h threadpool.h cooling.h
 
 # Common source files
 AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \
@@ -51,12 +51,13 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.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 potentials.c hydro_properties.c \
-    runner_doiact_fft.c cooling.c
+    runner_doiact_fft.c threadpool.c cooling.c
 
 # Include files for distribution, not installation.
 nobase_noinst_HEADERS = approx_math.h atomic.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \
 		 kernel_long_gravity.h vector.h runner_doiact.h runner_doiact_grav.h runner_doiact_fft.h \
                  units.h intrinsics.h minmax.h kick.h timestep.h drift.h adiabatic_index.h io_properties.h \
+		 dimension.h equation_of_state.h \
 		 gravity.h gravity_io.h \
 		 gravity/Default/gravity.h gravity/Default/gravity_iact.h gravity/Default/gravity_io.h \
 		 gravity/Default/gravity_debug.h gravity/Default/gravity_part.h  \
diff --git a/src/adiabatic_index.h b/src/adiabatic_index.h
index ba7a1a64d5c97f1f6d276e5969782351762be4b1..a0c9ce09e3e004af07e8b208ef9f1af5f46c9e81 100644
--- a/src/adiabatic_index.h
+++ b/src/adiabatic_index.h
@@ -1,6 +1,7 @@
 /*******************************************************************************
  * This file is part of SWIFT.
  * Copyright (c) 2016   Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ *                      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
@@ -19,6 +20,12 @@
 #ifndef SWIFT_ADIABATIC_INDEX_H
 #define SWIFT_ADIABATIC_INDEX_H
 
+/**
+ * @file adiabatic_index.h
+ * @brief Defines the adiabatic index (polytropix index) \f$\gamma\f$ of the
+ * problem and (fast) mathematical functions involving it.
+ */
+
 /* Config parameters. */
 #include "../config.h"
 
@@ -28,6 +35,7 @@
 /* Local headers. */
 #include "const.h"
 #include "debug.h"
+#include "error.h"
 #include "inline.h"
 
 /* First define some constants */
@@ -36,18 +44,60 @@
 #define hydro_gamma 1.66666666666666667f
 #define hydro_gamma_minus_one 0.66666666666666667f
 #define hydro_one_over_gamma_minus_one 1.5f
+#define hydro_gamma_plus_one_over_two_gamma 0.8f
+#define hydro_gamma_minus_one_over_two_gamma 0.2f
+#define hydro_gamma_minus_one_over_gamma_plus_one 0.25f
+#define hydro_two_over_gamma_plus_one 0.75f
+#define hydro_two_over_gamma_minus_one 3.f
+#define hydro_gamma_minus_one_over_two 0.33333333333333333f
+#define hydro_two_gamma_over_gamma_minus_one 5.f
+#define hydro_one_over_gamma 0.6f
+
+#elif defined(HYDRO_GAMMA_7_5)
+
+#define hydro_gamma 1.4f
+#define hydro_gamma_minus_one 0.4f
+#define hydro_one_over_gamma_minus_one 2.5f
+#define hydro_gamma_plus_one_over_two_gamma 0.857142857f
+#define hydro_gamma_minus_one_over_two_gamma 0.142857143f
+#define hydro_gamma_minus_one_over_gamma_plus_one 0.166666667f
+#define hydro_two_over_gamma_plus_one 0.833333333
+#define hydro_two_over_gamma_minus_one 5.f
+#define hydro_gamma_minus_one_over_two 0.2f
+#define hydro_two_gamma_over_gamma_minus_one 7.f
+#define hydro_one_over_gamma 0.714285714f
 
 #elif defined(HYDRO_GAMMA_4_3)
 
 #define hydro_gamma 1.33333333333333333f
 #define hydro_gamma_minus_one 0.33333333333333333f
 #define hydro_one_over_gamma_minus_one 3.f
+#define hydro_gamma_plus_one_over_two_gamma 0.875f
+#define hydro_gamma_minus_one_over_two_gamma 0.125f
+#define hydro_gamma_minus_one_over_gamma_plus_one 0.142857143f
+#define hydro_two_over_gamma_plus_one 0.857142857f
+#define hydro_two_over_gamma_minus_one 6.f
+#define hydro_gamma_minus_one_over_two 0.166666666666666666f
+#define hydro_two_gamma_over_gamma_minus_one 8.f
+#define hydro_one_over_gamma 0.75f
 
 #elif defined(HYDRO_GAMMA_2_1)
 
 #define hydro_gamma 2.f
 #define hydro_gamma_minus_one 1.f
 #define hydro_one_over_gamma_minus_one 1.f
+#define hydro_gamma_plus_one_over_two_gamma 0.75f
+#define hydro_gamma_minus_one_over_two_gamma 0.25f
+#define hydro_gamma_minus_one_over_gamma_plus_one 0.33333333333333333f
+#define hydro_two_over_gamma_plus_one 0.66666666666666666f
+#define hydro_two_over_gamma_minus_one 2.f
+#define hydro_gamma_minus_one_over_two 0.5f
+#define hydro_two_gamma_over_gamma_minus_one 4.f
+#define hydro_one_over_gamma 0.5f
+
+#else
+
+#error "An adiabatic index needs to be chosen in const.h !"
 
 #endif
 
@@ -63,6 +113,10 @@ __attribute__((always_inline)) INLINE static float pow_gamma(float x) {
   const float cbrt = cbrtf(x); /* x^(1/3) */
   return cbrt * cbrt * x;      /* x^(5/3) */
 
+#elif defined(HYDRO_GAMMA_7_5)
+
+  return powf(x, 1.4f); /* x^(7/5) */
+
 #elif defined(HYDRO_GAMMA_4_3)
 
   return cbrtf(x) * x; /* x^(4/3) */
@@ -93,6 +147,10 @@ __attribute__((always_inline)) INLINE static float pow_gamma_minus_one(
   const float cbrt = cbrtf(x); /* x^(1/3) */
   return cbrt * cbrt;          /* x^(2/3) */
 
+#elif defined(HYDRO_GAMMA_7_5)
+
+  return powf(x, 0.4f); /* x^(2/5) */
+
 #elif defined(HYDRO_GAMMA_4_3)
 
   return cbrtf(x); /* x^(1/3) */
@@ -120,8 +178,12 @@ __attribute__((always_inline)) INLINE static float pow_minus_gamma_minus_one(
 
 #if defined(HYDRO_GAMMA_5_3)
 
-  const float inv_cbrt = 1.f / cbrtf(x); /* x^(-1/3) */
-  return inv_cbrt * inv_cbrt;            /* x^(-2/3) */
+  const float cbrt_inv = 1.f / cbrtf(x); /* x^(-1/3) */
+  return cbrt_inv * cbrt_inv;            /* x^(-2/3) */
+
+#elif defined(HYDRO_GAMMA_7_5)
+
+  return powf(x, -0.4f); /* x^(-2/5) */
 
 #elif defined(HYDRO_GAMMA_4_3)
 
@@ -139,4 +201,235 @@ __attribute__((always_inline)) INLINE static float pow_minus_gamma_minus_one(
 #endif
 }
 
+/**
+ * @brief Returns one over the argument to the power given by the adiabatic
+ * index
+ *
+ * Computes \f$x^{-\gamma}\f$.
+ *
+ * @param x Argument
+ * @return One over the argument to the power given by the adiabatic index
+ */
+__attribute__((always_inline)) INLINE static float pow_minus_gamma(float x) {
+
+#if defined(HYDRO_GAMMA_5_3)
+
+  const float cbrt_inv = 1.f / cbrtf(x);       /* x^(-1/3) */
+  const float cbrt_inv2 = cbrt_inv * cbrt_inv; /* x^(-2/3) */
+  return cbrt_inv * cbrt_inv2 * cbrt_inv2;     /* x^(-5/3) */
+
+#elif defined(HYDRO_GAMMA_7_5)
+
+  return powf(x, -1.4f); /* x^(-7/5) */
+
+#elif defined(HYDRO_GAMMA_4_3)
+
+  const float cbrt_inv = 1.f / cbrtf(x);       /* x^(-1/3) */
+  const float cbrt_inv2 = cbrt_inv * cbrt_inv; /* x^(-2/3) */
+  return cbrt_inv2 * cbrt_inv2;                /* x^(-4/3) */
+
+#elif defined(HYDRO_GAMMA_2_1)
+
+  const float inv = 1.f / x;
+  return inv * inv;
+
+#else
+
+  error("The adiabatic index is not defined !");
+  return 0.f;
+
+#endif
+}
+
+/**
+ * @brief Return the argument to the power given by two divided by the adiabatic
+ * index minus one
+ *
+ * Computes \f$x^{\frac{2}{\gamma - 1}}\f$.
+ *
+ * @param x Argument
+ * @return Argument to the power two divided by the adiabatic index minus one
+ */
+__attribute__((always_inline)) INLINE static float pow_two_over_gamma_minus_one(
+    float x) {
+
+#if defined(HYDRO_GAMMA_5_3)
+
+  return x * x * x; /* x^3 */
+
+#elif defined(HYDRO_GAMMA_7_5)
+
+  const float x2 = x * x;
+  const float x3 = x2 * x;
+  return x2 * x3;
+
+#elif defined(HYDRO_GAMMA_4_3)
+
+  const float x3 = x * x * x; /* x^3 */
+  return x3 * x3;             /* x^6 */
+
+#elif defined(HYDRO_GAMMA_2_1)
+
+  return x * x; /* x^2 */
+
+#else
+
+  error("The adiabatic index is not defined !");
+  return 0.f;
+
+#endif
+}
+
+/**
+ * @brief Return the argument to the power given by two times the adiabatic
+ * index divided by the adiabatic index minus one
+ *
+ * Computes \f$x^{\frac{2\gamma}{\gamma - 1}}\f$.
+ *
+ * @param x Argument
+ * @return Argument to the power two times the adiabatic index divided by the
+ * adiabatic index minus one
+ */
+__attribute__((always_inline)) INLINE static float
+pow_two_gamma_over_gamma_minus_one(float x) {
+
+#if defined(HYDRO_GAMMA_5_3)
+
+  const float x2 = x * x;
+  const float x3 = x2 * x;
+  return x2 * x3;
+
+#elif defined(HYDRO_GAMMA_7_5)
+
+  const float x2 = x * x;
+  const float x4 = x2 * x2;
+  return x4 * x2 * x;
+
+#elif defined(HYDRO_GAMMA_4_3)
+
+  const float x2 = x * x;
+  const float x4 = x2 * x2;
+  return x4 * x4; /* x^8 */
+
+#elif defined(HYDRO_GAMMA_2_1)
+
+  const float x2 = x * x;
+  return x2 * x2; /* x^4 */
+
+#else
+
+  error("The adiabatic index is not defined !");
+  return 0.f;
+
+#endif
+}
+
+/**
+ * @brief Return the argument to the power given by the adiabatic index minus
+ * one  divided by two times the adiabatic index
+ *
+ * Computes \f$x^{\frac{\gamma - 1}{2\gamma}}\f$.
+ *
+ * @param x Argument
+ * @return Argument to the power the adiabatic index minus one divided by two
+ * times the adiabatic index
+ */
+__attribute__((always_inline)) INLINE static float
+pow_gamma_minus_one_over_two_gamma(float x) {
+
+#if defined(HYDRO_GAMMA_5_3)
+
+  return powf(x, 0.2f); /* x^0.2 */
+
+#elif defined(HYDRO_GAMMA_7_5)
+
+  return powf(x, hydro_gamma_minus_one_over_two_gamma);
+
+#elif defined(HYDRO_GAMMA_4_3)
+
+  return powf(x, 0.125f); /* x^0.125 */
+
+#elif defined(HYDRO_GAMMA_2_1)
+
+  return powf(x, 0.25f); /* x^0.25 */
+
+#else
+
+  error("The adiabatic index is not defined !");
+  return 0.f;
+
+#endif
+}
+
+/**
+ * @brief Return the inverse argument to the power given by the adiabatic index
+ * plus one divided by two times the adiabatic index
+ *
+ * Computes \f$x^{-\frac{\gamma + 1}{2\gamma}}\f$.
+ *
+ * @param x Argument
+ * @return Inverse argument to the power the adiabatic index plus one divided by
+ * two times the adiabatic index
+ */
+__attribute__((always_inline)) INLINE static float
+pow_minus_gamma_plus_one_over_two_gamma(float x) {
+
+#if defined(HYDRO_GAMMA_5_3)
+
+  return powf(x, -0.8f); /* x^-0.8 */
+
+#elif defined(HYDRO_GAMMA_7_5)
+
+  return powf(x, -hydro_gamma_plus_one_over_two_gamma);
+
+#elif defined(HYDRO_GAMMA_4_3)
+
+  return powf(x, -0.875f); /* x^-0.875 */
+
+#elif defined(HYDRO_GAMMA_2_1)
+
+  return powf(x, -0.75f); /* x^-0.75 */
+
+#else
+
+  error("The adiabatic index is not defined !");
+  return 0.f;
+
+#endif
+}
+
+/**
+ * @brief Return the argument to the power one over the adiabatic index
+ *
+ * Computes \f$x^{\frac{1}{\gamma}}\f$.
+ *
+ * @param x Argument
+ * @return Argument to the power one over the adiabatic index
+ */
+__attribute__((always_inline)) INLINE static float pow_one_over_gamma(float x) {
+
+#if defined(HYDRO_GAMMA_5_3)
+
+  return powf(x, 0.6f); /* x^(3/5) */
+
+#elif defined(HYDRO_GAMMA_7_5)
+
+  return powf(x, hydro_one_over_gamma);
+
+#elif defined(HYDRO_GAMMA_4_3)
+
+  return powf(x, 0.75f); /* x^(3/4) */
+
+#elif defined(HYDRO_GAMMA_2_1)
+
+  return sqrtf(x); /* x^(1/2) */
+
+#else
+
+  error("The adiabatic index is not defined !");
+  return 0.f;
+
+#endif
+}
+
 #endif /* SWIFT_ADIABATIC_INDEX_H */
diff --git a/src/atomic.h b/src/atomic.h
index 0b87a0f77e17bafc64a2a59b3c70bda782fc14d4..be24f96e5a9d2e955132f0d6d34bdfa58bc1649c 100644
--- a/src/atomic.h
+++ b/src/atomic.h
@@ -23,6 +23,7 @@
 #include "inline.h"
 
 #define atomic_add(v, i) __sync_fetch_and_add(v, i)
+#define atomic_or(v, i) __sync_fetch_and_or(v, i)
 #define atomic_inc(v) atomic_add(v, 1)
 #define atomic_dec(v) atomic_add(v, -1)
 #define atomic_cas(v, o, n) __sync_val_compare_and_swap(v, o, n)
diff --git a/src/cell.c b/src/cell.c
index 7df55ce04ca739da2de6e6061048ad1fe3998a1e..6e0d13edcb92ee07334d151de064ed4fbec8c5a8 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -63,7 +63,6 @@ int cell_next_tag = 0;
  *
  * @param c The #cell.
  */
-
 int cell_getsize(struct cell *c) {
 
   /* Number of cells in this subtree. */
@@ -87,9 +86,10 @@ int cell_getsize(struct cell *c) {
  *
  * @return The number of cells created.
  */
-
 int cell_unpack(struct pcell *pc, struct cell *c, struct space *s) {
 
+#ifdef WITH_MPI
+
   /* Unpack the current pcell. */
   c->h_max = pc->h_max;
   c->ti_end_min = pc->ti_end_min;
@@ -130,6 +130,11 @@ int cell_unpack(struct pcell *pc, struct cell *c, struct space *s) {
   /* Return the total number of unpacked cells. */
   c->pcell_size = count;
   return count;
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+  return 0;
+#endif
 }
 
 /**
@@ -140,7 +145,6 @@ int cell_unpack(struct pcell *pc, struct cell *c, struct space *s) {
  *
  * @return The number of particles linked.
  */
-
 int cell_link_parts(struct cell *c, struct part *parts) {
 
   c->parts = parts;
@@ -166,7 +170,6 @@ int cell_link_parts(struct cell *c, struct part *parts) {
  *
  * @return The number of particles linked.
  */
-
 int cell_link_gparts(struct cell *c, struct gpart *gparts) {
 
   c->gparts = gparts;
@@ -193,9 +196,10 @@ int cell_link_gparts(struct cell *c, struct gpart *gparts) {
  *
  * @return The number of packed cells.
  */
-
 int cell_pack(struct cell *c, struct pcell *pc) {
 
+#ifdef WITH_MPI
+
   /* Start by packing the data of the current cell. */
   pc->h_max = c->h_max;
   pc->ti_end_min = c->ti_end_min;
@@ -216,10 +220,25 @@ int cell_pack(struct cell *c, struct pcell *pc) {
   /* Return the number of packed cells used. */
   c->pcell_size = count;
   return count;
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+  return 0;
+#endif
 }
 
+/**
+ * @brief Pack the time information of the given cell and all it's sub-cells.
+ *
+ * @param c The #cell.
+ * @param ti_ends (output) The time information we pack into
+ *
+ * @return The number of packed cells.
+ */
 int cell_pack_ti_ends(struct cell *c, int *ti_ends) {
 
+#ifdef WITH_MPI
+
   /* Pack this cell's data. */
   ti_ends[0] = c->ti_end_min;
 
@@ -232,10 +251,25 @@ int cell_pack_ti_ends(struct cell *c, int *ti_ends) {
 
   /* Return the number of packed values. */
   return count;
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+  return 0;
+#endif
 }
 
+/**
+ * @brief Unpack the time information of a given cell and its sub-cells.
+ *
+ * @param c The #cell
+ * @param ti_ends The time information to unpack
+ *
+ * @return The number of cells created.
+ */
 int cell_unpack_ti_ends(struct cell *c, int *ti_ends) {
 
+#ifdef WITH_MPI
+
   /* Unpack this cell's data. */
   c->ti_end_min = ti_ends[0];
 
@@ -248,14 +282,19 @@ int cell_unpack_ti_ends(struct cell *c, int *ti_ends) {
 
   /* Return the number of packed values. */
   return count;
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+  return 0;
+#endif
 }
 
 /**
- * @brief Lock a cell and hold its parents.
+ * @brief Lock a cell for access to its array of #part and hold its parents.
  *
  * @param c The #cell.
+ * @return 0 on success, 1 on failure
  */
-
 int cell_locktree(struct cell *c) {
 
   TIMER_TIC
@@ -314,6 +353,12 @@ int cell_locktree(struct cell *c) {
   }
 }
 
+/**
+ * @brief Lock a cell for access to its array of #gpart and hold its parents.
+ *
+ * @param c The #cell.
+ * @return 0 on success, 1 on failure
+ */
 int cell_glocktree(struct cell *c) {
 
   TIMER_TIC
@@ -373,11 +418,10 @@ int cell_glocktree(struct cell *c) {
 }
 
 /**
- * @brief Unlock a cell's parents.
+ * @brief Unlock a cell's parents for access to #part array.
  *
  * @param c The #cell.
  */
-
 void cell_unlocktree(struct cell *c) {
 
   TIMER_TIC
@@ -392,6 +436,11 @@ void cell_unlocktree(struct cell *c) {
   TIMER_TOC(timer_locktree);
 }
 
+/**
+ * @brief Unlock a cell's parents for access to #gpart array.
+ *
+ * @param c The #cell.
+ */
 void cell_gunlocktree(struct cell *c) {
 
   TIMER_TIC
@@ -413,7 +462,6 @@ void cell_gunlocktree(struct cell *c) {
  * @param parts_offset Offset of the cell parts array relative to the
  *        space's parts array, i.e. c->parts - s->parts.
  */
-
 void cell_split(struct cell *c, ptrdiff_t parts_offset) {
 
   int i, j;
@@ -630,57 +678,9 @@ void cell_split(struct cell *c, ptrdiff_t parts_offset) {
   part_relink_parts(gparts, gcount, parts - parts_offset);
 }
 
-/**
- * @brief Initialises all particles to a valid state even if the ICs were stupid
- *
- * @param c Cell to act upon
- * @param data Unused parameter
- */
-void cell_init_parts(struct cell *c, void *data) {
-
-  struct part *restrict p = c->parts;
-  struct xpart *restrict xp = c->xparts;
-  const size_t count = c->count;
-
-  for (size_t i = 0; i < count; ++i) {
-    p[i].ti_begin = 0;
-    p[i].ti_end = 0;
-    xp[i].v_full[0] = p[i].v[0];
-    xp[i].v_full[1] = p[i].v[1];
-    xp[i].v_full[2] = p[i].v[2];
-    hydro_first_init_part(&p[i], &xp[i]);
-    hydro_init_part(&p[i]);
-    hydro_reset_acceleration(&p[i]);
-  }
-  c->ti_end_min = 0;
-  c->ti_end_max = 0;
-}
-
-/**
- * @brief Initialises all g-particles to a valid state even if the ICs were
- *stupid
- *
- * @param c Cell to act upon
- * @param data Unused parameter
- */
-void cell_init_gparts(struct cell *c, void *data) {
-
-  struct gpart *restrict gp = c->gparts;
-  const size_t gcount = c->gcount;
-
-  for (size_t i = 0; i < gcount; ++i) {
-    gp[i].ti_begin = 0;
-    gp[i].ti_end = 0;
-    gravity_first_init_gpart(&gp[i]);
-    gravity_init_gpart(&gp[i]);
-  }
-  c->ti_end_min = 0;
-  c->ti_end_max = 0;
-}
-
 /**
  * @brief Converts hydro quantities to a valid state after the initial density
- *calculation
+ * calculation
  *
  * @param c Cell to act upon
  * @param data Unused parameter
@@ -704,12 +704,14 @@ void cell_clean_links(struct cell *c, void *data) {
   c->density = NULL;
   c->nr_density = 0;
 
+  c->gradient = NULL;
+  c->nr_gradient = 0;
+
   c->force = NULL;
   c->nr_force = 0;
 }
 
 /**
-<<<<<<< HEAD
  * @brief Checks whether the cells are direct neighbours ot not. Both cells have
  * to be of the same size
  *
@@ -794,8 +796,10 @@ void cell_check_multipole(struct cell *c, void *data) {
   }
 }
 
-/*
- * @brief Frees up the memory allocated for this #cell
+/**
+ * @brief Frees up the memory allocated for this #cell.
+ *
+ * @param c The #cell.
  */
 void cell_clean(struct cell *c) {
 
@@ -805,3 +809,32 @@ void cell_clean(struct cell *c) {
   for (int k = 0; k < 8; k++)
     if (c->progeny[k]) cell_clean(c->progeny[k]);
 }
+
+/**
+ * @brief Checks whether a given cell needs drifting or not.
+ *
+ * @param c the #cell.
+ * @param ti_current The current time on the integer time-line.
+ *
+ * @return 1 If the cell needs drifting, 0 otherwise.
+ */
+int cell_is_drift_needed(struct cell *c, int ti_current) {
+
+  /* Do we have at least one active particle in the cell ?*/
+  if (c->ti_end_min == ti_current) return 1;
+
+  /* Loop over the pair tasks that involve this cell */
+  for (struct link *l = c->density; l != NULL; l = l->next) {
+
+    if (l->t->type != task_type_pair && l->t->type != task_type_sub_pair)
+      continue;
+
+    /* Does the other cell in the pair have an active particle ? */
+    if ((l->t->ci == c && l->t->cj->ti_end_min == ti_current) ||
+        (l->t->cj == c && l->t->ci->ti_end_min == ti_current))
+      return 1;
+  }
+
+  /* No neighbouring cell has active particles. Drift not necessary */
+  return 0;
+}
diff --git a/src/cell.h b/src/cell.h
index 61f0b0550a0d888aa5361d12c90fd59586841a03..4abf5cfaa6b02486d5ac74aabcb4199f4a1a2dc5 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -23,6 +23,9 @@
 #ifndef SWIFT_CELL_H
 #define SWIFT_CELL_H
 
+/* Config parameters. */
+#include "../config.h"
+
 /* Includes. */
 #include <stddef.h>
 
@@ -41,9 +44,21 @@ struct space;
  * The maximum was lowered by a further factor of 2 to be on the safe side.*/
 #define cell_max_tag (1 << 29)
 
+#define cell_align 32
+
 /* Global variables. */
 extern int cell_next_tag;
 
+/* Mini struct to link cells to tasks. Used as a linked list. */
+struct link {
+
+  /* The task pointer. */
+  struct task *t;
+
+  /* The next pointer. */
+  struct link *next;
+};
+
 /* Packed cell. */
 struct pcell {
 
@@ -70,19 +85,22 @@ struct cell {
   /* The cell dimensions. */
   double width[3];
 
-  /* Max radii in this cell. */
+  /* Max smoothing length in this cell. */
   double h_max;
 
   /* Minimum and maximum end of time step in this cell. */
   int ti_end_min, ti_end_max;
 
+  /* Last time the cell's content was drifted forward in time. */
+  int ti_old;
+
   /* Minimum dimension, i.e. smallest edge of this cell. */
   float dmin;
 
   /* Maximum slack allowed for particle movement. */
   float slack;
 
-  /* Maximum particle movement in this cell. */
+  /* Maximum particle movement in this cell since last construction. */
   float dx_max;
 
   /* The depth of this cell in the tree. */
@@ -101,8 +119,8 @@ struct cell {
   struct gpart *gparts;
 
   /* Pointers for the sorted indices. */
-  struct entry *sort, *gsort;
-  unsigned int sorted, gsorted;
+  struct entry *sort;
+  unsigned int sorted;
 
   /* Pointers to the next level of cells. */
   struct cell *progeny[8];
@@ -114,21 +132,25 @@ struct cell {
   struct cell *super;
 
   /* The task computing this cell's sorts. */
-  struct task *sorts, *gsorts;
-  int sortsize, gsortsize;
+  struct task *sorts;
+  int sortsize;
 
   /* The tasks computing this cell's density. */
-  struct link *density, *force, *grav;
-  int nr_density, nr_force, nr_grav;
+  struct link *density, *gradient, *force, *grav;
+  int nr_density, nr_gradient, nr_force, nr_grav;
 
   /* The hierarchical tasks. */
-  struct task *ghost, *init, *drift, *kick;
+  struct task *extra_ghost, *ghost, *init, *kick;
+
+#ifdef WITH_MPI
 
   /* Task receiving data. */
-  struct task *recv_xv, *recv_rho, *recv_ti;
+  struct task *recv_xv, *recv_rho, *recv_gradient, *recv_ti;
 
   /* Task send data. */
-  struct link *send_xv, *send_rho, *send_ti;
+  struct link *send_xv, *send_rho, *send_gradient, *send_ti;
+
+#endif
 
   /* Tasks for gravity tree. */
   struct task *grav_up, *grav_down;
@@ -163,9 +185,14 @@ struct cell {
   /* Linking pointer for "memory management". */
   struct cell *next;
 
+  /* This cell's multipole. */
+  struct multipole multipole;
+
   /* ID of the node this cell lives on. */
   int nodeID;
 
+#ifdef WITH_MPI
+
   /* Bit mask of the proxies this cell is registered with. */
   unsigned long long int sendto;
 
@@ -174,10 +201,9 @@ struct cell {
   int pcell_size;
   int tag;
 
-  /* This cell's multipole. */
-  struct multipole multipole;
+#endif
 
-} __attribute__((aligned(64)));
+} __attribute__((aligned(cell_align)));
 
 /* Convert cell location to ID. */
 #define cell_getid(cdim, i, j, k) \
@@ -196,13 +222,12 @@ int cell_unpack_ti_ends(struct cell *c, int *ti_ends);
 int cell_getsize(struct cell *c);
 int cell_link_parts(struct cell *c, struct part *parts);
 int cell_link_gparts(struct cell *c, struct gpart *gparts);
-void cell_init_parts(struct cell *c, void *data);
-void cell_init_gparts(struct cell *c, void *data);
 void cell_convert_hydro(struct cell *c, void *data);
 void cell_clean_links(struct cell *c, void *data);
 int cell_are_neighbours(const struct cell *restrict ci,
                         const struct cell *restrict cj);
 void cell_check_multipole(struct cell *c, void *data);
 void cell_clean(struct cell *c);
+int cell_is_drift_needed(struct cell *c, int ti_current);
 
 #endif /* SWIFT_CELL_H */
diff --git a/src/common_io.c b/src/common_io.c
index 3c001d9da106a46ef5033c8cdec9346d68c54ecd..37e2837fbaeee87916ddea9264439c824149479c 100644
--- a/src/common_io.c
+++ b/src/common_io.c
@@ -42,6 +42,7 @@
 /* Local includes. */
 #include "const.h"
 #include "error.h"
+#include "hydro.h"
 #include "kernel_hydro.h"
 #include "part.h"
 #include "units.h"
@@ -515,13 +516,13 @@ void writeXMFgroupheader(FILE* xmfFile, char* hdfFileName, size_t N,
   fprintf(xmfFile, "\n<Grid Name=\"%s\" GridType=\"Uniform\">\n",
           particle_type_names[ptype]);
   fprintf(xmfFile,
-          "<Topology TopologyType=\"Polyvertex\" Dimensions=\"%zi\"/>\n", N);
+          "<Topology TopologyType=\"Polyvertex\" Dimensions=\"%zu\"/>\n", N);
   fprintf(xmfFile, "<Geometry GeometryType=\"XYZ\">\n");
   fprintf(xmfFile,
-          "<DataItem Dimensions=\"%zi 3\" NumberType=\"Double\" "
+          "<DataItem Dimensions=\"%zu 3\" NumberType=\"Double\" "
           "Precision=\"8\" "
           "Format=\"HDF\">%s:/PartType%d/Coordinates</DataItem>\n",
-          N, hdfFileName, ptype);
+          N, hdfFileName, (int)ptype);
   fprintf(xmfFile,
           "</Geometry>\n <!-- Done geometry for %s, start of particle fields "
           "list -->\n",
@@ -555,12 +556,12 @@ void writeXMFline(FILE* xmfFile, const char* fileName,
           name, dim == 1 ? "Scalar" : "Vector");
   if (dim == 1)
     fprintf(xmfFile,
-            "<DataItem Dimensions=\"%zi\" NumberType=\"Double\" "
+            "<DataItem Dimensions=\"%zu\" NumberType=\"Double\" "
             "Precision=\"%d\" Format=\"HDF\">%s:%s/%s</DataItem>\n",
             N, type == FLOAT ? 4 : 8, fileName, partTypeGroupName, name);
   else
     fprintf(xmfFile,
-            "<DataItem Dimensions=\"%zi %d\" NumberType=\"Double\" "
+            "<DataItem Dimensions=\"%zu %d\" NumberType=\"Double\" "
             "Precision=\"%d\" Format=\"HDF\">%s:%s/%s</DataItem>\n",
             N, dim, type == FLOAT ? 4 : 8, fileName, partTypeGroupName, name);
   fprintf(xmfFile, "</Attribute>\n");
@@ -582,7 +583,7 @@ void prepare_dm_gparts(struct gpart* const gparts, size_t Ndm) {
   for (size_t i = 0; i < Ndm; ++i) {
     /* 0 or negative ids are not allowed */
     if (gparts[i].id_or_neg_offset <= 0)
-      error("0 or negative ID for DM particle %zd: ID=%lld", i,
+      error("0 or negative ID for DM particle %zu: ID=%lld", i,
             gparts[i].id_or_neg_offset);
   }
 }
@@ -614,7 +615,7 @@ void duplicate_hydro_gparts(struct part* const parts,
     gparts[i + Ndm].v_full[1] = parts[i].v[1];
     gparts[i + Ndm].v_full[2] = parts[i].v[2];
 
-    gparts[i + Ndm].mass = parts[i].mass;
+    gparts[i + Ndm].mass = hydro_get_mass(&parts[i]);
 
     /* Link the particles */
     gparts[i + Ndm].id_or_neg_offset = -i;
@@ -650,7 +651,7 @@ void collect_dm_gparts(const struct gpart* const gparts, size_t Ntot,
 
   /* Check that everything is fine */
   if (count != Ndm)
-    error("Collected the wrong number of dm particles (%zd vs. %zd expected)",
+    error("Collected the wrong number of dm particles (%zu vs. %zu expected)",
           count, Ndm);
 }
 
diff --git a/src/const.h b/src/const.h
index add857140b0dbdae06b50baca6655accf2d7324a..3c2ef65219d57559284ba11b189daef18de0958f 100644
--- a/src/const.h
+++ b/src/const.h
@@ -36,11 +36,24 @@
 /* Time integration constants. */
 #define const_max_u_change 0.1f
 
+/* Thermal energy per unit mass used as a constant for the isothermal EoS */
+#define const_isothermal_internal_energy 20.2615290634f
+
+/* Dimensionality of the problem */
+#define HYDRO_DIMENSION_3D
+//#define HYDRO_DIMENSION_2D
+//#define HYDRO_DIMENSION_1D
+
 /* Hydrodynamical adiabatic index. */
 #define HYDRO_GAMMA_5_3
+//#define HYDRO_GAMMA_7_5
 //#define HYDRO_GAMMA_4_3
 //#define HYDRO_GAMMA_2_1
 
+/* Equation of state choice */
+#define EOS_IDEAL_GAS
+//#define EOS_ISOTHERMAL_GAS
+
 /* Kernel function to use */
 #define CUBIC_SPLINE_KERNEL
 //#define QUARTIC_SPLINE_KERNEL
@@ -53,6 +66,22 @@
 //#define MINIMAL_SPH
 #define GADGET2_SPH
 //#define DEFAULT_SPH
+//#define GIZMO_SPH
+
+/* Riemann solver to use (GIZMO_SPH only) */
+#define RIEMANN_SOLVER_EXACT
+//#define RIEMANN_SOLVER_TRRS
+//#define RIEMANN_SOLVER_HLLC
+
+/* Type of gradients to use (GIZMO_SPH only) */
+/* If no option is chosen, no gradients are used (first order scheme) */
+//#define GRADIENTS_SPH
+#define GRADIENTS_GIZMO
+
+/* Types of slope limiter to use (GIZMO_SPH only) */
+/* Different slope limiters can be combined */
+#define SLOPE_LIMITER_PER_FACE
+#define SLOPE_LIMITER_CELL_WIDE
 
 /* Self gravity stuff. */
 #define const_gravity_multipole_order 2
@@ -63,6 +92,7 @@
 /* External gravity properties */
 #define EXTERNAL_POTENTIAL_POINTMASS
 //#define EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL
+//#define EXTERNAL_POTENTIAL_DISK_PATCH
 
 /* Cooling properties */
 //#define CONST_COOLING
diff --git a/src/debug.c b/src/debug.c
index 487fd53e74399ef7bd1802704adbf84bbc3dc0a3..15354b7d419544a8456543b79c38235eb3b68b2c 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -24,17 +24,20 @@
 #include "../config.h"
 
 /* Some standard headers. */
+#include <float.h>
 #include <stdio.h>
 
 /* This object's header. */
 #include "debug.h"
 
 /* Local includes. */
-#include "config.h"
+#include "cell.h"
 #include "const.h"
+#include "engine.h"
 #include "hydro.h"
 #include "inline.h"
 #include "part.h"
+#include "space.h"
 
 /* Import the right hydro definition */
 #if defined(MINIMAL_SPH)
@@ -43,6 +46,8 @@
 #include "./hydro/Gadget2/hydro_debug.h"
 #elif defined(DEFAULT_SPH)
 #include "./hydro/Default/hydro_debug.h"
+#elif defined(GIZMO_SPH)
+#include "./hydro/Gizmo/hydro_debug.h"
 #else
 #error "Invalid choice of SPH variant"
 #endif
@@ -68,7 +73,7 @@ void printParticle(const struct part *parts, struct xpart *xparts,
   /* Look for the particle. */
   for (size_t i = 0; i < N; i++)
     if (parts[i].id == id) {
-      printf("## Particle[%zd]:\n id=%lld ", i, parts[i].id);
+      printf("## Particle[%zu]:\n id=%lld ", i, parts[i].id);
       hydro_debug_particle(&parts[i], &xparts[i]);
       found = 1;
       break;
@@ -97,13 +102,13 @@ void printgParticle(const struct gpart *gparts, const struct part *parts,
   /* Look for the particle. */
   for (size_t i = 0; i < N; i++)
     if (gparts[i].id_or_neg_offset == id) {
-      printf("## gParticle[%zd] (DM) :\n id=%lld", i, id);
+      printf("## gParticle[%zu] (DM) :\n id=%lld", i, id);
       gravity_debug_particle(&gparts[i]);
       found = 1;
       break;
     } else if (gparts[i].id_or_neg_offset < 0 &&
                parts[-gparts[i].id_or_neg_offset].id == id) {
-      printf("## gParticle[%zd] (hydro) :\n id=%lld", i, id);
+      printf("## gParticle[%zu] (hydro) :\n id=%lld", i, id);
       gravity_debug_particle(&gparts[i]);
       found = 1;
       break;
@@ -137,6 +142,54 @@ void printgParticle_single(struct gpart *gp) {
   printf("\n");
 }
 
+/**
+ * @brief Check that the cells and particles of a space have consistent h_max
+ *        values.
+ *
+ * @param s the space.
+ * @result 1 or 0
+ */
+int checkSpacehmax(struct space *s) {
+
+  /* Loop over local cells. */
+  float cell_h_max = 0.0f;
+  for (int k = 0; k < s->nr_cells; k++) {
+    if (s->cells[k].nodeID == s->e->nodeID && s->cells[k].h_max > cell_h_max) {
+      cell_h_max = s->cells[k].h_max;
+    }
+  }
+
+  /* Now all particles. */
+  float part_h_max = 0.0f;
+  for (size_t k = 0; k < s->nr_parts; k++) {
+    if (s->parts[k].h > part_h_max) {
+      part_h_max = s->parts[k].h;
+    }
+  }
+
+  /*  If within some epsilon we are OK. */
+  if (abs(cell_h_max - part_h_max) <= FLT_EPSILON) return 1;
+
+  /* There is a problem. Hunt it down. */
+  for (int k = 0; k < s->nr_cells; k++) {
+    if (s->cells[k].nodeID == s->e->nodeID) {
+      if (s->cells[k].h_max > part_h_max) {
+        message("cell %d is inconsistent (%f > %f)", k, s->cells[k].h_max,
+                part_h_max);
+      }
+    }
+  }
+
+  for (size_t k = 0; k < s->nr_parts; k++) {
+    if (s->parts[k].h > cell_h_max) {
+      message("part %lld is inconsistent (%f > %f)", s->parts[k].id,
+              s->parts[k].h, cell_h_max);
+    }
+  }
+
+  return 0;
+}
+
 #ifdef HAVE_METIS
 
 /**
diff --git a/src/debug.h b/src/debug.h
index 367241201977d9b79a8c2913dbae5d08f1148529..22b63820745ca7282b7699f0be09e493238d83c2 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -22,6 +22,7 @@
 /* Includes. */
 #include "cell.h"
 #include "part.h"
+#include "space.h"
 
 void printParticle(const struct part *parts, struct xpart *xparts,
                    long long int id, size_t N);
@@ -30,6 +31,8 @@ void printgParticle(const struct gpart *gparts, const struct part *parts,
 void printParticle_single(const struct part *p, const struct xpart *xp);
 void printgParticle_single(struct gpart *gp);
 
+int checkSpacehmax(struct space *s);
+
 #ifdef HAVE_METIS
 #include "metis.h"
 void dumpMETISGraph(const char *prefix, idx_t nvtxs, idx_t ncon, idx_t *xadj,
diff --git a/src/dimension.h b/src/dimension.h
new file mode 100644
index 0000000000000000000000000000000000000000..0fae2c5602b87622ff67f6f5feb325efc6422472
--- /dev/null
+++ b/src/dimension.h
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016   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_DIMENSION_H
+#define SWIFT_DIMENSION_H
+
+/**
+ * @file dimension.h
+ * @brief Defines the dimensionality \f$d\f$ of the problem and (fast)
+ * mathematical functions involving it
+ */
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Local headers. */
+#include "const.h"
+#include "inline.h"
+#include "vector.h"
+
+#include <math.h>
+
+/* First define some constants */
+#if defined(HYDRO_DIMENSION_3D)
+
+#define hydro_dimension 3.f
+#define hydro_dimension_inv 0.3333333333f
+#define hydro_dimention_unit_sphere ((float)(4. * M_PI / 3.))
+
+#elif defined(HYDRO_DIMENSION_2D)
+
+#define hydro_dimension 2.f
+#define hydro_dimension_inv 0.5f
+#define hydro_dimention_unit_sphere ((float)M_PI)
+
+#elif defined(HYDRO_DIMENSION_1D)
+
+#define hydro_dimension 1.f
+#define hydro_dimension_inv 1.f
+#define hydro_dimention_unit_sphere 2.f
+
+#else
+
+#error "A problem dimensionality must be chosen in const.h !"
+
+#endif
+
+/**
+ * @brief Returns the argument to the power given by the dimension
+ *
+ * Computes \f$x^d\f$.
+ */
+__attribute__((always_inline)) INLINE static float pow_dimension(float x) {
+
+#if defined(HYDRO_DIMENSION_3D)
+
+  return x * x * x;
+
+#elif defined(HYDRO_DIMENSION_2D)
+
+  return x * x;
+
+#elif defined(HYDRO_DIMENSION_1D)
+
+  return x;
+
+#else
+
+  error("The dimension is not defined !");
+  return 0.f;
+
+#endif
+}
+
+/**
+ * @brief Returns the argument to the power given by the dimension plus one
+ *
+ * Computes \f$x^{d+1}\f$.
+ */
+__attribute__((always_inline)) INLINE static float pow_dimension_plus_one(
+    float x) {
+
+#if defined(HYDRO_DIMENSION_3D)
+
+  const float x2 = x * x;
+  return x2 * x2;
+
+#elif defined(HYDRO_DIMENSION_2D)
+
+  return x * x * x;
+
+#elif defined(HYDRO_DIMENSION_1D)
+
+  return x * x;
+
+#else
+
+  error("The dimension is not defined !");
+  return 0.f;
+
+#endif
+}
+
+/**
+ * @brief Inverts the given dimension by dimension matrix (in place)
+ *
+ * @param A A 3x3 matrix of which we want to invert the top left dxd part
+ */
+__attribute__((always_inline)) INLINE static void
+invert_dimension_by_dimension_matrix(float A[3][3]) {
+
+#if defined(HYDRO_DIMENSION_3D)
+
+  float detA, Ainv[3][3];
+
+  detA = A[0][0] * A[1][1] * A[2][2] + A[0][1] * A[1][2] * A[2][0] +
+         A[0][2] * A[1][0] * A[2][1] - A[0][2] * A[1][1] * A[2][0] -
+         A[0][1] * A[1][0] * A[2][2] - A[0][0] * A[1][2] * A[2][1];
+
+  if (detA && !isnan(detA)) {
+    Ainv[0][0] = (A[1][1] * A[2][2] - A[1][2] * A[2][1]) / detA;
+    Ainv[0][1] = (A[0][2] * A[2][1] - A[0][1] * A[2][2]) / detA;
+    Ainv[0][2] = (A[0][1] * A[1][2] - A[0][2] * A[1][1]) / detA;
+    Ainv[1][0] = (A[1][2] * A[2][0] - A[1][0] * A[2][2]) / detA;
+    Ainv[1][1] = (A[0][0] * A[2][2] - A[0][2] * A[2][0]) / detA;
+    Ainv[1][2] = (A[0][2] * A[1][0] - A[0][0] * A[1][2]) / detA;
+    Ainv[2][0] = (A[1][0] * A[2][1] - A[1][1] * A[2][0]) / detA;
+    Ainv[2][1] = (A[0][1] * A[2][0] - A[0][0] * A[2][1]) / detA;
+    Ainv[2][2] = (A[0][0] * A[1][1] - A[0][1] * A[1][0]) / detA;
+  } else {
+    Ainv[0][0] = 0.0f;
+    Ainv[0][1] = 0.0f;
+    Ainv[0][2] = 0.0f;
+    Ainv[1][0] = 0.0f;
+    Ainv[1][1] = 0.0f;
+    Ainv[1][2] = 0.0f;
+    Ainv[2][0] = 0.0f;
+    Ainv[2][1] = 0.0f;
+    Ainv[2][2] = 0.0f;
+  }
+
+  A[0][0] = Ainv[0][0];
+  A[0][1] = Ainv[0][1];
+  A[0][2] = Ainv[0][2];
+  A[1][0] = Ainv[1][0];
+  A[1][1] = Ainv[1][1];
+  A[1][2] = Ainv[1][2];
+  A[2][0] = Ainv[2][0];
+  A[2][1] = Ainv[2][1];
+  A[2][2] = Ainv[2][2];
+
+#elif defined(HYDRO_DIMENSION_2D)
+
+  float detA, Ainv[2][2];
+
+  detA = A[0][0] * A[1][1] - A[0][1] * A[1][0];
+
+  if (detA && !isnan(detA)) {
+    Ainv[0][0] = A[1][1] / detA;
+    Ainv[0][1] = -A[0][1] / detA;
+    Ainv[1][0] = -A[1][0] / detA;
+    Ainv[1][1] = A[0][0] / detA;
+  } else {
+    Ainv[0][0] = 0.0f;
+    Ainv[0][1] = 0.0f;
+    Ainv[1][0] = 0.0f;
+    Ainv[1][1] = 0.0f;
+  }
+
+  A[0][0] = Ainv[0][0];
+  A[0][1] = Ainv[0][1];
+  A[1][0] = Ainv[1][0];
+  A[1][1] = Ainv[1][1];
+
+#elif defined(HYDRO_DIMENSION_1D)
+
+  if (A[0][0] && !isnan(A[0][0])) {
+    A[0][0] = 1.0f / A[0][0];
+  } else {
+    A[0][0] = 0.0f;
+  }
+
+#else
+
+  error("The dimension is not defined !");
+
+#endif
+}
+
+/* ------------------------------------------------------------------------- */
+#ifdef WITH_VECTORIZATION
+
+/**
+ * @brief Returns the argument to the power given by the dimension (vector
+ * version)
+ *
+ * Computes \f$x^d\f$.
+ */
+__attribute__((always_inline)) INLINE static vector pow_dimension_vec(
+    vector x) {
+
+#if defined(HYDRO_DIMENSION_3D)
+
+  return (vector)(x.v * x.v * x.v);
+
+#elif defined(HYDRO_DIMENSION_2D)
+
+  return (vector)(x.v * x.v);
+
+#elif defined(HYDRO_DIMENSION_1D)
+
+  return x;
+
+#else
+
+  error("The dimension is not defined !");
+  return vec_set(0.f);
+
+#endif
+}
+
+/**
+ * @brief Returns the argument to the power given by the dimension plus one
+ * (vector version)
+ *
+ * Computes \f$x^{d+1}\f$.
+ */
+__attribute__((always_inline)) INLINE static vector pow_dimension_plus_one_vec(
+    vector x) {
+
+#if defined(HYDRO_DIMENSION_3D)
+
+  const vector x2 = (vector)(x.v * x.v);
+  return (vector)(x2.v * x2.v);
+
+#elif defined(HYDRO_DIMENSION_2D)
+
+  return (vector)(x.v * x.v * x.v);
+
+#elif defined(HYDRO_DIMENSION_1D)
+
+  return (vector)(x.v * x.v);
+
+#else
+
+  error("The dimension is not defined !");
+  return vec_set(0.f);
+
+#endif
+}
+#endif
+
+#endif /* SWIFT_DIMENSION_H */
diff --git a/src/drift.h b/src/drift.h
index a6e347cc337385f89bece53bc477bff7d163c202..bd1b35926740d49a67291ede4676f3387cd66748 100644
--- a/src/drift.h
+++ b/src/drift.h
@@ -25,6 +25,7 @@
 /* Local headers. */
 #include "const.h"
 #include "debug.h"
+#include "dimension.h"
 #include "hydro.h"
 #include "part.h"
 
@@ -64,8 +65,6 @@ __attribute__((always_inline)) INLINE static void drift_gpart(
 __attribute__((always_inline)) INLINE static void drift_part(
     struct part *restrict p, struct xpart *restrict xp, float dt,
     double timeBase, int ti_old, int ti_current) {
-  /* Useful quantity */
-  const float h_inv = 1.0f / p->h;
 
   /* Drift... */
   p->x[0] += xp->v_full[0] * dt;
@@ -77,22 +76,8 @@ __attribute__((always_inline)) INLINE static void drift_part(
   p->v[1] += p->a_hydro[1] * dt;
   p->v[2] += p->a_hydro[2] * dt;
 
-  /* Predict smoothing length */
-  const float w1 = p->force.h_dt * h_inv * dt;
-  if (fabsf(w1) < 0.2f)
-    p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */
-  else
-    p->h *= expf(w1);
-
-  /* Predict density */
-  const float w2 = -3.0f * w1;
-  if (fabsf(w2) < 0.2f)
-    p->rho *= approx_expf(w2); /* 4th order expansion of exp(w) */
-  else
-    p->rho *= expf(w2);
-
   /* Predict the values of the extra fields */
-  hydro_predict_extra(p, xp, ti_old, ti_current, timeBase);
+  hydro_predict_extra(p, xp, dt, ti_old, ti_current, timeBase);
 
   /* Compute offset since last cell construction */
   xp->x_diff[0] -= xp->v_full[0] * dt;
diff --git a/src/engine.c b/src/engine.c
index fab9b4f0845df0d7e12f1efa8b06e576de39a176..946a7b3b1b2450523958f6444f3c02348036517c 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -68,7 +68,7 @@
 #include "units.h"
 #include "version.h"
 
-const char *engine_policy_names[14] = {"none",
+const char *engine_policy_names[15] = {"none",
                                        "rand",
                                        "steal",
                                        "keep",
@@ -81,35 +81,33 @@ const char *engine_policy_names[14] = {"none",
                                        "self_gravity",
                                        "external_gravity",
                                        "cosmology_integration",
-                                       "cooling"};
+                                       "drift_all",
+				       "cooling"};
 
 /** The rank of the engine as a global variable (for messages). */
 int engine_rank;
 
-#ifdef HAVE_SETAFFINITY
-/** The initial affinity of the main thread (set by engin_pin()) */
-static cpu_set_t entry_affinity;
-#endif
-
 /**
  * @brief Link a density/force task to a cell.
  *
  * @param e The #engine.
- * @param l The #link.
+ * @param l A pointer to the #link, will be modified atomically.
  * @param t The #task.
  *
  * @return The new #link pointer.
  */
-struct link *engine_addlink(struct engine *e, struct link *l, struct task *t) {
+void engine_addlink(struct engine *e, struct link **l, struct task *t) {
 
+  /* Get the next free link. */
   const int ind = atomic_inc(&e->nr_links);
   if (ind >= e->size_links) {
     error("Link table overflow.");
   }
   struct link *res = &e->links[ind];
-  res->next = l;
+
+  /* Set it atomically. */
   res->t = t;
-  return res;
+  res->next = atomic_swap(l, res);
 }
 
 /**
@@ -145,11 +143,6 @@ void engine_make_gravity_hierarchical_tasks(struct engine *e, struct cell *c,
         c->init = scheduler_addtask(s, task_type_init, task_subtype_none, 0, 0,
                                     c, NULL, 0);
 
-      /* Add the drift task. */
-      if (c->drift == NULL)
-        c->drift = scheduler_addtask(s, task_type_drift, task_subtype_none, 0,
-                                     0, c, NULL, 0);
-
       /* Add the kick task that matches the policy. */
       if (is_fixdt) {
         if (c->kick == NULL)
@@ -210,11 +203,6 @@ void engine_make_hydro_hierarchical_tasks(struct engine *e, struct cell *c,
         c->init = scheduler_addtask(s, task_type_init, task_subtype_none, 0, 0,
                                     c, NULL, 0);
 
-      /* Add the drift task. */
-      if (c->drift == NULL)
-        c->drift = scheduler_addtask(s, task_type_drift, task_subtype_none, 0,
-                                     0, c, NULL, 0);
-
       /* Add the kick task that matches the policy. */
       if (is_fixdt) {
         if (c->kick == NULL)
@@ -230,9 +218,16 @@ void engine_make_hydro_hierarchical_tasks(struct engine *e, struct cell *c,
       c->ghost = scheduler_addtask(s, task_type_ghost, task_subtype_none, 0, 0,
                                    c, NULL, 0);
 
+#ifdef EXTRA_HYDRO_LOOP
+      /* Generate the extra ghost task. */
+      c->extra_ghost = scheduler_addtask(s, task_type_extra_ghost,
+                                         task_subtype_none, 0, 0, c, NULL, 0);
+#endif
+
       if (is_with_cooling)
-        c->cooling_task = scheduler_addtask(
+        c->cooling = scheduler_addtask(
             s, task_type_cooling, task_subtype_none, 0, 0, c, NULL, 0);
+ 	
     }
   }
 
@@ -311,7 +306,7 @@ void engine_redistribute(struct engine *e) {
                    parts[k].x[2] * iwidth[2]);
 #ifdef SWIFT_DEBUG_CHECKS
     if (cid < 0 || cid >= s->nr_cells)
-      error("Bad cell id %i for part %zi at [%.3e,%.3e,%.3e].", cid, k,
+      error("Bad cell id %i for part %zu at [%.3e,%.3e,%.3e].", cid, k,
             parts[k].x[0], parts[k].x[1], parts[k].x[2]);
 #endif
 
@@ -367,7 +362,7 @@ void engine_redistribute(struct engine *e) {
                    gparts[k].x[2] * iwidth[2]);
 #ifdef SWIFT_DEBUG_CHECKS
     if (cid < 0 || cid >= s->nr_cells)
-      error("Bad cell id %i for part %zi at [%.3e,%.3e,%.3e].", cid, k,
+      error("Bad cell id %i for part %zu at [%.3e,%.3e,%.3e].", cid, k,
             gparts[k].x[0], gparts[k].x[1], gparts[k].x[2]);
 #endif
 
@@ -602,7 +597,7 @@ void engine_redistribute(struct engine *e) {
     int my_cells = 0;
     for (int k = 0; k < nr_cells; k++)
       if (cells[k].nodeID == nodeID) my_cells += 1;
-    message("node %i now has %zi parts and %zi gparts in %i cells.", nodeID,
+    message("node %i now has %zu parts and %zu gparts in %i cells.", nodeID,
             nr_parts, nr_gparts, my_cells);
   }
 
@@ -690,11 +685,12 @@ void engine_addtasks_grav(struct engine *e, struct cell *c, struct task *up,
  * @param cj Dummy cell containing the nodeID of the receiving node.
  * @param t_xv The send_xv #task, if it has already been created.
  * @param t_rho The send_rho #task, if it has already been created.
+ * @param t_gradient The send_gradient #task, if already created.
  * @param t_ti The send_ti #task, if required and has already been created.
  */
 void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj,
                           struct task *t_xv, struct task *t_rho,
-                          struct task *t_ti) {
+                          struct task *t_gradient, struct task *t_ti) {
 
 #ifdef WITH_MPI
   struct link *l = NULL;
@@ -713,37 +709,62 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj,
     /* Create the tasks and their dependencies? */
     if (t_xv == NULL) {
       t_xv = scheduler_addtask(s, task_type_send, task_subtype_none,
-                               3 * ci->tag, 0, ci, cj, 0);
+                               4 * ci->tag, 0, ci, cj, 0);
       t_rho = scheduler_addtask(s, task_type_send, task_subtype_none,
-                                3 * ci->tag + 1, 0, ci, cj, 0);
+                                4 * ci->tag + 1, 0, ci, cj, 0);
       if (!(e->policy & engine_policy_fixdt))
         t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend,
-                                 3 * ci->tag + 2, 0, ci, cj, 0);
+                                 4 * ci->tag + 2, 0, ci, cj, 0);
+#ifdef EXTRA_HYDRO_LOOP
+      t_gradient = scheduler_addtask(s, task_type_send, task_subtype_none,
+                                     4 * ci->tag + 3, 0, ci, cj, 0);
+#endif
+
+#ifdef EXTRA_HYDRO_LOOP
+
+      scheduler_addunlock(s, t_gradient, ci->super->kick);
+
+      scheduler_addunlock(s, ci->super->extra_ghost, t_gradient);
+
+      /* The send_rho task should unlock the super-cell's extra_ghost task. */
+      scheduler_addunlock(s, t_rho, ci->super->extra_ghost);
 
       /* The send_rho task depends on the cell's ghost task. */
       scheduler_addunlock(s, ci->super->ghost, t_rho);
 
+      /* The send_xv task should unlock the super-cell's ghost task. */
+      scheduler_addunlock(s, t_xv, ci->super->ghost);
+
+#else
       /* The send_rho task should unlock the super-cell's kick task. */
       scheduler_addunlock(s, t_rho, ci->super->kick);
 
+      /* The send_rho task depends on the cell's ghost task. */
+      scheduler_addunlock(s, ci->super->ghost, t_rho);
+
       /* The send_xv task should unlock the super-cell's ghost task. */
       scheduler_addunlock(s, t_xv, ci->super->ghost);
+#endif
 
       /* The super-cell's kick task should unlock the send_ti task. */
       if (t_ti != NULL) scheduler_addunlock(s, ci->super->kick, t_ti);
     }
 
     /* Add them to the local cell. */
-    ci->send_xv = engine_addlink(e, ci->send_xv, t_xv);
-    ci->send_rho = engine_addlink(e, ci->send_rho, t_rho);
-    if (t_ti != NULL) ci->send_ti = engine_addlink(e, ci->send_ti, t_ti);
+    engine_addlink(e, &ci->send_xv, t_xv);
+    engine_addlink(e, &ci->send_rho, t_rho);
+#ifdef EXTRA_HYDRO_LOOP
+    engine_addlink(e, &ci->send_gradient, t_gradient);
+#endif
+    if (t_ti != NULL) engine_addlink(e, &ci->send_ti, t_ti);
   }
 
   /* Recurse? */
   if (ci->split)
     for (int k = 0; k < 8; k++)
       if (ci->progeny[k] != NULL)
-        engine_addtasks_send(e, ci->progeny[k], cj, t_xv, t_rho, t_ti);
+        engine_addtasks_send(e, ci->progeny[k], cj, t_xv, t_rho, t_gradient,
+                             t_ti);
 
 #else
   error("SWIFT was not compiled with MPI support.");
@@ -757,10 +778,12 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj,
  * @param c The foreign #cell.
  * @param t_xv The recv_xv #task, if it has already been created.
  * @param t_rho The recv_rho #task, if it has already been created.
+ * @param t_gradient The recv_gradient #task, if it has already been created.
  * @param t_ti The recv_ti #task, if required and has already been created.
  */
 void engine_addtasks_recv(struct engine *e, struct cell *c, struct task *t_xv,
-                          struct task *t_rho, struct task *t_ti) {
+                          struct task *t_rho, struct task *t_gradient,
+                          struct task *t_ti) {
 
 #ifdef WITH_MPI
   struct scheduler *s = &e->sched;
@@ -771,19 +794,39 @@ void engine_addtasks_recv(struct engine *e, struct cell *c, struct task *t_xv,
   if (t_xv == NULL && c->density != NULL) {
 
     /* Create the tasks. */
-    t_xv = scheduler_addtask(s, task_type_recv, task_subtype_none, 3 * c->tag,
+    t_xv = scheduler_addtask(s, task_type_recv, task_subtype_none, 4 * c->tag,
                              0, c, NULL, 0);
     t_rho = scheduler_addtask(s, task_type_recv, task_subtype_none,
-                              3 * c->tag + 1, 0, c, NULL, 0);
+                              4 * c->tag + 1, 0, c, NULL, 0);
     if (!(e->policy & engine_policy_fixdt))
       t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend,
-                               3 * c->tag + 2, 0, c, NULL, 0);
+                               4 * c->tag + 2, 0, c, NULL, 0);
+#ifdef EXTRA_HYDRO_LOOP
+    t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_none,
+                                   4 * c->tag + 3, 0, c, NULL, 0);
+#endif
   }
   c->recv_xv = t_xv;
   c->recv_rho = t_rho;
+  c->recv_gradient = t_gradient;
   c->recv_ti = t_ti;
 
-  /* Add dependencies. */
+/* Add dependencies. */
+#ifdef EXTRA_HYDRO_LOOP
+  for (struct link *l = c->density; l != NULL; l = l->next) {
+    scheduler_addunlock(s, t_xv, l->t);
+    scheduler_addunlock(s, l->t, t_rho);
+  }
+  for (struct link *l = c->gradient; l != NULL; l = l->next) {
+    scheduler_addunlock(s, t_rho, l->t);
+    scheduler_addunlock(s, l->t, t_gradient);
+  }
+  for (struct link *l = c->force; l != NULL; l = l->next) {
+    scheduler_addunlock(s, t_gradient, l->t);
+    if (t_ti != NULL) scheduler_addunlock(s, l->t, t_ti);
+  }
+  if (c->sorts != NULL) scheduler_addunlock(s, t_xv, c->sorts);
+#else
   for (struct link *l = c->density; l != NULL; l = l->next) {
     scheduler_addunlock(s, t_xv, l->t);
     scheduler_addunlock(s, l->t, t_rho);
@@ -793,12 +836,13 @@ void engine_addtasks_recv(struct engine *e, struct cell *c, struct task *t_xv,
     if (t_ti != NULL) scheduler_addunlock(s, l->t, t_ti);
   }
   if (c->sorts != NULL) scheduler_addunlock(s, t_xv, c->sorts);
+#endif
 
   /* Recurse? */
   if (c->split)
     for (int k = 0; k < 8; k++)
       if (c->progeny[k] != NULL)
-        engine_addtasks_recv(e, c->progeny[k], t_xv, t_rho, t_ti);
+        engine_addtasks_recv(e, c->progeny[k], t_xv, t_rho, t_gradient, t_ti);
 
 #else
   error("SWIFT was not compiled with MPI support.");
@@ -892,7 +936,7 @@ void engine_exchange_cells(struct engine *e) {
 
   /* Count the number of particles we need to import and re-allocate
      the buffer if needed. */
-  int count_parts_in = 0, count_gparts_in = 0;
+  size_t count_parts_in = 0, count_gparts_in = 0;
   for (int k = 0; k < nr_proxies; k++)
     for (int j = 0; j < e->proxies[k].nr_cells_in; j++) {
       count_parts_in += e->proxies[k].cells_in[j]->count;
@@ -982,7 +1026,7 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
     if (pid < 0) {
       error(
           "Do not have a proxy for the requested nodeID %i for part with "
-          "id=%llu, x=[%e,%e,%e].",
+          "id=%lld, x=[%e,%e,%e].",
           node_id, s->parts[offset_parts + k].id,
           s->parts[offset_parts + k].x[0], s->parts[offset_parts + k].x[1],
           s->parts[offset_parts + k].x[2]);
@@ -1046,7 +1090,7 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
     count_gparts_in += e->proxies[k].nr_gparts_in;
   }
   if (e->verbose) {
-    message("sent out %zi/%zi parts/gparts, got %i/%i back.", *Npart, *Ngpart,
+    message("sent out %zu/%zu parts/gparts, got %i/%i back.", *Npart, *Ngpart,
             count_parts_in, count_gparts_in);
   }
   if (offset_parts + count_parts_in > s->size_parts) {
@@ -1056,7 +1100,7 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
     struct xpart *xparts_new = NULL;
     if (posix_memalign((void **)&parts_new, part_align,
                        sizeof(struct part) * s->size_parts) != 0 ||
-        posix_memalign((void **)&xparts_new, part_align,
+        posix_memalign((void **)&xparts_new, xpart_align,
                        sizeof(struct xpart) * s->size_parts) != 0)
       error("Failed to allocate new part data.");
     memcpy(parts_new, s->parts, sizeof(struct part) * offset_parts);
@@ -1140,13 +1184,13 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
         reqs_in[pid + 1] == MPI_REQUEST_NULL &&
         reqs_in[pid + 2] == MPI_REQUEST_NULL) {
       /* Copy the particle data to the part/xpart/gpart arrays. */
-      struct proxy *p = &e->proxies[pid / 3];
-      memcpy(&s->parts[offset_parts + count_parts], p->parts_in,
-             sizeof(struct part) * p->nr_parts_in);
-      memcpy(&s->xparts[offset_parts + count_parts], p->xparts_in,
-             sizeof(struct xpart) * p->nr_parts_in);
-      memcpy(&s->gparts[offset_gparts + count_gparts], p->gparts_in,
-             sizeof(struct gpart) * p->nr_gparts_in);
+      struct proxy *prox = &e->proxies[pid / 3];
+      memcpy(&s->parts[offset_parts + count_parts], prox->parts_in,
+             sizeof(struct part) * prox->nr_parts_in);
+      memcpy(&s->xparts[offset_parts + count_parts], prox->xparts_in,
+             sizeof(struct xpart) * prox->nr_parts_in);
+      memcpy(&s->gparts[offset_gparts + count_gparts], prox->gparts_in,
+             sizeof(struct gpart) * prox->nr_gparts_in);
       /* for (int k = offset; k < offset + count; k++)
          message(
             "received particle %lli, x=[%.3e %.3e %.3e], h=%.3e, from node %i.",
@@ -1154,8 +1198,8 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
             s->parts[k].x[2], s->parts[k].h, p->nodeID); */
 
       /* Re-link the gparts. */
-      for (int k = 0; k < p->nr_gparts_in; k++) {
-        struct gpart *gp = &s->gparts[offset_gparts + count_gparts + k];
+      for (int kk = 0; kk < prox->nr_gparts_in; kk++) {
+        struct gpart *gp = &s->gparts[offset_gparts + count_gparts + kk];
         if (gp->id_or_neg_offset <= 0) {
           struct part *p =
               &s->parts[offset_gparts + count_parts - gp->id_or_neg_offset];
@@ -1165,8 +1209,8 @@ void engine_exchange_strays(struct engine *e, size_t offset_parts,
       }
 
       /* Advance the counters. */
-      count_parts += p->nr_parts_in;
-      count_gparts += p->nr_gparts_in;
+      count_parts += prox->nr_parts_in;
+      count_gparts += prox->nr_gparts_in;
     }
   }
 
@@ -1325,12 +1369,11 @@ void engine_make_hydroloop_tasks(struct engine *e) {
 void engine_count_and_link_tasks(struct engine *e) {
 
   struct scheduler *sched = &e->sched;
-  const int nr_tasks = sched->nr_tasks;
 
-  for (int k = 0; k < nr_tasks; k++) {
+  for (int ind = 0; ind < sched->nr_tasks; ind++) {
+
+    struct task *t = &sched->tasks[ind];
 
-    /* Get the current task. */
-    struct task *t = &sched->tasks[k];
     if (t->skip) continue;
 
     /* Link sort tasks together. */
@@ -1345,31 +1388,31 @@ void engine_count_and_link_tasks(struct engine *e) {
     if (t->type == task_type_self) {
       atomic_inc(&t->ci->nr_tasks);
       if (t->subtype == task_subtype_density) {
-        t->ci->density = engine_addlink(e, t->ci->density, t);
+        engine_addlink(e, &t->ci->density, t);
         atomic_inc(&t->ci->nr_density);
       }
     } else if (t->type == task_type_pair) {
       atomic_inc(&t->ci->nr_tasks);
       atomic_inc(&t->cj->nr_tasks);
       if (t->subtype == task_subtype_density) {
-        t->ci->density = engine_addlink(e, t->ci->density, t);
+        engine_addlink(e, &t->ci->density, t);
         atomic_inc(&t->ci->nr_density);
-        t->cj->density = engine_addlink(e, t->cj->density, t);
+        engine_addlink(e, &t->cj->density, t);
         atomic_inc(&t->cj->nr_density);
       }
     } else if (t->type == task_type_sub_self) {
       atomic_inc(&t->ci->nr_tasks);
       if (t->subtype == task_subtype_density) {
-        t->ci->density = engine_addlink(e, t->ci->density, t);
+        engine_addlink(e, &t->ci->density, t);
         atomic_inc(&t->ci->nr_density);
       }
     } else if (t->type == task_type_sub_pair) {
       atomic_inc(&t->ci->nr_tasks);
       atomic_inc(&t->cj->nr_tasks);
       if (t->subtype == task_subtype_density) {
-        t->ci->density = engine_addlink(e, t->ci->density, t);
+        engine_addlink(e, &t->ci->density, t);
         atomic_inc(&t->ci->nr_density);
-        t->cj->density = engine_addlink(e, t->cj->density, t);
+        engine_addlink(e, &t->cj->density, t);
         atomic_inc(&t->cj->nr_density);
       }
     }
@@ -1485,19 +1528,46 @@ void engine_link_gravity_tasks(struct engine *e) {
   }
 }
 
+#ifdef EXTRA_HYDRO_LOOP
+
 /**
  * @brief Creates the dependency network for the hydro tasks of a given cell.
  *
  * @param sched The #scheduler.
  * @param density The density task to link.
+ * @param gradient The gradient task to link.
  * @param force The force task to link.
  * @param c The cell.
  */
 static inline void engine_make_hydro_loops_dependencies(struct scheduler *sched,
                                                         struct task *density,
+                                                        struct task *gradient,
                                                         struct task *force,
                                                         struct cell *c) {
+  /* init --> density loop --> ghost --> gradient loop --> extra_ghost */
+  /* extra_ghost --> force loop --> kick */
+  scheduler_addunlock(sched, c->super->init, density);
+  scheduler_addunlock(sched, density, c->super->ghost);
+  scheduler_addunlock(sched, c->super->ghost, gradient);
+  scheduler_addunlock(sched, gradient, c->super->extra_ghost);
+  scheduler_addunlock(sched, c->super->extra_ghost, force);
+  scheduler_addunlock(sched, force, c->super->kick);
+}
 
+#else
+
+/**
+ * @brief Creates the dependency network for the hydro tasks of a given cell.
+ *
+ * @param sched The #scheduler.
+ * @param density The density task to link.
+ * @param force The force task to link.
+ * @param c The cell.
+ */
+static inline void engine_make_hydro_loops_dependencies(struct scheduler *sched,
+                                                        struct task *density,
+                                                        struct task *force,
+                                                        struct cell *c) {
   /* init --> density loop --> ghost --> force loop --> kick */
   scheduler_addunlock(sched, c->super->init, density);
   scheduler_addunlock(sched, density, c->super->ghost);
@@ -1505,6 +1575,7 @@ static inline void engine_make_hydro_loops_dependencies(struct scheduler *sched,
   scheduler_addunlock(sched, force, c->super->kick);
 }
 
+#endif
 /**
  * @brief Duplicates the first hydro loop and construct all the
  * dependencies for the hydro part
@@ -1520,13 +1591,11 @@ static inline void engine_make_hydro_loops_dependencies(struct scheduler *sched,
 void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
   struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
   const int nr_tasks = sched->nr_tasks;
+  const int nodeID = e->nodeID;
 
-  for (int k = 0; k < nr_tasks; k++) {
-
-    /* Get a pointer to the task. */
-    struct task *t = &sched->tasks[k];
+  for (int ind = 0; ind < nr_tasks; ind++) {
+    struct task *t = &sched->tasks[ind];
 
     /* Skip? */
     if (t->skip) continue;
@@ -1534,29 +1603,76 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
     /* Self-interaction? */
     if (t->type == task_type_self && t->subtype == task_subtype_density) {
 
+#ifdef EXTRA_HYDRO_LOOP
+      /* Start by constructing the task for the second  and third hydro loop */
+      struct task *t2 = scheduler_addtask(
+          sched, task_type_self, task_subtype_gradient, 0, 0, t->ci, NULL, 0);
+      struct task *t3 = scheduler_addtask(
+          sched, task_type_self, task_subtype_force, 0, 0, t->ci, NULL, 0);
+
+      /* Add the link between the new loops and the cell */
+      engine_addlink(e, &t->ci->gradient, t2);
+      atomic_inc(&t->ci->nr_gradient);
+      engine_addlink(e, &t->ci->force, t3);
+      atomic_inc(&t->ci->nr_force);
+
+      /* Now, build all the dependencies for the hydro */
+      engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci);
+
+#else
+
       /* Start by constructing the task for the second hydro loop */
       struct task *t2 = scheduler_addtask(
           sched, task_type_self, task_subtype_force, 0, 0, t->ci, NULL, 0);
 
       /* Add the link between the new loop and the cell */
-      t->ci->force = engine_addlink(e, t->ci->force, t2);
+      engine_addlink(e, &t->ci->force, t2);
       atomic_inc(&t->ci->nr_force);
 
       /* Now, build all the dependencies for the hydro */
       engine_make_hydro_loops_dependencies(sched, t, t2, t->ci);
+#endif
     }
 
     /* Otherwise, pair interaction? */
     else if (t->type == task_type_pair && t->subtype == task_subtype_density) {
 
+#ifdef EXTRA_HYDRO_LOOP
+      /* Start by constructing the task for the second and third hydro loop */
+      struct task *t2 = scheduler_addtask(
+          sched, task_type_pair, task_subtype_gradient, 0, 0, t->ci, t->cj, 0);
+      struct task *t3 = scheduler_addtask(
+          sched, task_type_pair, task_subtype_force, 0, 0, t->ci, t->cj, 0);
+
+      /* Add the link between the new loop and both cells */
+      engine_addlink(e, &t->ci->gradient, t2);
+      atomic_inc(&t->ci->nr_gradient);
+      engine_addlink(e, &t->cj->gradient, t2);
+      atomic_inc(&t->cj->nr_gradient);
+      engine_addlink(e, &t->ci->force, t3);
+      atomic_inc(&t->ci->nr_force);
+      engine_addlink(e, &t->cj->force, t3);
+      atomic_inc(&t->cj->nr_force);
+
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci);
+      }
+      if (t->cj->nodeID == nodeID && t->ci->super != t->cj->super) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->cj);
+      }
+
+#else
+
       /* Start by constructing the task for the second hydro loop */
       struct task *t2 = scheduler_addtask(
           sched, task_type_pair, task_subtype_force, 0, 0, t->ci, t->cj, 0);
 
       /* Add the link between the new loop and both cells */
-      t->ci->force = engine_addlink(e, t->ci->force, t2);
+      engine_addlink(e, &t->ci->force, t2);
       atomic_inc(&t->ci->nr_force);
-      t->cj->force = engine_addlink(e, t->cj->force, t2);
+      engine_addlink(e, &t->cj->force, t2);
       atomic_inc(&t->cj->nr_force);
 
       /* Now, build all the dependencies for the hydro for the cells */
@@ -1567,19 +1683,45 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
       if (t->cj->nodeID == nodeID && t->ci->super != t->cj->super) {
         engine_make_hydro_loops_dependencies(sched, t, t2, t->cj);
       }
+
+#endif
+
     }
 
     /* Otherwise, sub-self interaction? */
     else if (t->type == task_type_sub_self &&
              t->subtype == task_subtype_density) {
 
+#ifdef EXTRA_HYDRO_LOOP
+
+      /* Start by constructing the task for the second and third hydro loop */
+      struct task *t2 =
+          scheduler_addtask(sched, task_type_sub_self, task_subtype_gradient,
+                            t->flags, 0, t->ci, t->cj, 0);
+      struct task *t3 =
+          scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
+                            t->flags, 0, t->ci, t->cj, 0);
+
+      /* Add the link between the new loop and the cell */
+      engine_addlink(e, &t->ci->gradient, t2);
+      atomic_inc(&t->ci->nr_gradient);
+      engine_addlink(e, &t->ci->force, t3);
+      atomic_inc(&t->ci->nr_force);
+
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci);
+      }
+
+#else
       /* Start by constructing the task for the second hydro loop */
       struct task *t2 =
           scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
                             t->flags, 0, t->ci, t->cj, 0);
 
       /* Add the link between the new loop and the cell */
-      t->ci->force = engine_addlink(e, t->ci->force, t2);
+      engine_addlink(e, &t->ci->force, t2);
       atomic_inc(&t->ci->nr_force);
 
       /* Now, build all the dependencies for the hydro for the cells */
@@ -1587,21 +1729,52 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
       if (t->ci->nodeID == nodeID) {
         engine_make_hydro_loops_dependencies(sched, t, t2, t->ci);
       }
+#endif
     }
 
     /* Otherwise, sub-pair interaction? */
     else if (t->type == task_type_sub_pair &&
              t->subtype == task_subtype_density) {
 
+#ifdef EXTRA_HYDRO_LOOP
+
+      /* Start by constructing the task for the second and third hydro loop */
+      struct task *t2 =
+          scheduler_addtask(sched, task_type_sub_pair, task_subtype_gradient,
+                            t->flags, 0, t->ci, t->cj, 0);
+      struct task *t3 =
+          scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
+                            t->flags, 0, t->ci, t->cj, 0);
+
+      /* Add the link between the new loop and both cells */
+      engine_addlink(e, &t->ci->gradient, t2);
+      atomic_inc(&t->ci->nr_gradient);
+      engine_addlink(e, &t->cj->gradient, t2);
+      atomic_inc(&t->cj->nr_gradient);
+      engine_addlink(e, &t->ci->force, t3);
+      atomic_inc(&t->ci->nr_force);
+      engine_addlink(e, &t->cj->force, t3);
+      atomic_inc(&t->cj->nr_force);
+
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super-cells */
+      if (t->ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci);
+      }
+      if (t->cj->nodeID == nodeID && t->ci->super != t->cj->super) {
+        engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->cj);
+      }
+
+#else
       /* Start by constructing the task for the second hydro loop */
       struct task *t2 =
           scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
                             t->flags, 0, t->ci, t->cj, 0);
 
       /* Add the link between the new loop and both cells */
-      t->ci->force = engine_addlink(e, t->ci->force, t2);
+      engine_addlink(e, &t->ci->force, t2);
       atomic_inc(&t->ci->nr_force);
-      t->cj->force = engine_addlink(e, t->cj->force, t2);
+      engine_addlink(e, &t->cj->force, t2);
       atomic_inc(&t->cj->nr_force);
 
       /* Now, build all the dependencies for the hydro for the cells */
@@ -1612,6 +1785,7 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
       if (t->cj->nodeID == nodeID && t->ci->super != t->cj->super) {
         engine_make_hydro_loops_dependencies(sched, t, t2, t->cj);
       }
+#endif
     }
 
     /* External gravity tasks should depend on init and unlock the kick */
@@ -1682,14 +1856,6 @@ void engine_maketasks(struct engine *e) {
   /* Re-set the scheduler. */
   scheduler_reset(sched, s->tot_cells * engine_maxtaskspercell);
 
-  /* Add the space sorting tasks. */
-  for (int i = 0; i < e->nr_threads; i++) {
-    scheduler_addtask(sched, task_type_part_sort, task_subtype_none, i, 0, NULL,
-                      NULL, 0);
-    scheduler_addtask(sched, task_type_gpart_sort, task_subtype_none, i, 0,
-                      NULL, NULL, 0);
-  }
-
   /* Construct the firt hydro loop over neighbours */
   if (e->policy & engine_policy_hydro) engine_make_hydroloop_tasks(e);
 
@@ -1703,7 +1869,11 @@ void engine_maketasks(struct engine *e) {
      is the number of cells (s->tot_cells) times the number of neighbours (27)
      times the number of interaction types (2, density and force). */
   if (e->links != NULL) free(e->links);
+#ifdef EXTRA_HYDRO_LOOP
+  e->size_links = s->tot_cells * 27 * 3;
+#else
   e->size_links = s->tot_cells * 27 * 2;
+#endif
   if ((e->links = malloc(sizeof(struct link) * e->size_links)) == NULL)
     error("Failed to allocate cell-task links.");
   e->nr_links = 0;
@@ -1749,13 +1919,13 @@ void engine_maketasks(struct engine *e) {
       /* Loop through the proxy's incoming cells and add the
          recv tasks. */
       for (int k = 0; k < p->nr_cells_in; k++)
-        engine_addtasks_recv(e, p->cells_in[k], NULL, NULL, NULL);
+        engine_addtasks_recv(e, p->cells_in[k], NULL, NULL, NULL, NULL);
 
       /* Loop through the proxy's outgoing cells and add the
          send tasks. */
       for (int k = 0; k < p->nr_cells_out; k++)
         engine_addtasks_send(e, p->cells_out[k], p->cells_in[0], NULL, NULL,
-                             NULL);
+                             NULL, NULL);
     }
   }
 #endif
@@ -1779,211 +1949,245 @@ void engine_maketasks(struct engine *e) {
 
 /**
  * @brief Mark tasks to be skipped and set the sort flags accordingly.
+ *        Threadpool mapper function for fixdt version.
  *
- * @return 1 if the space has to be rebuilt, 0 otherwise.
+ * @param map_data pointer to the tasks
+ * @param num_elements number of tasks
+ * @param extra_data pointer to int that will define if a rebuild is needed.
  */
-int engine_marktasks(struct engine *e) {
+void engine_marktasks_fixdt_mapper(void *map_data, int num_elements,
+                                   void *extra_data) {
+  /* Unpack the arguments. */
+  struct task *tasks = (struct task *)map_data;
+  int *rebuild_space = (int *)extra_data;
 
-  struct scheduler *s = &e->sched;
-  const int ti_end = e->ti_current;
-  const int nr_tasks = s->nr_tasks;
-  const int *const ind = s->tasks_ind;
-  struct task *tasks = s->tasks;
-  const ticks tic = getticks();
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct task *t = &tasks[ind];
 
-  /* Much less to do here if we're on a fixed time-step. */
-  if (e->policy & engine_policy_fixdt) {
-
-    /* Run through the tasks and mark as skip or not. */
-    for (int k = 0; k < nr_tasks; k++) {
-
-      /* Get a handle on the kth task. */
-      struct task *t = &tasks[ind[k]];
+    /* Pair? */
+    if (t->type == task_type_pair || t->type == task_type_sub_pair) {
 
-      /* Pair? */
-      if (t->type == task_type_pair || t->type == task_type_sub_pair) {
+      /* Local pointers. */
+      const struct cell *ci = t->ci;
+      const struct cell *cj = t->cj;
 
-        /* Local pointers. */
-        const struct cell *ci = t->ci;
-        const struct cell *cj = t->cj;
+      /* Too much particle movement? */
+      if (t->tight &&
+          (fmaxf(ci->h_max, cj->h_max) + ci->dx_max + cj->dx_max > cj->dmin ||
+           ci->dx_max > space_maxreldx * ci->h_max ||
+           cj->dx_max > space_maxreldx * cj->h_max))
+        *rebuild_space = 1;
 
-        /* Too much particle movement? */
-        if (t->tight &&
-            (fmaxf(ci->h_max, cj->h_max) + ci->dx_max + cj->dx_max > cj->dmin ||
-             ci->dx_max > space_maxreldx * ci->h_max ||
-             cj->dx_max > space_maxreldx * cj->h_max))
-          return 1;
+    }
 
-      }
+    /* Sort? */
+    else if (t->type == task_type_sort) {
 
-      /* Sort? */
-      else if (t->type == task_type_sort) {
+      /* If all the sorts have been done, make this task implicit. */
+      if (!(t->flags & (t->flags ^ t->ci->sorted))) t->implicit = 1;
+    }
+  }
+}
 
-        /* If all the sorts have been done, make this task implicit. */
-        if (!(t->flags & (t->flags ^ t->ci->sorted))) t->implicit = 1;
-      }
+/**
+ * @brief Mark any sort tasks as initially skipped.
+ *        Threadpool mapper function.
+ *
+ * @param map_data pointer to the tasks
+ * @param num_elements number of tasks
+ * @param extra_data unused
+ */
+void engine_marktasks_sorts_mapper(void *map_data, int num_elements,
+                                   void *extra_data) {
+  /* Unpack the arguments. */
+  struct task *tasks = (struct task *)map_data;
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct task *t = &tasks[ind];
+    if (t->type == task_type_sort) {
+      t->flags = 0;
+      t->skip = 1;
     }
+  }
+}
 
-    /* Multiple-timestep case */
-  } else {
+/**
+ * @brief Mark tasks to be skipped and set the sort flags accordingly.
+ *        Threadpool mapper function.
+ *
+ * @param map_data pointer to the tasks
+ * @param num_elements number of tasks
+ * @param extra_data pointer to int that will define if a rebuild is needed.
+ */
+void engine_marktasks_mapper(void *map_data, int num_elements,
+                             void *extra_data) {
+  /* Unpack the arguments. */
+  struct task *tasks = (struct task *)map_data;
+  const int ti_end = ((int *)extra_data)[0];
+  int *rebuild_space = &((int *)extra_data)[1];
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct task *t = &tasks[ind];
+
+    /* Single-cell task? */
+    if (t->type == task_type_self || t->type == task_type_ghost ||
+        t->type == task_type_sub_self) {
+
+      /* Set this task's skip. */
+      t->skip = (t->ci->ti_end_min > ti_end);
+    }
 
-    /* Run through the tasks and mark as skip or not. */
-    for (int k = 0; k < nr_tasks; k++) {
+    /* Pair? */
+    else if (t->type == task_type_pair || t->type == task_type_sub_pair) {
 
-      /* Get a handle on the kth task. */
-      struct task *t = &tasks[k];
+      /* Local pointers. */
+      const struct cell *ci = t->ci;
+      const struct cell *cj = t->cj;
 
-      /* Sort-task? */
-      if (t->type == task_type_sort) {
+      /* Too much particle movement? */
+      if (t->tight &&
+          (fmaxf(ci->h_max, cj->h_max) + ci->dx_max + cj->dx_max > cj->dmin ||
+           ci->dx_max > space_maxreldx * ci->h_max ||
+           cj->dx_max > space_maxreldx * cj->h_max))
+        *rebuild_space = 1;
 
-        /* Re-set the flags. */
-        t->flags = 0;
-        t->skip = 1;
+      /* Set this task's skip. */
+      if ((t->skip = (ci->ti_end_min > ti_end && cj->ti_end_min > ti_end)) == 1)
+        continue;
 
+      /* Set the sort flags. */
+      if (t->type == task_type_pair && t->subtype != task_subtype_grav) {
+        if (!(ci->sorted & (1 << t->flags))) {
+          atomic_or(&ci->sorts->flags, (1 << t->flags));
+          ci->sorts->skip = 0;
+        }
+        if (!(cj->sorted & (1 << t->flags))) {
+          atomic_or(&cj->sorts->flags, (1 << t->flags));
+          cj->sorts->skip = 0;
+        }
       }
 
-      /* Send/recv-task? */
-      else if (t->type == task_type_send || t->type == task_type_recv) {
-        t->skip = 1;
+#ifdef WITH_MPI
+
+      /* Activate the send/recv flags. */
+      if (ci->nodeID != engine_rank) {
+
+        /* Activate the tasks to recv foreign cell ci's data. */
+        ci->recv_xv->skip = 0;
+        ci->recv_rho->skip = 0;
+        ci->recv_ti->skip = 0;
+
+        /* Look for the local cell cj's send tasks. */
+        struct link *l = NULL;
+        for (l = cj->send_xv; l != NULL && l->t->cj->nodeID != ci->nodeID;
+             l = l->next)
+          ;
+        if (l == NULL) error("Missing link to send_xv task.");
+        l->t->skip = 0;
+
+        for (l = cj->send_rho; l != NULL && l->t->cj->nodeID != ci->nodeID;
+             l = l->next)
+          ;
+        if (l == NULL) error("Missing link to send_rho task.");
+        l->t->skip = 0;
+
+        for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID;
+             l = l->next)
+          ;
+        if (l == NULL) error("Missing link to send_ti task.");
+        l->t->skip = 0;
+
+      } else if (cj->nodeID != engine_rank) {
+
+        /* Activate the tasks to recv foreign cell cj's data. */
+        cj->recv_xv->skip = 0;
+        cj->recv_rho->skip = 0;
+        cj->recv_ti->skip = 0;
+        /* Look for the local cell ci's send tasks. */
+        struct link *l = NULL;
+        for (l = ci->send_xv; l != NULL && l->t->cj->nodeID != cj->nodeID;
+             l = l->next)
+          ;
+        if (l == NULL) error("Missing link to send_xv task.");
+        l->t->skip = 0;
+
+        for (l = ci->send_rho; l != NULL && l->t->cj->nodeID != cj->nodeID;
+             l = l->next)
+          ;
+        if (l == NULL) error("Missing link to send_rho task.");
+        l->t->skip = 0;
+
+        for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID;
+             l = l->next)
+          ;
+        if (l == NULL) error("Missing link to send_ti task.");
+        l->t->skip = 0;
       }
+
+#endif
     }
 
-    /* Run through the tasks and mark as skip or not. */
-    for (int k = 0; k < nr_tasks; k++) {
+    /* Kick? */
+    else if (t->type == task_type_kick) {
+      t->skip = (t->ci->ti_end_min > ti_end);
+      t->ci->updated = 0;
+      t->ci->g_updated = 0;
+    }
 
-      /* Get a handle on the kth task. */
-      struct task *t = &tasks[k];
+    /* Init? */
+    else if (t->type == task_type_init) {
+      /* Set this task's skip. */
+      t->skip = (t->ci->ti_end_min > ti_end);
+    }
 
-      /* Skip sorts, sends, and recvs. */
-      if (t->type == task_type_sort || t->type == task_type_send ||
-          t->type == task_type_recv) {
-        continue;
-      }
+    /* None? */
+    else if (t->type == task_type_none)
+      t->skip = 1;
+  }
+}
 
-      /* Single-cell task? */
-      else if (t->type == task_type_self || t->type == task_type_ghost ||
-               t->type == task_type_sub_self) {
+/**
+ * @brief Mark tasks to be skipped and set the sort flags accordingly.
+ *
+ * @return 1 if the space has to be rebuilt, 0 otherwise.
+ */
+int engine_marktasks(struct engine *e) {
 
-        /* Set this task's skip. */
-        t->skip = (t->ci->ti_end_min > ti_end);
-      }
+  struct scheduler *s = &e->sched;
+  const ticks tic = getticks();
+  int rebuild_space = 0;
 
-      /* Pair? */
-      else if (t->type == task_type_pair || t->type == task_type_sub_pair) {
-
-        /* Local pointers. */
-        const struct cell *ci = t->ci;
-        const struct cell *cj = t->cj;
-
-        /* Too much particle movement? */
-        if (t->tight &&
-            (fmaxf(ci->h_max, cj->h_max) + ci->dx_max + cj->dx_max > cj->dmin ||
-             ci->dx_max > space_maxreldx * ci->h_max ||
-             cj->dx_max > space_maxreldx * cj->h_max))
-          return 1;
-
-        /* Set this task's skip. */
-        if ((t->skip = (ci->ti_end_min > ti_end && cj->ti_end_min > ti_end)) ==
-            1)
-          continue;
-
-        /* Set the sort flags. */
-        if (t->type == task_type_pair && t->subtype != task_subtype_grav) {
-          if (!(ci->sorted & (1 << t->flags))) {
-            ci->sorts->flags |= (1 << t->flags);
-            ci->sorts->skip = 0;
-          }
-          if (!(cj->sorted & (1 << t->flags))) {
-            cj->sorts->flags |= (1 << t->flags);
-            cj->sorts->skip = 0;
-          }
-        }
+  /* Much less to do here if we're on a fixed time-step. */
+  if (e->policy & engine_policy_fixdt) {
 
-        /* Activate the send/recv flags. */
-        if (ci->nodeID != e->nodeID) {
-
-          /* Activate the tasks to recv foreign cell ci's data. */
-          ci->recv_xv->skip = 0;
-          ci->recv_rho->skip = 0;
-          ci->recv_ti->skip = 0;
-
-          /* Look for the local cell cj's send tasks. */
-          struct link *l = NULL;
-          for (l = cj->send_xv; l != NULL && l->t->cj->nodeID != ci->nodeID;
-               l = l->next)
-            ;
-          if (l == NULL) {
-            abort();
-            error("Missing link to send_xv task.");
-          }
-          l->t->skip = 0;
-
-          for (l = cj->send_rho; l != NULL && l->t->cj->nodeID != ci->nodeID;
-               l = l->next)
-            ;
-          if (l == NULL) error("Missing link to send_rho task.");
-          l->t->skip = 0;
-
-          for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID;
-               l = l->next)
-            ;
-          if (l == NULL) error("Missing link to send_ti task.");
-          l->t->skip = 0;
-
-        } else if (cj->nodeID != e->nodeID) {
-
-          /* Activate the tasks to recv foreign cell cj's data. */
-          cj->recv_xv->skip = 0;
-          cj->recv_rho->skip = 0;
-          cj->recv_ti->skip = 0;
-
-          /* Look for the local cell ci's send tasks. */
-          struct link *l = NULL;
-          for (l = ci->send_xv; l != NULL && l->t->cj->nodeID != cj->nodeID;
-               l = l->next)
-            ;
-          if (l == NULL) {
-            abort();
-            error("Missing link to send_xv task.");
-          }
-          l->t->skip = 0;
-
-          for (l = ci->send_rho; l != NULL && l->t->cj->nodeID != cj->nodeID;
-               l = l->next)
-            ;
-          if (l == NULL) error("Missing link to send_rho task.");
-          l->t->skip = 0;
-
-          for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID;
-               l = l->next)
-            ;
-          if (l == NULL) error("Missing link to send_ti task.");
-          l->t->skip = 0;
-        }
+    /* Run through the tasks and mark as skip or not. */
+    threadpool_map(&e->threadpool, engine_marktasks_fixdt_mapper, s->tasks,
+                   s->nr_tasks, sizeof(struct task), 1000, &rebuild_space);
+    return rebuild_space;
 
-      }
+    /* Multiple-timestep case */
+  } else {
 
-      /* Kick? */
-      else if (t->type == task_type_kick) {
-        t->skip = (t->ci->ti_end_min > ti_end);
-        t->ci->updated = 0;
-        t->ci->g_updated = 0;
-      }
+    /* Run through the tasks and mark as skip or not. */
+    int extra_data[2] = {e->ti_current, rebuild_space};
+    threadpool_map(&e->threadpool, engine_marktasks_sorts_mapper, s->tasks,
+                   s->nr_tasks, sizeof(struct task), 10000, NULL);
 
-      /* Drift? */
-      else if (t->type == task_type_drift)
-        t->skip = 0;
+#ifdef WITH_MPI
+    if (e->policy & engine_policy_mpi) {
 
-      /* Init? */
-      else if (t->type == task_type_init) {
-        /* Set this task's skip. */
-        t->skip = (t->ci->ti_end_min > ti_end);
+      /* Skip all sends and recvs, we will unmark if needed. */
+      for (int k = 0; k < s->nr_tasks; k++) {
+        struct task *t = &s->tasks[k];
+        if (t->type == task_type_send || t->type == task_type_recv) {
+          t->skip = 1;
+        }
       }
-
-      /* None? */
-      else if (t->type == task_type_none)
-        t->skip = 1;
     }
+#endif
+
+    threadpool_map(&e->threadpool, engine_marktasks_mapper, s->tasks,
+                   s->nr_tasks, sizeof(struct task), 10000, extra_data);
+    rebuild_space = extra_data[1];
   }
 
   if (e->verbose)
@@ -1991,7 +2195,7 @@ int engine_marktasks(struct engine *e) {
             clocks_getunit());
 
   /* All is well... */
-  return 0;
+  return rebuild_space;
 }
 
 /**
@@ -2022,8 +2226,8 @@ void engine_print_task_counts(struct engine *e) {
     printf(" %s=%i", taskID_names[k], counts[k]);
   printf(" skipped=%i ]\n", counts[task_type_count]);
   fflush(stdout);
-  message("nr_parts = %zi.", e->s->nr_parts);
-  message("nr_gparts = %zi.", e->s->nr_gparts);
+  message("nr_parts = %zu.", e->s->nr_parts);
+  message("nr_gparts = %zu.", e->s->nr_gparts);
 }
 
 /**
@@ -2084,6 +2288,16 @@ void engine_prepare(struct engine *e) {
 
   /* Did this not go through? */
   if (rebuild) {
+
+    /* First drift all particles to the current time */
+    e->drift_all = 1;
+    threadpool_map(&e->threadpool, runner_do_drift_mapper, e->s->cells,
+                   e->s->nr_cells, sizeof(struct cell), 1, e);
+
+    /* Restore the default drifting policy */
+    e->drift_all = (e->policy & engine_policy_drift_all);
+
+    /* And now rebuild */
     engine_rebuild(e);
   }
 
@@ -2235,64 +2449,6 @@ void engine_collect_timestep(struct engine *e) {
   e->g_updates = g_updates;
 }
 
-/**
- * @brief Mapping function to collect the data from the drift.
- *
- * @param c A super-cell.
- */
-void engine_collect_drift(struct cell *c) {
-
-  /* Skip super-cells (Their values are already set) */
-  if (c->drift != NULL) return;
-
-  /* Counters for the different quantities. */
-  double e_kin = 0.0, e_int = 0.0, e_pot = 0.0, entropy = 0.0, mass = 0.0;
-  double mom[3] = {0.0, 0.0, 0.0}, ang_mom[3] = {0.0, 0.0, 0.0};
-
-  /* Only do something is the cell is non-empty */
-  if (c->count != 0 || c->gcount != 0) {
-
-    /* If this cell is not split, I'm in trouble. */
-    if (!c->split) error("Cell has no super-cell.");
-
-    /* Collect the values from the progeny. */
-    for (int k = 0; k < 8; k++) {
-      struct cell *cp = c->progeny[k];
-      if (cp != NULL) {
-
-        /* Recurse */
-        engine_collect_drift(cp);
-
-        /* And update */
-        mass += cp->mass;
-        e_kin += cp->e_kin;
-        e_int += cp->e_int;
-        e_pot += cp->e_pot;
-        entropy += cp->entropy;
-        mom[0] += cp->mom[0];
-        mom[1] += cp->mom[1];
-        mom[2] += cp->mom[2];
-        ang_mom[0] += cp->ang_mom[0];
-        ang_mom[1] += cp->ang_mom[1];
-        ang_mom[2] += cp->ang_mom[2];
-      }
-    }
-  }
-
-  /* Store the collected values in the cell. */
-  c->mass = mass;
-  c->e_kin = e_kin;
-  c->e_int = e_int;
-  c->e_pot = e_pot;
-  c->entropy = entropy;
-  c->mom[0] = mom[0];
-  c->mom[1] = mom[1];
-  c->mom[2] = mom[2];
-  c->ang_mom[0] = ang_mom[0];
-  c->ang_mom[1] = ang_mom[1];
-  c->ang_mom[2] = ang_mom[2];
-}
-
 /**
  * @brief Print the conserved quantities statistics to a log file
  *
@@ -2309,11 +2465,6 @@ void engine_print_stats(struct engine *e) {
   for (int k = 0; k < s->nr_cells; k++)
     if (s->cells[k].nodeID == e->nodeID) {
       struct cell *c = &s->cells[k];
-
-      /* Make the top-cells recurse */
-      engine_collect_drift(c);
-
-      /* And aggregate */
       mass += c->mass;
       e_kin += c->e_kin;
       e_int += c->e_int;
@@ -2425,17 +2576,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) {
   struct clocks_time time1, time2;
   clocks_gettime(&time1);
 
-  if (e->nodeID == 0) message("Initialising particles");
-
-  /* Make sure all particles are ready to go */
-  /* i.e. clean-up any stupid state in the ICs */
-  if (e->policy & engine_policy_hydro) {
-    space_map_cells_pre(s, 0, cell_init_parts, NULL);
-  }
-  if ((e->policy & engine_policy_self_gravity) ||
-      (e->policy & engine_policy_external_gravity)) {
-    space_map_cells_pre(s, 0, cell_init_gparts, NULL);
-  }
+  if (e->nodeID == 0) message("Running initialisation fake time-step.");
 
   engine_prepare(e);
 
@@ -2503,6 +2644,8 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) {
   /* Ready to go */
   e->step = -1;
   e->wallclock_time = (float)clocks_diff(&time1, &time2);
+
+  if (e->verbose) message("took %.3f %s.", e->wallclock_time, clocks_getunit());
 }
 
 /**
@@ -2535,7 +2678,12 @@ void engine_step(struct engine *e) {
     snapshot_drift_time = e->timeStep;
 
     /* Drift everybody to the snapshot position */
-    engine_launch(e, e->nr_threads, 1 << task_type_drift, 0);
+    e->drift_all = 1;
+    threadpool_map(&e->threadpool, runner_do_drift_mapper, e->s->cells,
+                   e->s->nr_cells, sizeof(struct cell), 1, e);
+
+    /* Restore the default drifting policy */
+    e->drift_all = (e->policy & engine_policy_drift_all);
 
     /* Dump... */
     engine_dump_snapshot(e);
@@ -2552,17 +2700,14 @@ void engine_step(struct engine *e) {
   e->timeOld = e->ti_old * e->timeBase + e->timeBegin;
   e->timeStep = (e->ti_current - e->ti_old) * e->timeBase + snapshot_drift_time;
 
-  /* Drift everybody */
-  engine_launch(e, e->nr_threads, 1 << task_type_drift, 0);
-
   if (e->nodeID == 0) {
 
     /* Print some information to the screen */
-    printf("  %6d %14e %14e %10zd %10zd %21.3f\n", e->step, e->time,
+    printf("  %6d %14e %14e %10zu %10zu %21.3f\n", e->step, e->time,
            e->timeStep, e->updates, e->g_updates, e->wallclock_time);
     fflush(stdout);
 
-    fprintf(e->file_timesteps, "  %6d %14e %14e %10zd %10zd %21.3f\n", e->step,
+    fprintf(e->file_timesteps, "  %6d %14e %14e %10zu %10zu %21.3f\n", e->step,
             e->time, e->timeStep, e->updates, e->g_updates, e->wallclock_time);
     fflush(e->file_timesteps);
   }
@@ -2573,6 +2718,10 @@ void engine_step(struct engine *e) {
     e->timeLastStatistics += e->deltaTimeStatistics;
   }
 
+  /* Drift only the necessary particles */
+  threadpool_map(&e->threadpool, runner_do_drift_mapper, e->s->cells,
+                 e->s->nr_cells, sizeof(struct cell), 1, e);
+
   /* Re-distribute the particles amongst the nodes? */
   if (e->forcerepart != REPART_NONE) engine_repartition(e);
 
@@ -2604,6 +2753,11 @@ void engine_step(struct engine *e) {
 
     submask |= 1 << task_subtype_density;
     submask |= 1 << task_subtype_force;
+
+#ifdef EXTRA_HYDRO_LOOP
+    mask |= 1 << task_type_extra_ghost;
+    submask |= 1 << task_subtype_gradient;
+#endif
   }
 
   /* Add the tasks corresponding to self-gravity to the masks */
@@ -2782,7 +2936,7 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
 
   /* Re-allocate the local parts. */
   if (e->verbose)
-    message("Re-allocating parts array from %zi to %zi.", s->size_parts,
+    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;
   struct part *parts_new = NULL;
@@ -2804,7 +2958,7 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
 
   /* Re-allocate the local gparts. */
   if (e->verbose)
-    message("Re-allocating gparts array from %zi to %zi.", s->size_gparts,
+    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;
   struct gpart *gparts_new = NULL;
@@ -2819,6 +2973,7 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
   part_relink_parts(s->gparts, s->nr_gparts, s->parts);
 
 #ifdef SWIFT_DEBUG_CHECKS
+
   /* Verify that the links are correct */
   for (size_t k = 0; k < s->nr_gparts; ++k) {
 
@@ -2838,6 +2993,7 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
     if (s->parts[k].gpart != NULL && s->parts[k].gpart->id_or_neg_offset != -k)
       error("Linking problem !");
   }
+
 #endif
 
 #else
@@ -2886,6 +3042,7 @@ void engine_dump_snapshot(struct engine *e) {
 static cpu_set_t *engine_entry_affinity() {
 
   static int use_entry_affinity = 0;
+  static cpu_set_t entry_affinity;
 
   if (!use_entry_affinity) {
     pthread_t engine = pthread_self();
@@ -2926,7 +3083,8 @@ void engine_pin() {
 void engine_unpin() {
 #ifdef HAVE_SETAFFINITY
   pthread_t main_thread = pthread_self();
-  pthread_setaffinity_np(main_thread, sizeof(entry_affinity), &entry_affinity);
+  cpu_set_t *entry_affinity = engine_entry_affinity();
+  pthread_setaffinity_np(main_thread, sizeof(*entry_affinity), entry_affinity);
 #else
   error("SWIFT was not compiled with support for pinning.");
 #endif
@@ -2985,6 +3143,7 @@ void engine_init(struct engine *e, struct space *s,
   e->timeStep = 0.;
   e->timeBase = 0.;
   e->timeBase_inv = 0.;
+  e->drift_all = (policy & engine_policy_drift_all);
   e->internalUnits = internal_units;
   e->timeFirstSnapshot =
       parser_get_param_double(params, "Snapshots:time_first");
@@ -2992,6 +3151,8 @@ void engine_init(struct engine *e, struct space *s,
       parser_get_param_double(params, "Snapshots:delta_time");
   e->ti_nextSnapshot = 0;
   parser_get_param_string(params, "Snapshots:basename", e->snapshotBaseName);
+  e->snapshotCompression =
+      parser_get_opt_param_int(params, "Snapshots:compression", 0);
   e->snapshotUnits = malloc(sizeof(struct UnitSystem));
   units_init_default(e->snapshotUnits, params, "Snapshots", internal_units);
   e->dt_min = parser_get_param_double(params, "TimeIntegration:dt_min");
@@ -3272,6 +3433,9 @@ void engine_init(struct engine *e, struct space *s,
   part_create_mpi_types();
 #endif
 
+  /* Initialize the threadpool. */
+  threadpool_init(&e->threadpool, e->nr_threads);
+
   /* First of all, init the barrier and lock it. */
   if (pthread_mutex_init(&e->barrier_mutex, NULL) != 0)
     error("Failed to initialize barrier mutex.");
@@ -3286,18 +3450,7 @@ void engine_init(struct engine *e, struct space *s,
   /* Init the scheduler with enough tasks for the initial sorting tasks. */
   const int nr_tasks = 2 * s->tot_cells + 2 * e->nr_threads;
   scheduler_init(&e->sched, e->s, nr_tasks, nr_queues, scheduler_flag_steal,
-                 e->nodeID);
-
-  /* Create the sorting tasks. */
-  for (int i = 0; i < e->nr_threads; i++) {
-    scheduler_addtask(&e->sched, task_type_part_sort, task_subtype_none, i, 0,
-                      NULL, NULL, 0);
-
-    scheduler_addtask(&e->sched, task_type_gpart_sort, task_subtype_none, i, 0,
-                      NULL, NULL, 0);
-  }
-
-  scheduler_ranktasks(&e->sched);
+                 e->nodeID, &e->threadpool);
 
   /* Allocate and init the threads. */
   if ((e->runners = (struct runner *)malloc(sizeof(struct runner) *
@@ -3430,4 +3583,5 @@ void engine_clean(struct engine *e) {
   free(e->links);
   scheduler_clean(&e->sched);
   space_clean(e->s);
+  threadpool_clean(&e->threadpool);
 }
diff --git a/src/engine.h b/src/engine.h
index 58c45142b729dd8c1d3bd657b925d09f524f11c7..eff0e5869cc979effa00ace8450c3f06c251294f 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -63,7 +63,8 @@ enum engine_policy {
   engine_policy_self_gravity = (1 << 9),
   engine_policy_external_gravity = (1 << 10),
   engine_policy_cosmology = (1 << 11),
-  engine_policy_cooling = (1 << 12)
+  engine_policy_drift_all = (1 << 12),
+  engine_policy_cooling = (1 << 13),
 };
 
 extern const char *engine_policy_names[];
@@ -83,16 +84,6 @@ extern int engine_rank;
 /* The maximal number of timesteps in a simulation */
 #define max_nr_timesteps (1 << 28)
 
-/* Mini struct to link cells to density/force tasks. */
-struct link {
-
-  /* The task pointer. */
-  struct task *t;
-
-  /* The next pointer. */
-  struct link *next;
-};
-
 /* Data structure for the engine. */
 struct engine {
 
@@ -111,6 +102,9 @@ struct engine {
   /* The task scheduler. */
   struct scheduler sched;
 
+  /* Common threadpool for all the engine's tasks. */
+  struct threadpool threadpool;
+
   /* The minimum and maximum allowed dt */
   double dt_min, dt_max;
 
@@ -138,6 +132,9 @@ struct engine {
   /* Minimal ti_end for the next time-step */
   int ti_end_min;
 
+  /* Are we drifting all particles now ? */
+  int drift_all;
+
   /* Number of particles updated */
   size_t updates, g_updates;
 
@@ -149,6 +146,7 @@ struct engine {
   double deltaTimeSnapshot;
   int ti_nextSnapshot;
   char snapshotBaseName[200];
+  int snapshotCompression;
   struct UnitSystem *snapshotUnits;
 
   /* Statistics information */
@@ -240,7 +238,6 @@ void engine_rebuild(struct engine *e);
 void engine_repartition(struct engine *e);
 void engine_makeproxies(struct engine *e);
 void engine_redistribute(struct engine *e);
-struct link *engine_addlink(struct engine *e, struct link *l, struct task *t);
 void engine_print_policy(struct engine *e);
 int engine_is_done(struct engine *e);
 void engine_pin();
diff --git a/src/equation_of_state.h b/src/equation_of_state.h
new file mode 100644
index 0000000000000000000000000000000000000000..af59d8a2cad1632c67b6d377b5ed9dfe9484b4aa
--- /dev/null
+++ b/src/equation_of_state.h
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016   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_EQUATION_OF_STATE_H
+#define SWIFT_EQUATION_OF_STATE_H
+
+/**
+ * @file equation_of_state.h
+ * @brief Defines the equation of state of the gas we simulate in the form of
+ * relations between thermodynamic quantities. These are later used internally
+ * by all hydro schemes
+ */
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local headers. */
+#include "adiabatic_index.h"
+#include "const.h"
+#include "debug.h"
+#include "inline.h"
+
+/* ------------------------------------------------------------------------- */
+#if defined(EOS_IDEAL_GAS)
+
+/**
+ * @brief Returns the internal energy given density and entropy
+ *
+ * Computes \f$u = \frac{S\rho^{\gamma-1} }{\gamma - 1}\f$.
+ *
+ * @param density The density \f$\rho\f$.
+ * @param entropy The entropy \f$S\f$.
+ */
+__attribute__((always_inline)) INLINE static float
+gas_internal_energy_from_entropy(float density, float entropy) {
+
+  return entropy * pow_gamma_minus_one(density) *
+         hydro_one_over_gamma_minus_one;
+}
+
+/**
+ * @brief Returns the pressure given density and entropy
+ *
+ * Computes \f$P = S\rho^\gamma\f$.
+ *
+ * @param density The density \f$\rho\f$.
+ * @param entropy The entropy \f$S\f$.
+ */
+__attribute__((always_inline)) INLINE static float gas_pressure_from_entropy(
+    float density, float entropy) {
+
+  return entropy * pow_gamma(density);
+}
+
+/**
+ * @brief Returns the sound speed given density and entropy
+ *
+ * Computes \f$c = \sqrt{\gamma S \rho^{\gamma-1}}\f$.
+ *
+ * @param density The density \f$\rho\f$.
+ * @param entropy The entropy \f$S\f$.
+ */
+__attribute__((always_inline)) INLINE static float gas_soundspeed_from_entropy(
+    float density, float entropy) {
+
+  return sqrtf(hydro_gamma * pow_gamma_minus_one(density) * entropy);
+}
+
+/**
+ * @brief Returns the entropy given density and internal energy
+ *
+ * Computes \f$S = \frac{(\gamma - 1)u}{\rho^{\gamma-1}}\f$.
+ *
+ * @param density The density \f$\rho\f$
+ * @param u The internal energy \f$u\f$
+ */
+__attribute__((always_inline)) INLINE static float
+gas_entropy_from_internal_energy(float density, float u) {
+
+  return hydro_gamma_minus_one * u * pow_minus_gamma_minus_one(density);
+}
+
+/**
+ * @brief Returns the pressure given density and internal energy
+ *
+ * Computes \f$P = (\gamma - 1)u\rho\f$.
+ *
+ * @param density The density \f$\rho\f$
+ * @param u The internal energy \f$u\f$
+ */
+__attribute__((always_inline)) INLINE static float
+gas_pressure_from_internal_energy(float density, float u) {
+
+  return hydro_gamma_minus_one * u * density;
+}
+
+/**
+ * @brief Returns the sound speed given density and internal energy
+ *
+ * Computes \f$c = \sqrt{\gamma (\gamma - 1) u }\f$.
+ *
+ * @param density The density \f$\rho\f$
+ * @param u The internal energy \f$u\f$
+ */
+__attribute__((always_inline)) INLINE static float
+gas_soundspeed_from_internal_energy(float density, float u) {
+
+  return sqrtf(u * hydro_gamma * hydro_gamma_minus_one);
+}
+
+/* ------------------------------------------------------------------------- */
+#elif defined(EOS_ISOTHERMAL_GAS)
+
+/**
+ * @brief Returns the internal energy given density and entropy
+ *
+ * Since we are using an isothermal EoS, the entropy value is ignored
+ * Computes \f$u = u_{cst}\f$.
+ *
+ * @param density The density \f$\rho\f$.
+ * @param entropy The entropy \f$S\f$.
+ */
+__attribute__((always_inline)) INLINE static float
+gas_internal_energy_from_entropy(float density, float entropy) {
+
+  return const_isothermal_internal_energy;
+}
+/**
+ * @brief Returns the pressure given density and entropy
+ *
+ * Since we are using an isothermal EoS, the entropy value is ignored
+ * Computes \f$P = (\gamma - 1)u_{cst}\rho\f$.
+ *
+ * @param density The density \f$\rho\f$.
+ * @param entropy The entropy \f$S\f$.
+ */
+__attribute__((always_inline)) INLINE static float gas_pressure_from_entropy(
+    float density, float entropy) {
+
+  return hydro_gamma_minus_one * const_isothermal_internal_energy * density;
+}
+
+/**
+ * @brief Returns the sound speed given density and entropy
+ *
+ * Since we are using an isothermal EoS, the entropy value is ignored
+ * Computes \f$c = \sqrt{u_{cst} \gamma \rho^{\gamma-1}}\f$.
+ *
+ * @param density The density \f$\rho\f$.
+ * @param entropy The entropy \f$S\f$.
+ */
+__attribute__((always_inline)) INLINE static float gas_soundspeed_from_entropy(
+    float density, float entropy) {
+
+  return sqrtf(const_isothermal_internal_energy * hydro_gamma *
+               hydro_gamma_minus_one);
+}
+
+/**
+ * @brief Returns the entropy given density and internal energy
+ *
+ * Since we are using an isothermal EoS, the energy value is ignored
+ * Computes \f$S = \frac{(\gamma - 1)u_{cst}}{\rho^{\gamma-1}}\f$.
+ *
+ * @param density The density \f$\rho\f$
+ * @param u The internal energy \f$u\f$
+ */
+__attribute__((always_inline)) INLINE static float
+gas_entropy_from_internal_energy(float density, float u) {
+
+  return hydro_gamma_minus_one * const_isothermal_internal_energy *
+         pow_minus_gamma_minus_one(density);
+}
+
+/**
+ * @brief Returns the pressure given density and internal energy
+ *
+ * Since we are using an isothermal EoS, the energy value is ignored
+ * Computes \f$P = (\gamma - 1)u_{cst}\rho\f$.
+ *
+ * @param density The density \f$\rho\f$
+ * @param u The internal energy \f$u\f$
+ */
+__attribute__((always_inline)) INLINE static float
+gas_pressure_from_internal_energy(float density, float u) {
+
+  return hydro_gamma_minus_one * const_isothermal_internal_energy * density;
+}
+
+/**
+ * @brief Returns the sound speed given density and internal energy
+ *
+ * Since we are using an isothermal EoS, the energy value is ignored
+ * Computes \f$c = \sqrt{u_{cst} \gamma \rho^{\gamma-1}}\f$.
+ *
+ * @param density The density \f$\rho\f$
+ * @param u The internal energy \f$u\f$
+ */
+__attribute__((always_inline)) INLINE static float
+gas_soundspeed_from_internal_energy(float density, float u) {
+
+  return sqrtf(const_isothermal_internal_energy * hydro_gamma *
+               hydro_gamma_minus_one);
+}
+
+/* ------------------------------------------------------------------------- */
+#else
+
+#error "An Equation of state needs to be chosen in const.h !"
+
+#endif
+
+#endif /* SWIFT_EQUATION_OF_STATE_H */
diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h
index 5903ce1384f616a123c74b579539fe53747d17e4..d4249c46a3150a357aaecfb02f9251901d97a157 100644
--- a/src/gravity/Default/gravity.h
+++ b/src/gravity/Default/gravity.h
@@ -17,6 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_DEFAULT_GRAVITY_H
+#define SWIFT_DEFAULT_GRAVITY_H
 
 #include <float.h>
 #include "potentials.h"
@@ -46,7 +48,10 @@ gravity_compute_timestep_external(const struct external_potential* potential,
   dt = fminf(dt, external_gravity_isothermalpotential_timestep(potential,
                                                                phys_const, gp));
 #endif
-
+#ifdef EXTERNAL_POTENTIAL_DISK_PATCH
+  dt = fminf(dt,
+             external_gravity_disk_patch_timestep(potential, phys_const, gp));
+#endif
   return dt;
 }
 
@@ -66,7 +71,7 @@ gravity_compute_timestep_self(const struct phys_const* const phys_const,
 
   const float ac = (ac2 > 0.f) ? sqrtf(ac2) : FLT_MIN;
 
-  const float dt = sqrt(2.f * const_gravity_eta * gp->epsilon / ac);
+  const float dt = sqrtf(2.f * const_gravity_eta * gp->epsilon / ac);
 
   return dt;
 }
@@ -81,6 +86,9 @@ gravity_compute_timestep_self(const struct phys_const* const phys_const,
  */
 __attribute__((always_inline)) INLINE static void gravity_first_init_gpart(
     struct gpart* gp) {
+
+  gp->ti_begin = 0;
+  gp->ti_end = 0;
   gp->epsilon = 0.;  // MATTHIEU
 }
 
@@ -107,10 +115,10 @@ __attribute__((always_inline)) INLINE static void gravity_init_gpart(
  * Multiplies the forces and accelerations by the appropiate constants
  *
  * @param gp The particle to act upon
- * @param const_G Newton's constant
+ * @param const_G Newton's constant in internal units
  */
 __attribute__((always_inline)) INLINE static void gravity_end_force(
-    struct gpart* gp, double const_G) {
+    struct gpart* gp, float const_G) {
 
   /* Let's get physical... */
   gp->a_grav[0] *= const_G;
@@ -123,12 +131,13 @@ __attribute__((always_inline)) INLINE static void gravity_end_force(
  *
  * This function only branches towards the potential chosen by the user.
  *
+ * @param time The current time in internal units.
  * @param potential The properties of the external potential.
  * @param phys_const The physical constants in internal units.
  * @param gp The particle to act upon.
  */
 __attribute__((always_inline)) INLINE static void external_gravity(
-    const struct external_potential* potential,
+    double time, const struct external_potential* potential,
     const struct phys_const* const phys_const, struct gpart* gp) {
 
 #ifdef EXTERNAL_POTENTIAL_POINTMASS
@@ -137,6 +146,9 @@ __attribute__((always_inline)) INLINE static void external_gravity(
 #ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL
   external_gravity_isothermalpotential(potential, phys_const, gp);
 #endif
+#ifdef EXTERNAL_POTENTIAL_DISK_PATCH
+  external_gravity_disk_patch_potential(time, potential, phys_const, gp);
+#endif
 }
 
 /**
@@ -148,3 +160,5 @@ __attribute__((always_inline)) INLINE static void external_gravity(
  */
 __attribute__((always_inline)) INLINE static void gravity_kick_extra(
     struct gpart* gp, float dt, float half_dt) {}
+
+#endif /* SWIFT_DEFAULT_GRAVITY_H */
diff --git a/src/gravity/Default/gravity_debug.h b/src/gravity/Default/gravity_debug.h
index 7cf375a1fdf7bccc4131dc415ab2d4acbbf2d3bc..c284f543b3be06297600c010e302423eb683adc9 100644
--- a/src/gravity/Default/gravity_debug.h
+++ b/src/gravity/Default/gravity_debug.h
@@ -16,6 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_DEFAULT_GRAVITY_DEBUG_H
+#define SWIFT_DEFAULT_GRAVITY_DEBUG_H
 
 __attribute__((always_inline)) INLINE static void gravity_debug_particle(
     const struct gpart* p) {
@@ -27,3 +29,5 @@ __attribute__((always_inline)) INLINE static void gravity_debug_particle(
       p->a_grav[0], p->a_grav[1], p->a_grav[2], p->mass, p->ti_begin,
       p->ti_end);
 }
+
+#endif /* SWIFT_DEFAULT_GRAVITY_DEBUG_H */
diff --git a/src/gravity/Default/gravity_iact.h b/src/gravity/Default/gravity_iact.h
index aa285ce3245aeac608aa022924a613a7a7b00375..7c7c5b9d244534ef3f6b5f509062019bfcd5f9fb 100644
--- a/src/gravity/Default/gravity_iact.h
+++ b/src/gravity/Default/gravity_iact.h
@@ -17,8 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_RUNNER_IACT_GRAV_H
-#define SWIFT_RUNNER_IACT_GRAV_H
+#ifndef SWIFT_DEFAULT_GRAVITY_IACT_H
+#define SWIFT_DEFAULT_GRAVITY_IACT_H
 
 /* Includes. */
 #include "const.h"
@@ -188,4 +188,4 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pm(
 #endif
 }
 
-#endif /* SWIFT_RUNNER_IACT_GRAV_H */
+#endif /* SWIFT_DEFAULT_GRAVITY_IACT_H */
diff --git a/src/gravity/Default/gravity_io.h b/src/gravity/Default/gravity_io.h
index e0fd15a91b6e20048f6844b03ea1dde40114d7ec..26eed8a0a680e13130dd257d8b31e6cd00392f6d 100644
--- a/src/gravity/Default/gravity_io.h
+++ b/src/gravity/Default/gravity_io.h
@@ -16,6 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_DEFAULT_GRAVITY_IO_H
+#define SWIFT_DEFAULT_GRAVITY_IO_H
 
 #include "io_properties.h"
 
@@ -68,3 +70,5 @@ void darkmatter_write_particles(struct gpart* gparts, struct io_props* list,
   list[4] = io_make_output_field("Acceleration", FLOAT, 3,
                                  UNIT_CONV_ACCELERATION, gparts, a_grav);
 }
+
+#endif /* SWIFT_DEFAULT_GRAVITY_IO_H */
diff --git a/src/gravity/Default/gravity_part.h b/src/gravity/Default/gravity_part.h
index 77e6543429993bea355c04dd28fcb5b04eee5519..1850ff0a1644d3593f78f150646eae8b2f074e1e 100644
--- a/src/gravity/Default/gravity_part.h
+++ b/src/gravity/Default/gravity_part.h
@@ -16,6 +16,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_DEFAULT_GRAVITY_PART_H
+#define SWIFT_DEFAULT_GRAVITY_PART_H
+
 /* Some standard headers. */
 #include <stdlib.h>
 
@@ -51,3 +54,5 @@ struct gpart {
   long long id_or_neg_offset;
 
 } __attribute__((aligned(gpart_align)));
+
+#endif /* SWIFT_DEFAULT_GRAVITY_PART_H */
diff --git a/src/hydro.h b/src/hydro.h
index b2ae9d57c399ecea818e9f3dc7db238e01487a9a..4a2b0bd029d494e2091b9081d22b7949cec5648c 100644
--- a/src/hydro.h
+++ b/src/hydro.h
@@ -38,6 +38,10 @@
 #include "./hydro/Default/hydro.h"
 #include "./hydro/Default/hydro_iact.h"
 #define SPH_IMPLEMENTATION "Default version of SPH"
+#elif defined(GIZMO_SPH)
+#include "./hydro/Gizmo/hydro.h"
+#include "./hydro/Gizmo/hydro_iact.h"
+#define SPH_IMPLEMENTATION "GIZMO (Hopkins 2015)"
 #else
 #error "Invalid choice of SPH variant"
 #endif
diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h
index 45b21531e610391fed6693daa07b8907bcdd8a5b..f61bff55821809fe1f5da27c95d75afbecbc04cc 100644
--- a/src/hydro/Default/hydro.h
+++ b/src/hydro/Default/hydro.h
@@ -16,12 +16,116 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_DEFAULT_HYDRO_H
+#define SWIFT_DEFAULT_HYDRO_H
 
 #include "adiabatic_index.h"
 #include "approx_math.h"
+#include "equation_of_state.h"
 
 #include <float.h>
 
+/**
+ * @brief Returns the internal energy of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_internal_energy(
+    const struct part *restrict p, float dt) {
+
+  return p->u;
+}
+
+/**
+ * @brief Returns the pressure of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_pressure(
+    const struct part *restrict p, float dt) {
+
+  return gas_pressure_from_internal_energy(p->rho, p->u);
+}
+
+/**
+ * @brief Returns the entropy of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_entropy(
+    const struct part *restrict p, float dt) {
+
+  return gas_entropy_from_internal_energy(p->rho, p->u);
+}
+
+/**
+ * @brief Returns the sound speed of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
+    const struct part *restrict p, float dt) {
+
+  return p->force.soundspeed;
+}
+
+/**
+ * @brief Returns the density of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_density(
+    const struct part *restrict p) {
+
+  return p->rho;
+}
+
+/**
+ * @brief Returns the mass of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_mass(
+    const struct part *restrict p) {
+
+  return p->mass;
+}
+
+/**
+ * @brief Modifies the thermal state of a particle to the imposed internal
+ * energy
+ *
+ * This overrides the current state of the particle but does *not* change its
+ * time-derivatives
+ *
+ * @param p The particle
+ * @param u The new internal energy
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_internal_energy(
+    struct part *restrict p, float u) {
+
+  p->u = u;
+}
+
+/**
+ * @brief Modifies the thermal state of a particle to the imposed entropy
+ *
+ * This overrides the current state of the particle but does *not* change its
+ * time-derivatives
+ *
+ * @param p The particle
+ * @param S The new entropy
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_entropy(
+    struct part *restrict p, float S) {
+
+  p->u = gas_internal_energy_from_entropy(p->rho, S);
+}
+
 /**
  * @brief Computes the hydro time-step of a given particle
  *
@@ -57,7 +161,15 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep(
  * @param xp The extended particle data to act upon
  */
 __attribute__((always_inline)) INLINE static void hydro_first_init_part(
-    struct part *restrict p, struct xpart *restrict xp) {}
+    struct part *restrict p, struct xpart *restrict xp) {
+
+  p->ti_begin = 0;
+  p->ti_end = 0;
+  xp->v_full[0] = p->v[0];
+  xp->v_full[1] = p->v[1];
+  xp->v_full[2] = p->v[2];
+  xp->u_full = p->u;
+}
 
 /**
  * @brief Prepares a particle for the density calculation.
@@ -93,33 +205,33 @@ __attribute__((always_inline)) INLINE static void hydro_end_density(
 
   /* Some smoothing length multiples. */
   const float h = p->h;
-  const float ih = 1.0f / h;
-  const float ih2 = ih * ih;
-  const float ih4 = ih2 * ih2;
+  const float h_inv = 1.0f / h;                       /* 1/h */
+  const float h_inv_dim = pow_dimension(h_inv);       /* 1/h^d */
+  const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */
 
   /* Final operation on the density (add self-contribution). */
   p->rho += p->mass * kernel_root;
-  p->rho_dh -= 3.0f * p->mass * kernel_root;
+  p->rho_dh -= hydro_dimension * p->mass * kernel_root;
   p->density.wcount += kernel_root;
 
   /* Finish the calculation by inserting the missing h-factors */
-  p->rho *= ih * ih2;
-  p->rho_dh *= ih4;
+  p->rho *= h_inv_dim;
+  p->rho_dh *= h_inv_dim_plus_one;
   p->density.wcount *= kernel_norm;
-  p->density.wcount_dh *= ih * kernel_gamma * kernel_norm;
+  p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm;
 
   const float irho = 1.f / p->rho;
 
   /* Compute the derivative term */
-  p->rho_dh = 1.f / (1.f + 0.33333333f * p->h * p->rho_dh * irho);
+  p->rho_dh = 1.f / (1.f + hydro_dimension_inv * p->h * p->rho_dh * irho);
 
   /* Finish calculation of the velocity curl components */
-  p->density.rot_v[0] *= ih4 * irho;
-  p->density.rot_v[1] *= ih4 * irho;
-  p->density.rot_v[2] *= ih4 * irho;
+  p->density.rot_v[0] *= h_inv_dim_plus_one * irho;
+  p->density.rot_v[1] *= h_inv_dim_plus_one * irho;
+  p->density.rot_v[2] *= h_inv_dim_plus_one * irho;
 
   /* Finish calculation of the velocity divergence */
-  p->density.div_v *= ih4 * irho;
+  p->density.div_v *= h_inv_dim_plus_one * irho;
 }
 
 /**
@@ -137,7 +249,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
 
   /* Some smoothing length multiples. */
   const float h = p->h;
-  const float ih = 1.0f / h;
+  const float h_inv = 1.0f / h;
 
   /* Pre-compute some stuff for the balsara switch. */
   const float normDiv_v = fabs(p->density.div_v);
@@ -151,11 +263,11 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
       sqrtf(hydro_gamma * hydro_gamma_minus_one * u);
 
   /* Compute the P/Omega/rho2. */
-  xp->omega = 1.0f + 0.3333333333f * h * p->rho_dh / p->rho;
+  xp->omega = 1.0f + hydro_dimension_inv * h * p->rho_dh / p->rho;
   p->force.P_over_rho2 = u * hydro_gamma_minus_one / (p->rho * xp->omega);
 
   /* Balsara switch */
-  p->force.balsara = normDiv_v / (normDiv_v + normRot_v + 0.0001f * fc * ih);
+  p->force.balsara = normDiv_v / (normDiv_v + normRot_v + 0.0001f * fc * h_inv);
 
   /* Viscosity parameter decay time */
   const float tau = h / (2.f * const_viscosity_length * p->force.soundspeed);
@@ -198,16 +310,31 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
  *
  * @param p The particle
  * @param xp The extended data of the particle
+ * @param dt The drift time-step.
  * @param t0 The time at the start of the drift
  * @param t1 The time at the end of the drift
  * @param timeBase The minimal time-step size
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
-    struct part *restrict p, struct xpart *restrict xp, int t0, int t1,
-    double timeBase) {
+    struct part *restrict p, struct xpart *restrict xp, float dt, int t0,
+    int t1, double timeBase) {
   float u, w;
 
-  const float dt = (t1 - t0) * timeBase;
+  const float h_inv = 1.f / p->h;
+
+  /* Predict smoothing length */
+  const float w1 = p->force.h_dt * h_inv * dt;
+  if (fabsf(w1) < 0.2f)
+    p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */
+  else
+    p->h *= expf(w1);
+
+  /* Predict density */
+  const float w2 = -hydro_dimension * w1;
+  if (fabsf(w2) < 0.2f)
+    p->rho *= approx_expf(w2); /* 4th order expansion of exp(w) */
+  else
+    p->rho *= expf(w2);
 
   /* Predict internal energy */
   w = p->force.u_dt / p->u * dt;
@@ -229,7 +356,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra(
  */
 __attribute__((always_inline)) INLINE static void hydro_end_force(
     struct part *restrict p) {
-  p->force.h_dt *= p->h * 0.333333333f;
+  p->force.h_dt *= p->h * hydro_dimension_inv;
 }
 
 /**
@@ -254,50 +381,4 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
 __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
     struct part *restrict p) {}
 
-/**
- * @brief Returns the internal energy of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_internal_energy(
-    const struct part *restrict p, float dt) {
-
-  return p->u;
-}
-
-/**
- * @brief Returns the pressure of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_pressure(
-    const struct part *restrict p, float dt) {
-
-  return p->force.P_over_rho2 * p->rho * p->rho / p->rho_dh;
-}
-
-/**
- * @brief Returns the entropy of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_entropy(
-    const struct part *restrict p, float dt) {
-
-  return hydro_gamma_minus_one * p->u * pow_minus_gamma_minus_one(p->rho);
-}
-
-/**
- * @brief Returns the sound speed of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
-    const struct part *restrict p, float dt) {
-
-  return p->force.soundspeed;
-}
+#endif /* SWIFT_DEFAULT_HYDRO_H */
diff --git a/src/hydro/Default/hydro_debug.h b/src/hydro/Default/hydro_debug.h
index 9b8136e8349398e4e9e804459a5de23d21043564..d02d3ef82c1b3d751731f49850c06df4b146b164 100644
--- a/src/hydro/Default/hydro_debug.h
+++ b/src/hydro/Default/hydro_debug.h
@@ -16,6 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_DEFAULT_HYDRO_DEBUG_H
+#define SWIFT_DEFAULT_HYDRO_DEBUG_H
 
 __attribute__((always_inline)) INLINE static void hydro_debug_particle(
     const struct part* p, const struct xpart* xp) {
@@ -29,3 +31,5 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle(
       p->h, (int)p->density.wcount, p->mass, p->rho_dh, p->rho, p->ti_begin,
       p->ti_end);
 }
+
+#endif /* SWIFT_DEFAULT_HYDRO_DEBUG_H */
diff --git a/src/hydro/Default/hydro_iact.h b/src/hydro/Default/hydro_iact.h
index 5db3604e1717ba2c14d50c531c0537409501226d..51fa7d07229f86918ef2d7019a9708110cef02e3 100644
--- a/src/hydro/Default/hydro_iact.h
+++ b/src/hydro/Default/hydro_iact.h
@@ -17,8 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_RUNNER_IACT_H
-#define SWIFT_RUNNER_IACT_H
+#ifndef SWIFT_DEFAULT_HYDRO_IACT_H
+#define SWIFT_DEFAULT_HYDRO_IACT_H
 
 #include "adiabatic_index.h"
 
@@ -75,7 +75,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
   kernel_deval(xi, &wi, &wi_dx);
 
   pi->rho += mj * wi;
-  pi->rho_dh -= mj * (3.0f * wi + xi * wi_dx);
+  pi->rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx);
   pi->density.wcount += wi;
   pi->density.wcount_dh -= xi * wi_dx;
 
@@ -88,7 +88,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
   kernel_deval(xj, &wj, &wj_dx);
 
   pj->rho += mi * wj;
-  pj->rho_dh -= mi * (3.0f * wj + xj * wj_dx);
+  pj->rho_dh -= mi * (hydro_dimension * wj + xj * wj_dx);
   pj->density.wcount += wj;
   pj->density.wcount_dh -= xj * wj_dx;
 
@@ -174,14 +174,14 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_density(
   for (k = 0; k < 3; k++) curlvr[k].v *= ri.v;
 
   rhoi.v = mj.v * wi.v;
-  rhoi_dh.v = mj.v * (vec_set1(3.0f) * wi.v + xi.v * wi_dx.v);
+  rhoi_dh.v = mj.v * (vec_set1(hydro_dimension) * wi.v + xi.v * wi_dx.v);
   wcounti.v = wi.v;
   wcounti_dh.v = xi.v * wi_dx.v;
   div_vi.v = mj.v * dvdr.v * wi_dx.v;
   for (k = 0; k < 3; k++) rot_vi[k].v = mj.v * curlvr[k].v * wi_dx.v;
 
   rhoj.v = mi.v * wj.v;
-  rhoj_dh.v = mi.v * (vec_set1(3.0f) * wj.v + xj.v * wj_dx.v);
+  rhoj_dh.v = mi.v * (vec_set1(hydro_dimension) * wj.v + xj.v * wj_dx.v);
   wcountj.v = wj.v;
   wcountj_dh.v = xj.v * wj_dx.v;
   div_vj.v = mi.v * dvdr.v * wj_dx.v;
@@ -250,7 +250,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
   kernel_deval(xi, &wi, &wi_dx);
 
   pi->rho += mj * wi;
-  pi->rho_dh -= mj * (3.0f * wi + xi * wi_dx);
+  pi->rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx);
   pi->density.wcount += wi;
   pi->density.wcount_dh -= xi * wi_dx;
 
@@ -327,7 +327,7 @@ runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj,
   for (k = 0; k < 3; k++) curlvr[k].v *= ri.v;
 
   rhoi.v = mj.v * wi.v;
-  rhoi_dh.v = mj.v * (vec_set1(3.0f) * wi.v + xi.v * wi_dx.v);
+  rhoi_dh.v = mj.v * (vec_set1(hydro_dimension) * wi.v + xi.v * wi_dx.v);
   wcounti.v = wi.v;
   wcounti_dh.v = xi.v * wi_dx.v;
   div_vi.v = mj.v * dvdr.v * wi_dx.v;
@@ -358,8 +358,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
 
   float r = sqrtf(r2), ri = 1.0f / r;
   float xi, xj;
-  float hi_inv, hi2_inv;
-  float hj_inv, hj2_inv;
+  float hi_inv, hid_inv;
+  float hj_inv, hjd_inv;
   float wi, wj, wi_dx, wj_dx, wi_dr, wj_dr, w, dvdr;
   float mi, mj, POrho2i, POrho2j, rhoi, rhoj;
   float v_sig, omega_ij, Pi_ij, alpha_ij, tc, v_sig_u;
@@ -376,17 +376,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
 
   /* Get the kernel for hi. */
   hi_inv = 1.0f / hi;
-  hi2_inv = hi_inv * hi_inv;
+  hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
   xi = r * hi_inv;
   kernel_deval(xi, &wi, &wi_dx);
-  wi_dr = hi2_inv * hi2_inv * wi_dx;
+  wi_dr = hid_inv * wi_dx;
 
   /* Get the kernel for hj. */
   hj_inv = 1.0f / hj;
-  hj2_inv = hj_inv * hj_inv;
+  hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
   xj = r * hj_inv;
   kernel_deval(xj, &wj, &wj_dx);
-  wj_dr = hj2_inv * hj2_inv * wj_dx;
+  wj_dr = hjd_inv * wj_dx;
 
   /* Compute dv dot r. */
   dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] +
@@ -457,7 +457,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
   vector r, r2, ri;
   vector xi, xj;
   vector hi, hj, hi_inv, hj_inv;
-  vector hi2_inv, hj2_inv;
+  vector hid_inv, hjd_inv;
   vector wi, wj, wi_dx, wj_dx, wi_dr, wj_dr, dvdr;
   vector w;
   vector piPOrho2, pjPOrho2, pirho, pjrho, piu, pju;
@@ -468,7 +468,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
   vector pia[3], pja[3];
   vector piu_dt, pju_dt;
   vector pih_dt, pjh_dt;
-  vector ci, cj, v_sig, vi_sig, vj_sig;
+  vector ci, cj, v_sig;
   vector omega_ij, Pi_ij, balsara;
   vector pialpha, pjalpha, alpha_ij, v_sig_u, tc;
   int j, k;
@@ -503,12 +503,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
                  pj[2]->force.soundspeed, pj[3]->force.soundspeed,
                  pj[4]->force.soundspeed, pj[5]->force.soundspeed,
                  pj[6]->force.soundspeed, pj[7]->force.soundspeed);
-  vi_sig.v = vec_set(pi[0]->force.v_sig, pi[1]->force.v_sig, pi[2]->force.v_sig,
-                     pi[3]->force.v_sig, pi[4]->force.v_sig, pi[5]->force.v_sig,
-                     pi[6]->force.v_sig, pi[7]->force.v_sig);
-  vj_sig.v = vec_set(pj[0]->force.v_sig, pj[1]->force.v_sig, pj[2]->force.v_sig,
-                     pj[3]->force.v_sig, pj[4]->force.v_sig, pj[5]->force.v_sig,
-                     pj[6]->force.v_sig, pj[7]->force.v_sig);
   for (k = 0; k < 3; k++) {
     vi[k].v = vec_set(pi[0]->v[k], pi[1]->v[k], pi[2]->v[k], pi[3]->v[k],
                       pi[4]->v[k], pi[5]->v[k], pi[6]->v[k], pi[7]->v[k]);
@@ -544,10 +538,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
                  pi[2]->force.soundspeed, pi[3]->force.soundspeed);
   cj.v = vec_set(pj[0]->force.soundspeed, pj[1]->force.soundspeed,
                  pj[2]->force.soundspeed, pj[3]->force.soundspeed);
-  vi_sig.v = vec_set(pi[0]->force.v_sig, pi[1]->force.v_sig, pi[2]->force.v_sig,
-                     pi[3]->force.v_sig);
-  vj_sig.v = vec_set(pj[0]->force.v_sig, pj[1]->force.v_sig, pj[2]->force.v_sig,
-                     pj[3]->force.v_sig);
   for (k = 0; k < 3; k++) {
     vi[k].v = vec_set(pi[0]->v[k], pi[1]->v[k], pi[2]->v[k], pi[3]->v[k]);
     vj[k].v = vec_set(pj[0]->v[k], pj[1]->v[k], pj[2]->v[k], pj[3]->v[k]);
@@ -574,19 +564,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
   hi.v = vec_load(Hi);
   hi_inv.v = vec_rcp(hi.v);
   hi_inv.v = hi_inv.v - hi_inv.v * (hi.v * hi_inv.v - vec_set1(1.0f));
-  hi2_inv.v = hi_inv.v * hi_inv.v;
+  hid_inv = pow_dimension_plus_one_vec(hi_inv);
   xi.v = r.v * hi_inv.v;
   kernel_deval_vec(&xi, &wi, &wi_dx);
-  wi_dr.v = hi2_inv.v * hi2_inv.v * wi_dx.v;
+  wi_dr.v = hid_inv.v * wi_dx.v;
 
   /* Get the kernel for hj. */
   hj.v = vec_load(Hj);
   hj_inv.v = vec_rcp(hj.v);
   hj_inv.v = hj_inv.v - hj_inv.v * (hj.v * hj_inv.v - vec_set1(1.0f));
-  hj2_inv.v = hj_inv.v * hj_inv.v;
+  hjd_inv = pow_dimension_plus_one_vec(hj_inv);
   xj.v = r.v * hj_inv.v;
   kernel_deval_vec(&xj, &wj, &wj_dx);
-  wj_dr.v = hj2_inv.v * hj2_inv.v * wj_dx.v;
+  wj_dr.v = hjd_inv.v * wj_dx.v;
 
   /* Compute dv dot r. */
   dvdr.v = ((vi[0].v - vj[0].v) * dx[0].v) + ((vi[1].v - vj[1].v) * dx[1].v) +
@@ -669,8 +659,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
 
   float r = sqrtf(r2), ri = 1.0f / r;
   float xi, xj;
-  float hi_inv, hi2_inv;
-  float hj_inv, hj2_inv;
+  float hi_inv, hid_inv;
+  float hj_inv, hjd_inv;
   float wi, wj, wi_dx, wj_dx, wi_dr, wj_dr, w, dvdr;
   float /*mi,*/ mj, POrho2i, POrho2j, rhoi, rhoj;
   float v_sig, omega_ij, Pi_ij, alpha_ij, tc, v_sig_u;
@@ -687,17 +677,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
 
   /* Get the kernel for hi. */
   hi_inv = 1.0f / hi;
-  hi2_inv = hi_inv * hi_inv;
+  hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
   xi = r * hi_inv;
   kernel_deval(xi, &wi, &wi_dx);
-  wi_dr = hi2_inv * hi2_inv * wi_dx;
+  wi_dr = hid_inv * wi_dx;
 
   /* Get the kernel for hj. */
   hj_inv = 1.0f / hj;
-  hj2_inv = hj_inv * hj_inv;
+  hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
   xj = r * hj_inv;
   kernel_deval(xj, &wj, &wj_dx);
-  wj_dr = hj2_inv * hj2_inv * wj_dx;
+  wj_dr = hjd_inv * wj_dx;
 
   /* Compute dv dot r. */
   dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] +
@@ -762,7 +752,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
   vector r, r2, ri;
   vector xi, xj;
   vector hi, hj, hi_inv, hj_inv;
-  vector hi2_inv, hj2_inv;
+  vector hid_inv, hjd_inv;
   vector wi, wj, wi_dx, wj_dx, wi_dr, wj_dr, dvdr;
   vector w;
   vector piPOrho2, pjPOrho2, pirho, pjrho, piu, pju;
@@ -773,7 +763,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
   vector pia[3];
   vector piu_dt;
   vector pih_dt;
-  vector ci, cj, v_sig, vi_sig, vj_sig;
+  vector ci, cj, v_sig;
   vector omega_ij, Pi_ij, balsara;
   vector pialpha, pjalpha, alpha_ij, v_sig_u, tc;
   int j, k;
@@ -806,12 +796,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
                  pj[2]->force.soundspeed, pj[3]->force.soundspeed,
                  pj[4]->force.soundspeed, pj[5]->force.soundspeed,
                  pj[6]->force.soundspeed, pj[7]->force.soundspeed);
-  vi_sig.v = vec_set(pi[0]->force.v_sig, pi[1]->force.v_sig, pi[2]->force.v_sig,
-                     pi[3]->force.v_sig, pi[4]->force.v_sig, pi[5]->force.v_sig,
-                     pi[6]->force.v_sig, pi[7]->force.v_sig);
-  vj_sig.v = vec_set(pj[0]->force.v_sig, pj[1]->force.v_sig, pj[2]->force.v_sig,
-                     pj[3]->force.v_sig, pj[4]->force.v_sig, pj[5]->force.v_sig,
-                     pj[6]->force.v_sig, pj[7]->force.v_sig);
   for (k = 0; k < 3; k++) {
     vi[k].v = vec_set(pi[0]->v[k], pi[1]->v[k], pi[2]->v[k], pi[3]->v[k],
                       pi[4]->v[k], pi[5]->v[k], pi[6]->v[k], pi[7]->v[k]);
@@ -846,10 +830,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
                  pi[2]->force.soundspeed, pi[3]->force.soundspeed);
   cj.v = vec_set(pj[0]->force.soundspeed, pj[1]->force.soundspeed,
                  pj[2]->force.soundspeed, pj[3]->force.soundspeed);
-  vi_sig.v = vec_set(pi[0]->force.v_sig, pi[1]->force.v_sig, pi[2]->force.v_sig,
-                     pi[3]->force.v_sig);
-  vj_sig.v = vec_set(pj[0]->force.v_sig, pj[1]->force.v_sig, pj[2]->force.v_sig,
-                     pj[3]->force.v_sig);
   for (k = 0; k < 3; k++) {
     vi[k].v = vec_set(pi[0]->v[k], pi[1]->v[k], pi[2]->v[k], pi[3]->v[k]);
     vj[k].v = vec_set(pj[0]->v[k], pj[1]->v[k], pj[2]->v[k], pj[3]->v[k]);
@@ -876,19 +856,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
   hi.v = vec_load(Hi);
   hi_inv.v = vec_rcp(hi.v);
   hi_inv.v = hi_inv.v - hi_inv.v * (hi.v * hi_inv.v - vec_set1(1.0f));
-  hi2_inv.v = hi_inv.v * hi_inv.v;
+  hid_inv = pow_dimension_plus_one_vec(hi_inv);
   xi.v = r.v * hi_inv.v;
   kernel_deval_vec(&xi, &wi, &wi_dx);
-  wi_dr.v = hi2_inv.v * hi2_inv.v * wi_dx.v;
+  wi_dr.v = hid_inv.v * wi_dx.v;
 
   /* Get the kernel for hj. */
   hj.v = vec_load(Hj);
   hj_inv.v = vec_rcp(hj.v);
   hj_inv.v = hj_inv.v - hj_inv.v * (hj.v * hj_inv.v - vec_set1(1.0f));
-  hj2_inv.v = hj_inv.v * hj_inv.v;
+  hjd_inv = pow_dimension_plus_one_vec(hj_inv);
   xj.v = r.v * hj_inv.v;
   kernel_deval_vec(&xj, &wj, &wj_dx);
-  wj_dr.v = hj2_inv.v * hj2_inv.v * wj_dx.v;
+  wj_dr.v = hjd_inv.v * wj_dx.v;
 
   /* Compute dv dot r. */
   dvdr.v = ((vi[0].v - vj[0].v) * dx[0].v) + ((vi[1].v - vj[1].v) * dx[1].v) +
@@ -952,4 +932,4 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
 #endif
 }
 
-#endif /* SWIFT_RUNNER_IACT_H */
+#endif /* SWIFT_DEFAULT_HYDRO_IACT_H */
diff --git a/src/hydro/Default/hydro_io.h b/src/hydro/Default/hydro_io.h
index 1c3e70753d417eb59d0fda5a7acfd1c24ab93045..bb35c914bcab8787f609b4dd49acd0cc883b4263 100644
--- a/src/hydro/Default/hydro_io.h
+++ b/src/hydro/Default/hydro_io.h
@@ -16,6 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_DEFAULT_HYDRO_IO_H
+#define SWIFT_DEFAULT_HYDRO_IO_H
 
 #include "io_properties.h"
 #include "kernel_hydro.h"
@@ -112,3 +114,5 @@ void writeSPHflavour(hid_t h_grpsph) {
  * @return 1 if entropy is in 'internal energy', 0 otherwise.
  */
 int writeEntropyFlag() { return 0; }
+
+#endif /* SWIFT_DEFAULT_HYDRO_IO_H */
diff --git a/src/hydro/Default/hydro_part.h b/src/hydro/Default/hydro_part.h
index a3972dc7ebd1b04a18ccdb6f815d24e36aff270c..f42c3dc886ae1ab8f472ffdf5ff508f6735d1bb1 100644
--- a/src/hydro/Default/hydro_part.h
+++ b/src/hydro/Default/hydro_part.h
@@ -16,6 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_DEFAULT_HYDRO_PART_H
+#define SWIFT_DEFAULT_HYDRO_PART_H
 
 /* Extra particle data not needed during the SPH loops over neighbours. */
 struct xpart {
@@ -26,6 +28,8 @@ struct xpart {
   /* Velocity at the last full step. */
   float v_full[3];
 
+  float u_full;
+
   /* Old density. */
   float omega;
 
@@ -117,3 +121,5 @@ struct part {
   struct gpart* gpart;
 
 } __attribute__((aligned(part_align)));
+
+#endif /* SWIFT_DEFAULT_HYDRO_PART_H */
diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h
index 5efce17d78ddc22911ca297a1ef9024d31971ba9..09a8f50d5b2e9abd43c3b9bd43a12fad8a347258 100644
--- a/src/hydro/Gadget2/hydro.h
+++ b/src/hydro/Gadget2/hydro.h
@@ -16,8 +16,120 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_GADGET2_HYDRO_H
+#define SWIFT_GADGET2_HYDRO_H
 
 #include "adiabatic_index.h"
+#include "approx_math.h"
+#include "dimension.h"
+#include "equation_of_state.h"
+#include "hydro_properties.h"
+#include "kernel_hydro.h"
+
+/**
+ * @brief Returns the internal energy of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_internal_energy(
+    const struct part *restrict p, float dt) {
+
+  const float entropy = p->entropy + p->entropy_dt * dt;
+
+  return gas_internal_energy_from_entropy(p->rho, entropy);
+}
+
+/**
+ * @brief Returns the pressure of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_pressure(
+    const struct part *restrict p, float dt) {
+
+  const float entropy = p->entropy + p->entropy_dt * dt;
+
+  return gas_pressure_from_entropy(p->rho, entropy);
+}
+
+/**
+ * @brief Returns the entropy of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_entropy(
+    const struct part *restrict p, float dt) {
+
+  return p->entropy + p->entropy_dt * dt;
+}
+
+/**
+ * @brief Returns the sound speed of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
+    const struct part *restrict p, float dt) {
+
+  return p->force.soundspeed;
+}
+
+/**
+ * @brief Returns the density of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_density(
+    const struct part *restrict p) {
+
+  return p->rho;
+}
+
+/**
+ * @brief Returns the mass of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_mass(
+    const struct part *restrict p) {
+
+  return p->mass;
+}
+
+/**
+ * @brief Modifies the thermal state of a particle to the imposed internal
+ * energy
+ *
+ * This overrides the current state of the particle but does *not* change its
+ * time-derivatives
+ *
+ * @param p The particle
+ * @param u The new internal energy
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_internal_energy(
+    struct part *restrict p, float u) {
+
+  p->entropy = gas_entropy_from_internal_energy(p->rho, u);
+}
+
+/**
+ * @brief Modifies the thermal state of a particle to the imposed entropy
+ *
+ * This overrides the current state of the particle but does *not* change its
+ * time-derivatives
+ *
+ * @param p The particle
+ * @param S The new entropy
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_entropy(
+    struct part *restrict p, float S) {
+
+  p->entropy = S;
+}
 
 /**
  * @brief Computes the hydro time-step of a given particle
@@ -49,7 +161,14 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep(
  * @param xp The extended particle data to act upon
  */
 __attribute__((always_inline)) INLINE static void hydro_first_init_part(
-    struct part *restrict p, struct xpart *restrict xp) {}
+    struct part *restrict p, struct xpart *restrict xp) {
+
+  p->ti_begin = 0;
+  p->ti_end = 0;
+  xp->v_full[0] = p->v[0];
+  xp->v_full[1] = p->v[1];
+  xp->v_full[2] = p->v[2];
+}
 
 /**
  * @brief Prepares a particle for the density calculation.
@@ -85,33 +204,33 @@ __attribute__((always_inline)) INLINE static void hydro_end_density(
 
   /* Some smoothing length multiples. */
   const float h = p->h;
-  const float ih = 1.0f / h;
-  const float ih2 = ih * ih;
-  const float ih4 = ih2 * ih2;
+  const float h_inv = 1.0f / h;                       /* 1/h */
+  const float h_inv_dim = pow_dimension(h_inv);       /* 1/h^d */
+  const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */
 
   /* Final operation on the density (add self-contribution). */
   p->rho += p->mass * kernel_root;
-  p->rho_dh -= 3.0f * p->mass * kernel_root;
+  p->rho_dh -= hydro_dimension * p->mass * kernel_root;
   p->density.wcount += kernel_root;
 
   /* Finish the calculation by inserting the missing h-factors */
-  p->rho *= ih * ih2;
-  p->rho_dh *= ih4;
+  p->rho *= h_inv_dim;
+  p->rho_dh *= h_inv_dim_plus_one;
   p->density.wcount *= kernel_norm;
-  p->density.wcount_dh *= ih * kernel_gamma * kernel_norm;
+  p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm;
 
   const float irho = 1.f / p->rho;
 
   /* Compute the derivative term */
-  p->rho_dh = 1.f / (1.f + 0.33333333f * p->h * p->rho_dh * irho);
+  p->rho_dh = 1.f / (1.f + hydro_dimension_inv * p->h * p->rho_dh * irho);
 
   /* Finish calculation of the velocity curl components */
-  p->density.rot_v[0] *= ih4 * irho;
-  p->density.rot_v[1] *= ih4 * irho;
-  p->density.rot_v[2] *= ih4 * irho;
+  p->density.rot_v[0] *= h_inv_dim_plus_one * irho;
+  p->density.rot_v[1] *= h_inv_dim_plus_one * irho;
+  p->density.rot_v[2] *= h_inv_dim_plus_one * irho;
 
   /* Finish calculation of the velocity divergence */
-  p->density.div_v *= ih4 * irho;
+  p->density.div_v *= h_inv_dim_plus_one * irho;
 }
 
 /**
@@ -140,8 +259,7 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
 
   /* Compute the pressure */
   const float half_dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase;
-  const float pressure =
-      (p->entropy + p->entropy_dt * half_dt) * pow_gamma(p->rho);
+  const float pressure = hydro_get_pressure(p, half_dt);
 
   const float irho = 1.f / p->rho;
 
@@ -190,18 +308,34 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
  *
  * @param p The particle
  * @param xp The extended data of the particle
+ * @param dt The drift time-step.
  * @param t0 The time at the start of the drift
  * @param t1 The time at the end of the drift
  * @param timeBase The minimal time-step size
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
-    struct part *restrict p, const struct xpart *restrict xp, int t0, int t1,
-    double timeBase) {
+    struct part *restrict p, const struct xpart *restrict xp, float dt, int t0,
+    int t1, double timeBase) {
+
+  const float h_inv = 1.f / p->h;
+
+  /* Predict smoothing length */
+  const float w1 = p->force.h_dt * h_inv * dt;
+  if (fabsf(w1) < 0.2f)
+    p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */
+  else
+    p->h *= expf(w1);
+
+  /* Predict density */
+  const float w2 = -hydro_dimension * w1;
+  if (fabsf(w2) < 0.2f)
+    p->rho *= approx_expf(w2); /* 4th order expansion of exp(w) */
+  else
+    p->rho *= expf(w2);
 
   /* Drift the pressure */
   const float dt_entr = (t1 - (p->ti_begin + p->ti_end) / 2) * timeBase;
-  const float pressure =
-      (p->entropy + p->entropy_dt * dt_entr) * pow_gamma(p->rho);
+  const float pressure = hydro_get_pressure(p, dt_entr);
 
   const float irho = 1.f / p->rho;
 
@@ -226,9 +360,10 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra(
 __attribute__((always_inline)) INLINE static void hydro_end_force(
     struct part *restrict p) {
 
-  p->force.h_dt *= p->h * 0.333333333f;
+  p->force.h_dt *= p->h * hydro_dimension_inv;
 
-  p->entropy_dt *= hydro_gamma_minus_one * pow_minus_gamma_minus_one(p->rho);
+  p->entropy_dt *=
+      0.5f * hydro_gamma_minus_one * pow_minus_gamma_minus_one(p->rho);
 }
 
 /**
@@ -265,56 +400,8 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
 __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
     struct part *restrict p) {
 
-  p->entropy =
-      hydro_gamma_minus_one * p->entropy * pow_minus_gamma_minus_one(p->rho);
+  /* We read u in the entropy field. We now get S from u */
+  p->entropy = gas_entropy_from_internal_energy(p->rho, p->entropy);
 }
 
-/**
- * @brief Returns the internal energy of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_internal_energy(
-    const struct part *restrict p, float dt) {
-
-  const float entropy = p->entropy + p->entropy_dt * dt;
-
-  return entropy * pow_gamma_minus_one(p->rho) * hydro_one_over_gamma_minus_one;
-}
-
-/**
- * @brief Returns the pressure of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_pressure(
-    const struct part *restrict p, float dt) {
-
-  return p->force.P_over_rho2 * p->rho * p->rho / p->rho_dh;
-}
-
-/**
- * @brief Returns the entropy of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_entropy(
-    const struct part *restrict p, float dt) {
-
-  return p->entropy + p->entropy_dt * dt;
-}
-
-/**
- * @brief Returns the sound speed of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
-    const struct part *restrict p, float dt) {
-
-  return p->force.soundspeed;
-}
+#endif /* SWIFT_GADGET2_HYDRO_H */
diff --git a/src/hydro/Gadget2/hydro_debug.h b/src/hydro/Gadget2/hydro_debug.h
index 0280ae7d7a0cdac4567b8e9d4c3a50faf20e93f6..7c8a6eaba96929b01f1901393d7d0498d58badf4 100644
--- a/src/hydro/Gadget2/hydro_debug.h
+++ b/src/hydro/Gadget2/hydro_debug.h
@@ -16,6 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_GADGET2_HYDRO_DEBUG_H
+#define SWIFT_GADGET2_HYDRO_DEBUG_H
 
 __attribute__((always_inline)) INLINE static void hydro_debug_particle(
     const struct part* p, const struct xpart* xp) {
@@ -34,3 +36,5 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle(
       p->density.rot_v[1], p->density.rot_v[2], p->force.balsara,
       p->force.v_sig, p->force.h_dt, p->ti_begin, p->ti_end);
 }
+
+#endif /* SWIFT_GADGET2_HYDRO_DEBUG_H */
diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h
index 323f066db6a047db7ae3401a3525afa941bfc047..0108e0663c0d84ff6b5698456f6be34d5ee08c14 100644
--- a/src/hydro/Gadget2/hydro_iact.h
+++ b/src/hydro/Gadget2/hydro_iact.h
@@ -17,8 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_RUNNER_IACT_LEGACY_H
-#define SWIFT_RUNNER_IACT_LEGACY_H
+#ifndef SWIFT_GADGET2_HYDRO_IACT_H
+#define SWIFT_GADGET2_HYDRO_IACT_H
 
 /**
  * @brief SPH interaction functions following the Gadget-2 version of SPH.
@@ -58,7 +58,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
 
   /* Compute contribution to the density */
   pi->rho += mj * wi;
-  pi->rho_dh -= mj * (3.f * wi + ui * wi_dx);
+  pi->rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx);
 
   /* Compute contribution to the number of neighbours */
   pi->density.wcount += wi;
@@ -71,7 +71,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
 
   /* Compute contribution to the density */
   pj->rho += mi * wj;
-  pj->rho_dh -= mi * (3.f * wj + uj * wj_dx);
+  pj->rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx);
 
   /* Compute contribution to the number of neighbours */
   pj->density.wcount += wj;
@@ -191,7 +191,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_density(
 
   /* Compute density of pi. */
   rhoi.v = mj.v * wi.v;
-  rhoi_dh.v = mj.v * (vec_set1(3.0f) * wi.v + xi.v * wi_dx.v);
+  rhoi_dh.v = mj.v * (vec_set1(hydro_dimension) * wi.v + xi.v * wi_dx.v);
   wcounti.v = wi.v;
   wcounti_dh.v = xi.v * wi_dx.v;
   div_vi.v = mj.v * dvdr.v * wi_dx.v;
@@ -199,7 +199,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_density(
 
   /* Compute density of pj. */
   rhoj.v = mi.v * wj.v;
-  rhoj_dh.v = mi.v * (vec_set1(3.0f) * wj.v + xj.v * wj_dx.v);
+  rhoj_dh.v = mi.v * (vec_set1(hydro_dimension) * wj.v + xj.v * wj_dx.v);
   wcountj.v = wj.v;
   wcountj_dh.v = xj.v * wj_dx.v;
   div_vj.v = mi.v * dvdr.v * wj_dx.v;
@@ -253,7 +253,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
 
   /* Compute contribution to the density */
   pi->rho += mj * wi;
-  pi->rho_dh -= mj * (3.f * wi + u * wi_dx);
+  pi->rho_dh -= mj * (hydro_dimension * wi + u * wi_dx);
 
   /* Compute contribution to the number of neighbours */
   pi->density.wcount += wi;
@@ -356,7 +356,7 @@ runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj,
 
   /* Compute density of pi. */
   rhoi.v = mj.v * wi.v;
-  rhoi_dh.v = mj.v * (vec_set1(3.0f) * wi.v + xi.v * wi_dx.v);
+  rhoi_dh.v = mj.v * (vec_set1(hydro_dimension) * wi.v + xi.v * wi_dx.v);
   wcounti.v = wi.v;
   wcounti_dh.v = xi.v * wi_dx.v;
   div_vi.v = mj.v * dvdr.v * wi_dx.v;
@@ -402,17 +402,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
 
   /* Get the kernel for hi. */
   const float hi_inv = 1.0f / hi;
-  const float hi2_inv = hi_inv * hi_inv;
+  const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
   const float ui = r * hi_inv;
   kernel_deval(ui, &wi, &wi_dx);
-  const float wi_dr = hi2_inv * hi2_inv * wi_dx;
+  const float wi_dr = hid_inv * wi_dx;
 
   /* Get the kernel for hj. */
   const float hj_inv = 1.0f / hj;
-  const float hj2_inv = hj_inv * hj_inv;
+  const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
   const float xj = r * hj_inv;
   kernel_deval(xj, &wj, &wj_dx);
-  const float wj_dr = hj2_inv * hj2_inv * wj_dx;
+  const float wj_dr = hjd_inv * wj_dx;
 
   /* Compute gradient terms */
   const float P_over_rho2_i = pi->force.P_over_rho2;
@@ -432,7 +432,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   const float balsara_j = pj->force.balsara;
 
   /* Are the particles moving towards each others ? */
-  const float omega_ij = fminf(dvdr, 0.f);
+  const float omega_ij = (dvdr < 0.f) ? dvdr : 0.f;
   const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
 
   /* Signal velocity */
@@ -465,12 +465,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr;
 
   /* Update the signal velocity. */
-  pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig);
-  pj->force.v_sig = fmaxf(pj->force.v_sig, v_sig);
+  pi->force.v_sig = (pi->force.v_sig > v_sig) ? pi->force.v_sig : v_sig;
+  pj->force.v_sig = (pj->force.v_sig > v_sig) ? pj->force.v_sig : v_sig;
 
   /* Change in entropy */
-  pi->entropy_dt += 0.5f * mj * visc_term * dvdr;
-  pj->entropy_dt += 0.5f * mi * visc_term * dvdr;
+  pi->entropy_dt += mj * visc_term * dvdr;
+  pj->entropy_dt += mi * visc_term * dvdr;
 }
 
 /**
@@ -485,7 +485,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
   vector r, r2, ri;
   vector xi, xj;
   vector hi, hj, hi_inv, hj_inv;
-  vector hi2_inv, hj2_inv;
+  vector hid_inv, hjd_inv;
   vector wi, wj, wi_dx, wj_dx, wi_dr, wj_dr, dvdr;
   vector piPOrho, pjPOrho, pirho, pjrho;
   vector mi, mj;
@@ -580,19 +580,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
   hi.v = vec_load(Hi);
   hi_inv.v = vec_rcp(hi.v);
   hi_inv.v = hi_inv.v - hi_inv.v * (hi.v * hi_inv.v - vec_set1(1.0f));
-  hi2_inv.v = hi_inv.v * hi_inv.v;
+  hid_inv = pow_dimension_plus_one_vec(hi_inv); /* 1/h^(d+1) */
   xi.v = r.v * hi_inv.v;
   kernel_deval_vec(&xi, &wi, &wi_dx);
-  wi_dr.v = hi2_inv.v * hi2_inv.v * wi_dx.v;
+  wi_dr.v = hid_inv.v * wi_dx.v;
 
   /* Get the kernel for hj. */
   hj.v = vec_load(Hj);
   hj_inv.v = vec_rcp(hj.v);
   hj_inv.v = hj_inv.v - hj_inv.v * (hj.v * hj_inv.v - vec_set1(1.0f));
-  hj2_inv.v = hj_inv.v * hj_inv.v;
+  hjd_inv = pow_dimension_plus_one_vec(hj_inv); /* 1/h^(d+1) */
   xj.v = r.v * hj_inv.v;
   kernel_deval_vec(&xj, &wj, &wj_dx);
-  wj_dr.v = hj2_inv.v * hj2_inv.v * wj_dx.v;
+  wj_dr.v = hjd_inv.v * wj_dx.v;
 
   /* Compute dv dot r. */
   dvdr.v = ((vi[0].v - vj[0].v) * dx[0].v) + ((vi[1].v - vj[1].v) * dx[1].v) +
@@ -631,7 +631,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
   pjh_dt.v = mi.v * dvdr.v * ri.v / pirho.v * wj_dr.v;
 
   /* Change in entropy */
-  entropy_dt.v = vec_set1(0.5f) * visc_term.v * dvdr.v;
+  entropy_dt.v = visc_term.v * dvdr.v;
 
   /* Store the forces back on the particles. */
   for (k = 0; k < VEC_SIZE; k++) {
@@ -644,7 +644,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
     pi[k]->force.v_sig = fmaxf(pi[k]->force.v_sig, v_sig.f[k]);
     pj[k]->force.v_sig = fmaxf(pj[k]->force.v_sig, v_sig.f[k]);
     pi[k]->entropy_dt += entropy_dt.f[k] * mj.f[k];
-    pj[k]->entropy_dt -= entropy_dt.f[k] * mi.f[k];
+    pj[k]->entropy_dt += entropy_dt.f[k] * mi.f[k];
   }
 
 #else
@@ -677,17 +677,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
 
   /* Get the kernel for hi. */
   const float hi_inv = 1.0f / hi;
-  const float hi2_inv = hi_inv * hi_inv;
+  const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
   const float ui = r * hi_inv;
   kernel_deval(ui, &wi, &wi_dx);
-  const float wi_dr = hi2_inv * hi2_inv * wi_dx;
+  const float wi_dr = hid_inv * wi_dx;
 
   /* Get the kernel for hj. */
   const float hj_inv = 1.0f / hj;
-  const float hj2_inv = hj_inv * hj_inv;
+  const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
   const float xj = r * hj_inv;
   kernel_deval(xj, &wj, &wj_dx);
-  const float wj_dr = hj2_inv * hj2_inv * wj_dx;
+  const float wj_dr = hjd_inv * wj_dx;
 
   /* Compute gradient terms */
   const float P_over_rho2_i = pi->force.P_over_rho2;
@@ -707,7 +707,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   const float balsara_j = pj->force.balsara;
 
   /* Are the particles moving towards each others ? */
-  const float omega_ij = fminf(dvdr, 0.f);
+  const float omega_ij = (dvdr < 0.f) ? dvdr : 0.f;
   const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
 
   /* Signal velocity */
@@ -735,10 +735,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr;
 
   /* Update the signal velocity. */
-  pi->force.v_sig = fmaxf(pi->force.v_sig, v_sig);
+  pi->force.v_sig = (pi->force.v_sig > v_sig) ? pi->force.v_sig : v_sig;
 
   /* Change in entropy */
-  pi->entropy_dt += 0.5f * mj * visc_term * dvdr;
+  pi->entropy_dt += mj * visc_term * dvdr;
 }
 
 /**
@@ -753,7 +753,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
   vector r, r2, ri;
   vector xi, xj;
   vector hi, hj, hi_inv, hj_inv;
-  vector hi2_inv, hj2_inv;
+  vector hid_inv, hjd_inv;
   vector wi, wj, wi_dx, wj_dx, wi_dr, wj_dr, dvdr;
   vector piPOrho, pjPOrho, pirho, pjrho;
   vector mj;
@@ -845,19 +845,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
   hi.v = vec_load(Hi);
   hi_inv.v = vec_rcp(hi.v);
   hi_inv.v = hi_inv.v - hi_inv.v * (hi.v * hi_inv.v - vec_set1(1.0f));
-  hi2_inv.v = hi_inv.v * hi_inv.v;
+  hid_inv = pow_dimension_plus_one_vec(hi_inv);
   xi.v = r.v * hi_inv.v;
   kernel_deval_vec(&xi, &wi, &wi_dx);
-  wi_dr.v = hi2_inv.v * hi2_inv.v * wi_dx.v;
+  wi_dr.v = hid_inv.v * wi_dx.v;
 
   /* Get the kernel for hj. */
   hj.v = vec_load(Hj);
   hj_inv.v = vec_rcp(hj.v);
   hj_inv.v = hj_inv.v - hj_inv.v * (hj.v * hj_inv.v - vec_set1(1.0f));
-  hj2_inv.v = hj_inv.v * hj_inv.v;
+  hjd_inv = pow_dimension_plus_one_vec(hj_inv);
   xj.v = r.v * hj_inv.v;
   kernel_deval_vec(&xj, &wj, &wj_dx);
-  wj_dr.v = hj2_inv.v * hj2_inv.v * wj_dx.v;
+  wj_dr.v = hjd_inv.v * wj_dx.v;
 
   /* Compute dv dot r. */
   dvdr.v = ((vi[0].v - vj[0].v) * dx[0].v) + ((vi[1].v - vj[1].v) * dx[1].v) +
@@ -894,7 +894,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
   pih_dt.v = mj.v * dvdr.v * ri.v / pjrho.v * wi_dr.v;
 
   /* Change in entropy */
-  entropy_dt.v = vec_set1(0.5f) * mj.v * visc_term.v * dvdr.v;
+  entropy_dt.v = mj.v * visc_term.v * dvdr.v;
 
   /* Store the forces back on the particles. */
   for (k = 0; k < VEC_SIZE; k++) {
@@ -913,4 +913,4 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
 #endif
 }
 
-#endif /* SWIFT_RUNNER_IACT_LEGACY_H */
+#endif /* SWIFT_GADGET2_HYDRO_IACT_H */
diff --git a/src/hydro/Gadget2/hydro_io.h b/src/hydro/Gadget2/hydro_io.h
index 1614a483ac3007d37770e04d40833f78d19a8cd2..433aef64c388c8bc4989e883f10a8f0d3eeb30e9 100644
--- a/src/hydro/Gadget2/hydro_io.h
+++ b/src/hydro/Gadget2/hydro_io.h
@@ -16,8 +16,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_GADGET2_HYDRO_IO_H
+#define SWIFT_GADGET2_HYDRO_IO_H
 
 #include "adiabatic_index.h"
+#include "hydro.h"
 #include "io_properties.h"
 #include "kernel_hydro.h"
 
@@ -54,8 +57,12 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
 
 float convert_u(struct engine* e, struct part* p) {
 
-  return p->entropy * pow_gamma_minus_one(p->rho) *
-         hydro_one_over_gamma_minus_one;
+  return hydro_get_internal_energy(p, 0);
+}
+
+float convert_P(struct engine* e, struct part* p) {
+
+  return hydro_get_pressure(p, 0);
 }
 
 /**
@@ -68,7 +75,7 @@ float convert_u(struct engine* e, struct part* p) {
 void hydro_write_particles(struct part* parts, struct io_props* list,
                            int* num_fields) {
 
-  *num_fields = 9;
+  *num_fields = 10;
 
   /* List what we want to write */
   list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH,
@@ -90,6 +97,8 @@ void hydro_write_particles(struct part* parts, struct io_props* list,
   list[8] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1,
                                               UNIT_CONV_ENERGY_PER_UNIT_MASS,
                                               parts, rho, convert_u);
+  list[9] = io_make_output_field_convert_part(
+      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, rho, convert_P);
 }
 
 /**
@@ -100,9 +109,10 @@ void writeSPHflavour(hid_t h_grpsph) {
 
   /* Viscosity and thermal conduction */
   writeAttribute_s(h_grpsph, "Thermal Conductivity Model",
-                   "(No treatment) Legacy Gadget-2 as in Springel (2005)");
-  writeAttribute_s(h_grpsph, "Viscosity Model",
-                   "Legacy Gadget-2 as in Springel (2005)");
+                   "(No treatment) as in Springel (2005)");
+  writeAttribute_s(
+      h_grpsph, "Viscosity Model",
+      "as in Springel (2005), i.e. Monaghan (1992) with Balsara (1995) switch");
   writeAttribute_f(h_grpsph, "Viscosity alpha", const_viscosity_alpha);
   writeAttribute_f(h_grpsph, "Viscosity beta", 3.f);
 }
@@ -113,3 +123,5 @@ void writeSPHflavour(hid_t h_grpsph) {
  * @return 1 if entropy is in 'internal energy', 0 otherwise.
  */
 int writeEntropyFlag() { return 0; }
+
+#endif /* SWIFT_GADGET2_HYDRO_IO_H */
diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h
index 0cd830c51a1836c0a56e7b4097990bf4d07a101f..484792438d2717413c1ca8d4f429eac2e6d21b20 100644
--- a/src/hydro/Gadget2/hydro_part.h
+++ b/src/hydro/Gadget2/hydro_part.h
@@ -16,6 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_GADGET2_HYDRO_PART_H
+#define SWIFT_GADGET2_HYDRO_PART_H
 
 /* Extra particle data not needed during the SPH loops over neighbours. */
 struct xpart {
@@ -109,3 +111,5 @@ struct part {
   struct gpart* gpart;
 
 } __attribute__((aligned(part_align)));
+
+#endif /* SWIFT_GADGET2_HYDRO_PART_H */
diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h
index f69dc3f1798f014e895c4a63760805b1739cec94..e24a44529dc1907c9ceadeedfbfc9e49c308bcda 100644
--- a/src/hydro/Gizmo/hydro.h
+++ b/src/hydro/Gizmo/hydro.h
@@ -17,17 +17,25 @@
  *
  ******************************************************************************/
 
+#include <float.h>
+#include "adiabatic_index.h"
+#include "approx_math.h"
+#include "hydro_gradients.h"
+
 /**
  * @brief Computes the hydro time-step of a given particle
  *
- * @param p Pointer to the particle data
- * @param xp Pointer to the extended particle data
- *
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the extended particle data.
+ * @param hydro_properties Pointer to the hydro parameters.
  */
 __attribute__((always_inline)) INLINE static float hydro_compute_timestep(
-    struct part* p, struct xpart* xp) {
+    const struct part* restrict p, const struct xpart* restrict xp,
+    const struct hydro_props* restrict hydro_properties) {
 
-  return const_cfl * p->h / fabs(p->timestepvars.vmax);
+  const float CFL_condition = hydro_properties->CFL_condition;
+
+  return CFL_condition * p->h / fabsf(p->timestepvars.vmax);
 }
 
 /**
@@ -36,39 +44,37 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep(
  * This function is called only once just after the ICs have been
  * read in to do some conversions.
  *
+ * In this case, we copy the particle velocities into the corresponding
+ * primitive variable field. We do this because the particle velocities in GIZMO
+ * can be independent of the actual fluid velocity. The latter is stored as a
+ * primitive variable and integrated using the linear momentum, a conserved
+ * variable.
+ *
  * @param p The particle to act upon
  * @param xp The extended particle data to act upon
  */
 __attribute__((always_inline)) INLINE static void hydro_first_init_part(
-    struct part* p, struct xpart* xp) {}
+    struct part* p, struct xpart* xp) {
+
+  xp->v_full[0] = p->v[0];
+  xp->v_full[1] = p->v[1];
+  xp->v_full[2] = p->v[2];
+
+  p->primitives.v[0] = p->v[0];
+  p->primitives.v[1] = p->v[1];
+  p->primitives.v[2] = p->v[2];
+}
 
 /**
  * @brief Prepares a particle for the volume calculation.
  *
+ * Simply makes sure all necessary variables are initialized to zero.
+ *
  * @param p The particle to act upon
  */
 __attribute__((always_inline)) INLINE static void hydro_init_part(
     struct part* p) {
 
-#ifdef SPH_GRADIENTS
-  /* use the old volumes to estimate new primitive variables to be used for the
-     gradient calculation */
-  if (p->conserved.mass) {
-    p->primitives.rho = p->conserved.mass / p->geometry.volume;
-    p->primitives.v[0] = p->conserved.momentum[0] / p->conserved.mass;
-    p->primitives.v[1] = p->conserved.momentum[1] / p->conserved.mass;
-    p->primitives.v[2] = p->conserved.momentum[2] / p->conserved.mass;
-    p->primitives.P =
-        (const_hydro_gamma - 1.) *
-        (p->conserved.energy -
-         0.5 * (p->conserved.momentum[0] * p->conserved.momentum[0] +
-                p->conserved.momentum[1] * p->conserved.momentum[1] +
-                p->conserved.momentum[2] * p->conserved.momentum[2]) /
-             p->conserved.mass) /
-        p->geometry.volume;
-  }
-#endif
-
   p->density.wcount = 0.0f;
   p->density.wcount_dh = 0.0f;
   p->geometry.volume = 0.0f;
@@ -81,540 +87,417 @@ __attribute__((always_inline)) INLINE static void hydro_init_part(
   p->geometry.matrix_E[2][0] = 0.0f;
   p->geometry.matrix_E[2][1] = 0.0f;
   p->geometry.matrix_E[2][2] = 0.0f;
-
-#ifdef SPH_GRADIENTS
-  p->primitives.gradients.rho[0] = 0.0f;
-  p->primitives.gradients.rho[1] = 0.0f;
-  p->primitives.gradients.rho[2] = 0.0f;
-
-  p->primitives.gradients.v[0][0] = 0.0f;
-  p->primitives.gradients.v[0][1] = 0.0f;
-  p->primitives.gradients.v[0][2] = 0.0f;
-
-  p->primitives.gradients.v[1][0] = 0.0f;
-  p->primitives.gradients.v[1][1] = 0.0f;
-  p->primitives.gradients.v[1][2] = 0.0f;
-
-  p->primitives.gradients.v[2][0] = 0.0f;
-  p->primitives.gradients.v[2][1] = 0.0f;
-  p->primitives.gradients.v[2][2] = 0.0f;
-
-  p->primitives.gradients.P[0] = 0.0f;
-  p->primitives.gradients.P[1] = 0.0f;
-  p->primitives.gradients.P[2] = 0.0f;
-
-  p->primitives.limiter.rho[0] = FLT_MAX;
-  p->primitives.limiter.rho[1] = -FLT_MAX;
-  p->primitives.limiter.v[0][0] = FLT_MAX;
-  p->primitives.limiter.v[0][1] = -FLT_MAX;
-  p->primitives.limiter.v[1][0] = FLT_MAX;
-  p->primitives.limiter.v[1][1] = -FLT_MAX;
-  p->primitives.limiter.v[2][0] = FLT_MAX;
-  p->primitives.limiter.v[2][1] = -FLT_MAX;
-  p->primitives.limiter.P[0] = FLT_MAX;
-  p->primitives.limiter.P[1] = -FLT_MAX;
-
-  p->primitives.limiter.maxr = -FLT_MAX;
-#endif
 }
 
 /**
- * @brief Finishes the density calculation.
+ * @brief Finishes the volume calculation.
  *
  * Multiplies the density and number of neighbours by the appropiate constants
- * and add the self-contribution term.
+ * and adds the self-contribution term. Calculates the volume and uses it to
+ * update the primitive variables (based on the conserved variables). The latter
+ * should only be done for active particles. This is okay, since this method is
+ * only called for active particles.
  *
- * @param p The particle to act upon
- */
-__attribute__((always_inline)) INLINE static void hydro_end_volume(
-    struct part* p) {
-
-  /* Some smoothing length multiples. */
-  const float h = p->h;
-  const float ih = 1.0f / h;
-
-  /* Final operation on the density. */
-  p->density.wcount =
-      (p->density.wcount + kernel_root) * (4.0f / 3.0 * M_PI * kernel_gamma3);
-  p->density.wcount_dh =
-      p->density.wcount_dh * ih * (4.0f / 3.0 * M_PI * kernel_gamma3);
-}
-
-/**
- * @brief Prepare a particle for the force calculation.
+ * Multiplies the components of the matrix E with the appropriate constants and
+ * inverts it. Initializes the variables used during the gradient loop. This
+ * cannot be done in hydro_prepare_force, since that method is called for all
+ * particles, and not just the active ones. If we would initialize the
+ * variables there, gradients for passive particles would be zero, while we
+ * actually use the old gradients in the flux calculation between active and
+ * passive particles.
  *
- * Computes viscosity term, conduction term and smoothing length gradient terms.
- *
- * @param p The particle to act upon
- * @param xp The extended particle data to act upon
+ * @param p The particle to act upon.
+ * @param The current physical time.
  */
-__attribute__((always_inline)) INLINE static void hydro_prepare_gradient(
-    struct part* p, struct xpart* xp) {
+__attribute__((always_inline)) INLINE static void hydro_end_density(
+    struct part* restrict p, float time) {
 
   /* Some smoothing length multiples. */
   const float h = p->h;
   const float ih = 1.0f / h;
-  const float ih2 = ih * ih;
 
-  float detE, volume;
-  float E[3][3];
-  GFLOAT m, momentum[3], energy;
+  /* Final operation on the density. */
+  p->density.wcount += kernel_root;
+  p->density.wcount *= kernel_norm;
 
-#ifndef THERMAL_ENERGY
-  GFLOAT momentum2;
-#endif
+  p->density.wcount_dh *= ih * kernel_gamma * kernel_norm;
 
-#if defined(SPH_GRADIENTS) && defined(SLOPE_LIMITER)
-  GFLOAT gradrho[3], gradv[3][3], gradP[3];
-  GFLOAT gradtrue, gradmax, gradmin, alpha;
-#endif
+  const float ihdim = pow_dimension(ih);
 
   /* Final operation on the geometry. */
   /* we multiply with the smoothing kernel normalization ih3 and calculate the
    * volume */
-  volume = ih * ih2 * (p->geometry.volume + kernel_root);
-  p->geometry.volume = volume = 1. / volume;
-  /* we multiply with the smoothing kernel normalization */
-  p->geometry.matrix_E[0][0] = E[0][0] = ih * ih2 * p->geometry.matrix_E[0][0];
-  p->geometry.matrix_E[0][1] = E[0][1] = ih * ih2 * p->geometry.matrix_E[0][1];
-  p->geometry.matrix_E[0][2] = E[0][2] = ih * ih2 * p->geometry.matrix_E[0][2];
-  p->geometry.matrix_E[1][0] = E[1][0] = ih * ih2 * p->geometry.matrix_E[1][0];
-  p->geometry.matrix_E[1][1] = E[1][1] = ih * ih2 * p->geometry.matrix_E[1][1];
-  p->geometry.matrix_E[1][2] = E[1][2] = ih * ih2 * p->geometry.matrix_E[1][2];
-  p->geometry.matrix_E[2][0] = E[2][0] = ih * ih2 * p->geometry.matrix_E[2][0];
-  p->geometry.matrix_E[2][1] = E[2][1] = ih * ih2 * p->geometry.matrix_E[2][1];
-  p->geometry.matrix_E[2][2] = E[2][2] = ih * ih2 * p->geometry.matrix_E[2][2];
-
-  /* invert the E-matrix */
-  /* code shamelessly stolen from the public version of GIZMO */
-  /* But since we should never invert a matrix, this code has to be replaced */
-  detE = E[0][0] * E[1][1] * E[2][2] + E[0][1] * E[1][2] * E[2][0] +
-         E[0][2] * E[1][0] * E[2][1] - E[0][2] * E[1][1] * E[2][0] -
-         E[0][1] * E[1][0] * E[2][2] - E[0][0] * E[1][2] * E[2][1];
-  /* check for zero determinant */
-  if ((detE != 0) && !isnan(detE)) {
-    p->geometry.matrix_E[0][0] = (E[1][1] * E[2][2] - E[1][2] * E[2][1]) / detE;
-    p->geometry.matrix_E[0][1] = (E[0][2] * E[2][1] - E[0][1] * E[2][2]) / detE;
-    p->geometry.matrix_E[0][2] = (E[0][1] * E[1][2] - E[0][2] * E[1][1]) / detE;
-    p->geometry.matrix_E[1][0] = (E[1][2] * E[2][0] - E[1][0] * E[2][2]) / detE;
-    p->geometry.matrix_E[1][1] = (E[0][0] * E[2][2] - E[0][2] * E[2][0]) / detE;
-    p->geometry.matrix_E[1][2] = (E[0][2] * E[1][0] - E[0][0] * E[1][2]) / detE;
-    p->geometry.matrix_E[2][0] = (E[1][0] * E[2][1] - E[1][1] * E[2][0]) / detE;
-    p->geometry.matrix_E[2][1] = (E[0][1] * E[2][0] - E[0][0] * E[2][1]) / detE;
-    p->geometry.matrix_E[2][2] = (E[0][0] * E[1][1] - E[0][1] * E[1][0]) / detE;
-  } else {
-    /* if the E-matrix is not well behaved, we cannot use it */
-    p->geometry.matrix_E[0][0] = 0.0f;
-    p->geometry.matrix_E[0][1] = 0.0f;
-    p->geometry.matrix_E[0][2] = 0.0f;
-    p->geometry.matrix_E[1][0] = 0.0f;
-    p->geometry.matrix_E[1][1] = 0.0f;
-    p->geometry.matrix_E[1][2] = 0.0f;
-    p->geometry.matrix_E[2][0] = 0.0f;
-    p->geometry.matrix_E[2][1] = 0.0f;
-    p->geometry.matrix_E[2][2] = 0.0f;
-  }
-
-#ifdef SPH_GRADIENTS
-  /* finalize gradients by multiplying with volume */
-  p->primitives.gradients.rho[0] *= ih2 * ih2 * volume;
-  p->primitives.gradients.rho[1] *= ih2 * ih2 * volume;
-  p->primitives.gradients.rho[2] *= ih2 * ih2 * volume;
-
-  p->primitives.gradients.v[0][0] *= ih2 * ih2 * volume;
-  p->primitives.gradients.v[0][1] *= ih2 * ih2 * volume;
-  p->primitives.gradients.v[0][2] *= ih2 * ih2 * volume;
-
-  p->primitives.gradients.v[1][0] *= ih2 * ih2 * volume;
-  p->primitives.gradients.v[1][1] *= ih2 * ih2 * volume;
-  p->primitives.gradients.v[1][2] *= ih2 * ih2 * volume;
-
-  p->primitives.gradients.v[2][0] *= ih2 * ih2 * volume;
-  p->primitives.gradients.v[2][1] *= ih2 * ih2 * volume;
-  p->primitives.gradients.v[2][2] *= ih2 * ih2 * volume;
-
-  p->primitives.gradients.P[0] *= ih2 * ih2 * volume;
-  p->primitives.gradients.P[1] *= ih2 * ih2 * volume;
-  p->primitives.gradients.P[2] *= ih2 * ih2 * volume;
-
-/* slope limiter */
-#ifdef SLOPE_LIMITER
-  gradrho[0] = p->primitives.gradients.rho[0];
-  gradrho[1] = p->primitives.gradients.rho[1];
-  gradrho[2] = p->primitives.gradients.rho[2];
-
-  gradv[0][0] = p->primitives.gradients.v[0][0];
-  gradv[0][1] = p->primitives.gradients.v[0][1];
-  gradv[0][2] = p->primitives.gradients.v[0][2];
-
-  gradv[1][0] = p->primitives.gradients.v[1][0];
-  gradv[1][1] = p->primitives.gradients.v[1][1];
-  gradv[1][2] = p->primitives.gradients.v[1][2];
-
-  gradv[2][0] = p->primitives.gradients.v[2][0];
-  gradv[2][1] = p->primitives.gradients.v[2][1];
-  gradv[2][2] = p->primitives.gradients.v[2][2];
-
-  gradP[0] = p->primitives.gradients.P[0];
-  gradP[1] = p->primitives.gradients.P[1];
-  gradP[2] = p->primitives.gradients.P[2];
-
-  gradtrue = sqrtf(gradrho[0] * gradrho[0] + gradrho[1] * gradrho[1] +
-                   gradrho[2] * gradrho[2]);
-  /* gradtrue might be zero. In this case, there is no gradient and we don't
-     need to slope limit anything... */
-  if (gradtrue) {
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.rho[1] - p->primitives.rho;
-    gradmin = p->primitives.rho - p->primitives.limiter.rho[0];
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.rho[0] *= alpha;
-    p->primitives.gradients.rho[1] *= alpha;
-    p->primitives.gradients.rho[2] *= alpha;
-  }
+  const float volume = 1.f / (ihdim * (p->geometry.volume + kernel_root));
+  p->geometry.volume = volume;
 
-  gradtrue = sqrtf(gradv[0][0] * gradv[0][0] + gradv[0][1] * gradv[0][1] +
-                   gradv[0][2] * gradv[0][2]);
-  if (gradtrue) {
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.v[0][1] - p->primitives.v[0];
-    gradmin = p->primitives.v[0] - p->primitives.limiter.v[0][0];
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.v[0][0] *= alpha;
-    p->primitives.gradients.v[0][1] *= alpha;
-    p->primitives.gradients.v[0][2] *= alpha;
-  }
-
-  gradtrue = sqrtf(gradv[1][0] * gradv[1][0] + gradv[1][1] * gradv[1][1] +
-                   gradv[1][2] * gradv[1][2]);
-  if (gradtrue) {
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.v[1][1] - p->primitives.v[1];
-    gradmin = p->primitives.v[1] - p->primitives.limiter.v[1][0];
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.v[1][0] *= alpha;
-    p->primitives.gradients.v[1][1] *= alpha;
-    p->primitives.gradients.v[1][2] *= alpha;
-  }
+  /* we multiply with the smoothing kernel normalization */
+  p->geometry.matrix_E[0][0] = ihdim * p->geometry.matrix_E[0][0];
+  p->geometry.matrix_E[0][1] = ihdim * p->geometry.matrix_E[0][1];
+  p->geometry.matrix_E[0][2] = ihdim * p->geometry.matrix_E[0][2];
+  p->geometry.matrix_E[1][0] = ihdim * p->geometry.matrix_E[1][0];
+  p->geometry.matrix_E[1][1] = ihdim * p->geometry.matrix_E[1][1];
+  p->geometry.matrix_E[1][2] = ihdim * p->geometry.matrix_E[1][2];
+  p->geometry.matrix_E[2][0] = ihdim * p->geometry.matrix_E[2][0];
+  p->geometry.matrix_E[2][1] = ihdim * p->geometry.matrix_E[2][1];
+  p->geometry.matrix_E[2][2] = ihdim * p->geometry.matrix_E[2][2];
 
-  gradtrue = sqrtf(gradv[2][0] * gradv[2][0] + gradv[2][1] * gradv[2][1] +
-                   gradv[2][2] * gradv[2][2]);
-  if (gradtrue) {
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.v[2][1] - p->primitives.v[2];
-    gradmin = p->primitives.v[2] - p->primitives.limiter.v[2][0];
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.v[2][0] *= alpha;
-    p->primitives.gradients.v[2][1] *= alpha;
-    p->primitives.gradients.v[2][2] *= alpha;
-  }
+  invert_dimension_by_dimension_matrix(p->geometry.matrix_E);
 
-  gradtrue =
-      sqrtf(gradP[0] * gradP[0] + gradP[1] * gradP[1] + gradP[2] * gradP[2]);
-  if (gradtrue) {
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.P[1] - p->primitives.P;
-    gradmin = p->primitives.P - p->primitives.limiter.P[0];
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.P[0] *= alpha;
-    p->primitives.gradients.P[1] *= alpha;
-    p->primitives.gradients.P[2] *= alpha;
-  }
-#endif  // SLOPE_LIMITER
-#else   // SPH_GRADIENTS
-  p->primitives.gradients.rho[0] = 0.0f;
-  p->primitives.gradients.rho[1] = 0.0f;
-  p->primitives.gradients.rho[2] = 0.0f;
-
-  p->primitives.gradients.v[0][0] = 0.0f;
-  p->primitives.gradients.v[0][1] = 0.0f;
-  p->primitives.gradients.v[0][2] = 0.0f;
-
-  p->primitives.gradients.v[1][0] = 0.0f;
-  p->primitives.gradients.v[1][1] = 0.0f;
-  p->primitives.gradients.v[1][2] = 0.0f;
-
-  p->primitives.gradients.v[2][0] = 0.0f;
-  p->primitives.gradients.v[2][1] = 0.0f;
-  p->primitives.gradients.v[2][2] = 0.0f;
-
-  p->primitives.gradients.P[0] = 0.0f;
-  p->primitives.gradients.P[1] = 0.0f;
-  p->primitives.gradients.P[2] = 0.0f;
-
-  p->primitives.limiter.rho[0] = FLT_MAX;
-  p->primitives.limiter.rho[1] = -FLT_MAX;
-  p->primitives.limiter.v[0][0] = FLT_MAX;
-  p->primitives.limiter.v[0][1] = -FLT_MAX;
-  p->primitives.limiter.v[1][0] = FLT_MAX;
-  p->primitives.limiter.v[1][1] = -FLT_MAX;
-  p->primitives.limiter.v[2][0] = FLT_MAX;
-  p->primitives.limiter.v[2][1] = -FLT_MAX;
-  p->primitives.limiter.P[0] = FLT_MAX;
-  p->primitives.limiter.P[1] = -FLT_MAX;
-
-  p->primitives.limiter.maxr = -FLT_MAX;
-#endif  // SPH_GRADIENTS
+  hydro_gradients_init(p);
 
   /* compute primitive variables */
   /* eqns (3)-(5) */
-  m = p->conserved.mass;
-  if (m) {
+  const float m = p->conserved.mass;
+  if (m > 0.f) {
+    float momentum[3];
     momentum[0] = p->conserved.momentum[0];
     momentum[1] = p->conserved.momentum[1];
     momentum[2] = p->conserved.momentum[2];
-#ifndef THERMAL_ENERGY
-    momentum2 = (momentum[0] * momentum[0] + momentum[1] * momentum[1] +
-                 momentum[2] * momentum[2]);
-#endif
-    energy = p->conserved.energy;
     p->primitives.rho = m / volume;
     p->primitives.v[0] = momentum[0] / m;
     p->primitives.v[1] = momentum[1] / m;
     p->primitives.v[2] = momentum[2] / m;
-#ifndef THERMAL_ENERGY
-    p->primitives.P =
-        (const_hydro_gamma - 1.) * (energy - 0.5 * momentum2 / m) / volume;
-#else
-    p->primitives.P = (const_hydro_gamma - 1.) * energy / volume;
-#endif
+    const float energy = p->conserved.energy;
+    p->primitives.P = hydro_gamma_minus_one * energy / volume;
   }
 }
 
 /**
- * @brief Finishes the gradient calculation.
+ * @brief Prepare a particle for the gradient calculation.
  *
- * @param p The particle to act upon
+ * The name of this method is confusing, as this method is really called after
+ * the density loop and before the gradient loop.
+ *
+ * We use it to set the physical timestep for the particle and to copy the
+ * actual velocities, which we need to boost our interfaces during the flux
+ * calculation. We also initialize the variables used for the time step
+ * calculation.
+ *
+ * @param p The particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param ti_current Current integer time.
+ * @param timeBase Conversion factor between integer time and physical time.
  */
-__attribute__((always_inline)) INLINE static void hydro_end_gradient(
-    struct part* p) {
-
-#ifndef SPH_GRADIENTS
-  float h, ih, ih2, ih3;
-#ifdef SLOPE_LIMITER
-  GFLOAT gradrho[3], gradv[3][3], gradP[3];
-  GFLOAT gradtrue, gradmax, gradmin, alpha;
-#endif
-
-  /* add kernel normalization to gradients */
-  h = p->h;
-  ih = 1.0f / h;
-  ih2 = ih * ih;
-  ih3 = ih * ih2;
-
-  p->primitives.gradients.rho[0] *= ih3;
-  p->primitives.gradients.rho[1] *= ih3;
-  p->primitives.gradients.rho[2] *= ih3;
-
-  p->primitives.gradients.v[0][0] *= ih3;
-  p->primitives.gradients.v[0][1] *= ih3;
-  p->primitives.gradients.v[0][2] *= ih3;
-  p->primitives.gradients.v[1][0] *= ih3;
-  p->primitives.gradients.v[1][1] *= ih3;
-  p->primitives.gradients.v[1][2] *= ih3;
-  p->primitives.gradients.v[2][0] *= ih3;
-  p->primitives.gradients.v[2][1] *= ih3;
-  p->primitives.gradients.v[2][2] *= ih3;
-
-  p->primitives.gradients.P[0] *= ih3;
-  p->primitives.gradients.P[1] *= ih3;
-  p->primitives.gradients.P[2] *= ih3;
-
-/* slope limiter */
-#ifdef SLOPE_LIMITER
-  gradrho[0] = p->primitives.gradients.rho[0];
-  gradrho[1] = p->primitives.gradients.rho[1];
-  gradrho[2] = p->primitives.gradients.rho[2];
-
-  gradv[0][0] = p->primitives.gradients.v[0][0];
-  gradv[0][1] = p->primitives.gradients.v[0][1];
-  gradv[0][2] = p->primitives.gradients.v[0][2];
-
-  gradv[1][0] = p->primitives.gradients.v[1][0];
-  gradv[1][1] = p->primitives.gradients.v[1][1];
-  gradv[1][2] = p->primitives.gradients.v[1][2];
-
-  gradv[2][0] = p->primitives.gradients.v[2][0];
-  gradv[2][1] = p->primitives.gradients.v[2][1];
-  gradv[2][2] = p->primitives.gradients.v[2][2];
-
-  gradP[0] = p->primitives.gradients.P[0];
-  gradP[1] = p->primitives.gradients.P[1];
-  gradP[2] = p->primitives.gradients.P[2];
-
-  gradtrue = gradrho[0] * gradrho[0] + gradrho[1] * gradrho[1] +
-             gradrho[2] * gradrho[2];
-  /* gradtrue might be zero. In this case, there is no gradient and we don't
-     need to slope limit anything... */
-  if (gradtrue) {
-    gradtrue = sqrtf(gradtrue);
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.rho[1] - p->primitives.rho;
-    gradmin = p->primitives.rho - p->primitives.limiter.rho[0];
-    /* gradmin and gradmax might be negative if the value of the current
-       particle is larger/smaller than all neighbouring values */
-    gradmax = fabs(gradmax);
-    gradmin = fabs(gradmin);
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.rho[0] *= alpha;
-    p->primitives.gradients.rho[1] *= alpha;
-    p->primitives.gradients.rho[2] *= alpha;
-  }
-
-  gradtrue = gradv[0][0] * gradv[0][0] + gradv[0][1] * gradv[0][1] +
-             gradv[0][2] * gradv[0][2];
-  if (gradtrue) {
-    gradtrue = sqrtf(gradtrue);
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.v[0][1] - p->primitives.v[0];
-    gradmin = p->primitives.v[0] - p->primitives.limiter.v[0][0];
-    gradmax = fabs(gradmax);
-    gradmin = fabs(gradmin);
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.v[0][0] *= alpha;
-    p->primitives.gradients.v[0][1] *= alpha;
-    p->primitives.gradients.v[0][2] *= alpha;
-  }
-
-  gradtrue = gradv[1][0] * gradv[1][0] + gradv[1][1] * gradv[1][1] +
-             gradv[1][2] * gradv[1][2];
-  if (gradtrue) {
-    gradtrue = sqrtf(gradtrue);
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.v[1][1] - p->primitives.v[1];
-    gradmin = p->primitives.v[1] - p->primitives.limiter.v[1][0];
-    gradmax = fabs(gradmax);
-    gradmin = fabs(gradmin);
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.v[1][0] *= alpha;
-    p->primitives.gradients.v[1][1] *= alpha;
-    p->primitives.gradients.v[1][2] *= alpha;
-  }
+__attribute__((always_inline)) INLINE static void hydro_prepare_force(
+    struct part* restrict p, struct xpart* restrict xp, int ti_current,
+    double timeBase) {
 
-  gradtrue = gradv[2][0] * gradv[2][0] + gradv[2][1] * gradv[2][1] +
-             gradv[2][2] * gradv[2][2];
-  if (gradtrue) {
-    gradtrue = sqrtf(gradtrue);
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.v[2][1] - p->primitives.v[2];
-    gradmin = p->primitives.v[2] - p->primitives.limiter.v[2][0];
-    gradmax = fabs(gradmax);
-    gradmin = fabs(gradmin);
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.v[2][0] *= alpha;
-    p->primitives.gradients.v[2][1] *= alpha;
-    p->primitives.gradients.v[2][2] *= alpha;
-  }
+  /* Set the physical time step */
+  p->force.dt = (p->ti_end - p->ti_begin) * timeBase;
 
-  gradtrue = gradP[0] * gradP[0] + gradP[1] * gradP[1] + gradP[2] * gradP[2];
-  if (gradtrue) {
-    gradtrue = sqrtf(gradtrue);
-    gradtrue *= p->primitives.limiter.maxr;
-    gradmax = p->primitives.limiter.P[1] - p->primitives.P;
-    gradmin = p->primitives.P - p->primitives.limiter.P[0];
-    gradmax = fabs(gradmax);
-    gradmin = fabs(gradmin);
-    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
-    p->primitives.gradients.P[0] *= alpha;
-    p->primitives.gradients.P[1] *= alpha;
-    p->primitives.gradients.P[2] *= alpha;
-  }
-#endif  // SLOPE_LIMITER
+  /* Initialize time step criterion variables */
+  p->timestepvars.vmax = 0.0f;
 
-#endif  // SPH_GRADIENTS
+  /* Set the actual velocity of the particle */
+  p->force.v_full[0] = xp->v_full[0];
+  p->force.v_full[1] = xp->v_full[1];
+  p->force.v_full[2] = xp->v_full[2];
 }
 
 /**
- * @brief Prepare a particle for the fluxes calculation.
+ * @brief Finishes the gradient calculation.
  *
- * @param p The particle to act upon
- * @param xp The extended particle data to act upon
+ * Just a wrapper around hydro_gradients_finalize, which can be an empty method,
+ * in which case no gradients are used.
+ *
+ * This method also initializes the force loop variables.
+ *
+ * @param p The particle to act upon.
  */
-__attribute__((always_inline)) INLINE static void hydro_prepare_fluxes(
-    struct part* p, struct xpart* xp) {
+__attribute__((always_inline)) INLINE static void hydro_end_gradient(
+    struct part* p) {
 
-  /* initialize variables used for timestep calculation */
-  p->timestepvars.vmax = 0.0f;
+  hydro_gradients_finalize(p);
+
+  p->gravity.mflux[0] = 0.0f;
+  p->gravity.mflux[1] = 0.0f;
+  p->gravity.mflux[2] = 0.0f;
 }
 
 /**
  * @brief Reset acceleration fields of a particle
  *
- * Resets all hydro acceleration and time derivative fields in preparation
- * for the sums taking place in the variaous force tasks
+ * This is actually not necessary for GIZMO, since we just set the accelerations
+ * after the flux calculation.
  *
- * @param p The particle to act upon
+ * @param p The particle to act upon.
  */
 __attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
     struct part* p) {
 
-  /* figure out what to put here */
+  /* Reset the acceleration. */
+  p->a_hydro[0] = 0.0f;
+  p->a_hydro[1] = 0.0f;
+  p->a_hydro[2] = 0.0f;
+
+  /* Reset the time derivatives. */
+  p->force.h_dt = 0.0f;
 }
 
 /**
- * @brief Finishes the fluxes calculation.
+ * @brief Converts the hydrodynamic variables from the initial condition file to
+ * conserved variables that can be used during the integration
  *
- * Multiplies the forces and accelerationsby the appropiate constants
+ * Requires the volume to be known.
  *
- * @param p The particle to act upon
+ * The initial condition file contains a mixture of primitive and conserved
+ * variables. Mass is a conserved variable, and we just copy the particle
+ * mass into the corresponding conserved quantity. We need the volume to
+ * also derive a density, which is then used to convert the internal energy
+ * to a pressure. However, we do not actually use these variables anymore.
+ * We do need to initialize the linear momentum, based on the mass and the
+ * velocity of the particle.
+ *
+ * @param p The particle to act upon.
  */
-__attribute__((always_inline)) INLINE static void hydro_end_fluxes(
+__attribute__((always_inline)) INLINE static void hydro_convert_quantities(
     struct part* p) {
 
-  /* do nothing */
+  const float volume = p->geometry.volume;
+  const float m = p->conserved.mass;
+  p->primitives.rho = m / volume;
+
+  p->conserved.momentum[0] = m * p->primitives.v[0];
+  p->conserved.momentum[1] = m * p->primitives.v[1];
+  p->conserved.momentum[2] = m * p->primitives.v[2];
+
+  p->primitives.P =
+      hydro_gamma_minus_one * p->conserved.energy * p->primitives.rho;
+
+  p->conserved.energy *= m;
 }
 
 /**
- * @brief Converts hydro quantity of a particle
+ * @brief Extra operations to be done during the drift
  *
- * Requires the volume to be known
+ * @param p Particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param dt The drift time-step.
+ * @param t0 Integer start time of the drift interval.
+ * @param t1 Integer end time of the drift interval.
+ * @param timeBase Conversion factor between integer and physical time.
+ */
+__attribute__((always_inline)) INLINE static void hydro_predict_extra(
+    struct part* p, struct xpart* xp, float dt, int t0, int t1,
+    double timeBase) {
+
+  const float h_inv = 1.0f / p->h;
+
+  /* Predict smoothing length */
+  const float w1 = p->force.h_dt * h_inv * dt;
+  if (fabsf(w1) < 0.2f)
+    p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */
+  else
+    p->h *= expf(w1);
+
+  const float w2 = -hydro_dimension * w1;
+  if (fabsf(w2) < 0.2f) {
+    p->primitives.rho *= approx_expf(w2);
+  } else {
+    p->primitives.rho *= expf(w2);
+  }
+
+  p->primitives.v[0] += (p->a_hydro[0] + p->gravity.old_a[0]) * dt;
+  p->primitives.v[1] += (p->a_hydro[1] + p->gravity.old_a[1]) * dt;
+  p->primitives.v[2] += (p->a_hydro[2] + p->gravity.old_a[2]) * dt;
+  const float u = p->conserved.energy + p->du_dt * dt;
+  p->primitives.P =
+      hydro_gamma_minus_one * u * p->primitives.rho / p->conserved.mass;
+}
+
+/**
+ * @brief Set the particle acceleration after the flux loop
  *
- * @param p The particle to act upon
+ * We use the new conserved variables to calculate the new velocity of the
+ * particle, and use that to derive the change of the velocity over the particle
+ * time step.
+ *
+ * If the particle time step is zero, we set the accelerations to zero. This
+ * should only happen at the start of the simulation.
+ *
+ * @param p Particle to act upon.
  */
-__attribute__((always_inline)) INLINE static void hydro_convert_quantities(
+__attribute__((always_inline)) INLINE static void hydro_end_force(
     struct part* p) {
 
-  float volume;
-  GFLOAT m;
-  GFLOAT momentum[3];
-#ifndef THERMAL_ENERGY
-  GFLOAT momentum2;
-#endif
-  volume = p->geometry.volume;
+  /* Add normalization to h_dt. */
+  p->force.h_dt *= p->h * hydro_dimension_inv;
 
-  /* set hydro velocities */
-  p->primitives.v[0] = p->v[0];
-  p->primitives.v[1] = p->v[1];
-  p->primitives.v[2] = p->v[2];
-  /* P actually contains internal energy at this point */
-  p->primitives.P *= (const_hydro_gamma - 1.) * p->primitives.rho;
-
-  p->conserved.mass = m = p->primitives.rho * volume;
-  p->conserved.momentum[0] = momentum[0] = m * p->primitives.v[0];
-  p->conserved.momentum[1] = momentum[1] = m * p->primitives.v[1];
-  p->conserved.momentum[2] = momentum[2] = m * p->primitives.v[2];
-#ifndef THERMAL_ENERGY
-  momentum2 = momentum[0] * momentum[0] + momentum[1] * momentum[1] +
-              momentum[2] * momentum[2];
-  p->conserved.energy =
-      p->primitives.P / (const_hydro_gamma - 1.) * volume + 0.5 * momentum2 / m;
-#else
-  p->conserved.energy = p->primitives.P / (const_hydro_gamma - 1.) * volume;
-#endif
+  /* Set the hydro acceleration, based on the new momentum and mass */
+  /* NOTE: the momentum and mass are only correct for active particles, since
+           only active particles have received flux contributions from all their
+           neighbours. Since this method is only called for active particles,
+           this is indeed the case. */
+  if (p->force.dt) {
+    float mnew;
+    float vnew[3];
+
+    mnew = p->conserved.mass + p->conserved.flux.mass;
+    vnew[0] = (p->conserved.momentum[0] + p->conserved.flux.momentum[0]) / mnew;
+    vnew[1] = (p->conserved.momentum[1] + p->conserved.flux.momentum[1]) / mnew;
+    vnew[2] = (p->conserved.momentum[2] + p->conserved.flux.momentum[2]) / mnew;
+
+    p->a_hydro[0] = (vnew[0] - p->force.v_full[0]) / p->force.dt;
+    p->a_hydro[1] = (vnew[1] - p->force.v_full[1]) / p->force.dt;
+    p->a_hydro[2] = (vnew[2] - p->force.v_full[2]) / p->force.dt;
+
+    p->du_dt = p->conserved.flux.energy / p->force.dt;
+  } else {
+    p->a_hydro[0] = 0.0f;
+    p->a_hydro[1] = 0.0f;
+    p->a_hydro[2] = 0.0f;
+
+    p->du_dt = 0.0f;
+  }
 }
 
-// MATTHIEU
-__attribute__((always_inline)) INLINE static void hydro_end_density(
-    struct part* p, float time) {}
-__attribute__((always_inline)) INLINE static void hydro_prepare_force(
-    struct part* p, struct xpart* xp, int ti_current, double timeBase) {}
-__attribute__((always_inline)) INLINE static void hydro_predict_extra(
-    struct part* p, struct xpart* xp, int t0, int t1, double timeBase) {}
-__attribute__((always_inline)) INLINE static void hydro_end_force(
-    struct part* p) {}
+/**
+ * @brief Extra operations done during the kick
+ *
+ * Not used for GIZMO.
+ *
+ * @param p Particle to act upon.
+ * @param xp Extended particle data to act upon.
+ * @param dt Physical time step.
+ * @param half_dt Half the physical time step.
+ */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
-    struct part* p, struct xpart* xp, float dt, float half_dt) {}
+    struct part* p, struct xpart* xp, float dt, float half_dt) {
+
+  float oldm, oldp[3], anew[3];
+
+  /* Retrieve the current value of the gravitational acceleration from the
+     gpart. We are only allowed to do this because this is the kick. We still
+     need to check whether gpart exists though.*/
+  if (p->gpart) {
+    anew[0] = p->gpart->a_grav[0];
+    anew[1] = p->gpart->a_grav[1];
+    anew[2] = p->gpart->a_grav[2];
+
+    /* Copy the old mass and momentum before updating the conserved variables */
+    oldm = p->conserved.mass;
+    oldp[0] = p->conserved.momentum[0];
+    oldp[1] = p->conserved.momentum[1];
+    oldp[2] = p->conserved.momentum[2];
+  }
+
+  /* Update conserved variables. */
+  p->conserved.mass += p->conserved.flux.mass;
+  p->conserved.momentum[0] += p->conserved.flux.momentum[0];
+  p->conserved.momentum[1] += p->conserved.flux.momentum[1];
+  p->conserved.momentum[2] += p->conserved.flux.momentum[2];
+  p->conserved.energy += p->conserved.flux.energy;
+
+  /* Add gravity. We only do this if we have gravity activated. */
+  if (p->gpart) {
+    p->conserved.momentum[0] +=
+        half_dt * (oldm * p->gravity.old_a[0] + p->conserved.mass * anew[0]);
+    p->conserved.momentum[1] +=
+        half_dt * (oldm * p->gravity.old_a[1] + p->conserved.mass * anew[1]);
+    p->conserved.momentum[2] +=
+        half_dt * (oldm * p->gravity.old_a[2] + p->conserved.mass * anew[2]);
+
+    float paold, panew;
+    paold = oldp[0] * p->gravity.old_a[0] + oldp[1] * p->gravity.old_a[1] +
+            oldp[2] * p->gravity.old_a[2];
+    panew = p->conserved.momentum[0] * anew[0] +
+            p->conserved.momentum[1] * anew[1] +
+            p->conserved.momentum[2] * anew[2];
+    p->conserved.energy += half_dt * (paold + panew);
+
+    float fluxaold, fluxanew;
+    fluxaold = p->gravity.old_a[0] * p->gravity.old_mflux[0] +
+               p->gravity.old_a[1] * p->gravity.old_mflux[1] +
+               p->gravity.old_a[2] * p->gravity.old_mflux[2];
+    fluxanew = anew[0] * p->gravity.mflux[0] + anew[1] * p->gravity.mflux[1] +
+               anew[2] * p->gravity.mflux[2];
+    p->conserved.energy += half_dt * (fluxaold + fluxanew);
+
+    /* Store gravitational acceleration and mass flux for next step */
+    p->gravity.old_a[0] = anew[0];
+    p->gravity.old_a[1] = anew[1];
+    p->gravity.old_a[2] = anew[2];
+    p->gravity.old_mflux[0] = p->gravity.mflux[0];
+    p->gravity.old_mflux[1] = p->gravity.mflux[1];
+    p->gravity.old_mflux[2] = p->gravity.mflux[2];
+  }
+
+  /* reset fluxes */
+  /* we can only do this here, since we need to keep the fluxes for inactive
+     particles */
+  p->conserved.flux.mass = 0.0f;
+  p->conserved.flux.momentum[0] = 0.0f;
+  p->conserved.flux.momentum[1] = 0.0f;
+  p->conserved.flux.momentum[2] = 0.0f;
+  p->conserved.flux.energy = 0.0f;
+}
+
+/**
+ * @brief Returns the internal energy of a particle
+ *
+ * @param p The particle of interest.
+ * @param dt Time since the last kick.
+ */
 __attribute__((always_inline)) INLINE static float hydro_get_internal_energy(
-    struct part* p) {
-  return 0.f;
+    const struct part* restrict p, float dt) {
+
+  return p->primitives.P / hydro_gamma_minus_one / p->primitives.rho;
+}
+
+/**
+ * @brief Returns the entropy of a particle
+ *
+ * @param p The particle of interest.
+ * @param dt Time since the last kick.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_entropy(
+    const struct part* restrict p, float dt) {
+
+  return p->primitives.P / pow_gamma(p->primitives.rho);
+}
+
+/**
+ * @brief Returns the sound speed of a particle
+ *
+ * @param p The particle of interest.
+ * @param dt Time since the last kick.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
+    const struct part* restrict p, float dt) {
+
+  return sqrtf(hydro_gamma * p->primitives.P / p->primitives.rho);
+}
+
+/**
+ * @brief Returns the pressure of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_pressure(
+    const struct part* restrict p, float dt) {
+
+  return p->primitives.P;
+}
+
+/**
+ * @brief Returns the mass of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_mass(
+    const struct part* restrict p) {
+
+  return p->conserved.mass;
+}
+
+/**
+ * @brief Returns the density of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_density(
+    const struct part* restrict p) {
+
+  return p->primitives.rho;
 }
diff --git a/src/hydro/Gizmo/hydro_debug.h b/src/hydro/Gizmo/hydro_debug.h
index 365d85a2f651cf98b0713e8d82f11ae70fa9beaa..f4c071023a627b177fd06373856f25611fc9485d 100644
--- a/src/hydro/Gizmo/hydro_debug.h
+++ b/src/hydro/Gizmo/hydro_debug.h
@@ -18,10 +18,65 @@
  ******************************************************************************/
 
 __attribute__((always_inline)) INLINE static void hydro_debug_particle(
-    struct part* p, struct xpart* xp) {
+    const struct part* p, const struct xpart* xp) {
   printf(
       "x=[%.16e,%.16e,%.16e], "
-      "v=[%.3e,%.3e,%.3e], a=[%.3e,%.3e,%.3e], volume=%.3e\n",
+      "v=[%.3e,%.3e,%.3e], "
+      "a=[%.3e,%.3e,%.3e], "
+      "h=%.3e, "
+      "ti_begin=%d, "
+      "ti_end=%d, "
+      "primitives={"
+      "v=[%.3e,%.3e,%.3e], "
+      "rho=%.3e, "
+      "P=%.3e, "
+      "gradients={"
+      "rho=[%.3e,%.3e,%.3e], "
+      "v=[[%.3e,%.3e,%.3e],[%.3e,%.3e,%.3e],[%.3e,%.3e,%.3e]], "
+      "P=[%.3e,%.3e,%.3e]}, "
+      "limiter={"
+      "rho=[%.3e,%.3e], "
+      "v=[[%.3e,%.3e],[%.3e,%.3e],[%.3e,%.3e]], "
+      "P=[%.3e,%.3e], "
+      "maxr=%.3e}}, "
+      "conserved={"
+      "momentum=[%.3e,%.3e,%.3e], "
+      "mass=%.3e, "
+      "energy=%.3e}, "
+      "geometry={"
+      "volume=%.3e, "
+      "matrix_E=[[%.3e,%.3e,%.3e],[%.3e,%.3e,%.3e],[%.3e,%.3e,%.3e]]}, "
+      "timestepvars={"
+      "vmax=%.3e}, "
+      "density={"
+      "div_v=%.3e, "
+      "wcount_dh=%.3e, "
+      "curl_v=[%.3e,%.3e,%.3e], "
+      "wcount=%.3e}\n",
       p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->a_hydro[0],
-      p->a_hydro[1], p->a_hydro[2], p->geometry.volume);
+      p->a_hydro[1], p->a_hydro[2], p->h, p->ti_begin, p->ti_end,
+      p->primitives.v[0], p->primitives.v[1], p->primitives.v[2],
+      p->primitives.rho, p->primitives.P, p->primitives.gradients.rho[0],
+      p->primitives.gradients.rho[1], p->primitives.gradients.rho[2],
+      p->primitives.gradients.v[0][0], p->primitives.gradients.v[0][1],
+      p->primitives.gradients.v[0][2], p->primitives.gradients.v[1][0],
+      p->primitives.gradients.v[1][1], p->primitives.gradients.v[1][2],
+      p->primitives.gradients.v[2][0], p->primitives.gradients.v[2][1],
+      p->primitives.gradients.v[2][2], p->primitives.gradients.P[0],
+      p->primitives.gradients.P[1], p->primitives.gradients.P[2],
+      p->primitives.limiter.rho[0], p->primitives.limiter.rho[1],
+      p->primitives.limiter.v[0][0], p->primitives.limiter.v[0][1],
+      p->primitives.limiter.v[1][0], p->primitives.limiter.v[1][1],
+      p->primitives.limiter.v[2][0], p->primitives.limiter.v[2][1],
+      p->primitives.limiter.P[0], p->primitives.limiter.P[1],
+      p->primitives.limiter.maxr, p->conserved.momentum[0],
+      p->conserved.momentum[1], p->conserved.momentum[2], p->conserved.mass,
+      p->conserved.energy, p->geometry.volume, p->geometry.matrix_E[0][0],
+      p->geometry.matrix_E[0][1], p->geometry.matrix_E[0][2],
+      p->geometry.matrix_E[1][0], p->geometry.matrix_E[1][1],
+      p->geometry.matrix_E[1][2], p->geometry.matrix_E[2][0],
+      p->geometry.matrix_E[2][1], p->geometry.matrix_E[2][2],
+      p->timestepvars.vmax, p->density.div_v, p->density.wcount_dh,
+      p->density.curl_v[0], p->density.curl_v[1], p->density.curl_v[2],
+      p->density.wcount);
 }
diff --git a/src/hydro/Gizmo/hydro_gradients.h b/src/hydro/Gizmo/hydro_gradients.h
new file mode 100644
index 0000000000000000000000000000000000000000..90448efc7adb8ccecaaa98c7388f89eaa8d16bcd
--- /dev/null
+++ b/src/hydro/Gizmo/hydro_gradients.h
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 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/>.
+ *
+ ******************************************************************************/
+
+#ifndef SWIFT_HYDRO_GRADIENTS_H
+#define SWIFT_HYDRO_GRADIENTS_H
+
+#include "hydro_slope_limiters.h"
+
+#if defined(GRADIENTS_SPH)
+
+#define HYDRO_GRADIENT_IMPLEMENTATION "SPH gradients (Price 2012)"
+#include "hydro_gradients_sph.h"
+
+#elif defined(GRADIENTS_GIZMO)
+
+#define HYDRO_GRADIENT_IMPLEMENTATION "GIZMO gradients (Hopkins 2015)"
+#include "hydro_gradients_gizmo.h"
+
+#else
+
+/* No gradients. Perfectly acceptable, but we have to provide empty functions */
+#define HYDRO_GRADIENT_IMPLEMENTATION "No gradients (first order scheme)"
+
+/**
+ * @brief Initialize gradient variables
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_init(
+    struct part* p) {}
+
+/**
+ * @brief Gradient calculations done during the neighbour loop
+ *
+ * @param r2 Squared distance between the two particles.
+ * @param dx Distance vector (pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_collect(
+    float r2, float* dx, float hi, float hj, struct part* pi, struct part* pj) {
+}
+
+/**
+ * @brief Gradient calculations done during the neighbour loop: non-symmetric
+ * version
+ *
+ * @param r2 Squared distance between the two particles.
+ * @param dx Distance vector (pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_gradients_nonsym_collect(float r2, float* dx, float hi, float hj,
+                               struct part* pi, struct part* pj) {}
+
+/**
+ * @brief Finalize the gradient variables after all data have been collected
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_finalize(
+    struct part* p) {}
+
+#endif
+
+/**
+ * @brief Gradients reconstruction. Is the same for all gradient types (although
+ * gradients_none does nothing, since all gradients are zero -- are they?).
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_predict(
+    struct part* pi, struct part* pj, float hi, float hj, float* dx, float r,
+    float* xij_i, float* Wi, float* Wj, float mindt) {
+
+  float dWi[5], dWj[5];
+  float xij_j[3];
+  int k;
+  float xfac;
+
+  /* perform gradient reconstruction in space and time */
+  /* space */
+  /* Compute interface position (relative to pj, since we don't need the actual
+   * position) */
+  /* eqn. (8) */
+  xfac = hj / (hi + hj);
+  for (k = 0; k < 3; k++) xij_j[k] = xfac * dx[k];
+
+  dWi[0] = pi->primitives.gradients.rho[0] * xij_i[0] +
+           pi->primitives.gradients.rho[1] * xij_i[1] +
+           pi->primitives.gradients.rho[2] * xij_i[2];
+  dWi[1] = pi->primitives.gradients.v[0][0] * xij_i[0] +
+           pi->primitives.gradients.v[0][1] * xij_i[1] +
+           pi->primitives.gradients.v[0][2] * xij_i[2];
+  dWi[2] = pi->primitives.gradients.v[1][0] * xij_i[0] +
+           pi->primitives.gradients.v[1][1] * xij_i[1] +
+           pi->primitives.gradients.v[1][2] * xij_i[2];
+  dWi[3] = pi->primitives.gradients.v[2][0] * xij_i[0] +
+           pi->primitives.gradients.v[2][1] * xij_i[1] +
+           pi->primitives.gradients.v[2][2] * xij_i[2];
+  dWi[4] = pi->primitives.gradients.P[0] * xij_i[0] +
+           pi->primitives.gradients.P[1] * xij_i[1] +
+           pi->primitives.gradients.P[2] * xij_i[2];
+
+  dWj[0] = pj->primitives.gradients.rho[0] * xij_j[0] +
+           pj->primitives.gradients.rho[1] * xij_j[1] +
+           pj->primitives.gradients.rho[2] * xij_j[2];
+  dWj[1] = pj->primitives.gradients.v[0][0] * xij_j[0] +
+           pj->primitives.gradients.v[0][1] * xij_j[1] +
+           pj->primitives.gradients.v[0][2] * xij_j[2];
+  dWj[2] = pj->primitives.gradients.v[1][0] * xij_j[0] +
+           pj->primitives.gradients.v[1][1] * xij_j[1] +
+           pj->primitives.gradients.v[1][2] * xij_j[2];
+  dWj[3] = pj->primitives.gradients.v[2][0] * xij_j[0] +
+           pj->primitives.gradients.v[2][1] * xij_j[1] +
+           pj->primitives.gradients.v[2][2] * xij_j[2];
+  dWj[4] = pj->primitives.gradients.P[0] * xij_j[0] +
+           pj->primitives.gradients.P[1] * xij_j[1] +
+           pj->primitives.gradients.P[2] * xij_j[2];
+
+  hydro_slope_limit_face(Wi, Wj, dWi, dWj, xij_i, xij_j, r);
+
+  /* time */
+  dWi[0] -= 0.5 * mindt * (Wi[1] * pi->primitives.gradients.rho[0] +
+                           Wi[2] * pi->primitives.gradients.rho[1] +
+                           Wi[3] * pi->primitives.gradients.rho[2] +
+                           Wi[0] * (pi->primitives.gradients.v[0][0] +
+                                    pi->primitives.gradients.v[1][1] +
+                                    pi->primitives.gradients.v[2][2]));
+  dWi[1] -= 0.5 * mindt * (Wi[1] * pi->primitives.gradients.v[0][0] +
+                           Wi[2] * pi->primitives.gradients.v[0][1] +
+                           Wi[3] * pi->primitives.gradients.v[0][2] +
+                           pi->primitives.gradients.P[0] / Wi[0]);
+  dWi[2] -= 0.5 * mindt * (Wi[1] * pi->primitives.gradients.v[1][0] +
+                           Wi[2] * pi->primitives.gradients.v[1][1] +
+                           Wi[3] * pi->primitives.gradients.v[1][2] +
+                           pi->primitives.gradients.P[1] / Wi[0]);
+  dWi[3] -= 0.5 * mindt * (Wi[1] * pi->primitives.gradients.v[2][0] +
+                           Wi[2] * pi->primitives.gradients.v[2][1] +
+                           Wi[3] * pi->primitives.gradients.v[2][2] +
+                           pi->primitives.gradients.P[2] / Wi[0]);
+  dWi[4] -=
+      0.5 * mindt * (Wi[1] * pi->primitives.gradients.P[0] +
+                     Wi[2] * pi->primitives.gradients.P[1] +
+                     Wi[3] * pi->primitives.gradients.P[2] +
+                     hydro_gamma * Wi[4] * (pi->primitives.gradients.v[0][0] +
+                                            pi->primitives.gradients.v[1][1] +
+                                            pi->primitives.gradients.v[2][2]));
+
+  dWj[0] -= 0.5 * mindt * (Wj[1] * pj->primitives.gradients.rho[0] +
+                           Wj[2] * pj->primitives.gradients.rho[1] +
+                           Wj[3] * pj->primitives.gradients.rho[2] +
+                           Wj[0] * (pj->primitives.gradients.v[0][0] +
+                                    pj->primitives.gradients.v[1][1] +
+                                    pj->primitives.gradients.v[2][2]));
+  dWj[1] -= 0.5 * mindt * (Wj[1] * pj->primitives.gradients.v[0][0] +
+                           Wj[2] * pj->primitives.gradients.v[0][1] +
+                           Wj[3] * pj->primitives.gradients.v[0][2] +
+                           pj->primitives.gradients.P[0] / Wj[0]);
+  dWj[2] -= 0.5 * mindt * (Wj[1] * pj->primitives.gradients.v[1][0] +
+                           Wj[2] * pj->primitives.gradients.v[1][1] +
+                           Wj[3] * pj->primitives.gradients.v[1][2] +
+                           pj->primitives.gradients.P[1] / Wj[0]);
+  dWj[3] -= 0.5 * mindt * (Wj[1] * pj->primitives.gradients.v[2][0] +
+                           Wj[2] * pj->primitives.gradients.v[2][1] +
+                           Wj[3] * pj->primitives.gradients.v[2][2] +
+                           pj->primitives.gradients.P[2] / Wj[0]);
+  dWj[4] -=
+      0.5 * mindt * (Wj[1] * pj->primitives.gradients.P[0] +
+                     Wj[2] * pj->primitives.gradients.P[1] +
+                     Wj[3] * pj->primitives.gradients.P[2] +
+                     hydro_gamma * Wj[4] * (pj->primitives.gradients.v[0][0] +
+                                            pj->primitives.gradients.v[1][1] +
+                                            pj->primitives.gradients.v[2][2]));
+
+  Wi[0] += dWi[0];
+  Wi[1] += dWi[1];
+  Wi[2] += dWi[2];
+  Wi[3] += dWi[3];
+  Wi[4] += dWi[4];
+
+  Wj[0] += dWj[0];
+  Wj[1] += dWj[1];
+  Wj[2] += dWj[2];
+  Wj[3] += dWj[3];
+  Wj[4] += dWj[4];
+}
+
+#endif  // SWIFT_HYDRO_GRADIENTS_H
diff --git a/src/hydro/Gizmo/hydro_gradients_gizmo.h b/src/hydro/Gizmo/hydro_gradients_gizmo.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa6e4406b94e7a5cafcd0ca556162476003477de
--- /dev/null
+++ b/src/hydro/Gizmo/hydro_gradients_gizmo.h
@@ -0,0 +1,341 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 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/>.
+ *
+ ******************************************************************************/
+
+/**
+ * @brief Initialize gradient variables
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_init(
+    struct part *p) {
+
+  p->primitives.gradients.rho[0] = 0.0f;
+  p->primitives.gradients.rho[1] = 0.0f;
+  p->primitives.gradients.rho[2] = 0.0f;
+
+  p->primitives.gradients.v[0][0] = 0.0f;
+  p->primitives.gradients.v[0][1] = 0.0f;
+  p->primitives.gradients.v[0][2] = 0.0f;
+
+  p->primitives.gradients.v[1][0] = 0.0f;
+  p->primitives.gradients.v[1][1] = 0.0f;
+  p->primitives.gradients.v[1][2] = 0.0f;
+
+  p->primitives.gradients.v[2][0] = 0.0f;
+  p->primitives.gradients.v[2][1] = 0.0f;
+  p->primitives.gradients.v[2][2] = 0.0f;
+
+  p->primitives.gradients.P[0] = 0.0f;
+  p->primitives.gradients.P[1] = 0.0f;
+  p->primitives.gradients.P[2] = 0.0f;
+
+  hydro_slope_limit_cell_init(p);
+}
+
+/**
+ * @brief Gradient calculations done during the neighbour loop
+ *
+ * @param r2 Squared distance between the two particles.
+ * @param dx Distance vector (pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_collect(
+    float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
+
+  float r = sqrtf(r2);
+  float xi, xj;
+  float hi_inv, hj_inv;
+  float wi, wj, wi_dx, wj_dx;
+  int k, l;
+  float Bi[3][3];
+  float Bj[3][3];
+  float Wi[5], Wj[5];
+
+  /* Initialize local variables */
+  for (k = 0; k < 3; k++) {
+    for (l = 0; l < 3; l++) {
+      Bi[k][l] = pi->geometry.matrix_E[k][l];
+      Bj[k][l] = pj->geometry.matrix_E[k][l];
+    }
+  }
+  Wi[0] = pi->primitives.rho;
+  Wi[1] = pi->primitives.v[0];
+  Wi[2] = pi->primitives.v[1];
+  Wi[3] = pi->primitives.v[2];
+  Wi[4] = pi->primitives.P;
+  Wj[0] = pj->primitives.rho;
+  Wj[1] = pj->primitives.v[0];
+  Wj[2] = pj->primitives.v[1];
+  Wj[3] = pj->primitives.v[2];
+  Wj[4] = pj->primitives.P;
+
+  /* Compute kernel of pi. */
+  hi_inv = 1.0 / hi;
+  xi = r * hi_inv;
+  kernel_deval(xi, &wi, &wi_dx);
+
+  /* Compute gradients for pi */
+  /* there is a sign difference w.r.t. eqn. (6) because of the inverse
+   * definition of dx */
+  pi->primitives.gradients.rho[0] +=
+      (Wi[0] - Wj[0]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.rho[1] +=
+      (Wi[0] - Wj[0]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.rho[2] +=
+      (Wi[0] - Wj[0]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+
+  pi->primitives.gradients.v[0][0] +=
+      (Wi[1] - Wj[1]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.v[0][1] +=
+      (Wi[1] - Wj[1]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.v[0][2] +=
+      (Wi[1] - Wj[1]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+  pi->primitives.gradients.v[1][0] +=
+      (Wi[2] - Wj[2]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.v[1][1] +=
+      (Wi[2] - Wj[2]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.v[1][2] +=
+      (Wi[2] - Wj[2]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+  pi->primitives.gradients.v[2][0] +=
+      (Wi[3] - Wj[3]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.v[2][1] +=
+      (Wi[3] - Wj[3]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.v[2][2] +=
+      (Wi[3] - Wj[3]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+
+  pi->primitives.gradients.P[0] +=
+      (Wi[4] - Wj[4]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.P[1] +=
+      (Wi[4] - Wj[4]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.P[2] +=
+      (Wi[4] - Wj[4]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+
+  hydro_slope_limit_cell_collect(pi, pj, r);
+
+  /* Compute kernel of pj. */
+  hj_inv = 1.0 / hj;
+  xj = r * hj_inv;
+  kernel_deval(xj, &wj, &wj_dx);
+
+  /* Compute gradients for pj */
+  /* there is no sign difference w.r.t. eqn. (6) because dx is now what we
+   * want
+   * it to be */
+  pj->primitives.gradients.rho[0] +=
+      (Wi[0] - Wj[0]) * wj *
+      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
+  pj->primitives.gradients.rho[1] +=
+      (Wi[0] - Wj[0]) * wj *
+      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
+  pj->primitives.gradients.rho[2] +=
+      (Wi[0] - Wj[0]) * wj *
+      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
+
+  pj->primitives.gradients.v[0][0] +=
+      (Wi[1] - Wj[1]) * wj *
+      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
+  pj->primitives.gradients.v[0][1] +=
+      (Wi[1] - Wj[1]) * wj *
+      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
+  pj->primitives.gradients.v[0][2] +=
+      (Wi[1] - Wj[1]) * wj *
+      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
+  pj->primitives.gradients.v[1][0] +=
+      (Wi[2] - Wj[2]) * wj *
+      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
+  pj->primitives.gradients.v[1][1] +=
+      (Wi[2] - Wj[2]) * wj *
+      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
+  pj->primitives.gradients.v[1][2] +=
+      (Wi[2] - Wj[2]) * wj *
+      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
+  pj->primitives.gradients.v[2][0] +=
+      (Wi[3] - Wj[3]) * wj *
+      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
+  pj->primitives.gradients.v[2][1] +=
+      (Wi[3] - Wj[3]) * wj *
+      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
+  pj->primitives.gradients.v[2][2] +=
+      (Wi[3] - Wj[3]) * wj *
+      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
+
+  pj->primitives.gradients.P[0] +=
+      (Wi[4] - Wj[4]) * wj *
+      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
+  pj->primitives.gradients.P[1] +=
+      (Wi[4] - Wj[4]) * wj *
+      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
+  pj->primitives.gradients.P[2] +=
+      (Wi[4] - Wj[4]) * wj *
+      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
+
+  hydro_slope_limit_cell_collect(pj, pi, r);
+}
+
+/**
+ * @brief Gradient calculations done during the neighbour loop
+ *
+ * @param r2 Squared distance between the two particles.
+ * @param dx Distance vector (pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_gradients_nonsym_collect(float r2, float *dx, float hi, float hj,
+                               struct part *pi, struct part *pj) {
+
+  float r = sqrtf(r2);
+  float xi;
+  float hi_inv;
+  float wi, wi_dx;
+  int k, l;
+  float Bi[3][3];
+  float Wi[5], Wj[5];
+
+  /* Initialize local variables */
+  for (k = 0; k < 3; k++) {
+    for (l = 0; l < 3; l++) {
+      Bi[k][l] = pi->geometry.matrix_E[k][l];
+    }
+  }
+  Wi[0] = pi->primitives.rho;
+  Wi[1] = pi->primitives.v[0];
+  Wi[2] = pi->primitives.v[1];
+  Wi[3] = pi->primitives.v[2];
+  Wi[4] = pi->primitives.P;
+  Wj[0] = pj->primitives.rho;
+  Wj[1] = pj->primitives.v[0];
+  Wj[2] = pj->primitives.v[1];
+  Wj[3] = pj->primitives.v[2];
+  Wj[4] = pj->primitives.P;
+
+  /* Compute kernel of pi. */
+  hi_inv = 1.0 / hi;
+  xi = r * hi_inv;
+  kernel_deval(xi, &wi, &wi_dx);
+
+  /* Compute gradients for pi */
+  /* there is a sign difference w.r.t. eqn. (6) because of the inverse
+   * definition of dx */
+  pi->primitives.gradients.rho[0] +=
+      (Wi[0] - Wj[0]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.rho[1] +=
+      (Wi[0] - Wj[0]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.rho[2] +=
+      (Wi[0] - Wj[0]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+
+  pi->primitives.gradients.v[0][0] +=
+      (Wi[1] - Wj[1]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.v[0][1] +=
+      (Wi[1] - Wj[1]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.v[0][2] +=
+      (Wi[1] - Wj[1]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+  pi->primitives.gradients.v[1][0] +=
+      (Wi[2] - Wj[2]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.v[1][1] +=
+      (Wi[2] - Wj[2]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.v[1][2] +=
+      (Wi[2] - Wj[2]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+  pi->primitives.gradients.v[2][0] +=
+      (Wi[3] - Wj[3]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.v[2][1] +=
+      (Wi[3] - Wj[3]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.v[2][2] +=
+      (Wi[3] - Wj[3]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+
+  pi->primitives.gradients.P[0] +=
+      (Wi[4] - Wj[4]) * wi *
+      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
+  pi->primitives.gradients.P[1] +=
+      (Wi[4] - Wj[4]) * wi *
+      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
+  pi->primitives.gradients.P[2] +=
+      (Wi[4] - Wj[4]) * wi *
+      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
+
+  hydro_slope_limit_cell_collect(pi, pj, r);
+}
+
+/**
+ * @brief Finalize the gradient variables after all data have been collected
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_finalize(
+    struct part *p) {
+
+  float h, ih;
+
+  /* add kernel normalization to gradients */
+  h = p->h;
+  ih = 1.0f / h;
+  const float ihdim = pow_dimension(ih);
+
+  p->primitives.gradients.rho[0] *= ihdim;
+  p->primitives.gradients.rho[1] *= ihdim;
+  p->primitives.gradients.rho[2] *= ihdim;
+
+  p->primitives.gradients.v[0][0] *= ihdim;
+  p->primitives.gradients.v[0][1] *= ihdim;
+  p->primitives.gradients.v[0][2] *= ihdim;
+  p->primitives.gradients.v[1][0] *= ihdim;
+  p->primitives.gradients.v[1][1] *= ihdim;
+  p->primitives.gradients.v[1][2] *= ihdim;
+  p->primitives.gradients.v[2][0] *= ihdim;
+  p->primitives.gradients.v[2][1] *= ihdim;
+  p->primitives.gradients.v[2][2] *= ihdim;
+
+  p->primitives.gradients.P[0] *= ihdim;
+  p->primitives.gradients.P[1] *= ihdim;
+  p->primitives.gradients.P[2] *= ihdim;
+
+  hydro_slope_limit_cell(p);
+}
diff --git a/src/hydro/Gizmo/hydro_gradients_sph.h b/src/hydro/Gizmo/hydro_gradients_sph.h
new file mode 100644
index 0000000000000000000000000000000000000000..f635faecea549f7da280ade9b944021a5e4aeb4c
--- /dev/null
+++ b/src/hydro/Gizmo/hydro_gradients_sph.h
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 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/>.
+ *
+ ******************************************************************************/
+
+/**
+ * @brief Initialize gradient variables
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_init(
+    struct part *p) {
+
+  p->primitives.gradients.rho[0] = 0.0f;
+  p->primitives.gradients.rho[1] = 0.0f;
+  p->primitives.gradients.rho[2] = 0.0f;
+
+  p->primitives.gradients.v[0][0] = 0.0f;
+  p->primitives.gradients.v[0][1] = 0.0f;
+  p->primitives.gradients.v[0][2] = 0.0f;
+
+  p->primitives.gradients.v[1][0] = 0.0f;
+  p->primitives.gradients.v[1][1] = 0.0f;
+  p->primitives.gradients.v[1][2] = 0.0f;
+  p->primitives.gradients.v[2][0] = 0.0f;
+  p->primitives.gradients.v[2][1] = 0.0f;
+  p->primitives.gradients.v[2][2] = 0.0f;
+
+  p->primitives.gradients.P[0] = 0.0f;
+  p->primitives.gradients.P[1] = 0.0f;
+  p->primitives.gradients.P[2] = 0.0f;
+
+  hydro_slope_limit_cell_init(p);
+}
+
+/**
+ * @brief Gradient calculations done during the neighbour loop
+ *
+ * @param r2 Squared distance between the two particles.
+ * @param dx Distance vector (pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_collect(
+    float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
+
+  float wi, wi_dx, xi, hi_inv;
+  float wj, wj_dx, xj, hj_inv;
+  float r = sqrtf(r2);
+
+  hi_inv = 1.0f / hi;
+  xi = r * hi_inv;
+  kernel_deval(xi, &wi, &wi_dx);
+
+  /* very basic gradient estimate */
+  pi->primitives.gradients.rho[0] -=
+      wi_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) / r;
+  pi->primitives.gradients.rho[1] -=
+      wi_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) / r;
+  pi->primitives.gradients.rho[2] -=
+      wi_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) / r;
+
+  pi->primitives.gradients.v[0][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
+  pi->primitives.gradients.v[0][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
+  pi->primitives.gradients.v[0][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
+
+  pi->primitives.gradients.v[1][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
+  pi->primitives.gradients.v[1][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
+  pi->primitives.gradients.v[1][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
+
+  pi->primitives.gradients.v[2][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
+  pi->primitives.gradients.v[2][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
+  pi->primitives.gradients.v[2][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
+
+  pi->primitives.gradients.P[0] -=
+      wi_dx * dx[0] * (pi->primitives.P - pj->primitives.P) / r;
+  pi->primitives.gradients.P[1] -=
+      wi_dx * dx[1] * (pi->primitives.P - pj->primitives.P) / r;
+  pi->primitives.gradients.P[2] -=
+      wi_dx * dx[2] * (pi->primitives.P - pj->primitives.P) / r;
+
+  hydro_slope_limit_cell_collect(pi, pj, r);
+
+  hj_inv = 1.0f / hj;
+  xj = r * hj_inv;
+  kernel_deval(xj, &wj, &wj_dx);
+
+  /* signs are the same as before, since we swap i and j twice */
+  pj->primitives.gradients.rho[0] -=
+      wj_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) / r;
+  pj->primitives.gradients.rho[1] -=
+      wj_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) / r;
+  pj->primitives.gradients.rho[2] -=
+      wj_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) / r;
+
+  pj->primitives.gradients.v[0][0] -=
+      wj_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
+  pj->primitives.gradients.v[0][1] -=
+      wj_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
+  pj->primitives.gradients.v[0][2] -=
+      wj_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
+
+  pj->primitives.gradients.v[1][0] -=
+      wj_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
+  pj->primitives.gradients.v[1][1] -=
+      wj_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
+  pj->primitives.gradients.v[1][2] -=
+      wj_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
+  pj->primitives.gradients.v[2][0] -=
+      wj_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
+  pj->primitives.gradients.v[2][1] -=
+      wj_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
+  pj->primitives.gradients.v[2][2] -=
+      wj_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
+
+  pj->primitives.gradients.P[0] -=
+      wj_dx * dx[0] * (pi->primitives.P - pj->primitives.P) / r;
+  pj->primitives.gradients.P[1] -=
+      wj_dx * dx[1] * (pi->primitives.P - pj->primitives.P) / r;
+  pj->primitives.gradients.P[2] -=
+      wj_dx * dx[2] * (pi->primitives.P - pj->primitives.P) / r;
+
+  hydro_slope_limit_cell_collect(pj, pi, r);
+}
+
+/**
+ * @brief Gradient calculations done during the neighbour loop: non-symmetric
+ * version
+ *
+ * @param r2 Squared distance between the two particles.
+ * @param dx Distance vector (pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_gradients_nonsym_collect(float r2, float *dx, float hi, float hj,
+                               struct part *pi, struct part *pj) {
+
+  float wi, wi_dx, xi, hi_inv;
+  float r = sqrtf(r2);
+
+  hi_inv = 1.0f / hi;
+  xi = r * hi_inv;
+  kernel_deval(xi, &wi, &wi_dx);
+
+  /* very basic gradient estimate */
+  pi->primitives.gradients.rho[0] -=
+      wi_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) / r;
+  pi->primitives.gradients.rho[1] -=
+      wi_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) / r;
+  pi->primitives.gradients.rho[2] -=
+      wi_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) / r;
+
+  pi->primitives.gradients.v[0][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
+  pi->primitives.gradients.v[0][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
+  pi->primitives.gradients.v[0][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
+
+  pi->primitives.gradients.v[1][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
+  pi->primitives.gradients.v[1][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
+  pi->primitives.gradients.v[1][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
+
+  pi->primitives.gradients.v[2][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
+  pi->primitives.gradients.v[2][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
+  pi->primitives.gradients.v[2][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
+
+  pi->primitives.gradients.P[0] -=
+      wi_dx * dx[0] * (pi->primitives.P - pj->primitives.P) / r;
+  pi->primitives.gradients.P[1] -=
+      wi_dx * dx[1] * (pi->primitives.P - pj->primitives.P) / r;
+  pi->primitives.gradients.P[2] -=
+      wi_dx * dx[2] * (pi->primitives.P - pj->primitives.P) / r;
+
+  hydro_slope_limit_cell_collect(pi, pj, r);
+}
+
+/**
+ * @brief Finalize the gradient variables after all data have been collected
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_gradients_finalize(
+    struct part *p) {
+
+  const float h = p->h;
+  const float ih = 1.0f / h;
+  const float ihdimp1 = pow_dimension_plus_one(ih);
+
+  float volume = p->geometry.volume;
+
+  /* finalize gradients by multiplying with volume */
+  p->primitives.gradients.rho[0] *= ihdimp1 * volume;
+  p->primitives.gradients.rho[1] *= ihdimp1 * volume;
+  p->primitives.gradients.rho[2] *= ihdimp1 * volume;
+
+  p->primitives.gradients.v[0][0] *= ihdimp1 * volume;
+  p->primitives.gradients.v[0][1] *= ihdimp1 * volume;
+  p->primitives.gradients.v[0][2] *= ihdimp1 * volume;
+
+  p->primitives.gradients.v[1][0] *= ihdimp1 * volume;
+  p->primitives.gradients.v[1][1] *= ihdimp1 * volume;
+  p->primitives.gradients.v[1][2] *= ihdimp1 * volume;
+
+  p->primitives.gradients.v[2][0] *= ihdimp1 * volume;
+  p->primitives.gradients.v[2][1] *= ihdimp1 * volume;
+  p->primitives.gradients.v[2][2] *= ihdimp1 * volume;
+
+  p->primitives.gradients.P[0] *= ihdimp1 * volume;
+  p->primitives.gradients.P[1] *= ihdimp1 * volume;
+  p->primitives.gradients.P[2] *= ihdimp1 * volume;
+
+  hydro_slope_limit_cell(p);
+}
diff --git a/src/hydro/Gizmo/hydro_iact.h b/src/hydro/Gizmo/hydro_iact.h
index 30a8d6cbebc851b44a5ee2339950aec9e15057c0..79973364617bb04855115bff9bfbf3808f46d04f 100644
--- a/src/hydro/Gizmo/hydro_iact.h
+++ b/src/hydro/Gizmo/hydro_iact.h
@@ -19,14 +19,28 @@
  *
  ******************************************************************************/
 
+#include "adiabatic_index.h"
+#include "hydro_gradients.h"
 #include "riemann.h"
 
-#define USE_GRADIENTS
-#define PER_FACE_LIMITER
-/* #define PRINT_ID 0 */
-
-/* this corresponds to task_subtype_hydro_loop1 */
-__attribute__((always_inline)) INLINE static void runner_iact_hydro_loop1(
+/**
+ * @brief Calculate the volume interaction between particle i and particle j
+ *
+ * The volume is in essence the same as the weighted number of neighbours in a
+ * classical SPH density calculation.
+ *
+ * We also calculate the components of the matrix E, which is used for second
+ * order accurate gradient calculations and for the calculation of the interface
+ * surface areas.
+ *
+ * @param r2 Squared distance between particle i and particle j.
+ * @param dx Distance vector between the particles (dx = pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_density(
     float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
 
   float r = sqrtf(r2);
@@ -48,71 +62,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_hydro_loop1(
   for (k = 0; k < 3; k++)
     for (l = 0; l < 3; l++) pi->geometry.matrix_E[k][l] += dx[k] * dx[l] * wi;
 
-#ifdef SPH_GRADIENTS
-  /* very basic gradient estimate */
-  pi->primitives.gradients.rho[0] -=
-      wi_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) / r;
-  pi->primitives.gradients.rho[1] -=
-      wi_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) / r;
-  pi->primitives.gradients.rho[2] -=
-      wi_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) / r;
-
-  pi->primitives.gradients.v[0][0] -=
-      wi_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
-  pi->primitives.gradients.v[0][1] -=
-      wi_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
-  pi->primitives.gradients.v[0][2] -=
-      wi_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
-
-  pi->primitives.gradients.v[1][0] -=
-      wi_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
-  pi->primitives.gradients.v[1][1] -=
-      wi_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
-  pi->primitives.gradients.v[1][2] -=
-      wi_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
-
-  pi->primitives.gradients.v[2][0] -=
-      wi_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
-  pi->primitives.gradients.v[2][1] -=
-      wi_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
-  pi->primitives.gradients.v[2][2] -=
-      wi_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
-
-  pi->primitives.gradients.P[0] -=
-      wi_dx * dx[0] * (pi->primitives.P - pj->primitives.P) / r;
-  pi->primitives.gradients.P[1] -=
-      wi_dx * dx[1] * (pi->primitives.P - pj->primitives.P) / r;
-  pi->primitives.gradients.P[2] -=
-      wi_dx * dx[2] * (pi->primitives.P - pj->primitives.P) / r;
-
-  /* basic slope limiter: collect the maximal and the minimal value for the
-   * primitive variables among the ngbs */
-  pi->primitives.limiter.rho[0] =
-      fmin(pj->primitives.rho, pi->primitives.limiter.rho[0]);
-  pi->primitives.limiter.rho[1] =
-      fmax(pj->primitives.rho, pi->primitives.limiter.rho[1]);
-
-  pi->primitives.limiter.v[0][0] =
-      fmin(pj->primitives.v[0], pi->primitives.limiter.v[0][0]);
-  pi->primitives.limiter.v[0][1] =
-      fmax(pj->primitives.v[0], pi->primitives.limiter.v[0][1]);
-  pi->primitives.limiter.v[1][0] =
-      fmin(pj->primitives.v[1], pi->primitives.limiter.v[1][0]);
-  pi->primitives.limiter.v[1][1] =
-      fmax(pj->primitives.v[1], pi->primitives.limiter.v[1][1]);
-  pi->primitives.limiter.v[2][0] =
-      fmin(pj->primitives.v[2], pi->primitives.limiter.v[2][0]);
-  pi->primitives.limiter.v[2][1] =
-      fmax(pj->primitives.v[2], pi->primitives.limiter.v[2][1]);
-
-  pi->primitives.limiter.P[0] =
-      fmin(pj->primitives.P, pi->primitives.limiter.P[0]);
-  pi->primitives.limiter.P[1] =
-      fmax(pj->primitives.P, pi->primitives.limiter.P[1]);
-
-  pi->primitives.limiter.maxr = fmax(r, pi->primitives.limiter.maxr);
-#endif
-
   /* Compute density of pj. */
   h_inv = 1.0 / hj;
   xj = r * h_inv;
@@ -125,78 +74,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_hydro_loop1(
   pj->geometry.volume += wj;
   for (k = 0; k < 3; k++)
     for (l = 0; l < 3; l++) pj->geometry.matrix_E[k][l] += dx[k] * dx[l] * wj;
-
-#ifdef SPH_GRADIENTS
-  /* very basic gradient estimate */
-  /* signs are the same as before, since we swap i and j twice */
-  pj->primitives.gradients.rho[0] -=
-      wj_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) / r;
-  pj->primitives.gradients.rho[1] -=
-      wj_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) / r;
-  pj->primitives.gradients.rho[2] -=
-      wj_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) / r;
-
-  pj->primitives.gradients.v[0][0] -=
-      wj_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
-  pj->primitives.gradients.v[0][1] -=
-      wj_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
-  pj->primitives.gradients.v[0][2] -=
-      wj_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
-
-  pj->primitives.gradients.v[1][0] -=
-      wj_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
-  pj->primitives.gradients.v[1][1] -=
-      wj_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
-  pj->primitives.gradients.v[1][2] -=
-      wj_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
-
-  pj->primitives.gradients.v[2][0] -=
-      wj_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
-  pj->primitives.gradients.v[2][1] -=
-      wj_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
-  pj->primitives.gradients.v[2][2] -=
-      wj_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
-
-  pj->primitives.gradients.P[0] -=
-      wj_dx * dx[0] * (pi->primitives.P - pj->primitives.P) / r;
-  pj->primitives.gradients.P[1] -=
-      wj_dx * dx[1] * (pi->primitives.P - pj->primitives.P) / r;
-  pj->primitives.gradients.P[2] -=
-      wj_dx * dx[2] * (pi->primitives.P - pj->primitives.P) / r;
-
-  /* basic slope limiter: collect the maximal and the minimal value for the
-   * primitive variables among the ngbs */
-  pj->primitives.limiter.rho[0] =
-      fmin(pi->primitives.rho, pj->primitives.limiter.rho[0]);
-  pj->primitives.limiter.rho[1] =
-      fmax(pi->primitives.rho, pj->primitives.limiter.rho[1]);
-
-  pj->primitives.limiter.v[0][0] =
-      fmin(pi->primitives.v[0], pj->primitives.limiter.v[0][0]);
-  pj->primitives.limiter.v[0][1] =
-      fmax(pi->primitives.v[0], pj->primitives.limiter.v[0][1]);
-  pj->primitives.limiter.v[1][0] =
-      fmin(pi->primitives.v[1], pj->primitives.limiter.v[1][0]);
-  pj->primitives.limiter.v[1][1] =
-      fmax(pi->primitives.v[1], pj->primitives.limiter.v[1][1]);
-  pj->primitives.limiter.v[2][0] =
-      fmin(pi->primitives.v[2], pj->primitives.limiter.v[2][0]);
-  pj->primitives.limiter.v[2][1] =
-      fmax(pi->primitives.v[2], pj->primitives.limiter.v[2][1]);
-
-  pj->primitives.limiter.P[0] =
-      fmin(pi->primitives.P, pj->primitives.limiter.P[0]);
-  pj->primitives.limiter.P[1] =
-      fmax(pi->primitives.P, pj->primitives.limiter.P[1]);
-
-  pj->primitives.limiter.maxr = fmax(r, pj->primitives.limiter.maxr);
-#endif
 }
 
-/* this corresponds to task_subtype_hydro_loop1 */
-__attribute__((always_inline)) INLINE static void
-runner_iact_nonsym_hydro_loop1(float r2, float *dx, float hi, float hj,
-                               struct part *pi, struct part *pj) {
+/**
+ * @brief Calculate the volume interaction between particle i and particle j:
+ * non-symmetric version
+ *
+ * The volume is in essence the same as the weighted number of neighbours in a
+ * classical SPH density calculation.
+ *
+ * We also calculate the components of the matrix E, which is used for second
+ * order accurate gradient calculations and for the calculation of the interface
+ * surface areas.
+ *
+ * @param r2 Squared distance between particle i and particle j.
+ * @param dx Distance vector between the particles (dx = pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
+    float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
 
   float r;
   float xi;
@@ -218,397 +117,80 @@ runner_iact_nonsym_hydro_loop1(float r2, float *dx, float hi, float hj,
   pi->geometry.volume += wi;
   for (k = 0; k < 3; k++)
     for (l = 0; l < 3; l++) pi->geometry.matrix_E[k][l] += dx[k] * dx[l] * wi;
-
-#ifdef SPH_GRADIENTS
-  /* very basic gradient estimate */
-  pi->primitives.gradients.rho[0] -=
-      wi_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) / r;
-  pi->primitives.gradients.rho[1] -=
-      wi_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) / r;
-  pi->primitives.gradients.rho[2] -=
-      wi_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) / r;
-
-  pi->primitives.gradients.v[0][0] -=
-      wi_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
-  pi->primitives.gradients.v[0][1] -=
-      wi_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
-  pi->primitives.gradients.v[0][2] -=
-      wi_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) / r;
-
-  pi->primitives.gradients.v[1][0] -=
-      wi_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
-  pi->primitives.gradients.v[1][1] -=
-      wi_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
-  pi->primitives.gradients.v[1][2] -=
-      wi_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) / r;
-
-  pi->primitives.gradients.v[2][0] -=
-      wi_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
-  pi->primitives.gradients.v[2][1] -=
-      wi_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
-  pi->primitives.gradients.v[2][2] -=
-      wi_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) / r;
-
-  pi->primitives.gradients.P[0] -=
-      wi_dx * dx[0] * (pi->primitives.P - pj->primitives.P) / r;
-  pi->primitives.gradients.P[1] -=
-      wi_dx * dx[1] * (pi->primitives.P - pj->primitives.P) / r;
-  pi->primitives.gradients.P[2] -=
-      wi_dx * dx[2] * (pi->primitives.P - pj->primitives.P) / r;
-
-  /* slope limiter */
-  pi->primitives.limiter.rho[0] =
-      fmin(pj->primitives.rho, pi->primitives.limiter.rho[0]);
-  pi->primitives.limiter.rho[1] =
-      fmax(pj->primitives.rho, pi->primitives.limiter.rho[1]);
-
-  pi->primitives.limiter.v[0][0] =
-      fmin(pj->primitives.v[0], pi->primitives.limiter.v[0][0]);
-  pi->primitives.limiter.v[0][1] =
-      fmax(pj->primitives.v[0], pi->primitives.limiter.v[0][1]);
-  pi->primitives.limiter.v[1][0] =
-      fmin(pj->primitives.v[1], pi->primitives.limiter.v[1][0]);
-  pi->primitives.limiter.v[1][1] =
-      fmax(pj->primitives.v[1], pi->primitives.limiter.v[1][1]);
-  pi->primitives.limiter.v[2][0] =
-      fmin(pj->primitives.v[2], pi->primitives.limiter.v[2][0]);
-  pi->primitives.limiter.v[2][1] =
-      fmax(pj->primitives.v[2], pi->primitives.limiter.v[2][1]);
-
-  pi->primitives.limiter.P[0] =
-      fmin(pj->primitives.P, pi->primitives.limiter.P[0]);
-  pi->primitives.limiter.P[1] =
-      fmax(pj->primitives.P, pi->primitives.limiter.P[1]);
-
-  pi->primitives.limiter.maxr = fmax(r, pi->primitives.limiter.maxr);
-#endif
 }
 
-__attribute__((always_inline)) INLINE static void runner_iact_hydro_loop2(
+/**
+ * @brief Calculate the gradient interaction between particle i and particle j
+ *
+ * This method wraps around hydro_gradients_collect, which can be an empty
+ * method, in which case no gradients are used.
+ *
+ * @param r2 Squared distance between particle i and particle j.
+ * @param dx Distance vector between the particles (dx = pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_gradient(
     float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
 
-#ifndef SPH_GRADIENTS
-
-  float r = sqrtf(r2);
-  float xi, xj;
-  float hi_inv, hj_inv;
-  float wi, wj, wi_dx, wj_dx;
-  int k, l;
-  float Bi[3][3];
-  float Bj[3][3];
-  GFLOAT Wi[5], Wj[5];
-
-  /* Initialize local variables */
-  for (k = 0; k < 3; k++) {
-    for (l = 0; l < 3; l++) {
-      Bi[k][l] = pi->geometry.matrix_E[k][l];
-      Bj[k][l] = pj->geometry.matrix_E[k][l];
-    }
-  }
-  Wi[0] = pi->primitives.rho;
-  Wi[1] = pi->primitives.v[0];
-  Wi[2] = pi->primitives.v[1];
-  Wi[3] = pi->primitives.v[2];
-  Wi[4] = pi->primitives.P;
-  Wj[0] = pj->primitives.rho;
-  Wj[1] = pj->primitives.v[0];
-  Wj[2] = pj->primitives.v[1];
-  Wj[3] = pj->primitives.v[2];
-  Wj[4] = pj->primitives.P;
-
-  /* Compute kernel of pi. */
-  hi_inv = 1.0 / hi;
-  xi = r * hi_inv;
-  kernel_deval(xi, &wi, &wi_dx);
-
-  /* Compute gradients for pi */
-  /* there is a sign difference w.r.t. eqn. (6) because of the inverse
-   * definition of dx */
-  pi->primitives.gradients.rho[0] +=
-      (Wi[0] - Wj[0]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.rho[1] +=
-      (Wi[0] - Wj[0]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.rho[2] +=
-      (Wi[0] - Wj[0]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-
-  pi->primitives.gradients.v[0][0] +=
-      (Wi[1] - Wj[1]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.v[0][1] +=
-      (Wi[1] - Wj[1]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.v[0][2] +=
-      (Wi[1] - Wj[1]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-  pi->primitives.gradients.v[1][0] +=
-      (Wi[2] - Wj[2]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.v[1][1] +=
-      (Wi[2] - Wj[2]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.v[1][2] +=
-      (Wi[2] - Wj[2]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-  pi->primitives.gradients.v[2][0] +=
-      (Wi[3] - Wj[3]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.v[2][1] +=
-      (Wi[3] - Wj[3]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.v[2][2] +=
-      (Wi[3] - Wj[3]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-
-  pi->primitives.gradients.P[0] +=
-      (Wi[4] - Wj[4]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.P[1] +=
-      (Wi[4] - Wj[4]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.P[2] +=
-      (Wi[4] - Wj[4]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-
-  /* basic slope limiter: collect the maximal and the minimal value for the
-   * primitive variables among the ngbs */
-  pi->primitives.limiter.rho[0] =
-      fmin(pj->primitives.rho, pi->primitives.limiter.rho[0]);
-  pi->primitives.limiter.rho[1] =
-      fmax(pj->primitives.rho, pi->primitives.limiter.rho[1]);
-
-  pi->primitives.limiter.v[0][0] =
-      fmin(pj->primitives.v[0], pi->primitives.limiter.v[0][0]);
-  pi->primitives.limiter.v[0][1] =
-      fmax(pj->primitives.v[0], pi->primitives.limiter.v[0][1]);
-  pi->primitives.limiter.v[1][0] =
-      fmin(pj->primitives.v[1], pi->primitives.limiter.v[1][0]);
-  pi->primitives.limiter.v[1][1] =
-      fmax(pj->primitives.v[1], pi->primitives.limiter.v[1][1]);
-  pi->primitives.limiter.v[2][0] =
-      fmin(pj->primitives.v[2], pi->primitives.limiter.v[2][0]);
-  pi->primitives.limiter.v[2][1] =
-      fmax(pj->primitives.v[2], pi->primitives.limiter.v[2][1]);
-
-  pi->primitives.limiter.P[0] =
-      fmin(pj->primitives.P, pi->primitives.limiter.P[0]);
-  pi->primitives.limiter.P[1] =
-      fmax(pj->primitives.P, pi->primitives.limiter.P[1]);
-
-  pi->primitives.limiter.maxr = fmax(r, pi->primitives.limiter.maxr);
-
-  /* Compute kernel of pj. */
-  hj_inv = 1.0 / hj;
-  xj = r * hj_inv;
-  kernel_deval(xj, &wj, &wj_dx);
-
-  /* Compute gradients for pj */
-  /* there is no sign difference w.r.t. eqn. (6) because dx is now what we want
-   * it to be */
-  pj->primitives.gradients.rho[0] +=
-      (Wi[0] - Wj[0]) * wj *
-      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
-  pj->primitives.gradients.rho[1] +=
-      (Wi[0] - Wj[0]) * wj *
-      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
-  pj->primitives.gradients.rho[2] +=
-      (Wi[0] - Wj[0]) * wj *
-      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
-
-  pj->primitives.gradients.v[0][0] +=
-      (Wi[1] - Wj[1]) * wj *
-      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
-  pj->primitives.gradients.v[0][1] +=
-      (Wi[1] - Wj[1]) * wj *
-      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
-  pj->primitives.gradients.v[0][2] +=
-      (Wi[1] - Wj[1]) * wj *
-      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
-  pj->primitives.gradients.v[1][0] +=
-      (Wi[2] - Wj[2]) * wj *
-      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
-  pj->primitives.gradients.v[1][1] +=
-      (Wi[2] - Wj[2]) * wj *
-      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
-  pj->primitives.gradients.v[1][2] +=
-      (Wi[2] - Wj[2]) * wj *
-      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
-  pj->primitives.gradients.v[2][0] +=
-      (Wi[3] - Wj[3]) * wj *
-      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
-  pj->primitives.gradients.v[2][1] +=
-      (Wi[3] - Wj[3]) * wj *
-      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
-  pj->primitives.gradients.v[2][2] +=
-      (Wi[3] - Wj[3]) * wj *
-      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
-
-  pj->primitives.gradients.P[0] +=
-      (Wi[4] - Wj[4]) * wj *
-      (Bj[0][0] * dx[0] + Bj[0][1] * dx[1] + Bj[0][2] * dx[2]);
-  pj->primitives.gradients.P[1] +=
-      (Wi[4] - Wj[4]) * wj *
-      (Bj[1][0] * dx[0] + Bj[1][1] * dx[1] + Bj[1][2] * dx[2]);
-  pj->primitives.gradients.P[2] +=
-      (Wi[4] - Wj[4]) * wj *
-      (Bj[2][0] * dx[0] + Bj[2][1] * dx[1] + Bj[2][2] * dx[2]);
-
-  /* basic slope limiter: collect the maximal and the minimal value for the
-   * primitive variables among the ngbs */
-  pj->primitives.limiter.rho[0] =
-      fmin(pi->primitives.rho, pj->primitives.limiter.rho[0]);
-  pj->primitives.limiter.rho[1] =
-      fmax(pi->primitives.rho, pj->primitives.limiter.rho[1]);
-
-  pj->primitives.limiter.v[0][0] =
-      fmin(pi->primitives.v[0], pj->primitives.limiter.v[0][0]);
-  pj->primitives.limiter.v[0][1] =
-      fmax(pi->primitives.v[0], pj->primitives.limiter.v[0][1]);
-  pj->primitives.limiter.v[1][0] =
-      fmin(pi->primitives.v[1], pj->primitives.limiter.v[1][0]);
-  pj->primitives.limiter.v[1][1] =
-      fmax(pi->primitives.v[1], pj->primitives.limiter.v[1][1]);
-  pj->primitives.limiter.v[2][0] =
-      fmin(pi->primitives.v[2], pj->primitives.limiter.v[2][0]);
-  pj->primitives.limiter.v[2][1] =
-      fmax(pi->primitives.v[2], pj->primitives.limiter.v[2][1]);
-
-  pj->primitives.limiter.P[0] =
-      fmin(pi->primitives.P, pj->primitives.limiter.P[0]);
-  pj->primitives.limiter.P[1] =
-      fmax(pi->primitives.P, pj->primitives.limiter.P[1]);
-
-  pj->primitives.limiter.maxr = fmax(r, pj->primitives.limiter.maxr);
-
-#endif
+  hydro_gradients_collect(r2, dx, hi, hj, pi, pj);
 }
 
-__attribute__((always_inline)) INLINE static void
-runner_iact_nonsym_hydro_loop2(float r2, float *dx, float hi, float hj,
-                               struct part *pi, struct part *pj) {
-
-#ifndef SPH_GRADIENTS
-
-  float r = sqrtf(r2);
-  float xi;
-  float hi_inv;
-  float wi, wi_dx;
-  int k, l;
-  float Bi[3][3];
-  GFLOAT Wi[5], Wj[5];
-
-  /* Initialize local variables */
-  for (k = 0; k < 3; k++) {
-    for (l = 0; l < 3; l++) {
-      Bi[k][l] = pi->geometry.matrix_E[k][l];
-    }
-  }
-  Wi[0] = pi->primitives.rho;
-  Wi[1] = pi->primitives.v[0];
-  Wi[2] = pi->primitives.v[1];
-  Wi[3] = pi->primitives.v[2];
-  Wi[4] = pi->primitives.P;
-  Wj[0] = pj->primitives.rho;
-  Wj[1] = pj->primitives.v[0];
-  Wj[2] = pj->primitives.v[1];
-  Wj[3] = pj->primitives.v[2];
-  Wj[4] = pj->primitives.P;
-
-  /* Compute kernel of pi. */
-  hi_inv = 1.0 / hi;
-  xi = r * hi_inv;
-  kernel_deval(xi, &wi, &wi_dx);
-
-  /* Compute gradients for pi */
-  /* there is a sign difference w.r.t. eqn. (6) because of the inverse
-   * definition of dx */
-  pi->primitives.gradients.rho[0] +=
-      (Wi[0] - Wj[0]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.rho[1] +=
-      (Wi[0] - Wj[0]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.rho[2] +=
-      (Wi[0] - Wj[0]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-
-  pi->primitives.gradients.v[0][0] +=
-      (Wi[1] - Wj[1]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.v[0][1] +=
-      (Wi[1] - Wj[1]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.v[0][2] +=
-      (Wi[1] - Wj[1]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-  pi->primitives.gradients.v[1][0] +=
-      (Wi[2] - Wj[2]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.v[1][1] +=
-      (Wi[2] - Wj[2]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.v[1][2] +=
-      (Wi[2] - Wj[2]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-  pi->primitives.gradients.v[2][0] +=
-      (Wi[3] - Wj[3]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.v[2][1] +=
-      (Wi[3] - Wj[3]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.v[2][2] +=
-      (Wi[3] - Wj[3]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-
-  pi->primitives.gradients.P[0] +=
-      (Wi[4] - Wj[4]) * wi *
-      (Bi[0][0] * dx[0] + Bi[0][1] * dx[1] + Bi[0][2] * dx[2]);
-  pi->primitives.gradients.P[1] +=
-      (Wi[4] - Wj[4]) * wi *
-      (Bi[1][0] * dx[0] + Bi[1][1] * dx[1] + Bi[1][2] * dx[2]);
-  pi->primitives.gradients.P[2] +=
-      (Wi[4] - Wj[4]) * wi *
-      (Bi[2][0] * dx[0] + Bi[2][1] * dx[1] + Bi[2][2] * dx[2]);
-
-  /* slope limiter */
-  pi->primitives.limiter.rho[0] =
-      fmin(pj->primitives.rho, pi->primitives.limiter.rho[0]);
-  pi->primitives.limiter.rho[1] =
-      fmax(pj->primitives.rho, pi->primitives.limiter.rho[1]);
-
-  pi->primitives.limiter.v[0][0] =
-      fmin(pj->primitives.v[0], pi->primitives.limiter.v[0][0]);
-  pi->primitives.limiter.v[0][1] =
-      fmax(pj->primitives.v[0], pi->primitives.limiter.v[0][1]);
-  pi->primitives.limiter.v[1][0] =
-      fmin(pj->primitives.v[1], pi->primitives.limiter.v[1][0]);
-  pi->primitives.limiter.v[1][1] =
-      fmax(pj->primitives.v[1], pi->primitives.limiter.v[1][1]);
-  pi->primitives.limiter.v[2][0] =
-      fmin(pj->primitives.v[2], pi->primitives.limiter.v[2][0]);
-  pi->primitives.limiter.v[2][1] =
-      fmax(pj->primitives.v[2], pi->primitives.limiter.v[2][1]);
-
-  pi->primitives.limiter.P[0] =
-      fmin(pj->primitives.P, pi->primitives.limiter.P[0]);
-  pi->primitives.limiter.P[1] =
-      fmax(pj->primitives.P, pi->primitives.limiter.P[1]);
-
-  pi->primitives.limiter.maxr = fmax(r, pi->primitives.limiter.maxr);
+/**
+ * @brief Calculate the gradient interaction between particle i and particle j:
+ * non-symmetric version
+ *
+ * This method wraps around hydro_gradients_nonsym_collect, which can be an
+ * empty method, in which case no gradients are used.
+ *
+ * @param r2 Squared distance between particle i and particle j.
+ * @param dx Distance vector between the particles (dx = pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient(
+    float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
 
-#endif
+  hydro_gradients_nonsym_collect(r2, dx, hi, hj, pi, pj);
 }
 
+/**
+ * @brief Common part of the flux calculation between particle i and j
+ *
+ * Since the only difference between the symmetric and non-symmetric version
+ * of the flux calculation  is in the update of the conserved variables at the
+ * very end (which is not done for particle j if mode is 0 and particle j is
+ * active), both runner_iact_force and runner_iact_nonsym_force call this
+ * method, with an appropriate mode.
+ *
+ * This method calculates the surface area of the interface between particle i
+ * and particle j, as well as the interface position and velocity. These are
+ * then used to reconstruct and predict the primitive variables, which are then
+ * fed to a Riemann solver that calculates a flux. This flux is used to update
+ * the conserved variables of particle i or both particles.
+ *
+ * This method also calculates the maximal velocity used to calculate the time
+ * step.
+ *
+ * @param r2 Squared distance between particle i and particle j.
+ * @param dx Distance vector between the particles (dx = pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
 __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
     float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj,
     int mode) {
 
   float r = sqrtf(r2);
   float xi, xj;
-  float hi_inv, hi_inv2;
-  float hj_inv, hj_inv2;
+  float hi_inv, hi_inv_dim;
+  float hj_inv, hj_inv_dim;
   float wi, wj, wi_dx, wj_dx;
   int k, l;
   float A[3];
@@ -619,13 +201,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   float xij_i[3], xfac, xijdotdx;
   float vmax, dvdotdx;
   float vi[3], vj[3], vij[3];
-  GFLOAT Wi[5], Wj[5];  //, Whalf[5];
-#ifdef USE_GRADIENTS
-  GFLOAT dWi[5], dWj[5];
-  float xij_j[3];
-#endif
-  //    GFLOAT rhoe;
-  //    GFLOAT flux[5][3];
+  float Wi[5], Wj[5];
   float dti, dtj, mindt;
   float n_unit[3];
 
@@ -635,8 +211,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
       Bi[k][l] = pi->geometry.matrix_E[k][l];
       Bj[k][l] = pj->geometry.matrix_E[k][l];
     }
-    vi[k] = pi->v[k]; /* particle velocities */
-    vj[k] = pj->v[k];
+    vi[k] = pi->force.v_full[k]; /* particle velocities */
+    vj[k] = pj->force.v_full[k];
   }
   Vi = pi->geometry.volume;
   Vj = pj->geometry.volume;
@@ -650,16 +226,17 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   Wj[2] = pj->primitives.v[1];
   Wj[3] = pj->primitives.v[2];
   Wj[4] = pj->primitives.P;
-  dti = pi->ti_end - pi->ti_begin;  // MATTHIEU
-  dtj = pj->ti_end - pj->ti_begin;
 
-  //    if(dti > 1.e-7 || dtj > 1.e-7){
-  //        message("Timestep too large: %g %g!", dti, dtj);
-  //    }
+  dti = pi->force.dt;
+  dtj = pj->force.dt;
 
   /* calculate the maximal signal velocity */
-  vmax = sqrtf(const_hydro_gamma * Wi[4] / Wi[0]) +
-         sqrtf(const_hydro_gamma * Wj[4] / Wj[0]);
+  if (Wi[0] && Wj[0]) {
+    vmax =
+        sqrtf(hydro_gamma * Wi[4] / Wi[0]) + sqrtf(hydro_gamma * Wj[4] / Wj[0]);
+  } else {
+    vmax = 0.0f;
+  }
   dvdotdx = (Wi[1] - Wj[1]) * dx[0] + (Wi[2] - Wj[2]) * dx[1] +
             (Wi[3] - Wj[3]) * dx[2];
   if (dvdotdx > 0.) {
@@ -673,28 +250,44 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   /* The flux will be exchanged using the smallest time step of the two
    * particles */
   mindt = fminf(dti, dtj);
+  dti = mindt;
+  dtj = mindt;
 
   /* Compute kernel of pi. */
   hi_inv = 1.0 / hi;
-  hi_inv2 = hi_inv * hi_inv;
+  hi_inv_dim = pow_dimension(hi_inv);
   xi = r * hi_inv;
   kernel_deval(xi, &wi, &wi_dx);
 
   /* Compute kernel of pj. */
   hj_inv = 1.0 / hj;
-  hj_inv2 = hj_inv * hj_inv;
+  hj_inv_dim = pow_dimension(hj_inv);
   xj = r * hj_inv;
   kernel_deval(xj, &wj, &wj_dx);
 
+  /* Compute h_dt. We are going to use an SPH-like estimate of div_v for that */
+  float dvdr = (pi->v[0] - pj->v[0]) * dx[0] + (pi->v[1] - pj->v[1]) * dx[1] +
+               (pi->v[2] - pj->v[2]) * dx[2];
+  float ri = 1.0f / r;
+  float hidp1 = pow_dimension_plus_one(hi_inv);
+  float hjdp1 = pow_dimension_plus_one(hj_inv);
+  float wi_dr = hidp1 * wi_dx;
+  float wj_dr = hjdp1 * wj_dx;
+  dvdr *= ri;
+  pi->force.h_dt -= pj->conserved.mass * dvdr / pj->primitives.rho * wi_dr;
+  if (mode == 1) {
+    pj->force.h_dt -= pi->conserved.mass * dvdr / pi->primitives.rho * wj_dr;
+  }
+
   /* Compute area */
   /* eqn. (7) */
   Anorm = 0.0f;
   for (k = 0; k < 3; k++) {
     /* we add a minus sign since dx is pi->x - pj->x */
     A[k] = -Vi * (Bi[k][0] * dx[0] + Bi[k][1] * dx[1] + Bi[k][2] * dx[2]) * wi *
-               hi_inv * hi_inv2 -
+               hi_inv_dim -
            Vj * (Bj[k][0] * dx[0] + Bj[k][1] * dx[1] + Bj[k][2] * dx[2]) * wj *
-               hj_inv * hj_inv2;
+               hj_inv_dim;
     Anorm += A[k] * A[k];
   }
 
@@ -708,13 +301,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   Anorm = sqrtf(Anorm);
   for (k = 0; k < 3; k++) n_unit[k] = A[k] / Anorm;
 
-#ifdef PRINT_ID
-  if (pi->id == PRINT_ID || pj->id == PRINT_ID) {
-    printf("pi: %g %g %g\npj: %g %g %g\nA = %g %g %g\n", pi->x[0], pi->x[1],
-           pi->x[2], pj->x[0], pj->x[1], pj->x[2], A[0], A[1], A[2]);
-  }
-#endif
-
   /* Compute interface position (relative to pi, since we don't need the actual
    * position) */
   /* eqn. (8) */
@@ -746,192 +332,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   Wj[2] -= vij[1];
   Wj[3] -= vij[2];
 
-#ifdef USE_GRADIENTS
-  /* perform gradient reconstruction in space and time */
-  /* space */
-  /* Compute interface position (relative to pj, since we don't need the actual
-   * position) */
-  /* eqn. (8) */
-  xfac = hj / (hi + hj);
-  for (k = 0; k < 3; k++) xij_j[k] = xfac * dx[k];
-
-  dWi[0] = pi->primitives.gradients.rho[0] * xij_i[0] +
-           pi->primitives.gradients.rho[1] * xij_i[1] +
-           pi->primitives.gradients.rho[2] * xij_i[2];
-  dWi[1] = pi->primitives.gradients.v[0][0] * xij_i[0] +
-           pi->primitives.gradients.v[0][1] * xij_i[1] +
-           pi->primitives.gradients.v[0][2] * xij_i[2];
-  dWi[2] = pi->primitives.gradients.v[1][0] * xij_i[0] +
-           pi->primitives.gradients.v[1][1] * xij_i[1] +
-           pi->primitives.gradients.v[1][2] * xij_i[2];
-  dWi[3] = pi->primitives.gradients.v[2][0] * xij_i[0] +
-           pi->primitives.gradients.v[2][1] * xij_i[1] +
-           pi->primitives.gradients.v[2][2] * xij_i[2];
-  dWi[4] = pi->primitives.gradients.P[0] * xij_i[0] +
-           pi->primitives.gradients.P[1] * xij_i[1] +
-           pi->primitives.gradients.P[2] * xij_i[2];
-
-  dWj[0] = pj->primitives.gradients.rho[0] * xij_j[0] +
-           pj->primitives.gradients.rho[1] * xij_j[1] +
-           pj->primitives.gradients.rho[2] * xij_j[2];
-  dWj[1] = pj->primitives.gradients.v[0][0] * xij_j[0] +
-           pj->primitives.gradients.v[0][1] * xij_j[1] +
-           pj->primitives.gradients.v[0][2] * xij_j[2];
-  dWj[2] = pj->primitives.gradients.v[1][0] * xij_j[0] +
-           pj->primitives.gradients.v[1][1] * xij_j[1] +
-           pj->primitives.gradients.v[1][2] * xij_j[2];
-  dWj[3] = pj->primitives.gradients.v[2][0] * xij_j[0] +
-           pj->primitives.gradients.v[2][1] * xij_j[1] +
-           pj->primitives.gradients.v[2][2] * xij_j[2];
-  dWj[4] = pj->primitives.gradients.P[0] * xij_j[0] +
-           pj->primitives.gradients.P[1] * xij_j[1] +
-           pj->primitives.gradients.P[2] * xij_j[2];
-
-#ifdef PER_FACE_LIMITER
-
-  float xij_i_norm;
-  GFLOAT phi_i, phi_j;
-  GFLOAT delta1, delta2;
-  GFLOAT phiminus, phiplus;
-  GFLOAT phimin, phimax;
-  GFLOAT phibar;
-  /* free parameters, values from Hopkins */
-  GFLOAT psi1 = 0.5, psi2 = 0.25;
-  GFLOAT phi_mid0, phi_mid;
-
-  for (k = 0; k < 10; k++) {
-    if (k < 5) {
-      phi_i = Wi[k];
-      phi_j = Wj[k];
-      phi_mid0 = Wi[k] + dWi[k];
-      xij_i_norm = sqrtf(xij_i[0] * xij_i[0] + xij_i[1] * xij_i[1] +
-                         xij_i[2] * xij_i[2]);
-    } else {
-      phi_i = Wj[k - 5];
-      phi_j = Wi[k - 5];
-      phi_mid0 = Wj[k - 5] + dWj[k - 5];
-      xij_i_norm = sqrtf(xij_j[0] * xij_j[0] + xij_j[1] * xij_j[1] +
-                         xij_j[2] * xij_j[2]);
-    }
-
-    delta1 = psi1 * fabs(phi_i - phi_j);
-    delta2 = psi2 * fabs(phi_i - phi_j);
-
-    phimin = fmin(phi_i, phi_j);
-    phimax = fmax(phi_i, phi_j);
-
-    phibar = phi_i + xij_i_norm / r * (phi_j - phi_i);
-
-    /* if sign(phimax+delta1) == sign(phimax) */
-    if ((phimax + delta1) * phimax > 0.0f) {
-      phiplus = phimax + delta1;
-    } else {
-      phiplus = phimax / (1.0f + delta1 / fabs(phimax));
-    }
-
-    /* if sign(phimin-delta1) == sign(phimin) */
-    if ((phimin - delta1) * phimin > 0.0f) {
-      phiminus = phimin - delta1;
-    } else {
-      phiminus = phimin / (1.0f + delta1 / fabs(phimin));
-    }
-
-    if (phi_i == phi_j) {
-      phi_mid = phi_i;
-    } else {
-      if (phi_i < phi_j) {
-        phi_mid = fmax(phiminus, fmin(phibar + delta2, phi_mid0));
-      } else {
-        phi_mid = fmin(phiplus, fmax(phibar - delta2, phi_mid0));
-      }
-    }
-
-    if (k < 5) {
-      dWi[k] = phi_mid - phi_i;
-    } else {
-      dWj[k - 5] = phi_mid - phi_i;
-    }
-  }
-
-#endif
-
-  //    printf("dWL: %g %g %g %g %g\n", dWi[0], dWi[1], dWi[2], dWi[3], dWi[4]);
-  //    printf("dWR: %g %g %g %g %g\n", dWj[0], dWj[1], dWj[2], dWj[3], dWj[4]);
-
-  /* time */
-  dWi[0] -= 0.5 * mindt * (Wi[1] * pi->primitives.gradients.rho[0] +
-                           Wi[2] * pi->primitives.gradients.rho[1] +
-                           Wi[3] * pi->primitives.gradients.rho[2] +
-                           Wi[0] * (pi->primitives.gradients.v[0][0] +
-                                    pi->primitives.gradients.v[1][1] +
-                                    pi->primitives.gradients.v[2][2]));
-  dWi[1] -= 0.5 * mindt * (Wi[1] * pi->primitives.gradients.v[0][0] +
-                           Wi[2] * pi->primitives.gradients.v[0][1] +
-                           Wi[3] * pi->primitives.gradients.v[0][2] +
-                           pi->primitives.gradients.P[0] / Wi[0]);
-  dWi[2] -= 0.5 * mindt * (Wi[1] * pi->primitives.gradients.v[1][0] +
-                           Wi[2] * pi->primitives.gradients.v[1][1] +
-                           Wi[3] * pi->primitives.gradients.v[1][2] +
-                           pi->primitives.gradients.P[1] / Wi[0]);
-  dWi[3] -= 0.5 * mindt * (Wi[1] * pi->primitives.gradients.v[2][0] +
-                           Wi[2] * pi->primitives.gradients.v[2][1] +
-                           Wi[3] * pi->primitives.gradients.v[2][2] +
-                           pi->primitives.gradients.P[2] / Wi[0]);
-  dWi[4] -= 0.5 * mindt *
-            (Wi[1] * pi->primitives.gradients.P[0] +
-             Wi[2] * pi->primitives.gradients.P[1] +
-             Wi[3] * pi->primitives.gradients.P[2] +
-             const_hydro_gamma * Wi[4] * (pi->primitives.gradients.v[0][0] +
-                                          pi->primitives.gradients.v[1][1] +
-                                          pi->primitives.gradients.v[2][2]));
-
-  dWj[0] -= 0.5 * mindt * (Wj[1] * pj->primitives.gradients.rho[0] +
-                           Wj[2] * pj->primitives.gradients.rho[1] +
-                           Wj[3] * pj->primitives.gradients.rho[2] +
-                           Wj[0] * (pj->primitives.gradients.v[0][0] +
-                                    pj->primitives.gradients.v[1][1] +
-                                    pj->primitives.gradients.v[2][2]));
-  dWj[1] -= 0.5 * mindt * (Wj[1] * pj->primitives.gradients.v[0][0] +
-                           Wj[2] * pj->primitives.gradients.v[0][1] +
-                           Wj[3] * pj->primitives.gradients.v[0][2] +
-                           pj->primitives.gradients.P[0] / Wj[0]);
-  dWj[2] -= 0.5 * mindt * (Wj[1] * pj->primitives.gradients.v[1][0] +
-                           Wj[2] * pj->primitives.gradients.v[1][1] +
-                           Wj[3] * pj->primitives.gradients.v[1][2] +
-                           pj->primitives.gradients.P[1] / Wj[0]);
-  dWj[3] -= 0.5 * mindt * (Wj[1] * pj->primitives.gradients.v[2][0] +
-                           Wj[2] * pj->primitives.gradients.v[2][1] +
-                           Wj[3] * pj->primitives.gradients.v[2][2] +
-                           pj->primitives.gradients.P[2] / Wj[0]);
-  dWj[4] -= 0.5 * mindt *
-            (Wj[1] * pj->primitives.gradients.P[0] +
-             Wj[2] * pj->primitives.gradients.P[1] +
-             Wj[3] * pj->primitives.gradients.P[2] +
-             const_hydro_gamma * Wj[4] * (pj->primitives.gradients.v[0][0] +
-                                          pj->primitives.gradients.v[1][1] +
-                                          pj->primitives.gradients.v[2][2]));
-
-  //    printf("WL: %g %g %g %g %g\n", Wi[0], Wi[1], Wi[2], Wi[3], Wi[4]);
-  //    printf("WR: %g %g %g %g %g\n", Wj[0], Wj[1], Wj[2], Wj[3], Wj[4]);
-
-  //    printf("dWL: %g %g %g %g %g\n", dWi[0], dWi[1], dWi[2], dWi[3], dWi[4]);
-  //    printf("dWR: %g %g %g %g %g\n", dWj[0], dWj[1], dWj[2], dWj[3], dWj[4]);
-
-  Wi[0] += dWi[0];
-  Wi[1] += dWi[1];
-  Wi[2] += dWi[2];
-  Wi[3] += dWi[3];
-  Wi[4] += dWi[4];
-
-  Wj[0] += dWj[0];
-  Wj[1] += dWj[1];
-  Wj[2] += dWj[2];
-  Wj[3] += dWj[3];
-  Wj[4] += dWj[4];
-#endif
-
-  /* apply slope limiter interface by interface */
-  /* ... to be done ... */
+  hydro_gradients_predict(pi, pj, hi, hj, dx, r, xij_i, Wi, Wj, mindt);
 
   /* we don't need to rotate, we can use the unit vector in the Riemann problem
    * itself (see GIZMO) */
@@ -940,7 +341,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
     printf("mindt: %g\n", mindt);
     printf("WL: %g %g %g %g %g\n", pi->primitives.rho, pi->primitives.v[0],
            pi->primitives.v[1], pi->primitives.v[2], pi->primitives.P);
+#ifdef USE_GRADIENTS
     printf("dWL: %g %g %g %g %g\n", dWi[0], dWi[1], dWi[2], dWi[3], dWi[4]);
+#endif
     printf("gradWL[0]: %g %g %g\n", pi->primitives.gradients.rho[0],
            pi->primitives.gradients.rho[1], pi->primitives.gradients.rho[2]);
     printf("gradWL[1]: %g %g %g\n", pi->primitives.gradients.v[0][0],
@@ -954,7 +357,9 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
     printf("WL': %g %g %g %g %g\n", Wi[0], Wi[1], Wi[2], Wi[3], Wi[4]);
     printf("WR: %g %g %g %g %g\n", pj->primitives.rho, pj->primitives.v[0],
            pj->primitives.v[1], pj->primitives.v[2], pj->primitives.P);
+#ifdef USE_GRADIENTS
     printf("dWR: %g %g %g %g %g\n", dWj[0], dWj[1], dWj[2], dWj[3], dWj[4]);
+#endif
     printf("gradWR[0]: %g %g %g\n", pj->primitives.gradients.rho[0],
            pj->primitives.gradients.rho[1], pj->primitives.gradients.rho[2]);
     printf("gradWR[1]: %g %g %g\n", pj->primitives.gradients.v[0][0],
@@ -969,61 +374,133 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
     error("Negative density or pressure!\n");
   }
 
-  GFLOAT totflux[5];
+  float totflux[5];
   riemann_solve_for_flux(Wi, Wj, n_unit, vij, totflux);
 
+  /* Store mass flux */
+  float mflux = dti * Anorm * totflux[0];
+  pi->gravity.mflux[0] += mflux * dx[0];
+  pi->gravity.mflux[1] += mflux * dx[1];
+  pi->gravity.mflux[2] += mflux * dx[2];
+
   /* Update conserved variables */
   /* eqn. (16) */
-  pi->conserved.mass -= mindt * Anorm * totflux[0];
-  pi->conserved.momentum[0] -= mindt * Anorm * totflux[1];
-  pi->conserved.momentum[1] -= mindt * Anorm * totflux[2];
-  pi->conserved.momentum[2] -= mindt * Anorm * totflux[3];
-  pi->conserved.energy -= mindt * Anorm * totflux[4];
-
-#ifdef THERMAL_ENERGY
-  float ekin = 0.5 * (pi->primitives.v[0] * pi->primitives.v[0] +
-                      pi->primitives.v[1] * pi->primitives.v[1] +
-                      pi->primitives.v[2] * pi->primitives.v[2]);
-  pi->conserved.energy += mindt * Anorm * totflux[1] * pi->primitives.v[0];
-  pi->conserved.energy += mindt * Anorm * totflux[2] * pi->primitives.v[1];
-  pi->conserved.energy += mindt * Anorm * totflux[3] * pi->primitives.v[2];
-  pi->conserved.energy -= mindt * Anorm * totflux[0] * ekin;
-#endif
-
-  /* the non symmetric version is never called when using mindt, whether this
-   * piece of code
-   * should always be executed or only in the symmetric case is currently
-   * unclear */
-  if (mode == 1) {
-    pj->conserved.mass += mindt * Anorm * totflux[0];
-    pj->conserved.momentum[0] += mindt * Anorm * totflux[1];
-    pj->conserved.momentum[1] += mindt * Anorm * totflux[2];
-    pj->conserved.momentum[2] += mindt * Anorm * totflux[3];
-    pj->conserved.energy += mindt * Anorm * totflux[4];
-
-#ifdef THERMAL_ENERGY
-    ekin = 0.5 * (pj->primitives.v[0] * pj->primitives.v[0] +
-                  pj->primitives.v[1] * pj->primitives.v[1] +
-                  pj->primitives.v[2] * pj->primitives.v[2]);
-    pj->conserved.energy -= mindt * Anorm * totflux[1] * pj->primitives.v[0];
-    pj->conserved.energy -= mindt * Anorm * totflux[2] * pj->primitives.v[1];
-    pj->conserved.energy -= mindt * Anorm * totflux[3] * pj->primitives.v[2];
-    pj->conserved.energy += mindt * Anorm * totflux[0] * ekin;
-#endif
+  pi->conserved.flux.mass -= dti * Anorm * totflux[0];
+  pi->conserved.flux.momentum[0] -= dti * Anorm * totflux[1];
+  pi->conserved.flux.momentum[1] -= dti * Anorm * totflux[2];
+  pi->conserved.flux.momentum[2] -= dti * Anorm * totflux[3];
+  pi->conserved.flux.energy -= dti * Anorm * totflux[4];
+
+  float ekin = 0.5f * (pi->primitives.v[0] * pi->primitives.v[0] +
+                       pi->primitives.v[1] * pi->primitives.v[1] +
+                       pi->primitives.v[2] * pi->primitives.v[2]);
+  pi->conserved.flux.energy += dti * Anorm * totflux[1] * pi->primitives.v[0];
+  pi->conserved.flux.energy += dti * Anorm * totflux[2] * pi->primitives.v[1];
+  pi->conserved.flux.energy += dti * Anorm * totflux[3] * pi->primitives.v[2];
+  pi->conserved.flux.energy -= dti * Anorm * totflux[0] * ekin;
+
+  /* here is how it works:
+     Mode will only be 1 if both particles are ACTIVE and they are in the same
+     cell. In this case, this method IS the flux calculation for particle j, and
+     we HAVE TO UPDATE it.
+     Mode 0 can mean several things: it can mean that particle j is INACTIVE, in
+     which case we NEED TO UPDATE it, since otherwise the flux is lost from the
+     system and the conserved variable is not conserved.
+     It can also mean that particle j sits in another cell and is ACTIVE. In
+     this case, the flux exchange for particle j is done TWICE and we SHOULD NOT
+     UPDATE particle j.
+     ==> we update particle j if (MODE IS 1) OR (j IS INACTIVE)
+  */
+  if (mode == 1 || pj->ti_end > pi->ti_end) {
+    /* Store mass flux */
+    mflux = dtj * Anorm * totflux[0];
+    pj->gravity.mflux[0] -= mflux * dx[0];
+    pj->gravity.mflux[1] -= mflux * dx[1];
+    pj->gravity.mflux[2] -= mflux * dx[2];
+
+    pj->conserved.flux.mass += dtj * Anorm * totflux[0];
+    pj->conserved.flux.momentum[0] += dtj * Anorm * totflux[1];
+    pj->conserved.flux.momentum[1] += dtj * Anorm * totflux[2];
+    pj->conserved.flux.momentum[2] += dtj * Anorm * totflux[3];
+    pj->conserved.flux.energy += dtj * Anorm * totflux[4];
+
+    ekin = 0.5f * (pj->primitives.v[0] * pj->primitives.v[0] +
+                   pj->primitives.v[1] * pj->primitives.v[1] +
+                   pj->primitives.v[2] * pj->primitives.v[2]);
+    pj->conserved.flux.energy -= dtj * Anorm * totflux[1] * pj->primitives.v[0];
+    pj->conserved.flux.energy -= dtj * Anorm * totflux[2] * pj->primitives.v[1];
+    pj->conserved.flux.energy -= dtj * Anorm * totflux[3] * pj->primitives.v[2];
+    pj->conserved.flux.energy += dtj * Anorm * totflux[0] * ekin;
   }
 }
 
-/* this corresponds to task_subtype_fluxes */
-__attribute__((always_inline)) INLINE static void runner_iact_hydro_loop3(
+/**
+ * @brief Flux calculation between particle i and particle j
+ *
+ * This method calls runner_iact_fluxes_common with mode 1.
+ *
+ * @param r2 Squared distance between particle i and particle j.
+ * @param dx Distance vector between the particles (dx = pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_force(
     float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
 
   runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 1);
 }
 
-/* this corresponds to task_subtype_fluxes */
-__attribute__((always_inline)) INLINE static void
-runner_iact_nonsym_hydro_loop3(float r2, float *dx, float hi, float hj,
-                               struct part *pi, struct part *pj) {
+/**
+ * @brief Flux calculation between particle i and particle j: non-symmetric
+ * version
+ *
+ * This method calls runner_iact_fluxes_common with mode 0.
+ *
+ * @param r2 Squared distance between particle i and particle j.
+ * @param dx Distance vector between the particles (dx = pi->x - pj->x).
+ * @param hi Smoothing length of particle i.
+ * @param hj Smoothing length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
+    float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
 
   runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0);
 }
+
+//// EMPTY VECTORIZED VERSIONS (gradients methods are missing...)
+
+__attribute__((always_inline)) INLINE static void runner_iact_vec_density(
+    float *R2, float *Dx, float *Hi, float *Hj, struct part **pi,
+    struct part **pj) {
+  error(
+      "Vectorised versions of the Gizmo interaction functions do not exist "
+      "yet!");
+}
+
+__attribute__((always_inline)) INLINE static void
+runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj,
+                               struct part **pi, struct part **pj) {
+  error(
+      "Vectorised versions of the Gizmo interaction functions do not exist "
+      "yet!");
+}
+
+__attribute__((always_inline)) INLINE static void runner_iact_vec_force(
+    float *R2, float *Dx, float *Hi, float *Hj, struct part **pi,
+    struct part **pj) {
+  error(
+      "Vectorised versions of the Gizmo interaction functions do not exist "
+      "yet!");
+}
+
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
+    float *R2, float *Dx, float *Hi, float *Hj, struct part **pi,
+    struct part **pj) {
+  error(
+      "Vectorised versions of the Gizmo interaction functions do not exist "
+      "yet!");
+}
diff --git a/src/hydro/Gizmo/hydro_io.h b/src/hydro/Gizmo/hydro_io.h
index 3c51653d994bd9f01864bcc24c6886eba25d1d05..e5f221ae4345dc519a50d332131ecf296f318338 100644
--- a/src/hydro/Gizmo/hydro_io.h
+++ b/src/hydro/Gizmo/hydro_io.h
@@ -1,6 +1,6 @@
 /*******************************************************************************
  * This file is part of SWIFT.
- * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ * Coypright (c) 2016 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
@@ -17,77 +17,124 @@
  *
  ******************************************************************************/
 
+#include "adiabatic_index.h"
+#include "hydro_gradients.h"
+#include "hydro_slope_limiters.h"
+#include "io_properties.h"
+#include "riemann.h"
+
 /**
- * @brief Reads the different particles to the HDF5 file
+ * @brief Specifies which particle fields to read from a dataset
  *
- * @param h_grp The HDF5 group in which to read the arrays.
- * @param N The number of particles on that MPI rank.
- * @param N_total The total number of particles (only used in MPI mode)
- * @param offset The offset of the particles for this MPI rank (only used in MPI
- *mode)
- * @param parts The particle array
+ * @param parts The particle array.
+ * @param list The list of i/o properties to read.
+ * @param num_fields The number of i/o fields to read.
+ */
+void hydro_read_particles(struct part* parts, struct io_props* list,
+                          int* num_fields) {
+
+  *num_fields = 8;
+
+  /* List what we want to read */
+  list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY,
+                                UNIT_CONV_LENGTH, parts, x);
+  list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY,
+                                UNIT_CONV_SPEED, parts, v);
+  list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS,
+                                parts, conserved.mass);
+  list[3] = io_make_input_field("SmoothingLength", FLOAT, 1, COMPULSORY,
+                                UNIT_CONV_LENGTH, parts, h);
+  list[4] = io_make_input_field("InternalEnergy", FLOAT, 1, COMPULSORY,
+                                UNIT_CONV_ENERGY_PER_UNIT_MASS, parts,
+                                conserved.energy);
+  list[5] = io_make_input_field("ParticleIDs", ULONGLONG, 1, COMPULSORY,
+                                UNIT_CONV_NO_UNITS, parts, id);
+  list[6] = io_make_input_field("Accelerations", FLOAT, 3, OPTIONAL,
+                                UNIT_CONV_ACCELERATION, parts, a_hydro);
+  list[7] = io_make_input_field("Density", FLOAT, 1, OPTIONAL,
+                                UNIT_CONV_DENSITY, parts, primitives.rho);
+}
+
+/**
+ * @brief Get the internal energy of a particle
  *
+ * @param e #engine.
+ * @param p Particle.
+ * @return Internal energy of the particle
  */
-__attribute__((always_inline)) INLINE static void hydro_read_particles(
-    hid_t h_grp, int N, long long N_total, long long offset,
-    struct part* parts) {
-
-  /* Read arrays */
-  readArray(h_grp, "Coordinates", DOUBLE, N, 3, parts, N_total, offset, x,
-            COMPULSORY);
-  readArray(h_grp, "Velocities", FLOAT, N, 3, parts, N_total, offset, v,
-            COMPULSORY);
-  readArray(h_grp, "Masses", FLOAT, N, 1, parts, N_total, offset,
-            conserved.mass, COMPULSORY);
-  readArray(h_grp, "SmoothingLength", FLOAT, N, 1, parts, N_total, offset, h,
-            COMPULSORY);
-  readArray(h_grp, "InternalEnergy", FLOAT, N, 1, parts, N_total, offset,
-            primitives.P, COMPULSORY);
-  readArray(h_grp, "ParticleIDs", ULONGLONG, N, 1, parts, N_total, offset, id,
-            COMPULSORY);
-  readArray(h_grp, "Acceleration", FLOAT, N, 3, parts, N_total, offset, a_hydro,
-            OPTIONAL);
-  readArray(h_grp, "Density", FLOAT, N, 1, parts, N_total, offset,
-            primitives.rho, OPTIONAL);
+float convert_u(struct engine* e, struct part* p) {
+  return p->primitives.P / hydro_gamma_minus_one / p->primitives.rho;
 }
 
 /**
- * @brief Writes the different particles to the HDF5 file
+ * @brief Get the entropic function of a particle
  *
- * @param h_grp The HDF5 group in which to write the arrays.
- * @param fileName The name of the file (unsued in MPI mode).
- * @param xmfFile The XMF file to write to (unused in MPI mode).
- * @param N The number of particles on that MPI rank.
- * @param N_total The total number of particles (only used in MPI mode)
- * @param mpi_rank The MPI rank of this node (only used in MPI mode)
- * @param offset The offset of the particles for this MPI rank (only used in MPI
- *mode)
- * @param parts The particle array
- * @param us The unit system to use
+ * @param e #engine.
+ * @param p Particle.
+ * @return Entropic function of the particle
+ */
+float convert_A(struct engine* e, struct part* p) {
+  return p->primitives.P / pow_gamma(p->primitives.rho);
+}
+
+/**
+ * @brief Get the total energy of a particle
  *
+ * @param e #engine.
+ * @param p Particle.
+ * @return Total energy of the particle
  */
-__attribute__((always_inline)) INLINE static void hydro_write_particles(
-    hid_t h_grp, char* fileName, FILE* xmfFile, int N, long long N_total,
-    int mpi_rank, long long offset, struct part* parts, struct UnitSystem* us) {
-
-  /* Write arrays */
-  writeArray(h_grp, fileName, xmfFile, "Coordinates", DOUBLE, N, 3, parts,
-             N_total, mpi_rank, offset, x, us, UNIT_CONV_LENGTH);
-  writeArray(h_grp, fileName, xmfFile, "Velocities", FLOAT, N, 3, parts,
-             N_total, mpi_rank, offset, v, us, UNIT_CONV_SPEED);
-  writeArray(h_grp, fileName, xmfFile, "Masses", FLOAT, N, 1, parts, N_total,
-             mpi_rank, offset, conserved.mass, us, UNIT_CONV_MASS);
-  writeArray(h_grp, fileName, xmfFile, "SmoothingLength", FLOAT, N, 1, parts,
-             N_total, mpi_rank, offset, h, us, UNIT_CONV_LENGTH);
-  writeArray(h_grp, fileName, xmfFile, "InternalEnergy", FLOAT, N, 1, parts,
-             N_total, mpi_rank, offset, primitives.P, us,
-             UNIT_CONV_ENTROPY_PER_UNIT_MASS);
-  writeArray(h_grp, fileName, xmfFile, "ParticleIDs", ULONGLONG, N, 1, parts,
-             N_total, mpi_rank, offset, id, us, UNIT_CONV_NO_UNITS);
-  writeArray(h_grp, fileName, xmfFile, "Acceleration", FLOAT, N, 3, parts,
-             N_total, mpi_rank, offset, a_hydro, us, UNIT_CONV_ACCELERATION);
-  writeArray(h_grp, fileName, xmfFile, "Density", FLOAT, N, 1, parts, N_total,
-             mpi_rank, offset, primitives.rho, us, UNIT_CONV_DENSITY);
+float convert_Etot(struct engine* e, struct part* p) {
+  float momentum2;
+
+  momentum2 = p->conserved.momentum[0] * p->conserved.momentum[0] +
+              p->conserved.momentum[1] * p->conserved.momentum[1] +
+              p->conserved.momentum[2] * p->conserved.momentum[2];
+
+  return p->conserved.energy + 0.5f * momentum2 / p->conserved.mass;
+}
+
+/**
+ * @brief Specifies which particle fields to write to a dataset
+ *
+ * @param parts The particle array.
+ * @param list The list of i/o properties to write.
+ * @param num_fields The number of i/o fields to write.
+ */
+void hydro_write_particles(struct part* parts, struct io_props* list,
+                           int* num_fields) {
+
+  *num_fields = 13;
+
+  /* List what we want to write */
+  list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH,
+                                 parts, x);
+  list[1] = io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts,
+                                 primitives.v);
+  list[2] = io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts,
+                                 conserved.mass);
+  list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH,
+                                 parts, h);
+  list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1,
+                                              UNIT_CONV_ENERGY_PER_UNIT_MASS,
+                                              parts, primitives.P, convert_u);
+  list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
+                                 UNIT_CONV_NO_UNITS, parts, id);
+  list[6] = io_make_output_field("Acceleration", FLOAT, 3,
+                                 UNIT_CONV_ACCELERATION, parts, a_hydro);
+  list[7] = io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts,
+                                 primitives.rho);
+  list[8] = io_make_output_field("Volume", FLOAT, 1, UNIT_CONV_VOLUME, parts,
+                                 geometry.volume);
+  list[9] = io_make_output_field("GradDensity", FLOAT, 3, UNIT_CONV_DENSITY,
+                                 parts, primitives.gradients.rho);
+  list[10] = io_make_output_field_convert_part(
+      "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY, parts, primitives.P, convert_A);
+  list[11] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE,
+                                  parts, primitives.P);
+  list[12] =
+      io_make_output_field_convert_part("TotEnergy", FLOAT, 1, UNIT_CONV_ENERGY,
+                                        parts, conserved.energy, convert_Etot);
 }
 
 /**
@@ -95,26 +142,24 @@ __attribute__((always_inline)) INLINE static void hydro_write_particles(
  * @param h_grpsph The HDF5 group in which to write
  */
 void writeSPHflavour(hid_t h_grpsph) {
+  /* Gradient information */
+  writeAttribute_s(h_grpsph, "Gradient reconstruction model",
+                   HYDRO_GRADIENT_IMPLEMENTATION);
+
+  /* Slope limiter information */
+  writeAttribute_s(h_grpsph, "Cell wide slope limiter model",
+                   HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION);
+  writeAttribute_s(h_grpsph, "Piecewise slope limiter model",
+                   HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION);
 
-  /* Kernel function description */
-  writeAttribute_s(h_grpsph, "Kernel", kernel_name);
-  writeAttribute_f(h_grpsph, "Kernel eta", const_eta_kernel);
-  writeAttribute_f(h_grpsph, "Weighted N_ngb", kernel_nwneigh);
-  writeAttribute_f(h_grpsph, "Delta N_ngb", const_delta_nwneigh);
-  writeAttribute_f(h_grpsph, "Hydro gamma", const_hydro_gamma);
-
-  /* Viscosity and thermal conduction */
-  writeAttribute_s(h_grpsph, "Thermal Conductivity Model",
-                   "(No treatment) Legacy Gadget-2 as in Springel (2005)");
-  writeAttribute_s(h_grpsph, "Viscosity Model",
-                   "Legacy Gadget-2 as in Springel (2005)");
-  writeAttribute_f(h_grpsph, "Viscosity alpha", const_viscosity_alpha);
-  writeAttribute_f(h_grpsph, "Viscosity beta", 3.f);
-
-  /* Time integration properties */
-  writeAttribute_f(h_grpsph, "CFL parameter", const_cfl);
-  writeAttribute_f(h_grpsph, "Maximal ln(Delta h) change over dt",
-                   const_ln_max_h_change);
-  writeAttribute_f(h_grpsph, "Maximal Delta h change over dt",
-                   exp(const_ln_max_h_change));
+  /* Riemann solver information */
+  writeAttribute_s(h_grpsph, "Riemann solver type",
+                   RIEMANN_SOLVER_IMPLEMENTATION);
 }
+
+/**
+ * @brief Are we writing entropy in the internal energy field ?
+ *
+ * @return 1 if entropy is in 'internal energy', 0 otherwise.
+ */
+int writeEntropyFlag() { return 0; }
diff --git a/src/hydro/Gizmo/hydro_part.h b/src/hydro/Gizmo/hydro_part.h
index 9e5f32f758248d1d1616f4556c81fc8e0b52e83b..d425294671d4bc172f45c928c2290f8cfa8e093c 100644
--- a/src/hydro/Gizmo/hydro_part.h
+++ b/src/hydro/Gizmo/hydro_part.h
@@ -1,8 +1,6 @@
 /*******************************************************************************
  * This file is part of SWIFT.
- * Coypright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
- *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- *                    Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
+ * Coypright (c) 2014 Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
  *
  * 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
@@ -19,26 +17,15 @@
  *
  ******************************************************************************/
 
-/* Some standard headers. */
-#include <stdlib.h>
-
-#define GFLOAT float
-
 /* Extra particle data not needed during the computation. */
 struct xpart {
 
-  /* Old position, at last tree rebuild. */
-  double x_old[3];
+  /* Offset between current position and position at last tree rebuild. */
+  float x_diff[3];
 
   /* Velocity at the last full step. */
   float v_full[3];
 
-  /* Entropy at the half-step. */
-  float u_hdt;
-
-  /* Old density. */
-  float omega;
-
 } __attribute__((aligned(xpart_align)));
 
 /* Data of a single particle. */
@@ -47,18 +34,13 @@ struct part {
   /* Particle position. */
   double x[3];
 
-  /* Particle velocity. */
+  /* Particle predicted velocity. */
   float v[3];
 
   /* Particle acceleration. */
   float a_hydro[3];
 
-  float mass;  // MATTHIEU
-  float h_dt;
-  float rho;
-  float rho_dh;
-
-  /* Particle cutoff radius. */
+  /* Particle smoothing length. */
   float h;
 
   /* Particle time of beginning of time-step. */
@@ -67,76 +49,103 @@ struct part {
   /* Particle time of end of time-step. */
   int ti_end;
 
-  /* The primitive hydrodynamical variables */
+  /* Old internal energy flux */
+  float du_dt;
+
+  /* The primitive hydrodynamical variables. */
   struct {
 
-    /* fluid velocity */
-    GFLOAT v[3];
+    /* Fluid velocity. */
+    float v[3];
 
-    /* density */
-    GFLOAT rho;
+    /* Density. */
+    float rho;
 
-    /* pressure */
-    GFLOAT P;
+    /* Pressure. */
+    float P;
 
+    /* Gradients of the primitive variables. */
     struct {
 
-      GFLOAT rho[3];
+      /* Density gradients. */
+      float rho[3];
 
-      GFLOAT v[3][3];
+      /* Fluid velocity gradients. */
+      float v[3][3];
 
-      GFLOAT P[3];
+      /* Pressure gradients. */
+      float P[3];
 
     } gradients;
 
+    /* Quantities needed by the slope limiter. */
     struct {
 
-      /* extreme values among the neighbours */
-      GFLOAT rho[2];
+      /* Extreme values of the density among the neighbours. */
+      float rho[2];
 
-      GFLOAT v[3][2];
+      /* Extreme values of the fluid velocity among the neighbours. */
+      float v[3][2];
 
-      GFLOAT P[2];
+      /* Extreme values of the pressure among the neighbours. */
+      float P[2];
 
-      /* maximal distance to all neighbouring faces */
+      /* Maximal distance to all neighbouring faces. */
       float maxr;
 
     } limiter;
 
   } primitives;
 
-  /* The conserved hydrodynamical variables */
+  /* The conserved hydrodynamical variables. */
   struct {
 
-    /* fluid momentum */
-    GFLOAT momentum[3];
+    /* Fluid momentum. */
+    float momentum[3];
+
+    /* Fluid mass */
+    float mass;
+
+    /* Fluid thermal energy (not per unit mass!). */
+    float energy;
+
+    /* Fluxes. */
+    struct {
+
+      /* Mass flux. */
+      float mass;
+
+      /* Momentum flux. */
+      float momentum[3];
 
-    /* fluid mass */
-    GFLOAT mass;
+      /* Energy flux. */
+      float energy;
 
-    /* fluid energy */
-    GFLOAT energy;
+    } flux;
 
   } conserved;
 
-  /* Geometrical quantities used for hydro */
+  /* Geometrical quantities used for hydro. */
   struct {
 
-    /* volume of the particle */
+    /* Volume of the particle. */
     float volume;
 
-    /* gradient matrix */
+    /* Geometrical shear matrix used to calculate second order accurate
+       gradients */
     float matrix_E[3][3];
 
   } geometry;
 
+  /* Variables used for timestep calculation (currently not used). */
   struct {
 
+    /* Maximum fluid velocity among all neighbours. */
     float vmax;
 
   } timestepvars;
 
-  /* Quantities used during the density loop */
+  /* Quantities used during the volume (=density) loop. */
   struct {
 
     /* Particle velocity divergence. */
@@ -153,8 +162,36 @@ struct part {
 
   } density;
 
+  /* Quantities used during the force loop. */
+  struct {
+
+    /* Needed to drift the primitive variables. */
+    float h_dt;
+
+    /* Physical time step of the particle. */
+    float dt;
+
+    /* Actual velocity of the particle. */
+    float v_full[3];
+
+  } force;
+
+  /* Specific stuff for the gravity-hydro coupling. */
+  struct {
+
+    /* Previous value of the gravitational acceleration. */
+    float old_a[3];
+
+    /* Previous value of the mass flux vector. */
+    float old_mflux[3];
+
+    /* Current value of the mass flux vector. */
+    float mflux[3];
+
+  } gravity;
+
   /* Particle ID. */
-  unsigned long long id;
+  long long id;
 
   /* Associated gravitas. */
   struct gpart *gpart;
diff --git a/src/hydro/Gizmo/hydro_slope_limiters.h b/src/hydro/Gizmo/hydro_slope_limiters.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd66f05ac9eb9d51744723d93f899b0c8c668e2e
--- /dev/null
+++ b/src/hydro/Gizmo/hydro_slope_limiters.h
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 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/>.
+ *
+ ******************************************************************************/
+
+#ifndef SWIFT_HYDRO_SLOPE_LIMITERS_H
+#define SWIFT_HYDRO_SLOPE_LIMITERS_H
+
+#include "dimension.h"
+#include "kernel_hydro.h"
+
+#ifdef SLOPE_LIMITER_PER_FACE
+
+#define HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION \
+  "GIZMO piecewise slope limiter (Hopkins 2015)"
+#include "hydro_slope_limiters_face.h"
+
+#else
+
+#define HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION "No piecewise slope limiter"
+
+/**
+ * @brief Slope limit the slopes at the interface between two particles
+ *
+ * @param Wi Hydrodynamic variables of particle i.
+ * @param Wj Hydrodynamic variables of particle j.
+ * @param dWi Difference between the hydrodynamic variables of particle i at the
+ * position of particle i and at the interface position.
+ * @param dWj Difference between the hydrodynamic variables of particle j at the
+ * position of particle j and at the interface position.
+ * @param xij_i Relative position vector of the interface w.r.t. particle i.
+ * @param xij_j Relative position vector of the interface w.r.t. partilce j.
+ * @param r Distance between particle i and particle j.
+ */
+__attribute__((always_inline)) INLINE static void hydro_slope_limit_face(
+    float *Wi, float *Wj, float *dWi, float *dWj, float *xij_i, float *xij_j,
+    float r) {}
+
+#endif
+
+#ifdef SLOPE_LIMITER_CELL_WIDE
+
+#define HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION \
+  "Cell wide slope limiter (Springel 2010)"
+#include "hydro_slope_limiters_cell.h"
+
+#else
+
+#define HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION "No cell wide slope limiter"
+
+/**
+ * @brief Initialize variables for the cell wide slope limiter
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_init(
+    struct part *p) {}
+
+/**
+ * @brief Collect information for the cell wide slope limiter during the
+ * neighbour loop
+ *
+ * @param pi Particle i.
+ * @param pj Particle j.
+ * @param r Distance between particle i and particle j.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_slope_limit_cell_collect(struct part *pi, struct part *pj, float r) {}
+
+/**
+ * @brief Slope limit cell gradients
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_slope_limit_cell(
+    struct part *p) {}
+
+#endif
+
+#endif  // SWIFT_HYDRO_SLOPE_LIMITERS_H
diff --git a/src/hydro/Gizmo/hydro_slope_limiters_cell.h b/src/hydro/Gizmo/hydro_slope_limiters_cell.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa99b43721f669f47a7888a5da0b1933ca1ebd62
--- /dev/null
+++ b/src/hydro/Gizmo/hydro_slope_limiters_cell.h
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 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/>.
+ *
+ ******************************************************************************/
+
+#include <float.h>
+
+/**
+ * @brief Initialize variables for the cell wide slope limiter
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_init(
+    struct part* p) {
+
+  p->primitives.limiter.rho[0] = FLT_MAX;
+  p->primitives.limiter.rho[1] = -FLT_MAX;
+  p->primitives.limiter.v[0][0] = FLT_MAX;
+  p->primitives.limiter.v[0][1] = -FLT_MAX;
+  p->primitives.limiter.v[1][0] = FLT_MAX;
+  p->primitives.limiter.v[1][1] = -FLT_MAX;
+  p->primitives.limiter.v[2][0] = FLT_MAX;
+  p->primitives.limiter.v[2][1] = -FLT_MAX;
+  p->primitives.limiter.P[0] = FLT_MAX;
+  p->primitives.limiter.P[1] = -FLT_MAX;
+
+  p->primitives.limiter.maxr = -FLT_MAX;
+}
+
+/**
+ * @brief Collect information for the cell wide slope limiter during the
+ * neighbour loop
+ *
+ * @param pi Particle i.
+ * @param pj Particle j.
+ * @param r Distance between particle i and particle j.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_slope_limit_cell_collect(struct part* pi, struct part* pj, float r) {
+
+  /* basic slope limiter: collect the maximal and the minimal value for the
+   * primitive variables among the ngbs */
+  pi->primitives.limiter.rho[0] =
+      fmin(pj->primitives.rho, pi->primitives.limiter.rho[0]);
+  pi->primitives.limiter.rho[1] =
+      fmax(pj->primitives.rho, pi->primitives.limiter.rho[1]);
+
+  pi->primitives.limiter.v[0][0] =
+      fmin(pj->primitives.v[0], pi->primitives.limiter.v[0][0]);
+  pi->primitives.limiter.v[0][1] =
+      fmax(pj->primitives.v[0], pi->primitives.limiter.v[0][1]);
+  pi->primitives.limiter.v[1][0] =
+      fmin(pj->primitives.v[1], pi->primitives.limiter.v[1][0]);
+  pi->primitives.limiter.v[1][1] =
+      fmax(pj->primitives.v[1], pi->primitives.limiter.v[1][1]);
+  pi->primitives.limiter.v[2][0] =
+      fmin(pj->primitives.v[2], pi->primitives.limiter.v[2][0]);
+  pi->primitives.limiter.v[2][1] =
+      fmax(pj->primitives.v[2], pi->primitives.limiter.v[2][1]);
+
+  pi->primitives.limiter.P[0] =
+      fmin(pj->primitives.P, pi->primitives.limiter.P[0]);
+  pi->primitives.limiter.P[1] =
+      fmax(pj->primitives.P, pi->primitives.limiter.P[1]);
+
+  pi->primitives.limiter.maxr = fmax(r, pi->primitives.limiter.maxr);
+}
+
+/**
+ * @brief Slope limit cell gradients
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_slope_limit_cell(
+    struct part* p) {
+
+  float gradrho[3], gradv[3][3], gradP[3];
+  float gradtrue, gradmax, gradmin, alpha;
+
+  gradrho[0] = p->primitives.gradients.rho[0];
+  gradrho[1] = p->primitives.gradients.rho[1];
+  gradrho[2] = p->primitives.gradients.rho[2];
+
+  gradv[0][0] = p->primitives.gradients.v[0][0];
+  gradv[0][1] = p->primitives.gradients.v[0][1];
+  gradv[0][2] = p->primitives.gradients.v[0][2];
+
+  gradv[1][0] = p->primitives.gradients.v[1][0];
+  gradv[1][1] = p->primitives.gradients.v[1][1];
+  gradv[1][2] = p->primitives.gradients.v[1][2];
+
+  gradv[2][0] = p->primitives.gradients.v[2][0];
+  gradv[2][1] = p->primitives.gradients.v[2][1];
+  gradv[2][2] = p->primitives.gradients.v[2][2];
+
+  gradP[0] = p->primitives.gradients.P[0];
+  gradP[1] = p->primitives.gradients.P[1];
+  gradP[2] = p->primitives.gradients.P[2];
+
+  gradtrue = sqrtf(gradrho[0] * gradrho[0] + gradrho[1] * gradrho[1] +
+                   gradrho[2] * gradrho[2]);
+  if (gradtrue) {
+    gradtrue *= p->primitives.limiter.maxr;
+    gradmax = p->primitives.limiter.rho[1] - p->primitives.rho;
+    gradmin = p->primitives.rho - p->primitives.limiter.rho[0];
+    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
+    p->primitives.gradients.rho[0] *= alpha;
+    p->primitives.gradients.rho[1] *= alpha;
+    p->primitives.gradients.rho[2] *= alpha;
+  }
+
+  gradtrue = sqrtf(gradv[0][0] * gradv[0][0] + gradv[0][1] * gradv[0][1] +
+                   gradv[0][2] * gradv[0][2]);
+  if (gradtrue) {
+    gradtrue *= p->primitives.limiter.maxr;
+    gradmax = p->primitives.limiter.v[0][1] - p->primitives.v[0];
+    gradmin = p->primitives.v[0] - p->primitives.limiter.v[0][0];
+    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
+    p->primitives.gradients.v[0][0] *= alpha;
+    p->primitives.gradients.v[0][1] *= alpha;
+    p->primitives.gradients.v[0][2] *= alpha;
+  }
+
+  gradtrue = sqrtf(gradv[1][0] * gradv[1][0] + gradv[1][1] * gradv[1][1] +
+                   gradv[1][2] * gradv[1][2]);
+  if (gradtrue) {
+    gradtrue *= p->primitives.limiter.maxr;
+    gradmax = p->primitives.limiter.v[1][1] - p->primitives.v[1];
+    gradmin = p->primitives.v[1] - p->primitives.limiter.v[1][0];
+    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
+    p->primitives.gradients.v[1][0] *= alpha;
+    p->primitives.gradients.v[1][1] *= alpha;
+    p->primitives.gradients.v[1][2] *= alpha;
+  }
+
+  gradtrue = sqrtf(gradv[2][0] * gradv[2][0] + gradv[2][1] * gradv[2][1] +
+                   gradv[2][2] * gradv[2][2]);
+  if (gradtrue) {
+    gradtrue *= p->primitives.limiter.maxr;
+    gradmax = p->primitives.limiter.v[2][1] - p->primitives.v[2];
+    gradmin = p->primitives.v[2] - p->primitives.limiter.v[2][0];
+    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
+    p->primitives.gradients.v[2][0] *= alpha;
+    p->primitives.gradients.v[2][1] *= alpha;
+    p->primitives.gradients.v[2][2] *= alpha;
+  }
+
+  gradtrue =
+      sqrtf(gradP[0] * gradP[0] + gradP[1] * gradP[1] + gradP[2] * gradP[2]);
+  if (gradtrue) {
+    gradtrue *= p->primitives.limiter.maxr;
+    gradmax = p->primitives.limiter.P[1] - p->primitives.P;
+    gradmin = p->primitives.P - p->primitives.limiter.P[0];
+    alpha = fmin(1.0f, fmin(gradmax / gradtrue, gradmin / gradtrue));
+    p->primitives.gradients.P[0] *= alpha;
+    p->primitives.gradients.P[1] *= alpha;
+    p->primitives.gradients.P[2] *= alpha;
+  }
+}
diff --git a/src/hydro/Gizmo/hydro_slope_limiters_face.h b/src/hydro/Gizmo/hydro_slope_limiters_face.h
new file mode 100644
index 0000000000000000000000000000000000000000..7ae5dd2eb073d9aae8ab6f2efffdf8df15b4bb4a
--- /dev/null
+++ b/src/hydro/Gizmo/hydro_slope_limiters_face.h
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 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/>.
+ *
+ ******************************************************************************/
+
+/**
+ * @brief Slope limit a single quantity at the interface
+ *
+ * @param phi_i Value of the quantity at the particle position.
+ * @param phi_j Value of the quantity at the neighbouring particle position.
+ * @param phi_mid0 Extrapolated value of the quantity at the interface position.
+ * @param xij_norm Distance between the particle position and the interface
+ * position.
+ * @param r Distance between the particle and its neighbour.
+ * @return The slope limited difference between the quantity at the particle
+ * position and the quantity at the interface position.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_slope_limit_face_quantity(float phi_i, float phi_j, float phi_mid0,
+                                float xij_norm, float r) {
+
+  float delta1, delta2, phimin, phimax, phibar, phiplus, phiminus, phi_mid;
+  const float psi1 = 0.5f;
+  const float psi2 = 0.25f;
+
+  if (phi_i == phi_j) {
+    return 0.0f;
+  }
+
+  delta1 = psi1 * fabs(phi_i - phi_j);
+  delta2 = psi2 * fabs(phi_i - phi_j);
+
+  phimin = fmin(phi_i, phi_j);
+  phimax = fmax(phi_i, phi_j);
+
+  phibar = phi_i + xij_norm / r * (phi_j - phi_i);
+
+  /* if sign(phimax+delta1) == sign(phimax) */
+  if ((phimax + delta1) * phimax > 0.0f) {
+    phiplus = phimax + delta1;
+  } else {
+    phiplus = phimax / (1.0f + delta1 / fabs(phimax));
+  }
+
+  /* if sign(phimin-delta1) == sign(phimin) */
+  if ((phimin - delta1) * phimin > 0.0f) {
+    phiminus = phimin - delta1;
+  } else {
+    phiminus = phimin / (1.0f + delta1 / fabs(phimin));
+  }
+
+  if (phi_i < phi_j) {
+    phi_mid = fmax(phiminus, fmin(phibar + delta2, phi_mid0));
+  } else {
+    phi_mid = fmin(phiplus, fmax(phibar - delta2, phi_mid0));
+  }
+
+  return phi_mid - phi_i;
+}
+
+/**
+ * @brief Slope limit the slopes at the interface between two particles
+ *
+ * @param Wi Hydrodynamic variables of particle i.
+ * @param Wj Hydrodynamic variables of particle j.
+ * @param dWi Difference between the hydrodynamic variables of particle i at the
+ * position of particle i and at the interface position.
+ * @param dWj Difference between the hydrodynamic variables of particle j at the
+ * position of particle j and at the interface position.
+ * @param xij_i Relative position vector of the interface w.r.t. particle i.
+ * @param xij_j Relative position vector of the interface w.r.t. partilce j.
+ * @param r Distance between particle i and particle j.
+ */
+__attribute__((always_inline)) INLINE static void hydro_slope_limit_face(
+    float *Wi, float *Wj, float *dWi, float *dWj, float *xij_i, float *xij_j,
+    float r) {
+
+  float xij_i_norm, xij_j_norm;
+
+  xij_i_norm =
+      sqrtf(xij_i[0] * xij_i[0] + xij_i[1] * xij_i[1] + xij_i[2] * xij_i[2]);
+
+  xij_j_norm =
+      sqrtf(xij_j[0] * xij_j[0] + xij_j[1] * xij_j[1] + xij_j[2] * xij_j[2]);
+
+  dWi[0] = hydro_slope_limit_face_quantity(Wi[0], Wj[0], Wi[0] + dWi[0],
+                                           xij_i_norm, r);
+  dWi[1] = hydro_slope_limit_face_quantity(Wi[1], Wj[1], Wi[1] + dWi[1],
+                                           xij_i_norm, r);
+  dWi[2] = hydro_slope_limit_face_quantity(Wi[2], Wj[2], Wi[2] + dWi[2],
+                                           xij_i_norm, r);
+  dWi[3] = hydro_slope_limit_face_quantity(Wi[3], Wj[3], Wi[3] + dWi[3],
+                                           xij_i_norm, r);
+  dWi[4] = hydro_slope_limit_face_quantity(Wi[4], Wj[4], Wi[4] + dWi[4],
+                                           xij_i_norm, r);
+
+  dWj[0] = hydro_slope_limit_face_quantity(Wj[0], Wi[0], Wj[0] + dWj[0],
+                                           xij_j_norm, r);
+  dWj[1] = hydro_slope_limit_face_quantity(Wj[1], Wi[1], Wj[1] + dWj[1],
+                                           xij_j_norm, r);
+  dWj[2] = hydro_slope_limit_face_quantity(Wj[2], Wi[2], Wj[2] + dWj[2],
+                                           xij_j_norm, r);
+  dWj[3] = hydro_slope_limit_face_quantity(Wj[3], Wi[3], Wj[3] + dWj[3],
+                                           xij_j_norm, r);
+  dWj[4] = hydro_slope_limit_face_quantity(Wj[4], Wi[4], Wj[4] + dWj[4],
+                                           xij_j_norm, r);
+}
diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h
index f1f284d5d2b56af9d20b24c91eaf4a481f94851f..0bbc77f4a2384c79f7d3329c20b92990598d5c63 100644
--- a/src/hydro/Minimal/hydro.h
+++ b/src/hydro/Minimal/hydro.h
@@ -16,9 +16,145 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_MINIMAL_HYDRO_H
+#define SWIFT_MINIMAL_HYDRO_H
+
+/**
+ * @file Minimal/hydro.h
+ * @brief Minimal conservative implementation of SPH (Non-neighbour loop
+ * equations)
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
+ *
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of
+ * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3,
+ * pp. 759-794.
+ */
 
 #include "adiabatic_index.h"
 #include "approx_math.h"
+#include "dimension.h"
+#include "equation_of_state.h"
+#include "hydro_properties.h"
+#include "kernel_hydro.h"
+
+/**
+ * @brief Returns the internal energy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not internal energy, this function computes the internal
+ * energy from the thermodynamic variable.
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_internal_energy(
+    const struct part *restrict p, float dt) {
+
+  return p->u + p->u_dt * dt;
+}
+
+/**
+ * @brief Returns the pressure of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_pressure(
+    const struct part *restrict p, float dt) {
+
+  const float u = p->u + p->u_dt * dt;
+
+  return gas_pressure_from_internal_energy(p->rho, u);
+}
+
+/**
+ * @brief Returns the entropy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not entropy, this function computes the entropy from
+ * the thermodynamic variable.
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_entropy(
+    const struct part *restrict p, float dt) {
+
+  const float u = p->u + p->u_dt * dt;
+
+  return gas_entropy_from_internal_energy(p->rho, u);
+}
+
+/**
+ * @brief Returns the sound speed of a particle
+ *
+ * @param p The particle of interest
+ * @param dt Time since the last kick
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
+    const struct part *restrict p, float dt) {
+
+  const float u = p->u + p->u_dt * dt;
+
+  return gas_soundspeed_from_internal_energy(p->rho, u);
+}
+
+/**
+ * @brief Returns the density of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_density(
+    const struct part *restrict p) {
+
+  return p->rho;
+}
+
+/**
+ * @brief Returns the mass of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_mass(
+    const struct part *restrict p) {
+
+  return p->mass;
+}
+
+/**
+ * @brief Modifies the thermal state of a particle to the imposed internal
+ * energy
+ *
+ * This overrides the current state of the particle but does *not* change its
+ * time-derivatives
+ *
+ * @param p The particle
+ * @param u The new internal energy
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_internal_energy(
+    struct part *restrict p, float u) {
+
+  p->u = u;
+}
+
+/**
+ * @brief Modifies the thermal state of a particle to the imposed entropy
+ *
+ * This overrides the current state of the particle but does *not* change its
+ * time-derivatives
+ *
+ * @param p The particle
+ * @param S The new entropy
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_entropy(
+    struct part *restrict p, float S) {
+
+  p->u = gas_internal_energy_from_entropy(p->rho, S);
+}
 
 /**
  * @brief Computes the hydro time-step of a given particle
@@ -57,7 +193,11 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep(
 __attribute__((always_inline)) INLINE static void hydro_first_init_part(
     struct part *restrict p, struct xpart *restrict xp) {
 
-  xp->u_full = p->u;
+  p->ti_begin = 0;
+  p->ti_end = 0;
+  xp->v_full[0] = p->v[0];
+  xp->v_full[1] = p->v[1];
+  xp->v_full[2] = p->v[2];
 }
 
 /**
@@ -71,6 +211,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part(
  */
 __attribute__((always_inline)) INLINE static void hydro_init_part(
     struct part *restrict p) {
+
   p->density.wcount = 0.f;
   p->density.wcount_dh = 0.f;
   p->rho = 0.f;
@@ -94,25 +235,25 @@ __attribute__((always_inline)) INLINE static void hydro_end_density(
 
   /* Some smoothing length multiples. */
   const float h = p->h;
-  const float ih = 1.0f / h;
-  const float ih2 = ih * ih;
-  const float ih4 = ih2 * ih2;
+  const float h_inv = 1.0f / h;                       /* 1/h */
+  const float h_inv_dim = pow_dimension(h_inv);       /* 1/h^d */
+  const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */
 
   /* Final operation on the density (add self-contribution). */
   p->rho += p->mass * kernel_root;
-  p->rho_dh -= 3.0f * p->mass * kernel_root;
+  p->rho_dh -= hydro_dimension * p->mass * kernel_root;
   p->density.wcount += kernel_root;
 
   /* Finish the calculation by inserting the missing h-factors */
-  p->rho *= ih * ih2;
-  p->rho_dh *= ih4;
+  p->rho *= h_inv_dim;
+  p->rho_dh *= h_inv_dim_plus_one;
   p->density.wcount *= kernel_norm;
-  p->density.wcount_dh *= ih * kernel_gamma * kernel_norm;
+  p->density.wcount_dh *= h_inv * kernel_gamma * kernel_norm;
 
   const float irho = 1.f / p->rho;
 
   /* Compute the derivative term */
-  p->rho_dh = 1.f / (1.f + 0.33333333f * p->h * p->rho_dh * irho);
+  p->rho_dh = 1.f / (1.f + hydro_dimension_inv * p->h * p->rho_dh * irho);
 }
 
 /**
@@ -134,7 +275,10 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp, int ti_current,
     double timeBase) {
 
-  p->force.pressure = p->rho * p->u * hydro_gamma_minus_one;
+  const float half_dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase;
+  const float pressure = hydro_get_pressure(p, half_dt);
+
+  p->force.pressure = pressure;
 }
 
 /**
@@ -165,20 +309,36 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
  * Additional hydrodynamic quantites are drifted forward in time here. These
  * include thermal quantities (thermal energy or total energy or entropy, ...).
  *
- * @param p The particle
- * @param xp The extended data of the particle
- * @param t0 The time at the start of the drift (on the timeline)
- * @param t1 The time at the end of the drift (on the timeline)
- * @param timeBase The minimal time-step size
+ * @param p The particle.
+ * @param xp The extended data of the particle.
+ * @param dt The drift time-step.
+ * @param t0 The time at the start of the drift (on the timeline).
+ * @param t1 The time at the end of the drift (on the timeline).
+ * @param timeBase The minimal time-step size.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
-    struct part *restrict p, const struct xpart *restrict xp, int t0, int t1,
-    double timeBase) {
-
-  p->u = xp->u_full;
-
-  /* Need to recompute the pressure as well */
-  p->force.pressure = p->rho * p->u * hydro_gamma_minus_one;
+    struct part *restrict p, const struct xpart *restrict xp, float dt, int t0,
+    int t1, double timeBase) {
+
+  const float h_inv = 1.f / p->h;
+
+  /* Predict smoothing length */
+  const float w1 = p->force.h_dt * h_inv * dt;
+  if (fabsf(w1) < 0.2f)
+    p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */
+  else
+    p->h *= expf(w1);
+
+  /* Predict density */
+  const float w2 = -hydro_dimension * w1;
+  if (fabsf(w2) < 0.2f)
+    p->rho *= approx_expf(w2); /* 4th order expansion of exp(w) */
+  else
+    p->rho *= expf(w2);
+
+  /* Drift the pressure */
+  const float dt_entr = (t1 - (p->ti_begin + p->ti_end) / 2) * timeBase;
+  p->force.pressure = hydro_get_pressure(p, dt_entr);
 }
 
 /**
@@ -193,7 +353,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra(
 __attribute__((always_inline)) INLINE static void hydro_end_force(
     struct part *restrict p) {
 
-  p->force.h_dt *= p->h * 0.333333333f;
+  p->force.h_dt *= p->h * hydro_dimension_inv;
 }
 
 /**
@@ -211,11 +371,15 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt,
     float half_dt) {
 
-  /* Kick in momentum space */
-  xp->u_full += p->u_dt * dt;
+  /* Do not decrease the energy by more than a factor of 2*/
+  const float u_change = p->u_dt * dt;
+  if (u_change > -0.5f * p->u)
+    p->u += u_change;
+  else
+    p->u *= 0.5f;
 
-  /* Get the predicted internal energy */
-  p->u = xp->u_full - half_dt * p->u_dt;
+  /* Do not 'overcool' when timestep increases */
+  if (p->u + p->u_dt * half_dt < 0.5f * p->u) p->u_dt = -0.5f * p->u / half_dt;
 }
 
 /**
@@ -231,54 +395,4 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
 __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
     struct part *restrict p) {}
 
-/**
- * @brief Returns the internal energy of a particle
- *
- * For implementations where the main thermodynamic variable
- * is not internal energy, this function computes the internal
- * energy from the thermodynamic variable.
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_internal_energy(
-    const struct part *restrict p, float dt) {
-
-  return p->u;
-}
-
-/**
- * @brief Returns the pressure of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_pressure(
-    const struct part *restrict p, float dt) {
-
-  return p->u * p->rho * hydro_gamma_minus_one;
-}
-
-/**
- * @brief Returns the entropy of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_entropy(
-    const struct part *restrict p, float dt) {
-
-  return hydro_gamma_minus_one * p->u * pow_minus_gamma_minus_one(p->rho);
-}
-
-/**
- * @brief Returns the sound speed of a particle
- *
- * @param p The particle of interest
- * @param dt Time since the last kick
- */
-__attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
-    const struct part *restrict p, float dt) {
-
-  return sqrt(p->u * hydro_gamma * hydro_gamma_minus_one);
-}
+#endif /* SWIFT_MINIMAL_HYDRO_H */
diff --git a/src/hydro/Minimal/hydro_debug.h b/src/hydro/Minimal/hydro_debug.h
index 7ab348d255173a33fc29e482eb6032c03c82d9eb..0a6e50da6051fd961f4bf35f32caee311888a13e 100644
--- a/src/hydro/Minimal/hydro_debug.h
+++ b/src/hydro/Minimal/hydro_debug.h
@@ -16,18 +16,36 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_MINIMAL_HYDRO_DEBUG_H
+#define SWIFT_MINIMAL_HYDRO_DEBUG_H
+
+/**
+ * @file Minimal/hydro_debug.h
+ * @brief Minimal conservative implementation of SPH (Debugging routines)
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
+ *
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of
+ * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3,
+ * pp. 759-794.
+ */
 
 __attribute__((always_inline)) INLINE static void hydro_debug_particle(
     const struct part* p, const struct xpart* xp) {
   printf(
       "x=[%.3e,%.3e,%.3e], "
       "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e], "
-      "u_full=%.3e, u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n"
-      "h=%.3e, dh/dt=%.3e "
-      "wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, t_begin=%d, t_end=%d\n",
+      "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n"
+      "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, "
+      "t_begin=%d, t_end=%d\n",
       p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0],
       xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2],
-      xp->u_full, p->u, p->u_dt, p->force.v_sig, p->force.pressure, p->h,
-      p->force.h_dt, (int)p->density.wcount, p->mass, p->rho_dh, p->rho,
-      p->ti_begin, p->ti_end);
+      p->u, p->u_dt, p->force.v_sig, p->force.pressure, p->h, p->force.h_dt,
+      (int)p->density.wcount, p->mass, p->rho_dh, p->rho, p->ti_begin,
+      p->ti_end);
 }
+
+#endif /* SWIFT_MINIMAL_HYDRO_DEBUG_H */
diff --git a/src/hydro/Minimal/hydro_iact.h b/src/hydro/Minimal/hydro_iact.h
index 311bdc90744059ea1272c2ca6cf419e5fbc1ba82..a8a855d9db81f6927c1d8b45410a57d50a8366de 100644
--- a/src/hydro/Minimal/hydro_iact.h
+++ b/src/hydro/Minimal/hydro_iact.h
@@ -16,18 +16,25 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_RUNNER_IACT_MINIMAL_H
-#define SWIFT_RUNNER_IACT_MINIMAL_H
-
-#include "adiabatic_index.h"
+#ifndef SWIFT_MINIMAL_HYDRO_IACT_H
+#define SWIFT_MINIMAL_HYDRO_IACT_H
 
 /**
- * @brief Minimal conservative implementation of SPH
+ * @file Minimal/hydro_iact.h
+ * @brief Minimal conservative implementation of SPH (Neighbour loop equations)
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
  *
- * The thermal variable is the internal energy (u). No viscosity nor
- * thermal conduction terms are implemented.
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of
+ * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3,
+ * pp. 759-794.
  */
 
+#include "adiabatic_index.h"
+
 /**
  * @brief Density loop
  */
@@ -48,7 +55,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
   kernel_deval(xi, &wi, &wi_dx);
 
   pi->rho += mj * wi;
-  pi->rho_dh -= mj * (3.f * wi + xi * wi_dx);
+  pi->rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx);
   pi->density.wcount += wi;
   pi->density.wcount_dh -= xi * wi_dx;
 
@@ -58,7 +65,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
   kernel_deval(xj, &wj, &wj_dx);
 
   pj->rho += mi * wj;
-  pj->rho_dh -= mi * (3.f * wj + xj * wj_dx);
+  pj->rho_dh -= mi * (hydro_dimension * wj + xj * wj_dx);
   pj->density.wcount += wj;
   pj->density.wcount_dh -= xj * wj_dx;
 }
@@ -93,7 +100,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
   kernel_deval(xi, &wi, &wi_dx);
 
   pi->rho += mj * wi;
-  pi->rho_dh -= mj * (3.f * wi + xi * wi_dx);
+  pi->rho_dh -= mj * (hydro_dimension * wi + xi * wi_dx);
   pi->density.wcount += wi;
   pi->density.wcount_dh -= xi * wi_dx;
 }
@@ -115,6 +122,8 @@ runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj,
 __attribute__((always_inline)) INLINE static void runner_iact_force(
     float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
 
+  const float fac_mu = 1.f; /* Will change with cosmological integration */
+
   const float r = sqrtf(r2);
   const float r_inv = 1.0f / r;
 
@@ -128,49 +137,75 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
 
   /* Get the kernel for hi. */
   const float hi_inv = 1.0f / hi;
-  const float hi2_inv = hi_inv * hi_inv;
+  const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
   const float xi = r * hi_inv;
   float wi, wi_dx;
   kernel_deval(xi, &wi, &wi_dx);
-  const float wi_dr = hi2_inv * hi2_inv * wi_dx;
+  const float wi_dr = hid_inv * wi_dx;
 
   /* Get the kernel for hj. */
   const float hj_inv = 1.0f / hj;
-  const float hj2_inv = hj_inv * hj_inv;
+  const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
   const float xj = r * hj_inv;
   float wj, wj_dx;
   kernel_deval(xj, &wj, &wj_dx);
-  const float wj_dr = hj2_inv * hj2_inv * wj_dx;
+  const float wj_dr = hjd_inv * wj_dx;
 
   /* Compute gradient terms */
-  const float P_over_rho_i = pressurei / (rhoi * rhoi) * pi->rho_dh;
-  const float P_over_rho_j = pressurej / (rhoj * rhoj) * pj->rho_dh;
+  const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->rho_dh;
+  const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->rho_dh;
 
   /* Compute dv dot r. */
   const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] +
                      (pi->v[1] - pj->v[1]) * dx[1] +
                      (pi->v[2] - pj->v[2]) * dx[2];
 
-  /* Compute sound speeds */
+  /* Are the particles moving towards each others ? */
+  const float omega_ij = fminf(dvdr, 0.f);
+  const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
+
+  /* Compute sound speeds and signal velocity */
   const float ci = sqrtf(hydro_gamma * pressurei / rhoi);
   const float cj = sqrtf(hydro_gamma * pressurej / rhoj);
-  const float v_sig = ci + cj;
+  const float v_sig = ci + cj - 3.f * mu_ij;
+
+  /* Construct the full viscosity term */
+  const float rho_ij = 0.5f * (rhoi + rhoj);
+  const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij;
+
+  /* Convolve with the kernel */
+  const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
 
   /* SPH acceleration term */
-  const float sph_term = (P_over_rho_i * wi_dr + P_over_rho_j * wj_dr) * r_inv;
+  const float sph_acc_term =
+      (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv;
+
+  /* Assemble the acceleration */
+  const float acc = sph_acc_term + visc_acc_term;
 
   /* Use the force Luke ! */
-  pi->a_hydro[0] -= mj * sph_term * dx[0];
-  pi->a_hydro[1] -= mj * sph_term * dx[1];
-  pi->a_hydro[2] -= mj * sph_term * dx[2];
+  pi->a_hydro[0] -= mj * acc * dx[0];
+  pi->a_hydro[1] -= mj * acc * dx[1];
+  pi->a_hydro[2] -= mj * acc * dx[2];
 
-  pj->a_hydro[0] += mi * sph_term * dx[0];
-  pj->a_hydro[1] += mi * sph_term * dx[1];
-  pj->a_hydro[2] += mi * sph_term * dx[2];
+  pj->a_hydro[0] += mi * acc * dx[0];
+  pj->a_hydro[1] += mi * acc * dx[1];
+  pj->a_hydro[2] += mi * acc * dx[2];
 
   /* Get the time derivative for u. */
-  pi->u_dt += P_over_rho_i * mj * dvdr * r_inv * wi_dr;
-  pj->u_dt += P_over_rho_j * mi * dvdr * r_inv * wj_dr;
+  const float sph_du_term_i = P_over_rho2_i * dvdr * r_inv * wi_dr;
+  const float sph_du_term_j = P_over_rho2_j * dvdr * r_inv * wj_dr;
+
+  /* Viscosity term */
+  const float visc_du_term = 0.5f * visc_acc_term * dvdr;
+
+  /* Assemble the energy equation term */
+  const float du_dt_i = sph_du_term_i + visc_du_term;
+  const float du_dt_j = sph_du_term_j + visc_du_term;
+
+  /* Internal energy time derivatibe */
+  pi->u_dt += du_dt_i * mj;
+  pj->u_dt += du_dt_j * mi;
 
   /* Get the time derivative for h. */
   pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr;
@@ -198,6 +233,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
 __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
     float r2, float *dx, float hi, float hj, struct part *pi, struct part *pj) {
 
+  const float fac_mu = 1.f; /* Will change with cosmological integration */
+
   const float r = sqrtf(r2);
   const float r_inv = 1.0f / r;
 
@@ -211,44 +248,68 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
 
   /* Get the kernel for hi. */
   const float hi_inv = 1.0f / hi;
-  const float hi2_inv = hi_inv * hi_inv;
+  const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
   const float xi = r * hi_inv;
   float wi, wi_dx;
   kernel_deval(xi, &wi, &wi_dx);
-  const float wi_dr = hi2_inv * hi2_inv * wi_dx;
+  const float wi_dr = hid_inv * wi_dx;
 
   /* Get the kernel for hj. */
   const float hj_inv = 1.0f / hj;
-  const float hj2_inv = hj_inv * hj_inv;
+  const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
   const float xj = r * hj_inv;
   float wj, wj_dx;
   kernel_deval(xj, &wj, &wj_dx);
-  const float wj_dr = hj2_inv * hj2_inv * wj_dx;
+  const float wj_dr = hjd_inv * wj_dx;
 
   /* Compute gradient terms */
-  const float P_over_rho_i = pressurei / (rhoi * rhoi) * pi->rho_dh;
-  const float P_over_rho_j = pressurej / (rhoj * rhoj) * pj->rho_dh;
+  const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->rho_dh;
+  const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->rho_dh;
 
   /* Compute dv dot r. */
   const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] +
                      (pi->v[1] - pj->v[1]) * dx[1] +
                      (pi->v[2] - pj->v[2]) * dx[2];
 
-  /* Compute sound speeds */
+  /* Are the particles moving towards each others ? */
+  const float omega_ij = fminf(dvdr, 0.f);
+  const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
+
+  /* Compute sound speeds and signal velocity */
   const float ci = sqrtf(hydro_gamma * pressurei / rhoi);
   const float cj = sqrtf(hydro_gamma * pressurej / rhoj);
-  const float v_sig = ci + cj;
+  const float v_sig = ci + cj - 3.f * mu_ij;
+
+  /* Construct the full viscosity term */
+  const float rho_ij = 0.5f * (rhoi + rhoj);
+  const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij;
+
+  /* Convolve with the kernel */
+  const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
 
   /* SPH acceleration term */
-  const float sph_term = (P_over_rho_i * wi_dr + P_over_rho_j * wj_dr) * r_inv;
+  const float sph_acc_term =
+      (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv;
+
+  /* Assemble the acceleration */
+  const float acc = sph_acc_term + visc_acc_term;
 
   /* Use the force Luke ! */
-  pi->a_hydro[0] -= mj * sph_term * dx[0];
-  pi->a_hydro[1] -= mj * sph_term * dx[1];
-  pi->a_hydro[2] -= mj * sph_term * dx[2];
+  pi->a_hydro[0] -= mj * acc * dx[0];
+  pi->a_hydro[1] -= mj * acc * dx[1];
+  pi->a_hydro[2] -= mj * acc * dx[2];
 
   /* Get the time derivative for u. */
-  pi->u_dt += P_over_rho_i * mj * dvdr * r_inv * wi_dr;
+  const float sph_du_term_i = P_over_rho2_i * dvdr * r_inv * wi_dr;
+
+  /* Viscosity term */
+  const float visc_du_term = 0.5f * visc_acc_term * dvdr;
+
+  /* Assemble the energy equation term */
+  const float du_dt_i = sph_du_term_i + visc_du_term;
+
+  /* Internal energy time derivatibe */
+  pi->u_dt += du_dt_i * mj;
 
   /* Get the time derivative for h. */
   pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr;
@@ -268,4 +329,4 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
       "function does not exist yet!");
 }
 
-#endif /* SWIFT_RUNNER_IACT_MINIMAL_H */
+#endif /* SWIFT_MINIMAL_HYDRO_IACT_H */
diff --git a/src/hydro/Minimal/hydro_io.h b/src/hydro/Minimal/hydro_io.h
index 1a5b2938354fb8ee3ef936f86ff442c731e0d4c7..01a75b17fd5577cfcfb48d3afac22579f30fcf7a 100644
--- a/src/hydro/Minimal/hydro_io.h
+++ b/src/hydro/Minimal/hydro_io.h
@@ -16,7 +16,25 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_MINIMAL_HYDRO_IO_H
+#define SWIFT_MINIMAL_HYDRO_IO_H
 
+/**
+ * @file Minimal/hydro_io.h
+ * @brief Minimal conservative implementation of SPH (i/o routines)
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
+ *
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of
+ * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3,
+ * pp. 759-794.
+ */
+
+#include "adiabatic_index.h"
+#include "hydro.h"
 #include "io_properties.h"
 #include "kernel_hydro.h"
 
@@ -51,6 +69,16 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
                                 UNIT_CONV_DENSITY, parts, rho);
 }
 
+float convert_S(struct engine* e, struct part* p) {
+
+  return hydro_get_entropy(p, 0);
+}
+
+float convert_P(struct engine* e, struct part* p) {
+
+  return hydro_get_pressure(p, 0);
+}
+
 /**
  * @brief Specifies which particle fields to write to a dataset
  *
@@ -61,7 +89,7 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
 void hydro_write_particles(struct part* parts, struct io_props* list,
                            int* num_fields) {
 
-  *num_fields = 8;
+  *num_fields = 10;
 
   /* List what we want to write */
   list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH,
@@ -80,6 +108,11 @@ void hydro_write_particles(struct part* parts, struct io_props* list,
                                  UNIT_CONV_ACCELERATION, parts, a_hydro);
   list[7] =
       io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho);
+  list[8] = io_make_output_field_convert_part("Entropy", FLOAT, 1,
+                                              UNIT_CONV_ENTROPY_PER_UNIT_MASS,
+                                              parts, rho, convert_S);
+  list[9] = io_make_output_field_convert_part(
+      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, rho, convert_P);
 }
 
 /**
@@ -90,8 +123,9 @@ void writeSPHflavour(hid_t h_grpsph) {
 
   /* Viscosity and thermal conduction */
   /* Nothing in this minimal model... */
-  writeAttribute_s(h_grpsph, "Thermal Conductivity Model", "No model");
-  writeAttribute_s(h_grpsph, "Viscosity Model", "No model");
+  writeAttribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment");
+  writeAttribute_s(h_grpsph, "Viscosity Model",
+                   "Minimal treatment as in Monaghan (1992)");
 
   /* Time integration properties */
   writeAttribute_f(h_grpsph, "Maximal Delta u change over dt",
@@ -104,3 +138,5 @@ void writeSPHflavour(hid_t h_grpsph) {
  * @return 1 if entropy is in 'internal energy', 0 otherwise.
  */
 int writeEntropyFlag() { return 0; }
+
+#endif /* SWIFT_MINIMAL_HYDRO_IO_H */
diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h
index f4feb2311ad2bfd8c7227f3b072c5971724a5815..ad65f8b44fc67f4aae6470246cbab91bc3710007 100644
--- a/src/hydro/Minimal/hydro_part.h
+++ b/src/hydro/Minimal/hydro_part.h
@@ -16,6 +16,22 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_MINIMAL_HYDRO_PART_H
+#define SWIFT_MINIMAL_HYDRO_PART_H
+
+/**
+ * @file Minimal/hydro_part.h
+ * @brief Minimal conservative implementation of SPH (Particle definition)
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
+ *
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of
+ * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3,
+ * pp. 759-794.
+ */
 
 /**
  * @brief Particle fields not needed during the SPH loops over neighbours.
@@ -31,8 +47,6 @@ struct xpart {
 
   float v_full[3]; /*!< Velocity at the last full step. */
 
-  float u_full; /*!< Thermal energy at the last full step. */
-
 } __attribute__((aligned(xpart_align)));
 
 /**
@@ -107,3 +121,5 @@ struct part {
   struct gpart* gpart; /*!< Pointer to corresponding gravity part. */
 
 } __attribute__((aligned(part_align)));
+
+#endif /* SWIFT_MINIMAL_HYDRO_PART_H */
diff --git a/src/hydro_io.h b/src/hydro_io.h
index 30d663f647c9b763e9b19177e9ba8ef374855768..f0a619b90b774574c434007b1c01a0e55e75e464 100644
--- a/src/hydro_io.h
+++ b/src/hydro_io.h
@@ -28,6 +28,8 @@
 #include "./hydro/Gadget2/hydro_io.h"
 #elif defined(DEFAULT_SPH)
 #include "./hydro/Default/hydro_io.h"
+#elif defined(GIZMO_SPH)
+#include "./hydro/Gizmo/hydro_io.h"
 #else
 #error "Invalid choice of SPH variant"
 #endif
diff --git a/src/hydro_properties.c b/src/hydro_properties.c
index 8dde4a23be3705b4a679c6d17eb8f28c1a8ff6b7..815969975b4f5e6b39099e71bbbec4e43c875ddc 100644
--- a/src/hydro_properties.c
+++ b/src/hydro_properties.c
@@ -27,6 +27,7 @@
 /* Local headers. */
 #include "adiabatic_index.h"
 #include "common_io.h"
+#include "dimension.h"
 #include "error.h"
 #include "hydro.h"
 #include "kernel_hydro.h"
@@ -39,8 +40,7 @@ void hydro_props_init(struct hydro_props *p,
 
   /* Kernel properties */
   p->eta_neighbours = parser_get_param_float(params, "SPH:resolution_eta");
-  const float eta3 = p->eta_neighbours * p->eta_neighbours * p->eta_neighbours;
-  p->target_neighbours = eta3 * kernel_norm;
+  p->target_neighbours = pow_dimension(p->eta_neighbours) * kernel_norm;
   p->delta_neighbours = parser_get_param_float(params, "SPH:delta_neighbours");
 
   /* Ghost stuff */
@@ -51,21 +51,35 @@ void hydro_props_init(struct hydro_props *p,
   p->CFL_condition = parser_get_param_float(params, "SPH:CFL_condition");
   const float max_volume_change = parser_get_opt_param_float(
       params, "SPH:max_volume_change", hydro_props_default_volume_change);
-  p->log_max_h_change = logf(powf(max_volume_change, 0.33333333333f));
+  p->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv));
 }
 
 void hydro_props_print(const struct hydro_props *p) {
 
+#if defined(EOS_IDEAL_GAS)
+  message("Equation of state: Ideal gas.");
+#elif defined(EOS_ISOTHERMAL_GAS)
+  message(
+      "Equation of state: Isothermal with internal energy "
+      "per unit mass set to %f.",
+      const_isothermal_internal_energy);
+#endif
+
   message("Adiabatic index gamma: %f.", hydro_gamma);
-  message("Hydrodynamic scheme: %s.", SPH_IMPLEMENTATION);
+
+  message("Hydrodynamic scheme: %s in %dD.", SPH_IMPLEMENTATION,
+          (int)hydro_dimension);
+
   message("Hydrodynamic kernel: %s with %.2f +/- %.2f neighbours (eta=%f).",
           kernel_name, p->target_neighbours, p->delta_neighbours,
           p->eta_neighbours);
+
   message("Hydrodynamic integration: CFL parameter: %.4f.", p->CFL_condition);
+
   message(
       "Hydrodynamic integration: Max change of volume: %.2f "
       "(max|dlog(h)/dt|=%f).",
-      powf(expf(p->log_max_h_change), 3.f), p->log_max_h_change);
+      pow_dimension(expf(p->log_max_h_change)), p->log_max_h_change);
 
   if (p->max_smoothing_iterations != hydro_props_default_max_iterations)
     message("Maximal iterations in ghost task set to %d (default is %d)",
@@ -76,6 +90,7 @@ void hydro_props_print(const struct hydro_props *p) {
 void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) {
 
   writeAttribute_f(h_grpsph, "Adiabatic index", hydro_gamma);
+  writeAttribute_i(h_grpsph, "Dimension", (int)hydro_dimension);
   writeAttribute_s(h_grpsph, "Scheme", SPH_IMPLEMENTATION);
   writeAttribute_s(h_grpsph, "Kernel function", kernel_name);
   writeAttribute_f(h_grpsph, "Kernel target N_ngb", p->target_neighbours);
@@ -84,8 +99,8 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) {
   writeAttribute_f(h_grpsph, "CFL parameter", p->CFL_condition);
   writeAttribute_f(h_grpsph, "Volume log(max(delta h))", p->log_max_h_change);
   writeAttribute_f(h_grpsph, "Volume max change time-step",
-                   powf(expf(p->log_max_h_change), 3.f));
-  writeAttribute_f(h_grpsph, "Max ghost iterations",
+                   pow_dimension(expf(p->log_max_h_change)));
+  writeAttribute_i(h_grpsph, "Max ghost iterations",
                    p->max_smoothing_iterations);
 }
 #endif
diff --git a/src/kernel_gravity.h b/src/kernel_gravity.h
index b38feb5758debf87add2007ee3684d869f393f7e..a1e382a21d04b7354aaf215069e999627e56ee07 100644
--- a/src/kernel_gravity.h
+++ b/src/kernel_gravity.h
@@ -50,7 +50,7 @@ static const float
                                     -10.66666667f,
                                     38.4f,
                                     -48.f,
-                                    21.3333333,
+                                    21.3333333f,
                                     0.f,
                                     0.f,
                                     -0.066666667f, /* 0.5 < u < 1 */
diff --git a/src/kernel_hydro.h b/src/kernel_hydro.h
index 7a73bf4f3d51550c5bf9096c0121d0faab819c0b..a2eb065d60beb1ea624f0e2387bc0b0f3f01c3f5 100644
--- a/src/kernel_hydro.h
+++ b/src/kernel_hydro.h
@@ -20,10 +20,23 @@
 #ifndef SWIFT_KERNEL_HYDRO_H
 #define SWIFT_KERNEL_HYDRO_H
 
+/**
+ * @file kernel_hydro.h
+ * @brief Kernel functions for SPH (scalar and vector version).
+ *
+ * All constants and kernel coefficients are taken from table 1 of
+ * Dehnen & Aly, MNRAS, 425, pp. 1062-1082 (2012).
+ */
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
 #include <math.h>
 
-/* Includes. */
+/* Local headers. */
 #include "const.h"
+#include "dimension.h"
 #include "error.h"
 #include "inline.h"
 #include "vector.h"
@@ -33,10 +46,18 @@
 
 /* Coefficients for the kernel. */
 #define kernel_name "Cubic spline (M4)"
-#define kernel_degree 3 /* Degree of the polynomial */
-#define kernel_ivals 2  /* Number of branches */
+#define kernel_degree 3 /*!< Degree of the polynomial */
+#define kernel_ivals 2  /*!< Number of branches */
+#if defined(HYDRO_DIMENSION_3D)
 #define kernel_gamma ((float)(1.825742))
 #define kernel_constant ((float)(16. * M_1_PI))
+#elif defined(HYDRO_DIMENSION_2D)
+#define kernel_gamma ((float)(1.778002))
+#define kernel_constant ((float)(80. * M_1_PI / 7.))
+#elif defined(HYDRO_DIMENSION_1D)
+#define kernel_gamma ((float)(1.732051))
+#define kernel_constant ((float)(8. / 3.))
+#endif
 static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
     __attribute__((aligned(16))) = {3.f,  -3.f, 0.f,  0.5f, /* 0 < u < 0.5 */
                                     -1.f, 3.f,  -3.f, 1.f,  /* 0.5 < u < 1 */
@@ -47,10 +68,18 @@ static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
 
 /* Coefficients for the kernel. */
 #define kernel_name "Quartic spline (M5)"
-#define kernel_degree 4
-#define kernel_ivals 5
+#define kernel_degree 4 /* Degree of the polynomial */
+#define kernel_ivals 5  /* Number of branches */
+#if defined(HYDRO_DIMENSION_3D)
 #define kernel_gamma ((float)(2.018932))
 #define kernel_constant ((float)(15625. * M_1_PI / 512.))
+#elif defined(HYDRO_DIMENSION_2D)
+#define kernel_gamma ((float)(1.977173))
+#define kernel_constant ((float)(46875. * M_1_PI / 2398.))
+#elif defined(HYDRO_DIMENSION_1D)
+#define kernel_gamma ((float)(1.936492))
+#define kernel_constant ((float)(3125. / 768.))
+#endif
 static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
     __attribute__((aligned(16))) = {
         6.f,  0.f,  -2.4f, 0.f,   0.368f, /* 0 < u < 0.2 */
@@ -65,10 +94,18 @@ static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
 
 /* Coefficients for the kernel. */
 #define kernel_name "Quintic spline (M6)"
-#define kernel_degree 5
-#define kernel_ivals 3
+#define kernel_degree 5 /* Degree of the polynomial */
+#define kernel_ivals 3  /* Number of branches */
+#if defined(HYDRO_DIMENSION_3D)
 #define kernel_gamma ((float)(2.195775))
 #define kernel_constant ((float)(2187. * M_1_PI / 40.))
+#elif defined(HYDRO_DIMENSION_2D)
+#define kernel_gamma ((float)(2.158131))
+#define kernel_constant ((float)(15309. * M_1_PI / 478.))
+#elif defined(HYDRO_DIMENSION_1D)
+#define kernel_gamma ((float)(2.121321))
+#define kernel_constant ((float)(243. / 40.))
+#endif
 static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
     __attribute__((aligned(16))) = {
         -10.f,        10.f,      0.f,
@@ -85,10 +122,17 @@ static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
 
 /* Coefficients for the kernel. */
 #define kernel_name "Wendland C2"
-#define kernel_degree 5
-#define kernel_ivals 1
+#define kernel_degree 5 /* Degree of the polynomial */
+#define kernel_ivals 1  /* Number of branches */
+#if defined(HYDRO_DIMENSION_3D)
 #define kernel_gamma ((float)(1.936492))
 #define kernel_constant ((float)(21. * M_1_PI / 2.))
+#elif defined(HYDRO_DIMENSION_2D)
+#define kernel_gamma ((float)(1.897367))
+#define kernel_constant ((float)(7. * M_1_PI))
+#elif defined(HYDRO_DIMENSION_1D)
+#error "Wendland C2 kernel not defined in 1D."
+#endif
 static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
     __attribute__((aligned(16))) = {
         4.f, -15.f, 20.f, -10.f, 0.f, 1.f,  /* 0 < u < 1 */
@@ -99,10 +143,17 @@ static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
 
 /* Coefficients for the kernel. */
 #define kernel_name "Wendland C4"
-#define kernel_degree 8
-#define kernel_ivals 1
+#define kernel_degree 8 /* Degree of the polynomial */
+#define kernel_ivals 1  /* Number of branches */
+#if defined(HYDRO_DIMENSION_3D)
 #define kernel_gamma ((float)(2.207940))
 #define kernel_constant ((float)(495. * M_1_PI / 32.))
+#elif defined(HYDRO_DIMENSION_2D)
+#define kernel_gamma ((float)(2.171239))
+#define kernel_constant ((float)(9. * M_1_PI))
+#elif defined(HYDRO_DIMENSION_1D)
+#error "Wendland C4 kernel not defined in 1D."
+#endif
 static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
     __attribute__((aligned(16))) = {
         11.666667f, -64.f,       140.f, -149.333333f, 70.f,
@@ -115,10 +166,17 @@ static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
 
 /* Coefficients for the kernel. */
 #define kernel_name "Wendland C6"
-#define kernel_degree 11
-#define kernel_ivals 1
+#define kernel_degree 11 /* Degree of the polynomial */
+#define kernel_ivals 1   /* Number of branches */
+#if defined(HYDRO_DIMENSION_3D)
 #define kernel_gamma ((float)(2.449490))
 #define kernel_constant ((float)(1365. * M_1_PI / 64.))
+#elif defined(HYDRO_DIMENSION_2D)
+#define kernel_gamma ((float)(2.415230))
+#define kernel_constant ((float)(78. * M_1_PI / 7.))
+#elif defined(HYDRO_DIMENSION_1D)
+#error "Wendland C6 kernel not defined in 1D."
+#endif
 static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
     __attribute__((aligned(16))) = {
         32.f, -231.f, 704.f, -1155.f, 1056.f, -462.f,
@@ -137,50 +195,68 @@ static const float kernel_coeffs[(kernel_degree + 1) * (kernel_ivals + 1)]
 /* Ok, now comes the real deal. */
 
 /* First some powers of gamma = H/h */
+#define kernel_gamma_inv ((float)(1. / kernel_gamma))
 #define kernel_gamma2 ((float)(kernel_gamma * kernel_gamma))
-#define kernel_gamma3 ((float)(kernel_gamma * kernel_gamma * kernel_gamma))
-#define kernel_gamma4 \
+
+/* define gamma^d, gamma^(d+1), 1/gamma^d and 1/gamma^(d+1) */
+#if defined(HYDRO_DIMENSION_3D)
+#define kernel_gamma_dim ((float)(kernel_gamma * kernel_gamma * kernel_gamma))
+#define kernel_gamma_dim_plus_one \
   ((float)(kernel_gamma * kernel_gamma * kernel_gamma * kernel_gamma))
-#define kernel_igamma ((float)(1. / kernel_gamma))
-#define kernel_igamma2 ((float)(kernel_igamma * kernel_igamma))
-#define kernel_igamma3 ((float)(kernel_igamma * kernel_igamma * kernel_igamma))
-#define kernel_igamma4 \
-  ((float)(kernel_igamma * kernel_igamma * kernel_igamma * kernel_igamma))
+#define kernel_gamma_inv_dim \
+  ((float)(1. / (kernel_gamma * kernel_gamma * kernel_gamma)))
+#define kernel_gamma_inv_dim_plus_one \
+  ((float)(1. / (kernel_gamma * kernel_gamma * kernel_gamma * kernel_gamma)))
+#elif defined(HYDRO_DIMENSION_2D)
+#define kernel_gamma_dim ((float)(kernel_gamma * kernel_gamma))
+#define kernel_gamma_dim_plus_one \
+  ((float)(kernel_gamma * kernel_gamma * kernel_gamma))
+#define kernel_gamma_inv_dim ((float)(1. / (kernel_gamma * kernel_gamma)))
+#define kernel_gamma_inv_dim_plus_one \
+  ((float)(1. / (kernel_gamma * kernel_gamma * kernel_gamma)))
+#elif defined(HYDRO_DIMENSION_1D)
+#define kernel_gamma_dim ((float)(kernel_gamma))
+#define kernel_gamma_dim_plus_one ((float)(kernel_gamma * kernel_gamma))
+#define kernel_gamma_inv_dim ((float)(1. / (kernel_gamma)))
+#define kernel_gamma_inv_dim_plus_one \
+  ((float)(1. / (kernel_gamma * kernel_gamma)))
+#endif
 
-/* The number of branches */
+/* The number of branches (floating point conversion) */
 #define kernel_ivals_f ((float)(kernel_ivals))
 
 /* Kernel self contribution (i.e. W(0,h)) */
-#define kernel_root \
-  ((float)(kernel_coeffs[kernel_degree]) * kernel_constant * kernel_igamma3)
+#define kernel_root                                          \
+  ((float)(kernel_coeffs[kernel_degree]) * kernel_constant * \
+   kernel_gamma_inv_dim)
 
 /* Kernel normalisation constant (volume term) */
-#define kernel_norm ((float)(4.0 * M_PI * kernel_gamma3 / 3.0))
+#define kernel_norm ((float)(hydro_dimention_unit_sphere * kernel_gamma_dim))
+
+/* ------------------------------------------------------------------------- */
 
 /**
  * @brief Computes the kernel function and its derivative.
  *
- * Return 0 if $u > \\gamma = H/h$
+ * The kernel function needs to be mutliplied by \f$h^{-d}\f$ and the gradient
+ * by \f$h^{-(d+1)}\f$, where \f$d\f$ is the dimensionality of the problem.
  *
- * @param u The ratio of the distance to the smoothing length $u = x/h$.
- * @param W (return) The value of the kernel function $W(x,h)$.
- * @param dW_dx (return) The norm of the gradient of $|\\nabla W(x,h)|$.
+ * Returns 0 if \f$u > \gamma = H/h\f$.
+ *
+ * @param u The ratio of the distance to the smoothing length \f$u = x/h\f$.
+ * @param W (return) The value of the kernel function \f$W(x,h)\f$.
+ * @param dW_dx (return) The norm of the gradient of \f$|\nabla W(x,h)|\f$.
  */
 __attribute__((always_inline)) INLINE static void kernel_deval(
     float u, float *restrict W, float *restrict dW_dx) {
 
   /* Go to the range [0,1[ from [0,H[ */
-  const float x = u * kernel_igamma;
+  const float x = u * kernel_gamma_inv;
 
-#if kernel_ivals == 1
-  /* Only one branch in this case */
-  const float *const coeffs = &kernel_coeffs[0];
-#else
   /* Pick the correct branch of the kernel */
   const int temp = (int)(x * kernel_ivals_f);
   const int ind = temp > kernel_ivals ? kernel_ivals : temp;
   const float *const coeffs = &kernel_coeffs[ind * (kernel_degree + 1)];
-#endif
 
   /* First two terms of the polynomial ... */
   float w = coeffs[0] * x + coeffs[1];
@@ -193,30 +269,31 @@ __attribute__((always_inline)) INLINE static void kernel_deval(
   }
 
   /* Return everything */
-  *W = w * kernel_constant * kernel_igamma3;
-  *dW_dx = dw_dx * kernel_constant * kernel_igamma4;
+  *W = w * kernel_constant * kernel_gamma_inv_dim;
+  *dW_dx = dw_dx * kernel_constant * kernel_gamma_inv_dim_plus_one;
 }
 
 /**
  * @brief Computes the kernel function.
  *
- * @param u The ratio of the distance to the smoothing length $u = x/h$.
- * @param W (return) The value of the kernel function $W(x,h)$.
+ * The kernel function needs to be mutliplied by \f$h^{-d}\f$,
+ * where \f$d\f$ is the dimensionality of the problem.
+ *
+ * Returns 0 if \f$u > \gamma = H/h\f$
+ *
+ * @param u The ratio of the distance to the smoothing length \f$u = x/h\f$.
+ * @param W (return) The value of the kernel function \f$W(x,h)\f$.
  */
 __attribute__((always_inline)) INLINE static void kernel_eval(
     float u, float *restrict W) {
+
   /* Go to the range [0,1[ from [0,H[ */
-  const float x = u * kernel_igamma;
+  const float x = u * kernel_gamma_inv;
 
-#if kernel_ivals == 1
-  /* Only one branch in this case */
-  const float *const coeffs = &kernel_coeffs[0];
-#else
   /* Pick the correct branch of the kernel */
   const int temp = (int)(x * kernel_ivals_f);
   const int ind = temp > kernel_ivals ? kernel_ivals : temp;
   const float *const coeffs = &kernel_coeffs[ind * (kernel_degree + 1)];
-#endif
 
   /* First two terms of the polynomial ... */
   float w = coeffs[0] * x + coeffs[1];
@@ -225,20 +302,24 @@ __attribute__((always_inline)) INLINE static void kernel_eval(
   for (int k = 2; k <= kernel_degree; k++) w = x * w + coeffs[k];
 
   /* Return everything */
-  *W = w * kernel_constant * kernel_igamma3;
+  *W = w * kernel_constant * kernel_gamma_inv_dim;
 }
 
+/* ------------------------------------------------------------------------- */
+
 #ifdef WITH_VECTORIZATION
 
-static const vector kernel_igamma_vec = FILL_VEC((float)kernel_igamma);
+static const vector kernel_gamma_inv_vec = FILL_VEC((float)kernel_gamma_inv);
 
 static const vector kernel_ivals_vec = FILL_VEC((float)kernel_ivals);
 
 static const vector kernel_constant_vec = FILL_VEC((float)kernel_constant);
 
-static const vector kernel_igamma3_vec = FILL_VEC((float)kernel_igamma3);
+static const vector kernel_gamma_inv_dim_vec =
+    FILL_VEC((float)kernel_gamma_inv_dim);
 
-static const vector kernel_igamma4_vec = FILL_VEC((float)kernel_igamma4);
+static const vector kernel_gamma_inv_dim_plus_one_vec =
+    FILL_VEC((float)kernel_gamma_inv_dim_plus_one);
 
 /**
  * @brief Computes the kernel function and its derivative (Vectorised version).
@@ -254,7 +335,7 @@ __attribute__((always_inline)) INLINE static void kernel_deval_vec(
 
   /* Go to the range [0,1[ from [0,H[ */
   vector x;
-  x.v = u->v * kernel_igamma_vec.v;
+  x.v = u->v * kernel_gamma_inv_vec.v;
 
   /* Load x and get the interval id. */
   vector ind;
@@ -277,8 +358,9 @@ __attribute__((always_inline)) INLINE static void kernel_deval_vec(
   }
 
   /* Return everything */
-  w->v = w->v * kernel_constant_vec.v * kernel_igamma3_vec.v;
-  dw_dx->v = dw_dx->v * kernel_constant_vec.v * kernel_igamma4_vec.v;
+  w->v = w->v * kernel_constant_vec.v * kernel_gamma_inv_dim_vec.v;
+  dw_dx->v =
+      dw_dx->v * kernel_constant_vec.v * kernel_gamma_inv_dim_plus_one_vec.v;
 }
 
 #endif
diff --git a/src/kernel_long_gravity.h b/src/kernel_long_gravity.h
index d247c7a461d4bd116f30ab106143f6c75e1b941e..6952681999f833bce7755a72aaee742a7fa0ed22 100644
--- a/src/kernel_long_gravity.h
+++ b/src/kernel_long_gravity.h
@@ -41,7 +41,7 @@ __attribute__((always_inline)) INLINE static void kernel_long_grav_eval(
   const float arg2 = u * one_over_sqrt_pi;
   const float arg3 = -arg1 * arg1;
 
-  const float term1 = erfc(arg1);
+  const float term1 = erfcf(arg1);
   const float term2 = arg2 * expf(arg3);
 
   *W = term1 + term2;
diff --git a/src/kick.h b/src/kick.h
index b57e13d4ebf27d3a366d571e7fd4cd819653f726..e3fa3bf78c7da514abacf697a9d94212020e5a7b 100644
--- a/src/kick.h
+++ b/src/kick.h
@@ -39,8 +39,8 @@ __attribute__((always_inline)) INLINE static void kick_gpart(
   /* Compute the time step for this kick */
   const int ti_start = (gp->ti_begin + gp->ti_end) / 2;
   const int ti_end = gp->ti_end + new_dti / 2;
-  const double dt = (ti_end - ti_start) * timeBase;
-  const double half_dt = (ti_end - gp->ti_end) * timeBase;
+  const float dt = (ti_end - ti_start) * timeBase;
+  const float half_dt = (ti_end - gp->ti_end) * timeBase;
 
   /* Move particle forward in time */
   gp->ti_begin = gp->ti_end;
@@ -70,8 +70,8 @@ __attribute__((always_inline)) INLINE static void kick_part(
   /* Compute the time step for this kick */
   const int ti_start = (p->ti_begin + p->ti_end) / 2;
   const int ti_end = p->ti_end + new_dti / 2;
-  const double dt = (ti_end - ti_start) * timeBase;
-  const double half_dt = (ti_end - p->ti_end) * timeBase;
+  const float dt = (ti_end - ti_start) * timeBase;
+  const float half_dt = (ti_end - p->ti_end) * timeBase;
 
   /* Move particle forward in time */
   p->ti_begin = p->ti_end;
diff --git a/src/lock.h b/src/lock.h
index ca7f01ee029cd1c57ed8fd0f3237ea54cb43e9a7..b2dd2eac9d0ca5d7807907e31cf3fa31894f9aed 100644
--- a/src/lock.h
+++ b/src/lock.h
@@ -34,6 +34,7 @@
 #define lock_trylock(l) (pthread_spin_lock(l) != 0)
 #define lock_unlock(l) (pthread_spin_unlock(l) != 0)
 #define lock_unlock_blind(l) pthread_spin_unlock(l)
+
 #elif defined(PTHREAD_LOCK)
 #include <pthread.h>
 #define swift_lock_type pthread_mutex_t
@@ -43,6 +44,7 @@
 #define lock_trylock(l) (pthread_mutex_trylock(l) != 0)
 #define lock_unlock(l) (pthread_mutex_unlock(l) != 0)
 #define lock_unlock_blind(l) pthread_mutex_unlock(l)
+
 #else
 #define swift_lock_type volatile int
 #define lock_init(l) (*(l) = 0)
diff --git a/src/parallel_io.c b/src/parallel_io.c
index 262ab7d9e4405b54538e5b687c0aadfccf1da2f0..0da34d4dad114db0920c8e8f3bb617295ff3da96 100644
--- a/src/parallel_io.c
+++ b/src/parallel_io.c
@@ -37,6 +37,7 @@
 
 /* Local includes. */
 #include "common_io.h"
+#include "dimension.h"
 #include "engine.h"
 #include "error.h"
 #include "gravity_io.h"
@@ -383,6 +384,7 @@ void read_ic_parallel(char* fileName, const struct UnitSystem* internal_units,
   size_t N[NUM_PARTICLE_TYPES] = {0};
   long long N_total[NUM_PARTICLE_TYPES] = {0};
   long long offset[NUM_PARTICLE_TYPES] = {0};
+  int dimension = 3; /* Assume 3D if nothing is specified */
 
   /* Open file */
   /* message("Opening file '%s' as IC.", fileName); */
@@ -409,6 +411,15 @@ void read_ic_parallel(char* fileName, const struct UnitSystem* internal_units,
   h_grp = H5Gopen(h_file, "/Header", H5P_DEFAULT);
   if (h_grp < 0) error("Error while opening file header\n");
 
+  /* Check the dimensionality of the ICs (if the info exists) */
+  const hid_t hid_dim = H5Aexists(h_grp, "Dimension");
+  if (hid_dim < 0)
+    error("Error while testing existance of 'Dimension' attribute");
+  if (hid_dim > 0) readAttribute(h_grp, "Dimension", INT, &dimension);
+  if (dimension != hydro_dimension)
+    error("ICs dimensionality (%dD) does not match code dimensionality (%dD)",
+          dimension, (int)hydro_dimension);
+
   /* Read the relevant information and print status */
   int flag_entropy_temp[6];
   readAttribute(h_grp, "Flag_Entropy_ICs", INT, flag_entropy_temp);
@@ -517,27 +528,18 @@ void read_ic_parallel(char* fileName, const struct UnitSystem* internal_units,
 
     int num_fields = 0;
     struct io_props list[100];
-    size_t N = 0;
+    size_t Nparticles = 0;
 
     /* Read particle fields into the particle structure */
     switch (ptype) {
 
       case GAS:
-        /* if (!dry_run) */
-        /*   hydro_read_particles(h_grp, N[ptype], N_total[ptype],
-         * offset[ptype], */
-        /*                        *parts); */
-        /* break; */
-        N = *Ngas;
+        Nparticles = *Ngas;
         hydro_read_particles(*parts, list, &num_fields);
         break;
 
       case DM:
-        /* if (!dry_run) */
-        /*   darkmatter_read_particles(h_grp, N[ptype], N_total[ptype], */
-        /*                             offset[ptype], *gparts); */
-        /* break; */
-        N = Ndm;
+        Nparticles = Ndm;
         darkmatter_read_particles(*gparts, list, &num_fields);
         break;
 
@@ -548,7 +550,7 @@ void read_ic_parallel(char* fileName, const struct UnitSystem* internal_units,
     /* Read everything */
     if (!dry_run)
       for (int i = 0; i < num_fields; ++i)
-        readArray(h_grp, list[i], N, N_total[ptype], offset[ptype],
+        readArray(h_grp, list[i], Nparticles, N_total[ptype], offset[ptype],
                   internal_units, ic_units);
 
     /* Close particle group */
@@ -675,6 +677,8 @@ void write_output_parallel(struct engine* e, const char* baseName,
   writeAttribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3);
   double dblTime = e->time;
   writeAttribute(h_grp, "Time", DOUBLE, &dblTime, 1);
+  int dimension = (int)hydro_dimension;
+  writeAttribute(h_grp, "Dimension", INT, &dimension, 1);
 
   /* GADGET-2 legacy values */
   /* Number of particles of each type */
@@ -782,13 +786,13 @@ void write_output_parallel(struct engine* e, const char* baseName,
 
     int num_fields = 0;
     struct io_props list[100];
-    size_t N = 0;
+    size_t Nparticles = 0;
 
     /* Write particle fields from the particle structure */
     switch (ptype) {
 
       case GAS:
-        N = Ngas;
+        Nparticles = Ngas;
         hydro_write_particles(parts, list, &num_fields);
         break;
 
@@ -805,7 +809,7 @@ void write_output_parallel(struct engine* e, const char* baseName,
         collect_dm_gparts(gparts, Ntot, dmparts, Ndm);
 
         /* Write DM particles */
-        N = Ndm;
+        Nparticles = Ndm;
         darkmatter_write_particles(dmparts, list, &num_fields);
 
         /* Free temporary array */
@@ -818,9 +822,9 @@ void write_output_parallel(struct engine* e, const char* baseName,
 
     /* Write everything */
     for (int i = 0; i < num_fields; ++i)
-      writeArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i], N,
-                 N_total[ptype], mpi_rank, offset[ptype], internal_units,
-                 snapshot_units);
+      writeArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i],
+                 Nparticles, N_total[ptype], mpi_rank, offset[ptype],
+                 internal_units, snapshot_units);
 
     /* Free temporary array */
     free(dmparts);
diff --git a/src/part.h b/src/part.h
index efca7b6b5bef49f20df1e2c45b30f65ecbbf4960..ea895e6e0295d6a8b63309c7bd6855daa2cf7d64 100644
--- a/src/part.h
+++ b/src/part.h
@@ -45,6 +45,9 @@
 #include "./hydro/Gadget2/hydro_part.h"
 #elif defined(DEFAULT_SPH)
 #include "./hydro/Default/hydro_part.h"
+#elif defined(GIZMO_SPH)
+#include "./hydro/Gizmo/hydro_part.h"
+#define EXTRA_HYDRO_LOOP
 #else
 #error "Invalid choice of SPH variant"
 #endif
diff --git a/src/partition.c b/src/partition.c
index 11de3a62be65179bb128ff89d9fbf48861a2175b..e216e12a5a23457b39b53070de3d84a2f257b927 100644
--- a/src/partition.c
+++ b/src/partition.c
@@ -455,8 +455,8 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID,
     /* Skip un-interesting tasks. */
     if (t->type != task_type_self && t->type != task_type_pair &&
         t->type != task_type_sub_self && t->type != task_type_sub_self &&
-        t->type != task_type_ghost && t->type != task_type_drift &&
-        t->type != task_type_kick && t->type != task_type_init)
+        t->type != task_type_ghost && t->type != task_type_kick &&
+        t->type != task_type_init)
       continue;
 
     /* Get the task weight. This can be slightly negative on multiple board
@@ -494,8 +494,7 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID,
     int cid = ci - cells;
 
     /* Different weights for different tasks. */
-    if (t->type == task_type_ghost || t->type == task_type_drift ||
-        t->type == task_type_kick) {
+    if (t->type == task_type_ghost || t->type == task_type_kick) {
       /* Particle updates add only to vertex weight. */
       if (taskvweights) weights_v[cid] += w;
 
diff --git a/src/potentials.c b/src/potentials.c
index 5cbe05e008b7de17310b1e738e032af90684c25e..dd7aed8712c01921a01462bf9de713433c81f5c1 100644
--- a/src/potentials.c
+++ b/src/potentials.c
@@ -29,11 +29,13 @@
  * of units.
  *
  * @param parameter_file The parsed parameter file
+ * @param phys_const Physical constants in internal units
  * @param us The current internal system of units
  * @param potential The external potential properties to initialize
  */
 void potential_init(const struct swift_params* parameter_file,
-                    struct UnitSystem* us,
+                    const struct phys_const* phys_const,
+                    const struct UnitSystem* us,
                     struct external_potential* potential) {
 
 #ifdef EXTERNAL_POTENTIAL_POINTMASS
@@ -65,6 +67,22 @@ void potential_init(const struct swift_params* parameter_file,
       parameter_file, "IsothermalPotential:timestep_mult");
 
 #endif /* EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL */
+#ifdef EXTERNAL_POTENTIAL_DISK_PATCH
+  potential->disk_patch_potential.surface_density = parser_get_param_double(
+      parameter_file, "Disk-PatchPotential:surface_density");
+  potential->disk_patch_potential.scale_height = parser_get_param_double(
+      parameter_file, "Disk-PatchPotential:scale_height");
+  potential->disk_patch_potential.z_disk =
+      parser_get_param_double(parameter_file, "Disk-PatchPotential:z_disk");
+  potential->disk_patch_potential.timestep_mult = parser_get_param_double(
+      parameter_file, "Disk-PatchPotential:timestep_mult");
+  potential->disk_patch_potential.growth_time = parser_get_opt_param_double(
+      parameter_file, "Disk-PatchPotential:growth_time", 0.);
+  potential->disk_patch_potential.dynamical_time =
+      sqrt(potential->disk_patch_potential.scale_height /
+           (phys_const->const_newton_G *
+            potential->disk_patch_potential.surface_density));
+#endif /* EXTERNAL_POTENTIAL_DISK_PATCH */
 }
 
 /**
@@ -78,7 +96,7 @@ void potential_print(const struct external_potential* potential) {
 
   message(
       "Point mass properties are (x,y,z) = (%e, %e, %e), M = %e timestep "
-      "multiplier = %e",
+      "multiplier = %e.",
       potential->point_mass.x, potential->point_mass.y, potential->point_mass.z,
       potential->point_mass.mass, potential->point_mass.timestep_mult);
 
@@ -88,10 +106,25 @@ void potential_print(const struct external_potential* potential) {
 
   message(
       "Isothermal potential properties are (x,y,z) = (%e, %e, %e), vrot = %e "
-      "timestep multiplier= %e",
+      "timestep multiplier = %e.",
       potential->isothermal_potential.x, potential->isothermal_potential.y,
       potential->isothermal_potential.z, potential->isothermal_potential.vrot,
       potential->isothermal_potential.timestep_mult);
 
 #endif /* EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL */
+
+#ifdef EXTERNAL_POTENTIAL_DISK_PATCH
+
+  message(
+      "Disk-patch potential properties are surface_density = %e disk height= "
+      "%e scale height = %e timestep multiplier = %e.",
+      potential->disk_patch_potential.surface_density,
+      potential->disk_patch_potential.z_disk,
+      potential->disk_patch_potential.scale_height,
+      potential->disk_patch_potential.timestep_mult);
+
+  if (potential->disk_patch_potential.growth_time > 0.)
+    message("Disk will grow for %f dynamiccal times.",
+            potential->disk_patch_potential.growth_time);
+#endif /* EXTERNAL_POTENTIAL_DISK_PATCH */
 }
diff --git a/src/potentials.h b/src/potentials.h
index 5373cc1f3f55b0c6d9876cbe6348fdcefbe242aa..74f0fd28566f355962c83e5d743aeae9afe09c59 100644
--- a/src/potentials.h
+++ b/src/potentials.h
@@ -25,6 +25,7 @@
 #include "../config.h"
 
 /* Some standard headers. */
+#include <float.h>
 #include <math.h>
 
 /* Local includes. */
@@ -53,13 +54,127 @@ struct external_potential {
     float timestep_mult;
   } isothermal_potential;
 #endif
+
+#ifdef EXTERNAL_POTENTIAL_DISK_PATCH
+  struct {
+    double surface_density;
+    double scale_height;
+    double z_disk;
+    double dynamical_time;
+    double growth_time;
+    double timestep_mult;
+  } disk_patch_potential;
+#endif
 };
 
-/* Include external isothermal potential */
+/* ------------------------------------------------------------------------- */
+
+/* external potential: disk_patch */
+#ifdef EXTERNAL_POTENTIAL_DISK_PATCH
+
+/**
+ * @brief Computes the time-step from the acceleration due to a hydrostatic
+ * disk.
+ *
+ * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948
+ *
+ * @param potential The properties of the potential.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+external_gravity_disk_patch_timestep(
+    const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const,
+    const struct gpart* restrict g) {
+
+  /* initilize time step to disk dynamical time */
+  const float dt_dyn = potential->disk_patch_potential.dynamical_time;
+  float dt = dt_dyn;
+
+  /* absolute value of height above disk */
+  const float dz = fabs(g->x[2] - potential->disk_patch_potential.z_disk);
+
+  /* vertical cceleration */
+  const float z_accel = 2 * M_PI * phys_const->const_newton_G *
+                        potential->disk_patch_potential.surface_density *
+                        tanh(dz / potential->disk_patch_potential.scale_height);
+
+  /* demand that dt * velocity <  fraction of scale height of disk */
+  float dt1 = FLT_MAX;
+  if (fabs(g->v_full[2]) > 0) {
+    dt1 = potential->disk_patch_potential.scale_height / fabs(g->v_full[2]);
+    if (dt1 < dt) dt = dt1;
+  }
+
+  /* demand that dt^2 * acceleration < fraction of scale height of disk */
+  float dt2 = FLT_MAX;
+  if (fabs(z_accel) > 0) {
+    dt2 = potential->disk_patch_potential.scale_height / fabs(z_accel);
+    if (dt2 < dt * dt) dt = sqrt(dt2);
+  }
+
+  /* demand that dt^3 jerk < fraction of scale height of disk */
+  float dt3 = FLT_MAX;
+  if (abs(g->v_full[2]) > 0) {
+    const float dz_accel_over_dt =
+        2 * M_PI * phys_const->const_newton_G *
+        potential->disk_patch_potential.surface_density /
+        potential->disk_patch_potential.scale_height /
+        cosh(dz / potential->disk_patch_potential.scale_height) /
+        cosh(dz / potential->disk_patch_potential.scale_height) *
+        fabs(g->v_full[2]);
+
+    dt3 = potential->disk_patch_potential.scale_height / fabs(dz_accel_over_dt);
+    if (dt3 < dt * dt * dt) dt = pow(dt3, 1. / 3.);
+  }
+
+  return potential->disk_patch_potential.timestep_mult * dt;
+}
+
+/**
+ * @brief Computes the gravitational acceleration along z due to a hydrostatic
+ * disk
+ *
+ * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948
+ *
+ * @param time The current time in internal units.
+ * @param potential The properties of the potential.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static void
+external_gravity_disk_patch_potential(
+    double time, const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const, struct gpart* restrict g) {
+
+  const float G_newton = phys_const->const_newton_G;
+  const float dz = g->x[2] - potential->disk_patch_potential.z_disk;
+  const float t_dyn = potential->disk_patch_potential.dynamical_time;
+
+  float reduction_factor = 1.;
+  if (time < potential->disk_patch_potential.growth_time * t_dyn)
+    reduction_factor =
+        time / (potential->disk_patch_potential.growth_time * t_dyn);
+
+  const float z_accel =
+      reduction_factor * 2 * G_newton * M_PI *
+      potential->disk_patch_potential.surface_density *
+      tanh(fabs(dz) / potential->disk_patch_potential.scale_height);
+
+  /* Accelerations. Note that they are multiplied by G later on */
+  if (dz > 0) g->a_grav[2] -= z_accel / G_newton;
+  if (dz < 0) g->a_grav[2] += z_accel / G_newton;
+}
+#endif /* EXTERNAL_POTENTIAL_DISK_PATCH */
+
+/* ------------------------------------------------------------------------- */
+
 #ifdef EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL
 
 /**
- * @brief Computes the time-step due to the acceleration from a point mass
+ * @brief Computes the time-step due to the acceleration from an isothermal
+ * potential.
  *
  * @param potential The #external_potential used in the run.
  * @param phys_const The physical constants in internal units.
@@ -67,12 +182,14 @@ struct external_potential {
  */
 __attribute__((always_inline)) INLINE static float
 external_gravity_isothermalpotential_timestep(
-    const struct external_potential* potential,
-    const struct phys_const* const phys_const, const struct gpart* const g) {
+    const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const,
+    const struct gpart* restrict g) {
 
   const float dx = g->x[0] - potential->isothermal_potential.x;
   const float dy = g->x[1] - potential->isothermal_potential.y;
   const float dz = g->x[2] - potential->isothermal_potential.z;
+
   const float rinv2 = 1.f / (dx * dx + dy * dy + dz * dz);
   const float drdv =
       dx * (g->v_full[0]) + dy * (g->v_full[1]) + dz * (g->v_full[2]);
@@ -92,10 +209,10 @@ external_gravity_isothermalpotential_timestep(
 }
 
 /**
- * @brief Computes the gravitational acceleration of a particle due to a point
- * mass
+ * @brief Computes the gravitational acceleration from an isothermal potential.
  *
- * Note that the accelerations are multiplied by Newton's G constant later on.
+ * Note that the accelerations are multiplied by Newton's G constant
+ * later on.
  *
  * @param potential The #external_potential used in the run.
  * @param phys_const The physical constants in internal units.
@@ -107,7 +224,6 @@ external_gravity_isothermalpotential(const struct external_potential* potential,
                                      struct gpart* g) {
 
   const float G_newton = phys_const->const_newton_G;
-
   const float dx = g->x[0] - potential->isothermal_potential.x;
   const float dy = g->x[1] - potential->isothermal_potential.y;
   const float dz = g->x[2] - potential->isothermal_potential.z;
@@ -119,11 +235,12 @@ external_gravity_isothermalpotential(const struct external_potential* potential,
   g->a_grav[0] += term * dx;
   g->a_grav[1] += term * dy;
   g->a_grav[2] += term * dz;
-  // error(" %f %f %f %f", vrot, rinv2, dx, g->a_grav[0]);
 }
 
 #endif /* EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL */
 
+/* ------------------------------------------------------------------------- */
+
 /* Include exteral pointmass potential */
 #ifdef EXTERNAL_POTENTIAL_POINTMASS
 
@@ -135,9 +252,10 @@ external_gravity_isothermalpotential(const struct external_potential* potential,
  * @param g Pointer to the g-particle data.
  */
 __attribute__((always_inline)) INLINE static float
-external_gravity_pointmass_timestep(const struct external_potential* potential,
-                                    const struct phys_const* const phys_const,
-                                    const struct gpart* const g) {
+external_gravity_pointmass_timestep(
+    const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const,
+    const struct gpart* restrict g) {
 
   const float G_newton = phys_const->const_newton_G;
   const float dx = g->x[0] - potential->point_mass.x;
@@ -161,18 +279,19 @@ external_gravity_pointmass_timestep(const struct external_potential* potential,
 }
 
 /**
- * @brief Computes the gravitational acceleration of a particle due to a point
- * mass
+ * @brief Computes the gravitational acceleration of a particle due to a
+ * point mass
  *
- * Note that the accelerations are multiplied by Newton's G constant later on.
+ * Note that the accelerations are multiplied by Newton's G constant later
+ * on.
  *
  * @param potential The proerties of the external potential.
  * @param phys_const The physical constants in internal units.
  * @param g Pointer to the g-particle data.
  */
 __attribute__((always_inline)) INLINE static void external_gravity_pointmass(
-    const struct external_potential* potential,
-    const struct phys_const* const phys_const, struct gpart* g) {
+    const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const, struct gpart* restrict g) {
 
   const float dx = g->x[0] - potential->point_mass.x;
   const float dy = g->x[1] - potential->point_mass.y;
@@ -187,9 +306,12 @@ __attribute__((always_inline)) INLINE static void external_gravity_pointmass(
 
 #endif /* EXTERNAL_POTENTIAL_POINTMASS */
 
+/* ------------------------------------------------------------------------- */
+
 /* Now, some generic functions, defined in the source file */
 void potential_init(const struct swift_params* parameter_file,
-                    struct UnitSystem* us,
+                    const struct phys_const* phys_const,
+                    const struct UnitSystem* us,
                     struct external_potential* potential);
 
 void potential_print(const struct external_potential* potential);
diff --git a/src/riemann.h b/src/riemann.h
index d647b021167317d14f4cd7316d09c247794f3d23..d0ae57a640e13c2098708735d6c34de70ebea5b0 100644
--- a/src/riemann.h
+++ b/src/riemann.h
@@ -27,18 +27,35 @@
 #include "stdio.h"
 #include "stdlib.h"
 
-#define HLLC_SOLVER
+/* Check that we use an ideal equation of state, since other equations of state
+   are not compatible with these Riemann solvers. */
+#ifndef EOS_IDEAL_GAS
+#error Currently there are no Riemann solvers that can handle the requested \
+       equation of state. Select an ideal gas equation of state if you want to \
+       use this hydro scheme!
+#endif
+
+#if defined(RIEMANN_SOLVER_EXACT)
 
-#ifdef EXACT_SOLVER
+#define RIEMANN_SOLVER_IMPLEMENTATION "Exact Riemann solver (Toro 2009)"
 #include "riemann/riemann_exact.h"
-#endif
 
-#ifdef TRRS_SOLVER
+#elif defined(RIEMANN_SOLVER_TRRS)
+
+#define RIEMANN_SOLVER_IMPLEMENTATION \
+  "Two Rarefaction Riemann Solver (Toro 2009)"
 #include "riemann/riemann_trrs.h"
-#endif
 
-#ifdef HLLC_SOLVER
+#elif defined(RIEMANN_SOLVER_HLLC)
+
+#define RIEMANN_SOLVER_IMPLEMENTATION \
+  "Harten-Lax-van Leer-Contact Riemann solver (Toro 2009)"
 #include "riemann/riemann_hllc.h"
+
+#else
+
+#error "Error: no Riemann solver selected!"
+
 #endif
 
 #endif /* SWIFT_RIEMANN_H */
diff --git a/src/riemann/riemann_exact.h b/src/riemann/riemann_exact.h
index a2f3c30fb1daf5d53bf35abe4ca7e73eafba6018..9763d9f0d12da32d9142c24481105b9f139be588 100644
--- a/src/riemann/riemann_exact.h
+++ b/src/riemann/riemann_exact.h
@@ -29,19 +29,9 @@
 #ifndef SWIFT_RIEMANN_EXACT_H
 #define SWIFT_RIEMANN_EXACT_H
 
-/* frequently used combinations of const_hydro_gamma */
-#define const_riemann_gp1d2g \
-  (0.5f * (const_hydro_gamma + 1.0f) / const_hydro_gamma)
-#define const_riemann_gm1d2g \
-  (0.5f * (const_hydro_gamma - 1.0f) / const_hydro_gamma)
-#define const_riemann_gm1dgp1 \
-  ((const_hydro_gamma - 1.0f) / (const_hydro_gamma + 1.0f))
-#define const_riemann_tdgp1 (2.0f / (const_hydro_gamma + 1.0f))
-#define const_riemann_tdgm1 (2.0f / (const_hydro_gamma - 1.0f))
-#define const_riemann_gm1d2 (0.5f * (const_hydro_gamma - 1.0f))
-#define const_riemann_tgdgm1 \
-  (2.0f * const_hydro_gamma / (const_hydro_gamma - 1.0f))
-#define const_riemann_ginv (1.0f / const_hydro_gamma)
+#include <float.h>
+#include "adiabatic_index.h"
+#include "riemann_vacuum.h"
 
 /**
  * @brief Functions (4.6) and (4.7) in Toro.
@@ -50,19 +40,18 @@
  * @param W The left or right state vector
  * @param a The left or right sound speed
  */
-__attribute__((always_inline)) INLINE static GFLOAT riemann_fb(GFLOAT p,
-                                                               GFLOAT* W,
-                                                               GFLOAT a) {
+__attribute__((always_inline)) INLINE static float riemann_fb(float p, float* W,
+                                                              float a) {
 
-  GFLOAT fval = 0.;
-  GFLOAT A, B;
+  float fval = 0.;
+  float A, B;
   if (p > W[4]) {
-    A = const_riemann_tdgp1 / W[0];
-    B = const_riemann_gm1dgp1 * W[4];
+    A = hydro_two_over_gamma_plus_one / W[0];
+    B = hydro_gamma_minus_one_over_gamma_plus_one * W[4];
     fval = (p - W[4]) * sqrtf(A / (p + B));
   } else {
-    fval =
-        const_riemann_tdgm1 * a * (powf(p / W[4], const_riemann_gm1d2g) - 1.0f);
+    fval = hydro_two_over_gamma_minus_one * a *
+           (pow_gamma_minus_one_over_two_gamma(p / W[4]) - 1.0f);
   }
   return fval;
 }
@@ -78,9 +67,8 @@ __attribute__((always_inline)) INLINE static GFLOAT riemann_fb(GFLOAT p,
  * @param aL The left sound speed
  * @param aR The right sound speed
  */
-__attribute__((always_inline)) INLINE static GFLOAT riemann_f(
-    GFLOAT p, GFLOAT* WL, GFLOAT* WR, GFLOAT vL, GFLOAT vR, GFLOAT aL,
-    GFLOAT aR) {
+__attribute__((always_inline)) INLINE static float riemann_f(
+    float p, float* WL, float* WR, float vL, float vR, float aL, float aR) {
 
   return riemann_fb(p, WL, aL) + riemann_fb(p, WR, aR) + (vR - vL);
 }
@@ -92,18 +80,18 @@ __attribute__((always_inline)) INLINE static GFLOAT riemann_f(
  * @param W The left or right state vector
  * @param a The left or right sound speed
  */
-__attribute__((always_inline)) INLINE static GFLOAT riemann_fprimeb(GFLOAT p,
-                                                                    GFLOAT* W,
-                                                                    GFLOAT a) {
+__attribute__((always_inline)) INLINE static float riemann_fprimeb(float p,
+                                                                   float* W,
+                                                                   float a) {
 
-  GFLOAT fval = 0.;
-  GFLOAT A, B;
+  float fval = 0.;
+  float A, B;
   if (p > W[4]) {
-    A = const_riemann_tdgp1 / W[0];
-    B = const_riemann_gm1dgp1 * W[4];
+    A = hydro_two_over_gamma_plus_one / W[0];
+    B = hydro_gamma_minus_one_over_gamma_plus_one * W[4];
     fval = (1.0f - 0.5f * (p - W[4]) / (B + p)) * sqrtf(A / (p + B));
   } else {
-    fval = 1.0f / (W[0] * a) * powf(p / W[4], -const_riemann_gp1d2g);
+    fval = 1.0f / W[0] / a * pow_minus_gamma_plus_one_over_two_gamma(p / W[4]);
   }
   return fval;
 }
@@ -117,8 +105,8 @@ __attribute__((always_inline)) INLINE static GFLOAT riemann_fprimeb(GFLOAT p,
  * @param aL The left sound speed
  * @param aR The right sound speed
  */
-__attribute__((always_inline)) INLINE static GFLOAT riemann_fprime(
-    GFLOAT p, GFLOAT* WL, GFLOAT* WR, GFLOAT aL, GFLOAT aR) {
+__attribute__((always_inline)) INLINE static float riemann_fprime(
+    float p, float* WL, float* WR, float aL, float aR) {
 
   return riemann_fprimeb(p, WL, aL) + riemann_fprimeb(p, WR, aR);
 }
@@ -129,12 +117,12 @@ __attribute__((always_inline)) INLINE static GFLOAT riemann_fprime(
  * @param p The current guess for the pressure
  * @param W The left or right state vector
  */
-__attribute__((always_inline)) INLINE static GFLOAT riemann_gb(GFLOAT p,
-                                                               GFLOAT* W) {
+__attribute__((always_inline)) INLINE static float riemann_gb(float p,
+                                                              float* W) {
 
-  GFLOAT A, B;
-  A = const_riemann_tdgp1 / W[0];
-  B = const_riemann_gm1dgp1 * W[4];
+  float A, B;
+  A = hydro_two_over_gamma_plus_one / W[0];
+  B = hydro_gamma_minus_one_over_gamma_plus_one * W[4];
   return sqrtf(A / (p + B));
 }
 
@@ -151,11 +139,11 @@ __attribute__((always_inline)) INLINE static GFLOAT riemann_gb(GFLOAT p,
  * @param aL The left sound speed
  * @param aR The right sound speed
  */
-__attribute__((always_inline)) INLINE static GFLOAT riemann_guess_p(
-    GFLOAT* WL, GFLOAT* WR, GFLOAT vL, GFLOAT vR, GFLOAT aL, GFLOAT aR) {
+__attribute__((always_inline)) INLINE static float riemann_guess_p(
+    float* WL, float* WR, float vL, float vR, float aL, float aR) {
 
-  GFLOAT pguess, pmin, pmax, qmax;
-  GFLOAT ppv;
+  float pguess, pmin, pmax, qmax;
+  float ppv;
 
   pmin = fminf(WL[4], WR[4]);
   pmax = fmaxf(WL[4], WR[4]);
@@ -168,10 +156,10 @@ __attribute__((always_inline)) INLINE static GFLOAT riemann_guess_p(
   } else {
     if (ppv < pmin) {
       /* two rarefactions */
-      pguess = powf((aL + aR - const_riemann_gm1d2 * (vR - vL)) /
-                        (aL / powf(WL[4], const_riemann_gm1d2g) +
-                         aR / powf(WR[4], const_riemann_gm1d2g)),
-                    const_riemann_tgdgm1);
+      pguess = pow_two_gamma_over_gamma_minus_one(
+          (aL + aR - hydro_gamma_minus_one_over_two * (vR - vL)) /
+          (aL / pow_gamma_minus_one_over_two_gamma(WL[4]) +
+           aR / pow_gamma_minus_one_over_two_gamma(WR[4])));
     } else {
       /* two shocks */
       pguess = (riemann_gb(ppv, WL) * WL[4] + riemann_gb(ppv, WR) * WR[4] - vR +
@@ -202,14 +190,14 @@ __attribute__((always_inline)) INLINE static GFLOAT riemann_guess_p(
  * @param aL The left sound speed
  * @param aR The right sound speed
  */
-__attribute__((always_inline)) INLINE static GFLOAT riemann_solve_brent(
-    GFLOAT lower_limit, GFLOAT upper_limit, GFLOAT lowf, GFLOAT upf,
-    GFLOAT error_tol, GFLOAT* WL, GFLOAT* WR, GFLOAT vL, GFLOAT vR, GFLOAT aL,
-    GFLOAT aR) {
-
-  GFLOAT a, b, c, d, s;
-  GFLOAT fa, fb, fc, fs;
-  GFLOAT tmp, tmp2;
+__attribute__((always_inline)) INLINE static float riemann_solve_brent(
+    float lower_limit, float upper_limit, float lowf, float upf,
+    float error_tol, float* WL, float* WR, float vL, float vR, float aL,
+    float aR) {
+
+  float a, b, c, d, s;
+  float fa, fb, fc, fs;
+  float tmp, tmp2;
   int mflag;
   int i;
 
@@ -296,148 +284,6 @@ __attribute__((always_inline)) INLINE static GFLOAT riemann_solve_brent(
   return b;
 }
 
-/**
- * @brief Vacuum Riemann solver, based on section 4.6 in Toro
- *
- * @param WL The left state vector
- * @param WR The right state vector
- * @param vL The left velocity along the interface normal
- * @param vR The right velocity along the interface normal
- * @param aL The left sound speed
- * @param aR The right sound speed
- * @param Whalf Empty state vector to store the solution in
- * @param n_unit Normal vector of the interface
- */
-__attribute__((always_inline)) INLINE static void riemann_solve_vacuum(
-    GFLOAT* WL, GFLOAT* WR, GFLOAT vL, GFLOAT vR, GFLOAT aL, GFLOAT aR,
-    GFLOAT* Whalf, float* n_unit) {
-
-  GFLOAT SL, SR;
-  GFLOAT vhalf;
-
-  if (!WR[0] && !WL[0]) {
-    /* if both states are vacuum, the solution is also vacuum */
-    Whalf[0] = 0.0f;
-    Whalf[1] = 0.0f;
-    Whalf[2] = 0.0f;
-    Whalf[3] = 0.0f;
-    Whalf[4] = 0.0f;
-    return;
-  }
-  if (!WR[0]) {
-    Whalf[1] = WL[1];
-    Whalf[2] = WL[2];
-    Whalf[3] = WL[3];
-    /* vacuum right state */
-    if (vL < aL) {
-      SL = vL + const_riemann_tdgm1 * aL;
-      if (SL > 0.0f) {
-        Whalf[0] =
-            WL[0] * powf(const_riemann_tdgp1 + const_riemann_gm1dgp1 / aL * vL,
-                         const_riemann_tdgm1);
-        vhalf = const_riemann_tdgp1 * (aL + const_riemann_gm1d2 * vL) - vL;
-        Whalf[4] =
-            WL[4] * powf(const_riemann_tdgp1 + const_riemann_gm1dgp1 / aL * vL,
-                         const_riemann_tgdgm1);
-      } else {
-        Whalf[0] = 0.0f;
-        Whalf[1] = 0.0f;
-        Whalf[2] = 0.0f;
-        Whalf[3] = 0.0f;
-        Whalf[4] = 0.0f;
-        return;
-      }
-    } else {
-      Whalf[0] = WL[0];
-      vhalf = 0.0f;
-      Whalf[4] = WL[4];
-    }
-  } else {
-    if (!WL[0]) {
-      Whalf[1] = WR[1];
-      Whalf[2] = WR[2];
-      Whalf[3] = WR[3];
-      /* vacuum left state */
-      if (-vR < aR) {
-        SR = vR - const_riemann_tdgm1 * aR;
-        if (SR >= 0.0f) {
-          Whalf[0] = 0.0f;
-          Whalf[1] = 0.0f;
-          Whalf[2] = 0.0f;
-          Whalf[3] = 0.0f;
-          Whalf[4] = 0.0f;
-          return;
-        } else {
-          Whalf[0] = WR[0] *
-                     powf(const_riemann_tdgp1 - const_riemann_gm1dgp1 / aR * vR,
-                          const_riemann_tdgm1);
-          vhalf = const_riemann_tdgp1 * (-aR + const_riemann_gm1d2 * vR) - vR;
-          Whalf[4] = WR[4] *
-                     powf(const_riemann_tdgp1 - const_riemann_gm1dgp1 / aR * vR,
-                          const_riemann_tgdgm1);
-        }
-      } else {
-        Whalf[0] = WR[0];
-        vhalf = 0.0f;
-        Whalf[4] = WR[4];
-      }
-    } else {
-      /* vacuum generation */
-      SR = vR - const_riemann_tdgm1 * aR;
-      SL = vL + const_riemann_tdgm1 * aL;
-      if (SR > 0.0f && SL < 0.0f) {
-        Whalf[0] = 0.0f;
-        Whalf[1] = 0.0f;
-        Whalf[2] = 0.0f;
-        Whalf[3] = 0.0f;
-        Whalf[4] = 0.0f;
-        return;
-      } else {
-        if (SL >= 0.0f) {
-          Whalf[1] = WL[1];
-          Whalf[2] = WL[2];
-          Whalf[3] = WL[3];
-          if (aL > vL) {
-            Whalf[0] = WL[0] * powf(const_riemann_tdgp1 +
-                                        const_riemann_gm1dgp1 / aL * vL,
-                                    const_riemann_tdgm1);
-            vhalf = const_riemann_tdgp1 * (aL + const_riemann_gm1d2 * vL) - vL;
-            Whalf[4] = WL[4] * powf(const_riemann_tdgp1 +
-                                        const_riemann_gm1dgp1 / aL * vL,
-                                    const_riemann_tgdgm1);
-          } else {
-            Whalf[0] = WL[0];
-            vhalf = 0.0f;
-            Whalf[4] = WL[4];
-          }
-        } else {
-          Whalf[1] = WR[1];
-          Whalf[2] = WR[2];
-          Whalf[3] = WR[3];
-          if (-vR < aR) {
-            Whalf[0] = WR[0] * powf(const_riemann_tdgp1 -
-                                        const_riemann_gm1dgp1 / aR * vR,
-                                    const_riemann_tdgm1);
-            vhalf = const_riemann_tdgp1 * (-aR + const_riemann_gm1d2 * vR) - vR;
-            Whalf[4] = WR[4] * powf(const_riemann_tdgp1 -
-                                        const_riemann_gm1dgp1 / aR * vR,
-                                    const_riemann_tgdgm1);
-          } else {
-            Whalf[0] = WR[0];
-            vhalf = 0.0f;
-            Whalf[4] = WR[4];
-          }
-        }
-      }
-    }
-  }
-
-  /* Add the velocity solution along the interface normal to the velocities */
-  Whalf[1] += vhalf * n_unit[0];
-  Whalf[2] += vhalf * n_unit[1];
-  Whalf[3] += vhalf * n_unit[2];
-}
-
 /* Solve the Riemann problem between the states WL and WR and store the result
  * in Whalf
  * The Riemann problem is solved in the x-direction; the velocities in the y-
@@ -456,20 +302,20 @@ __attribute__((always_inline)) INLINE static void riemann_solve_vacuum(
  * @param n_unit Normal vector of the interface
  */
 __attribute__((always_inline)) INLINE static void riemann_solver_solve(
-    GFLOAT* WL, GFLOAT* WR, GFLOAT* Whalf, float* n_unit) {
+    float* WL, float* WR, float* Whalf, float* n_unit) {
 
   /* velocity of the left and right state in a frame aligned with n_unit */
-  GFLOAT vL, vR, vhalf;
+  float vL, vR, vhalf;
   /* sound speeds */
-  GFLOAT aL, aR;
+  float aL, aR;
   /* variables used for finding pstar */
-  GFLOAT p, pguess, fp, fpguess;
+  float p, pguess, fp, fpguess;
   /* variables used for sampling the solution */
-  GFLOAT u;
-  GFLOAT pdpR, SR;
-  GFLOAT SHR, STR;
-  GFLOAT pdpL, SL;
-  GFLOAT SHL, STL;
+  float u;
+  float pdpR, SR;
+  float SHR, STR;
+  float pdpL, SL;
+  float SHL, STL;
   int errorFlag = 0;
 
   /* sanity checks */
@@ -500,156 +346,155 @@ __attribute__((always_inline)) INLINE static void riemann_solver_solve(
   vR = WR[1] * n_unit[0] + WR[2] * n_unit[1] + WR[3] * n_unit[2];
 
   /* calculate sound speeds */
-  aL = sqrtf(const_hydro_gamma * WL[4] / WL[0]);
-  aR = sqrtf(const_hydro_gamma * WR[4] / WR[0]);
+  aL = sqrtf(hydro_gamma * WL[4] / WL[0]);
+  aR = sqrtf(hydro_gamma * WR[4] / WR[0]);
 
-  if (!WL[0] || !WR[0]) {
-    /* vacuum: we need a vacuum riemann solver */
+  /* check vacuum (generation) condition */
+  if (riemann_is_vacuum(WL, WR, vL, vR, aL, aR)) {
     riemann_solve_vacuum(WL, WR, vL, vR, aL, aR, Whalf, n_unit);
     return;
   }
 
-  /* check vacuum generation condition */
-  if (2.0f * aL / (const_hydro_gamma - 1.0f) +
-          2.0f * aR / (const_hydro_gamma - 1.0f) <
-      fabs(vL - vR)) {
-    /* vacuum generation: need a vacuum riemann solver */
-    riemann_solve_vacuum(WL, WR, vL, vR, aL, aR, Whalf, n_unit);
-    return;
-  } else {
-    /* values are ok: let's find pstar (riemann_f(pstar) = 0)! */
-    /* We normally use a Newton-Raphson iteration to find the zeropoint
-       of riemann_f(p), but if pstar is close to 0, we risk negative p values.
-       Since riemann_f(p) is undefined for negative pressures, we don't
-       want this to happen.
-       We therefore use Brent's method if riemann_f(0) is larger than some
-       value. -5 makes the iteration fail safe while almost never invoking
-       the expensive Brent solver. */
-    p = 0.;
-    /* obtain a first guess for p */
-    pguess = riemann_guess_p(WL, WR, vL, vR, aL, aR);
-    fp = riemann_f(p, WL, WR, vL, vR, aL, aR);
-    fpguess = riemann_f(pguess, WL, WR, vL, vR, aL, aR);
-    /* ok, pstar is close to 0, better use Brent's method... */
-    /* we use Newton-Raphson until we find a suitable interval */
-    if (fp * fpguess >= 0.0f) {
-      /* Newton-Raphson until convergence or until suitable interval is found
-         to use Brent's method */
-      unsigned int counter = 0;
-      while (fabs(p - pguess) > 1.e-6f * 0.5f * (p + pguess) &&
-             fpguess < 0.0f) {
-        p = pguess;
-        pguess = pguess - fpguess / riemann_fprime(pguess, WL, WR, aL, aR);
-        fpguess = riemann_f(pguess, WL, WR, vL, vR, aL, aR);
-        counter++;
-        if (counter > 1000) {
-          error("Stuck in Newton-Raphson!\n");
-        }
-      }
-    }
-    /* As soon as there is a suitable interval: use Brent's method */
-    if (1.e6 * fabs(p - pguess) > 0.5f * (p + pguess) && fpguess > 0.0f) {
-      p = 0.0f;
-      fp = riemann_f(p, WL, WR, vL, vR, aL, aR);
-      /* use Brent's method to find the zeropoint */
-      p = riemann_solve_brent(p, pguess, fp, fpguess, 1.e-6, WL, WR, vL, vR, aL,
-                              aR);
-    } else {
+  /* values are ok: let's find pstar (riemann_f(pstar) = 0)! */
+  /* We normally use a Newton-Raphson iteration to find the zeropoint
+     of riemann_f(p), but if pstar is close to 0, we risk negative p values.
+     Since riemann_f(p) is undefined for negative pressures, we don't
+     want this to happen.
+     We therefore use Brent's method if riemann_f(0) is larger than some
+     value. -5 makes the iteration fail safe while almost never invoking
+     the expensive Brent solver. */
+  p = 0.;
+  /* obtain a first guess for p */
+  pguess = riemann_guess_p(WL, WR, vL, vR, aL, aR);
+  fp = riemann_f(p, WL, WR, vL, vR, aL, aR);
+  fpguess = riemann_f(pguess, WL, WR, vL, vR, aL, aR);
+  /* ok, pstar is close to 0, better use Brent's method... */
+  /* we use Newton-Raphson until we find a suitable interval */
+  if (fp * fpguess >= 0.0f) {
+    /* Newton-Raphson until convergence or until suitable interval is found
+       to use Brent's method */
+    unsigned int counter = 0;
+    while (fabs(p - pguess) > 1.e-6f * 0.5f * (p + pguess) && fpguess < 0.0f) {
       p = pguess;
+      pguess = pguess - fpguess / riemann_fprime(pguess, WL, WR, aL, aR);
+      fpguess = riemann_f(pguess, WL, WR, vL, vR, aL, aR);
+      counter++;
+      if (counter > 1000) {
+        error("Stuck in Newton-Raphson!\n");
+      }
     }
+  }
+  /* As soon as there is a suitable interval: use Brent's method */
+  if (1.e6 * fabs(p - pguess) > 0.5f * (p + pguess) && fpguess > 0.0f) {
+    p = 0.0f;
+    fp = riemann_f(p, WL, WR, vL, vR, aL, aR);
+    /* use Brent's method to find the zeropoint */
+    p = riemann_solve_brent(p, pguess, fp, fpguess, 1.e-6, WL, WR, vL, vR, aL,
+                            aR);
+  } else {
+    p = pguess;
+  }
 
-    /* calculate the velocity in the intermediate state */
-    u = 0.5f * (vL + vR) +
-        0.5f * (riemann_fb(p, WR, aR) - riemann_fb(p, WL, aL));
-
-    /* sample the solution */
-    /* This corresponds to the flow chart in Fig. 4.14 in Toro */
-    if (u < 0.0f) {
-      /* advect velocity components */
-      Whalf[1] = WR[1];
-      Whalf[2] = WR[2];
-      Whalf[3] = WR[3];
-      pdpR = p / WR[4];
-      if (p > WR[4]) {
-        /* shockwave */
-        SR =
-            vR + aR * sqrtf(const_riemann_gp1d2g * pdpR + const_riemann_gm1d2g);
-        if (SR > 0.0f) {
-          Whalf[0] = WR[0] * (pdpR + const_riemann_gm1dgp1) /
-                     (const_riemann_gm1dgp1 * pdpR + 1.0f);
+  /* calculate the velocity in the intermediate state */
+  u = 0.5f * (vL + vR) + 0.5f * (riemann_fb(p, WR, aR) - riemann_fb(p, WL, aL));
+
+  /* sample the solution */
+  /* This corresponds to the flow chart in Fig. 4.14 in Toro */
+  if (u < 0.0f) {
+    /* advect velocity components */
+    Whalf[1] = WR[1];
+    Whalf[2] = WR[2];
+    Whalf[3] = WR[3];
+    pdpR = p / WR[4];
+    if (p > WR[4]) {
+      /* shockwave */
+      SR = vR +
+           aR * sqrtf(hydro_gamma_plus_one_over_two_gamma * pdpR +
+                      hydro_gamma_minus_one_over_two_gamma);
+      if (SR > 0.0f) {
+        Whalf[0] = WR[0] * (pdpR + hydro_gamma_minus_one_over_gamma_plus_one) /
+                   (hydro_gamma_minus_one_over_gamma_plus_one * pdpR + 1.0f);
+        vhalf = u - vR;
+        Whalf[4] = p;
+      } else {
+        Whalf[0] = WR[0];
+        vhalf = 0.0f;
+        Whalf[4] = WR[4];
+      }
+    } else {
+      /* rarefaction wave */
+      SHR = vR + aR;
+      if (SHR > 0.0f) {
+        STR = u + aR * pow_gamma_minus_one_over_two_gamma(pdpR);
+        if (STR <= 0.0f) {
+          Whalf[0] =
+              WR[0] * pow_two_over_gamma_minus_one(
+                          hydro_two_over_gamma_plus_one -
+                          hydro_gamma_minus_one_over_gamma_plus_one / aR * vR);
+          vhalf = hydro_two_over_gamma_plus_one *
+                      (-aR + hydro_gamma_minus_one_over_two * vR) -
+                  vR;
+          Whalf[4] =
+              WR[4] * pow_two_gamma_over_gamma_minus_one(
+                          hydro_two_over_gamma_plus_one -
+                          hydro_gamma_minus_one_over_gamma_plus_one / aR * vR);
+        } else {
+          Whalf[0] = WR[0] * pow_one_over_gamma(pdpR);
           vhalf = u - vR;
           Whalf[4] = p;
-        } else {
-          Whalf[0] = WR[0];
-          vhalf = 0.0f;
-          Whalf[4] = WR[4];
         }
       } else {
-        /* rarefaction wave */
-        SHR = vR + aR;
-        if (SHR > 0.0f) {
-          STR = u + aR * powf(pdpR, const_riemann_gm1d2g);
-          if (STR <= 0.0f) {
-            Whalf[0] = WR[0] * powf(const_riemann_tdgp1 -
-                                        const_riemann_gm1dgp1 / aR * vR,
-                                    const_riemann_tdgm1);
-            vhalf = const_riemann_tdgp1 * (-aR + const_riemann_gm1d2 * vR) - vR;
-            Whalf[4] = WR[4] * powf(const_riemann_tdgp1 -
-                                        const_riemann_gm1dgp1 / aR * vR,
-                                    const_riemann_tgdgm1);
-          } else {
-            Whalf[0] = WR[0] * powf(pdpR, const_riemann_ginv);
-            vhalf = u - vR;
-            Whalf[4] = p;
-          }
-        } else {
-          Whalf[0] = WR[0];
-          vhalf = 0.0f;
-          Whalf[4] = WR[4];
-        }
+        Whalf[0] = WR[0];
+        vhalf = 0.0f;
+        Whalf[4] = WR[4];
+      }
+    }
+  } else {
+    Whalf[1] = WL[1];
+    Whalf[2] = WL[2];
+    Whalf[3] = WL[3];
+    pdpL = p / WL[4];
+    if (p > WL[4]) {
+      /* shockwave */
+      SL = vL -
+           aL * sqrtf(hydro_gamma_plus_one_over_two_gamma * pdpL +
+                      hydro_gamma_minus_one_over_two_gamma);
+      if (SL < 0.0f) {
+        Whalf[0] = WL[0] * (pdpL + hydro_gamma_minus_one_over_gamma_plus_one) /
+                   (hydro_gamma_minus_one_over_gamma_plus_one * pdpL + 1.0f);
+        vhalf = u - vL;
+        Whalf[4] = p;
+      } else {
+        Whalf[0] = WL[0];
+        vhalf = 0.0f;
+        Whalf[4] = WL[4];
       }
     } else {
-      Whalf[1] = WL[1];
-      Whalf[2] = WL[2];
-      Whalf[3] = WL[3];
-      pdpL = p / WL[4];
-      if (p > WL[4]) {
-        /* shockwave */
-        SL =
-            vL - aL * sqrtf(const_riemann_gp1d2g * pdpL + const_riemann_gm1d2g);
-        if (SL < 0.0f) {
-          Whalf[0] = WL[0] * (pdpL + const_riemann_gm1dgp1) /
-                     (const_riemann_gm1dgp1 * pdpL + 1.0f);
+      /* rarefaction wave */
+      SHL = vL - aL;
+      if (SHL < 0.0f) {
+        STL = u - aL * pow_gamma_minus_one_over_two_gamma(pdpL);
+        if (STL > 0.0f) {
+          Whalf[0] =
+              WL[0] * pow_two_over_gamma_minus_one(
+                          hydro_two_over_gamma_plus_one +
+                          hydro_gamma_minus_one_over_gamma_plus_one / aL * vL);
+          vhalf = hydro_two_over_gamma_plus_one *
+                      (aL + hydro_gamma_minus_one_over_two * vL) -
+                  vL;
+          Whalf[4] =
+              WL[4] * pow_two_gamma_over_gamma_minus_one(
+                          hydro_two_over_gamma_plus_one +
+                          hydro_gamma_minus_one_over_gamma_plus_one / aL * vL);
+        } else {
+          Whalf[0] = WL[0] * pow_one_over_gamma(pdpL);
           vhalf = u - vL;
           Whalf[4] = p;
-        } else {
-          Whalf[0] = WL[0];
-          vhalf = 0.0f;
-          Whalf[4] = WL[4];
         }
       } else {
-        /* rarefaction wave */
-        SHL = vL - aL;
-        if (SHL < 0.0f) {
-          STL = u - aL * powf(pdpL, const_riemann_gm1d2g);
-          if (STL > 0.0f) {
-            Whalf[0] = WL[0] * powf(const_riemann_tdgp1 +
-                                        const_riemann_gm1dgp1 / aL * vL,
-                                    const_riemann_tdgm1);
-            vhalf = const_riemann_tdgp1 * (aL + const_riemann_gm1d2 * vL) - vL;
-            Whalf[4] = WL[4] * powf(const_riemann_tdgp1 +
-                                        const_riemann_gm1dgp1 / aL * vL,
-                                    const_riemann_tgdgm1);
-          } else {
-            Whalf[0] = WL[0] * powf(pdpL, const_riemann_ginv);
-            vhalf = u - vL;
-            Whalf[4] = p;
-          }
-        } else {
-          Whalf[0] = WL[0];
-          vhalf = 0.0f;
-          Whalf[4] = WL[4];
-        }
+        Whalf[0] = WL[0];
+        vhalf = 0.0f;
+        Whalf[4] = WL[4];
       }
     }
   }
@@ -661,10 +506,10 @@ __attribute__((always_inline)) INLINE static void riemann_solver_solve(
 }
 
 __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
-    GFLOAT* Wi, GFLOAT* Wj, float* n_unit, float* vij, GFLOAT* totflux) {
+    float* Wi, float* Wj, float* n_unit, float* vij, float* totflux) {
 
-  GFLOAT Whalf[5];
-  GFLOAT flux[5][3];
+  float Whalf[5];
+  float flux[5][3];
   float vtot[3];
   float rhoe;
 
@@ -690,7 +535,7 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
   /* eqn. (15) */
   /* F_P = \rho e ( \vec{v} - \vec{v_{ij}} ) + P \vec{v} */
   /* \rho e = P / (\gamma-1) + 1/2 \rho \vec{v}^2 */
-  rhoe = Whalf[4] / (const_hydro_gamma - 1.0f) +
+  rhoe = Whalf[4] / hydro_gamma_minus_one +
          0.5f * Whalf[0] *
              (vtot[0] * vtot[0] + vtot[1] * vtot[1] + vtot[2] * vtot[2]);
   flux[4][0] = rhoe * Whalf[1] + Whalf[4] * vtot[0];
diff --git a/src/riemann/riemann_hllc.h b/src/riemann/riemann_hllc.h
index 6c583f6410f53ed64d630082926d816129768fab..fdc22ce05b8d63bdba66e530d1a5a968801a9f10 100644
--- a/src/riemann/riemann_hllc.h
+++ b/src/riemann/riemann_hllc.h
@@ -20,13 +20,16 @@
 #ifndef SWIFT_RIEMANN_HLLC_H
 #define SWIFT_RIEMANN_HLLC_H
 
+#include "adiabatic_index.h"
+#include "riemann_vacuum.h"
+
 __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
-    GFLOAT *WL, GFLOAT *WR, float *n, float *vij, GFLOAT *totflux) {
+    float *WL, float *WR, float *n, float *vij, float *totflux) {
 
-  GFLOAT uL, uR, aL, aR;
-  GFLOAT rhobar, abar, pPVRS, pstar, qL, qR, SL, SR, Sstar;
-  GFLOAT v2, eL, eR;
-  GFLOAT UstarL[5], UstarR[5];
+  float uL, uR, aL, aR;
+  float rhobar, abar, pPVRS, pstar, qL, qR, SL, SR, Sstar;
+  float v2, eL, eR;
+  float UstarL[5], UstarR[5];
 
   /* Handle pure vacuum */
   if (!WL[0] && !WR[0]) {
@@ -41,16 +44,13 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
   /* STEP 0: obtain velocity in interface frame */
   uL = WL[1] * n[0] + WL[2] * n[1] + WL[3] * n[2];
   uR = WR[1] * n[0] + WR[2] * n[1] + WR[3] * n[2];
-  aL = sqrtf(const_hydro_gamma * WL[4] / WL[0]);
-  aR = sqrtf(const_hydro_gamma * WR[4] / WR[0]);
+  aL = sqrtf(hydro_gamma * WL[4] / WL[0]);
+  aR = sqrtf(hydro_gamma * WR[4] / WR[0]);
 
   /* Handle vacuum: vacuum does not require iteration and is always exact */
-  if (!WL[0] || !WR[0]) {
-    error("Vacuum not yet supported");
-  }
-  if (2. * aL / (const_hydro_gamma - 1.) + 2. * aR / (const_hydro_gamma - 1.) <
-      fabs(uL - uR)) {
-    error("Vacuum not yet supported");
+  if (riemann_is_vacuum(WL, WR, uL, uR, aL, aR)) {
+    riemann_solve_vacuum_flux(WL, WR, uL, uR, aL, aR, n, vij, totflux);
+    return;
   }
 
   /* STEP 1: pressure estimate */
@@ -64,14 +64,12 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
   qL = 1.;
   if (pstar > WL[4]) {
     qL = sqrtf(1. +
-               0.5 * (const_hydro_gamma + 1.) / const_hydro_gamma *
-                   (pstar / WL[4] - 1.));
+               0.5 * (hydro_gamma + 1.) / hydro_gamma * (pstar / WL[4] - 1.));
   }
   qR = 1.;
   if (pstar > WR[4]) {
     qR = sqrtf(1. +
-               0.5 * (const_hydro_gamma + 1.) / const_hydro_gamma *
-                   (pstar / WR[4] - 1.));
+               0.5 * (hydro_gamma + 1.) / hydro_gamma * (pstar / WR[4] - 1.));
   }
   SL = uL - aL * qL;
   SR = uR + aR * qR;
@@ -86,9 +84,9 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
        (not rotated to interface frame) */
     totflux[1] = WL[0] * WL[1] * uL + WL[4] * n[0];
     totflux[2] = WL[0] * WL[2] * uL + WL[4] * n[1];
-    totflux[3] = WL[0] * WL[2] * uL + WL[4] * n[2];
+    totflux[3] = WL[0] * WL[3] * uL + WL[4] * n[2];
     v2 = WL[1] * WL[1] + WL[2] * WL[2] + WL[3] * WL[3];
-    eL = WL[4] / (const_hydro_gamma - 1.) / WL[0] + 0.5 * v2;
+    eL = WL[4] / hydro_gamma_minus_one / WL[0] + 0.5 * v2;
     totflux[4] = WL[0] * eL * uL + WL[4] * uL;
     if (SL < 0.) {
       /* add flux FstarL */
@@ -118,7 +116,7 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
     totflux[2] = WR[0] * WR[2] * uR + WR[4] * n[1];
     totflux[3] = WR[0] * WR[3] * uR + WR[4] * n[2];
     v2 = WR[1] * WR[1] + WR[2] * WR[2] + WR[3] * WR[3];
-    eR = WR[4] / (const_hydro_gamma - 1.) / WR[0] + 0.5 * v2;
+    eR = WR[4] / hydro_gamma_minus_one / WR[0] + 0.5 * v2;
     totflux[4] = WR[0] * eR * uR + WR[4] * uR;
     if (SR > 0.) {
       /* add flux FstarR */
diff --git a/src/riemann/riemann_trrs.h b/src/riemann/riemann_trrs.h
index efdbfb59877c09a59d535a4785ad74620c0f3651..b13a76b4c57af548497780e974e5c9ee3a721fac 100644
--- a/src/riemann/riemann_trrs.h
+++ b/src/riemann/riemann_trrs.h
@@ -20,19 +20,8 @@
 #ifndef SWIFT_RIEMANN_TRRS_H
 #define SWIFT_RIEMANN_TRRS_H
 
-/* frequently used combinations of const_hydro_gamma */
-#define const_riemann_gp1d2g \
-  (0.5f * (const_hydro_gamma + 1.0f) / const_hydro_gamma)
-#define const_riemann_gm1d2g \
-  (0.5f * (const_hydro_gamma - 1.0f) / const_hydro_gamma)
-#define const_riemann_gm1dgp1 \
-  ((const_hydro_gamma - 1.0f) / (const_hydro_gamma + 1.0f))
-#define const_riemann_tdgp1 (2.0f / (const_hydro_gamma + 1.0f))
-#define const_riemann_tdgm1 (2.0f / (const_hydro_gamma - 1.0f))
-#define const_riemann_gm1d2 (0.5f * (const_hydro_gamma - 1.0f))
-#define const_riemann_tgdgm1 \
-  (2.0f * const_hydro_gamma / (const_hydro_gamma - 1.0f))
-#define const_riemann_ginv (1.0f / const_hydro_gamma)
+#include "adiabatic_index.h"
+#include "riemann_vacuum.h"
 
 /**
  * @brief Solve the Riemann problem using the Two Rarefaction Riemann Solver
@@ -50,31 +39,39 @@
  * @param n_unit Normal vector of the interface
  */
 __attribute__((always_inline)) INLINE static void riemann_solver_solve(
-    GFLOAT* WL, GFLOAT* WR, GFLOAT* Whalf, float* n_unit) {
-  GFLOAT aL, aR;
-  GFLOAT PLR;
-  GFLOAT vL, vR;
-  GFLOAT ustar, pstar;
-  GFLOAT vhalf;
-  GFLOAT pdpR, SHR, STR;
-  GFLOAT pdpL, SHL, STL;
+    float* WL, float* WR, float* Whalf, float* n_unit) {
+  float aL, aR;
+  float PLR;
+  float vL, vR;
+  float ustar, pstar;
+  float vhalf;
+  float pdpR, SHR, STR;
+  float pdpL, SHL, STL;
 
   /* calculate the velocities along the interface normal */
   vL = WL[1] * n_unit[0] + WL[2] * n_unit[1] + WL[3] * n_unit[2];
   vR = WR[1] * n_unit[0] + WR[2] * n_unit[1] + WR[3] * n_unit[2];
 
   /* calculate the sound speeds */
-  aL = sqrtf(const_hydro_gamma * WL[4] / WL[0]);
-  aR = sqrtf(const_hydro_gamma * WR[4] / WR[0]);
+  aL = sqrtf(hydro_gamma * WL[4] / WL[0]);
+  aR = sqrtf(hydro_gamma * WR[4] / WR[0]);
+
+  if (riemann_is_vacuum(WL, WR, vL, vR, aL, aR)) {
+    riemann_solve_vacuum(WL, WR, vL, vR, aL, aR, Whalf, n_unit);
+    return;
+  }
 
   /* calculate the velocity and pressure in the intermediate state */
-  PLR = pow(WL[4] / WR[4], const_riemann_gm1d2g);
-  ustar = (PLR * vL / aL + vR / aR + const_riemann_tdgm1 * (PLR - 1.0f)) /
+  PLR = pow_gamma_minus_one_over_two_gamma(WL[4] / WR[4]);
+  ustar = (PLR * vL / aL + vR / aR +
+           hydro_two_over_gamma_minus_one * (PLR - 1.0f)) /
           (PLR / aL + 1.0f / aR);
-  pstar = 0.5f * (WL[4] * pow(1.0f + const_riemann_gm1d2 / aL * (vL - ustar),
-                              const_riemann_tgdgm1) +
-                  WR[4] * pow(1.0f + const_riemann_gm1d2 / aR * (ustar - vR),
-                              const_riemann_tgdgm1));
+  pstar =
+      0.5f *
+      (WL[4] * pow_two_gamma_over_gamma_minus_one(
+                   1.0f + hydro_gamma_minus_one_over_two / aL * (vL - ustar)) +
+       WR[4] * pow_two_gamma_over_gamma_minus_one(
+                   1.0f + hydro_gamma_minus_one_over_two / aR * (ustar - vR)));
 
   /* sample the solution */
   if (ustar < 0.0f) {
@@ -86,17 +83,21 @@ __attribute__((always_inline)) INLINE static void riemann_solver_solve(
     /* always a rarefaction wave, that's the approximation */
     SHR = vR + aR;
     if (SHR > 0.0f) {
-      STR = ustar + aR * pow(pdpR, const_riemann_gm1d2g);
+      STR = ustar + aR * pow_gamma_minus_one_over_two_gamma(pdpR);
       if (STR <= 0.0f) {
         Whalf[0] =
-            WR[0] * pow(const_riemann_tdgp1 - const_riemann_gm1dgp1 / aR * vR,
-                        const_riemann_tdgm1);
-        vhalf = const_riemann_tdgp1 * (-aR + const_riemann_gm1d2 * vR) - vR;
+            WR[0] * pow_two_over_gamma_minus_one(
+                        hydro_two_over_gamma_plus_one -
+                        hydro_gamma_minus_one_over_gamma_plus_one / aR * vR);
+        vhalf = hydro_two_over_gamma_plus_one *
+                    (-aR + hydro_gamma_minus_one_over_two * vR) -
+                vR;
         Whalf[4] =
-            WR[4] * pow(const_riemann_tdgp1 - const_riemann_gm1dgp1 / aR * vR,
-                        const_riemann_tgdgm1);
+            WR[4] * pow_two_gamma_over_gamma_minus_one(
+                        hydro_two_over_gamma_plus_one -
+                        hydro_gamma_minus_one_over_gamma_plus_one / aR * vR);
       } else {
-        Whalf[0] = WR[0] * pow(pdpR, const_riemann_ginv);
+        Whalf[0] = WR[0] * pow_one_over_gamma(pdpR);
         vhalf = ustar - vR;
         Whalf[4] = pstar;
       }
@@ -114,17 +115,21 @@ __attribute__((always_inline)) INLINE static void riemann_solver_solve(
     /* rarefaction wave */
     SHL = vL - aL;
     if (SHL < 0.0f) {
-      STL = ustar - aL * pow(pdpL, const_riemann_gm1d2g);
+      STL = ustar - aL * pow_gamma_minus_one_over_two_gamma(pdpL);
       if (STL > 0.0f) {
         Whalf[0] =
-            WL[0] * pow(const_riemann_tdgp1 + const_riemann_gm1dgp1 / aL * vL,
-                        const_riemann_tdgm1);
-        vhalf = const_riemann_tdgp1 * (aL + const_riemann_gm1d2 * vL) - vL;
+            WL[0] * pow_two_over_gamma_minus_one(
+                        hydro_two_over_gamma_plus_one +
+                        hydro_gamma_minus_one_over_gamma_plus_one / aL * vL);
+        vhalf = hydro_two_over_gamma_plus_one *
+                    (aL + hydro_gamma_minus_one_over_two * vL) -
+                vL;
         Whalf[4] =
-            WL[4] * pow(const_riemann_tdgp1 + const_riemann_gm1dgp1 / aL * vL,
-                        const_riemann_tgdgm1);
+            WL[4] * pow_two_gamma_over_gamma_minus_one(
+                        hydro_two_over_gamma_plus_one +
+                        hydro_gamma_minus_one_over_gamma_plus_one / aL * vL);
       } else {
-        Whalf[0] = WL[0] * pow(pdpL, const_riemann_ginv);
+        Whalf[0] = WL[0] * pow_one_over_gamma(pdpL);
         vhalf = ustar - vL;
         Whalf[4] = pstar;
       }
@@ -141,4 +146,53 @@ __attribute__((always_inline)) INLINE static void riemann_solver_solve(
   Whalf[3] += vhalf * n_unit[2];
 }
 
+__attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
+    float* Wi, float* Wj, float* n_unit, float* vij, float* totflux) {
+
+  float Whalf[5];
+  float flux[5][3];
+  float vtot[3];
+  float rhoe;
+
+  riemann_solver_solve(Wi, Wj, Whalf, n_unit);
+
+  flux[0][0] = Whalf[0] * Whalf[1];
+  flux[0][1] = Whalf[0] * Whalf[2];
+  flux[0][2] = Whalf[0] * Whalf[3];
+
+  vtot[0] = Whalf[1] + vij[0];
+  vtot[1] = Whalf[2] + vij[1];
+  vtot[2] = Whalf[3] + vij[2];
+  flux[1][0] = Whalf[0] * vtot[0] * Whalf[1] + Whalf[4];
+  flux[1][1] = Whalf[0] * vtot[0] * Whalf[2];
+  flux[1][2] = Whalf[0] * vtot[0] * Whalf[3];
+  flux[2][0] = Whalf[0] * vtot[1] * Whalf[1];
+  flux[2][1] = Whalf[0] * vtot[1] * Whalf[2] + Whalf[4];
+  flux[2][2] = Whalf[0] * vtot[1] * Whalf[3];
+  flux[3][0] = Whalf[0] * vtot[2] * Whalf[1];
+  flux[3][1] = Whalf[0] * vtot[2] * Whalf[2];
+  flux[3][2] = Whalf[0] * vtot[2] * Whalf[3] + Whalf[4];
+
+  /* eqn. (15) */
+  /* F_P = \rho e ( \vec{v} - \vec{v_{ij}} ) + P \vec{v} */
+  /* \rho e = P / (\gamma-1) + 1/2 \rho \vec{v}^2 */
+  rhoe = Whalf[4] / hydro_gamma_minus_one +
+         0.5f * Whalf[0] *
+             (vtot[0] * vtot[0] + vtot[1] * vtot[1] + vtot[2] * vtot[2]);
+  flux[4][0] = rhoe * Whalf[1] + Whalf[4] * vtot[0];
+  flux[4][1] = rhoe * Whalf[2] + Whalf[4] * vtot[1];
+  flux[4][2] = rhoe * Whalf[3] + Whalf[4] * vtot[2];
+
+  totflux[0] =
+      flux[0][0] * n_unit[0] + flux[0][1] * n_unit[1] + flux[0][2] * n_unit[2];
+  totflux[1] =
+      flux[1][0] * n_unit[0] + flux[1][1] * n_unit[1] + flux[1][2] * n_unit[2];
+  totflux[2] =
+      flux[2][0] * n_unit[0] + flux[2][1] * n_unit[1] + flux[2][2] * n_unit[2];
+  totflux[3] =
+      flux[3][0] * n_unit[0] + flux[3][1] * n_unit[1] + flux[3][2] * n_unit[2];
+  totflux[4] =
+      flux[4][0] * n_unit[0] + flux[4][1] * n_unit[1] + flux[4][2] * n_unit[2];
+}
+
 #endif /* SWIFT_RIEMANN_TRRS_H */
diff --git a/src/riemann/riemann_vacuum.h b/src/riemann/riemann_vacuum.h
new file mode 100644
index 0000000000000000000000000000000000000000..743abb910193380793ccdf3d7eddbcedc4968691
--- /dev/null
+++ b/src/riemann/riemann_vacuum.h
@@ -0,0 +1,254 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2016 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/>.
+ *
+ ******************************************************************************/
+
+#ifndef SWIFT_RIEMANN_VACUUM_H
+#define SWIFT_RIEMANN_VACUUM_H
+
+/**
+ * @brief Check if the given input states are vacuum or will generate vacuum
+ */
+__attribute__((always_inline)) INLINE static int riemann_is_vacuum(
+    float* WL, float* WR, float vL, float vR, float aL, float aR) {
+
+  /* vacuum */
+  if (!WL[0] || !WR[0]) {
+    return 1;
+  }
+  /* vacuum generation */
+  if (2.0f * aL / hydro_gamma_minus_one + 2.0f * aR / hydro_gamma_minus_one <=
+      vR - vL) {
+    return 1;
+  }
+
+  /* no vacuum */
+  return 0;
+}
+
+/**
+ * @brief Vacuum Riemann solver, based on section 4.6 in Toro
+ *
+ * @param WL The left state vector
+ * @param WR The right state vector
+ * @param vL The left velocity along the interface normal
+ * @param vR The right velocity along the interface normal
+ * @param aL The left sound speed
+ * @param aR The right sound speed
+ * @param Whalf Empty state vector to store the solution in
+ * @param n_unit Normal vector of the interface
+ */
+__attribute__((always_inline)) INLINE static void riemann_solve_vacuum(
+    float* WL, float* WR, float vL, float vR, float aL, float aR, float* Whalf,
+    float* n_unit) {
+
+  float SL, SR;
+  float vhalf;
+
+  if (!WR[0] && !WL[0]) {
+    /* if both states are vacuum, the solution is also vacuum */
+    Whalf[0] = 0.0f;
+    Whalf[1] = 0.0f;
+    Whalf[2] = 0.0f;
+    Whalf[3] = 0.0f;
+    Whalf[4] = 0.0f;
+    return;
+  }
+  if (!WR[0]) {
+    Whalf[1] = WL[1];
+    Whalf[2] = WL[2];
+    Whalf[3] = WL[3];
+    /* vacuum right state */
+    if (vL < aL) {
+      SL = vL + hydro_two_over_gamma_minus_one * aL;
+      if (SL > 0.0f) {
+        Whalf[0] =
+            WL[0] * pow_two_over_gamma_minus_one(
+                        hydro_two_over_gamma_plus_one +
+                        hydro_gamma_minus_one_over_gamma_plus_one / aL * vL);
+        vhalf = hydro_two_over_gamma_plus_one *
+                    (aL + hydro_gamma_minus_one_over_two * vL) -
+                vL;
+        Whalf[4] =
+            WL[4] * pow_two_gamma_over_gamma_minus_one(
+                        hydro_two_over_gamma_plus_one +
+                        hydro_gamma_minus_one_over_gamma_plus_one / aL * vL);
+      } else {
+        Whalf[0] = 0.0f;
+        Whalf[1] = 0.0f;
+        Whalf[2] = 0.0f;
+        Whalf[3] = 0.0f;
+        Whalf[4] = 0.0f;
+        return;
+      }
+    } else {
+      Whalf[0] = WL[0];
+      vhalf = 0.0f;
+      Whalf[4] = WL[4];
+    }
+  } else {
+    if (!WL[0]) {
+      Whalf[1] = WR[1];
+      Whalf[2] = WR[2];
+      Whalf[3] = WR[3];
+      /* vacuum left state */
+      if (-aR < vR) {
+        SR = vR - hydro_two_over_gamma_minus_one * aR;
+        if (SR >= 0.0f) {
+          Whalf[0] = 0.0f;
+          Whalf[1] = 0.0f;
+          Whalf[2] = 0.0f;
+          Whalf[3] = 0.0f;
+          Whalf[4] = 0.0f;
+          return;
+        } else {
+          Whalf[0] =
+              WR[0] * pow_two_over_gamma_minus_one(
+                          hydro_two_over_gamma_plus_one -
+                          hydro_gamma_minus_one_over_gamma_plus_one / aR * vR);
+          vhalf = hydro_two_over_gamma_plus_one *
+                      (-aR + hydro_gamma_minus_one_over_two * vR) -
+                  vR;
+          Whalf[4] =
+              WR[4] * pow_two_gamma_over_gamma_minus_one(
+                          hydro_two_over_gamma_plus_one -
+                          hydro_gamma_minus_one_over_gamma_plus_one / aR * vR);
+        }
+      } else {
+        Whalf[0] = WR[0];
+        vhalf = 0.0f;
+        Whalf[4] = WR[4];
+      }
+    } else {
+      /* vacuum generation */
+      SR = vR - hydro_two_over_gamma_minus_one * aR;
+      SL = vL + hydro_two_over_gamma_minus_one * aL;
+      if (SR > 0.0f && SL < 0.0f) {
+        Whalf[0] = 0.0f;
+        Whalf[1] = 0.0f;
+        Whalf[2] = 0.0f;
+        Whalf[3] = 0.0f;
+        Whalf[4] = 0.0f;
+        return;
+      } else {
+        if (SL >= 0.0f) {
+          Whalf[1] = WL[1];
+          Whalf[2] = WL[2];
+          Whalf[3] = WL[3];
+          if (aL > vL) {
+            Whalf[0] = WL[0] *
+                       pow_two_over_gamma_minus_one(
+                           hydro_two_over_gamma_plus_one +
+                           hydro_gamma_minus_one_over_gamma_plus_one / aL * vL);
+            vhalf = hydro_two_over_gamma_plus_one *
+                        (aL + hydro_gamma_minus_one_over_two * vL) -
+                    vL;
+            Whalf[4] = WL[4] *
+                       pow_two_gamma_over_gamma_minus_one(
+                           hydro_two_over_gamma_plus_one +
+                           hydro_gamma_minus_one_over_gamma_plus_one / aL * vL);
+          } else {
+            Whalf[0] = WL[0];
+            vhalf = 0.0f;
+            Whalf[4] = WL[4];
+          }
+        } else {
+          Whalf[1] = WR[1];
+          Whalf[2] = WR[2];
+          Whalf[3] = WR[3];
+          if (-aR < vR) {
+            Whalf[0] = WR[0] *
+                       pow_two_over_gamma_minus_one(
+                           hydro_two_over_gamma_plus_one -
+                           hydro_gamma_minus_one_over_gamma_plus_one / aR * vR);
+            vhalf = hydro_two_over_gamma_plus_one *
+                        (-aR + hydro_gamma_minus_one_over_two * vR) -
+                    vR;
+            Whalf[4] = WR[4] *
+                       pow_two_gamma_over_gamma_minus_one(
+                           hydro_two_over_gamma_plus_one -
+                           hydro_gamma_minus_one_over_gamma_plus_one / aR * vR);
+          } else {
+            Whalf[0] = WR[0];
+            vhalf = 0.0f;
+            Whalf[4] = WR[4];
+          }
+        }
+      }
+    }
+  }
+
+  /* Add the velocity solution along the interface normal to the velocities */
+  Whalf[1] += vhalf * n_unit[0];
+  Whalf[2] += vhalf * n_unit[1];
+  Whalf[3] += vhalf * n_unit[2];
+}
+
+/**
+ * @brief Solve the vacuum Riemann problem and return the fluxes
+ */
+__attribute__((always_inline)) INLINE static void riemann_solve_vacuum_flux(
+    float* WL, float* WR, float vL, float vR, float aL, float aR, float* n_unit,
+    float* vij, float* totflux) {
+
+  float Whalf[5];
+  float flux[5][3];
+  float vtot[3];
+  float rhoe;
+
+  riemann_solve_vacuum(WL, WR, vL, vR, aL, aR, Whalf, n_unit);
+
+  flux[0][0] = Whalf[0] * Whalf[1];
+  flux[0][1] = Whalf[0] * Whalf[2];
+  flux[0][2] = Whalf[0] * Whalf[3];
+
+  vtot[0] = Whalf[1] + vij[0];
+  vtot[1] = Whalf[2] + vij[1];
+  vtot[2] = Whalf[3] + vij[2];
+  flux[1][0] = Whalf[0] * vtot[0] * Whalf[1] + Whalf[4];
+  flux[1][1] = Whalf[0] * vtot[0] * Whalf[2];
+  flux[1][2] = Whalf[0] * vtot[0] * Whalf[3];
+  flux[2][0] = Whalf[0] * vtot[1] * Whalf[1];
+  flux[2][1] = Whalf[0] * vtot[1] * Whalf[2] + Whalf[4];
+  flux[2][2] = Whalf[0] * vtot[1] * Whalf[3];
+  flux[3][0] = Whalf[0] * vtot[2] * Whalf[1];
+  flux[3][1] = Whalf[0] * vtot[2] * Whalf[2];
+  flux[3][2] = Whalf[0] * vtot[2] * Whalf[3] + Whalf[4];
+
+  /* eqn. (15) */
+  /* F_P = \rho e ( \vec{v} - \vec{v_{ij}} ) + P \vec{v} */
+  /* \rho e = P / (\gamma-1) + 1/2 \rho \vec{v}^2 */
+  rhoe = Whalf[4] / hydro_gamma_minus_one +
+         0.5f * Whalf[0] *
+             (vtot[0] * vtot[0] + vtot[1] * vtot[1] + vtot[2] * vtot[2]);
+  flux[4][0] = rhoe * Whalf[1] + Whalf[4] * vtot[0];
+  flux[4][1] = rhoe * Whalf[2] + Whalf[4] * vtot[1];
+  flux[4][2] = rhoe * Whalf[3] + Whalf[4] * vtot[2];
+
+  totflux[0] =
+      flux[0][0] * n_unit[0] + flux[0][1] * n_unit[1] + flux[0][2] * n_unit[2];
+  totflux[1] =
+      flux[1][0] * n_unit[0] + flux[1][1] * n_unit[1] + flux[1][2] * n_unit[2];
+  totflux[2] =
+      flux[2][0] * n_unit[0] + flux[2][1] * n_unit[1] + flux[2][2] * n_unit[2];
+  totflux[3] =
+      flux[3][0] * n_unit[0] + flux[3][1] * n_unit[1] + flux[3][2] * n_unit[2];
+  totflux[4] =
+      flux[4][0] * n_unit[0] + flux[4][1] * n_unit[1] + flux[4][2] * n_unit[2];
+}
+
+#endif /* SWIFT_RIEMANN_VACUUM_H */
diff --git a/src/runner.c b/src/runner.c
index d72c47d0c60927c578d8659fe2b715fedc4ddbe9..bd381209ba49a719e7950bb270aaf17a553c2e55 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -83,6 +83,13 @@ const char runner_flip[27] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 #define FUNCTION density
 #include "runner_doiact.h"
 
+/* Import the gradient loop functions (if required). */
+#ifdef EXTRA_HYDRO_LOOP
+#undef FUNCTION
+#define FUNCTION gradient
+#include "runner_doiact.h"
+#endif
+
 /* Import the force loop functions. */
 #undef FUNCTION
 #define FUNCTION force
@@ -106,9 +113,13 @@ void runner_do_grav_external(struct runner *r, struct cell *c, int timer) {
   const int ti_current = r->e->ti_current;
   const struct external_potential *potential = r->e->external_potential;
   const struct phys_const *constants = r->e->physical_constants;
+  const double time = r->e->time;
 
   TIMER_TIC;
 
+  /* Anything to do here? */
+  if (c->ti_end_min > ti_current) return;
+
   /* Recurse? */
   if (c->split) {
     for (int k = 0; k < 8; k++)
@@ -129,7 +140,7 @@ void runner_do_grav_external(struct runner *r, struct cell *c, int timer) {
     /* Is this part within the time step? */
     if (g->ti_end <= ti_current) {
 
-      external_gravity(potential, constants, g);
+      external_gravity(time, potential, constants, g);
     }
   }
 
@@ -433,6 +444,9 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) {
 
   TIMER_TIC;
 
+  /* Anything to do here? */
+  if (c->ti_end_min > ti_current) return;
+
   /* Recurse? */
   if (c->split) {
     for (int k = 0; k < 8; k++)
@@ -471,7 +485,52 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) {
 }
 
 /**
- * @brief Intermediate task between density and force
+ * @brief Intermediate task after the gradient loop that does final operations
+ * on the gradient quantities and optionally slope limits the gradients
+ *
+ * @param r The runner thread.
+ * @param c The cell.
+ */
+void runner_do_extra_ghost(struct runner *r, struct cell *c) {
+
+#ifdef EXTRA_HYDRO_LOOP
+
+  struct part *restrict parts = c->parts;
+  const int count = c->count;
+  const int ti_current = r->e->ti_current;
+
+  /* Anything to do here? */
+  if (c->ti_end_min > ti_current) return;
+
+  /* Recurse? */
+  if (c->split) {
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL) runner_do_extra_ghost(r, c->progeny[k]);
+    return;
+  } else {
+
+    /* Loop over the parts in this cell. */
+    for (int i = 0; i < count; i++) {
+
+      /* Get a direct pointer on the part. */
+      struct part *restrict p = &parts[i];
+
+      if (p->ti_end <= ti_current) {
+
+        /* Get ready for a force calculation */
+        hydro_end_gradient(p);
+      }
+    }
+  }
+
+#else
+  error("SWIFT was not compiled with the extra hydro loop activated.");
+#endif
+}
+
+/**
+ * @brief Intermediate task after the density to check that the smoothing
+ * lengths are correct.
  *
  * @param r The runner thread.
  * @param c The cell.
@@ -493,6 +552,9 @@ void runner_do_ghost(struct runner *r, struct cell *c) {
 
   TIMER_TIC;
 
+  /* Anything to do here? */
+  if (c->ti_end_min > ti_current) return;
+
   /* Recurse? */
   if (c->split) {
     for (int k = 0; k < 8; k++)
@@ -536,8 +598,8 @@ void runner_do_ghost(struct runner *r, struct cell *c) {
           h_corr = (target_wcount - p->density.wcount) / p->density.wcount_dh;
 
           /* Truncate to the range [ -p->h/2 , p->h ]. */
-          h_corr = fminf(h_corr, p->h);
-          h_corr = fmaxf(h_corr, -p->h * 0.5f);
+          h_corr = (h_corr < p->h) ? h_corr : p->h;
+          h_corr = (h_corr > -0.5f * p->h) ? h_corr : -0.5f * p->h;
         }
 
         /* Did we get the right number density? */
@@ -633,42 +695,43 @@ void runner_do_ghost(struct runner *r, struct cell *c) {
 }
 
 /**
- * @brief Drift particles and g-particles forward in time
+ * @brief Drift particles and g-particles in a cell forward in time
  *
- * @param r The runner thread.
  * @param c The cell.
- * @param timer Are we timing this ?
+ * @param e The engine.
  */
-void runner_do_drift(struct runner *r, struct cell *c, int timer) {
+static void runner_do_drift(struct cell *c, struct engine *e) {
 
-  const double timeBase = r->e->timeBase;
-  const double dt = (r->e->ti_current - r->e->ti_old) * timeBase;
-  const int ti_old = r->e->ti_old;
-  const int ti_current = r->e->ti_current;
-  struct part *restrict parts = c->parts;
-  struct xpart *restrict xparts = c->xparts;
-  struct gpart *restrict gparts = c->gparts;
-  float dx_max = 0.f, dx2_max = 0.f, h_max = 0.f;
+  const double timeBase = e->timeBase;
+  const int ti_old = c->ti_old;
+  const int ti_current = e->ti_current;
+  struct part *const parts = c->parts;
+  struct xpart *const xparts = c->xparts;
+  struct gpart *const gparts = c->gparts;
 
+  /* Do we need to drift ? */
+  if (!e->drift_all && !cell_is_drift_needed(c, ti_current)) return;
+
+  /* Check that we are actually going to move forward. */
+  if (ti_current == ti_old) return;
+
+  /* Drift from the last time the cell was drifted to the current time */
+  const double dt = (ti_current - ti_old) * timeBase;
+
+  float dx_max = 0.f, dx2_max = 0.f, h_max = 0.f;
   double e_kin = 0.0, e_int = 0.0, e_pot = 0.0, entropy = 0.0, mass = 0.0;
   double mom[3] = {0.0, 0.0, 0.0};
   double ang_mom[3] = {0.0, 0.0, 0.0};
 
-  TIMER_TIC
-
-#ifdef TASK_VERBOSE
-  OUT;
-#endif
-
   /* No children? */
   if (!c->split) {
 
     /* Loop over all the g-particles in the cell */
-    const int nr_gparts = c->gcount;
+    const size_t nr_gparts = c->gcount;
     for (size_t k = 0; k < nr_gparts; k++) {
 
       /* Get a handle on the gpart. */
-      struct gpart *restrict gp = &gparts[k];
+      struct gpart *const gp = &gparts[k];
 
       /* Drift... */
       drift_gpart(gp, dt, timeBase, ti_old, ti_current);
@@ -677,7 +740,7 @@ void runner_do_drift(struct runner *r, struct cell *c, int timer) {
       const float dx2 = gp->x_diff[0] * gp->x_diff[0] +
                         gp->x_diff[1] * gp->x_diff[1] +
                         gp->x_diff[2] * gp->x_diff[2];
-      dx2_max = fmaxf(dx2_max, dx2);
+      dx2_max = (dx2_max > dx2) ? dx2_max : dx2;
     }
 
     /* Loop over all the particles in the cell (more work for these !) */
@@ -685,8 +748,8 @@ void runner_do_drift(struct runner *r, struct cell *c, int timer) {
     for (size_t k = 0; k < nr_parts; k++) {
 
       /* Get a handle on the part. */
-      struct part *restrict p = &parts[k];
-      struct xpart *restrict xp = &xparts[k];
+      struct part *const p = &parts[k];
+      struct xpart *const xp = &xparts[k];
 
       /* Drift... */
       drift_part(p, xp, dt, timeBase, ti_old, ti_current);
@@ -695,10 +758,10 @@ void runner_do_drift(struct runner *r, struct cell *c, int timer) {
       const float dx2 = xp->x_diff[0] * xp->x_diff[0] +
                         xp->x_diff[1] * xp->x_diff[1] +
                         xp->x_diff[2] * xp->x_diff[2];
-      dx2_max = fmaxf(dx2_max, dx2);
+      dx2_max = (dx2_max > dx2) ? dx2_max : dx2;
 
       /* Maximal smoothing length */
-      h_max = fmaxf(p->h, h_max);
+      h_max = (h_max > p->h) ? h_max : p->h;
 
       /* Now collect quantities for statistics */
 
@@ -708,7 +771,8 @@ void runner_do_drift(struct runner *r, struct cell *c, int timer) {
       const float v[3] = {xp->v_full[0] + p->a_hydro[0] * half_dt,
                           xp->v_full[1] + p->a_hydro[1] * half_dt,
                           xp->v_full[2] + p->a_hydro[2] * half_dt};
-      const float m = p->mass;
+
+      const float m = hydro_get_mass(p);
 
       /* Collect mass */
       mass += m;
@@ -739,13 +803,13 @@ void runner_do_drift(struct runner *r, struct cell *c, int timer) {
   /* Otherwise, aggregate data from children. */
   else {
 
-    /* Loop over the progeny. */
+    /* Loop over the progeny and collect their data. */
     for (int k = 0; k < 8; k++)
       if (c->progeny[k] != NULL) {
+        struct cell *cp = c->progeny[k];
 
-        /* Recurse */
-        struct cell *restrict cp = c->progeny[k];
-        runner_do_drift(r, cp, 0);
+        /* Recurse. */
+        runner_do_drift(cp, e);
 
         /* Collect */
         dx_max = fmaxf(dx_max, cp->dx_max);
@@ -779,7 +843,30 @@ void runner_do_drift(struct runner *r, struct cell *c, int timer) {
   c->ang_mom[1] = ang_mom[1];
   c->ang_mom[2] = ang_mom[2];
 
-  if (timer) TIMER_TOC(timer_drift);
+  /* Update the time of the last drift */
+  c->ti_old = ti_current;
+}
+
+/**
+ * @brief Mapper function to drift particles and g-particles forward in time.
+ *
+ * @param map_data An array of #cell%s.
+ * @param num_elements Chunk size.
+ * @param extra_data Pointer to an #engine.
+ */
+
+void runner_do_drift_mapper(void *map_data, int num_elements,
+                            void *extra_data) {
+
+  struct engine *e = (struct engine *)extra_data;
+  struct cell *cells = (struct cell *)map_data;
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct cell *c = &cells[ind];
+
+    /* Only drift local particles. */
+    if (c != NULL && c->nodeID == e->nodeID) runner_do_drift(c, e);
+  }
 }
 
 /**
@@ -914,15 +1001,22 @@ void runner_do_kick(struct runner *r, struct cell *c, int timer) {
   struct gpart *restrict gparts = c->gparts;
   const double const_G = r->e->physical_constants->const_newton_G;
 
-  int updated = 0, g_updated = 0;
-  int ti_end_min = max_nr_timesteps, ti_end_max = 0;
+  TIMER_TIC;
 
-  TIMER_TIC
+  /* Anything to do here? */
+  if (c->ti_end_min > ti_current) {
+    c->updated = 0;
+    c->g_updated = 0;
+    return;
+  }
 
 #ifdef TASK_VERBOSE
   OUT;
 #endif
 
+  int updated = 0, g_updated = 0;
+  int ti_end_min = max_nr_timesteps, ti_end_max = 0;
+
   /* No children? */
   if (!c->split) {
 
@@ -1118,13 +1212,15 @@ void *runner_main(void *data) {
       struct cell *ci = t->ci;
       struct cell *cj = t->cj;
       t->rid = r->cpuid;
-      t->last_rid = r->cpuid;
 
       /* Different types of tasks... */
       switch (t->type) {
         case task_type_self:
-          if (t->subtype == task_subtype_density)
-            runner_doself1_density(r, ci);
+          if (t->subtype == task_subtype_density) runner_doself1_density(r, ci);
+#ifdef EXTRA_HYDRO_LOOP
+          else if (t->subtype == task_subtype_gradient)
+            runner_doself1_gradient(r, ci);
+#endif
           else if (t->subtype == task_subtype_force)
             runner_doself2_force(r, ci);
           else if (t->subtype == task_subtype_grav)
@@ -1135,6 +1231,10 @@ void *runner_main(void *data) {
         case task_type_pair:
           if (t->subtype == task_subtype_density)
             runner_dopair1_density(r, ci, cj);
+#ifdef EXTRA_HYDRO_LOOP
+          else if (t->subtype == task_subtype_gradient)
+            runner_dopair1_gradient(r, ci, cj);
+#endif
           else if (t->subtype == task_subtype_force)
             runner_dopair2_force(r, ci, cj);
           else if (t->subtype == task_subtype_grav)
@@ -1148,6 +1248,10 @@ void *runner_main(void *data) {
         case task_type_sub_self:
           if (t->subtype == task_subtype_density)
             runner_dosub_self1_density(r, ci, 1);
+#ifdef EXTRA_HYDRO_LOOP
+          else if (t->subtype == task_subtype_gradient)
+            runner_dosub_self1_gradient(r, ci, 1);
+#endif
           else if (t->subtype == task_subtype_force)
             runner_dosub_self2_force(r, ci, 1);
           else if (t->subtype == task_subtype_grav)
@@ -1158,6 +1262,10 @@ void *runner_main(void *data) {
         case task_type_sub_pair:
           if (t->subtype == task_subtype_density)
             runner_dosub_pair1_density(r, ci, cj, t->flags, 1);
+#ifdef EXTRA_HYDRO_LOOP
+          else if (t->subtype == task_subtype_gradient)
+            runner_dosub_pair1_gradient(r, ci, cj, t->flags, 1);
+#endif
           else if (t->subtype == task_subtype_force)
             runner_dosub_pair2_force(r, ci, cj, t->flags, 1);
           else if (t->subtype == task_subtype_grav)
@@ -1171,15 +1279,18 @@ void *runner_main(void *data) {
         case task_type_ghost:
           runner_do_ghost(r, ci);
           break;
-        case task_type_drift:
-          runner_do_drift(r, ci, 1);
+#ifdef EXTRA_HYDRO_LOOP
+        case task_type_extra_ghost:
+          runner_do_extra_ghost(r, ci);
           break;
+#endif
         case task_type_kick:
           runner_do_kick(r, ci, 1);
           break;
         case task_type_kick_fixdt:
           runner_do_kick_fixdt(r, ci, 1);
           break;
+#ifdef WITH_MPI
         case task_type_send:
           if (t->subtype == task_subtype_tend) {
             free(t->buff);
@@ -1193,6 +1304,7 @@ void *runner_main(void *data) {
             runner_do_recv_cell(r, ci, 1);
           }
           break;
+#endif
         case task_type_grav_mm:
           runner_do_grav_mm(r, t->ci, 1);
           break;
@@ -1207,22 +1319,6 @@ void *runner_main(void *data) {
         case task_type_grav_external:
           runner_do_grav_external(r, t->ci, 1);
           break;
-	case task_type_cooling:
-          runner_do_cooling(r, t->ci, 1);
-          break;
-        case task_type_part_sort:
-          space_do_parts_sort();
-          break;
-        case task_type_gpart_sort:
-          space_do_gparts_sort();
-          break;
-        case task_type_split_cell:
-          space_do_split(e->s, t->ci);
-          break;
-        case task_type_rewait:
-          scheduler_do_rewait((struct task *)t->ci, (struct task *)t->cj,
-                              t->flags, t->rank);
-          break;
         default:
           error("Unknown task type.");
       }
diff --git a/src/runner.h b/src/runner.h
index c1bb57fa9a333abdd69d9ac18f6ce6f8672ff737..be19ab61b997f5730a04fa8c01c0787e8b99a8b2 100644
--- a/src/runner.h
+++ b/src/runner.h
@@ -29,31 +29,32 @@ extern const char runner_flip[27];
 struct cell;
 struct engine;
 
-/* A struct representing a runner's thread and its data. */
+/**
+ * @brief A struct representing a runner's thread and its data.
+ */
 struct runner {
 
-  /* The id of this thread. */
+  /*! The id of this thread. */
   int id;
 
-  /* The thread which it is running. */
+  /*! The actual thread which it is running. */
   pthread_t thread;
 
-  /* The queue to use to get tasks. */
+  /*! The queue to use to get tasks. */
   int cpuid, qid;
 
-  /* The underlying runner. */
+  /*! The engine owing this runner. */
   struct engine *e;
 };
 
 /* Function prototypes. */
 void runner_do_ghost(struct runner *r, struct cell *c);
 void runner_do_sort(struct runner *r, struct cell *c, int flag, int clock);
-void runner_do_gsort(struct runner *r, struct cell *c, int flag, int clock);
 void runner_do_kick(struct runner *r, struct cell *c, int timer);
 void runner_do_kick_fixdt(struct runner *r, struct cell *c, int timer);
-void runner_do_drift(struct runner *r, struct cell *c, int timer);
 void runner_do_init(struct runner *r, struct cell *c, int timer);
 void runner_do_cooling(struct runner *r, struct cell *c, int timer);
 void *runner_main(void *data);
+void runner_do_drift_mapper(void *map_data, int num_elements, void *extra_data);
 
 #endif /* SWIFT_RUNNER_H */
diff --git a/src/runner_doiact.h b/src/runner_doiact.h
index db439671ff5fee56c086444ddaa8268571c80a15..376400926432a9e9b6b9c736260dc3e119c2c64d 100644
--- a/src/runner_doiact.h
+++ b/src/runner_doiact.h
@@ -1739,7 +1739,7 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid,
   if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return;
 
   /* Get the cell dimensions. */
-  const float h = fmin(ci->width[0], fmin(ci->width[1], ci->width[2]));
+  const float h = fminf(ci->width[0], fminf(ci->width[1], ci->width[2]));
 
   /* Get the type of pair if not specified explicitly. */
   // if ( sid < 0 )
@@ -2023,7 +2023,7 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid,
   if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return;
 
   /* Get the cell dimensions. */
-  const float h = fmin(ci->width[0], fmin(ci->width[1], ci->width[2]));
+  const float h = fminf(ci->width[0], fminf(ci->width[1], ci->width[2]));
 
   /* Get the type of pair if not specified explicitly. */
   // if ( sid < 0 )
@@ -2336,7 +2336,7 @@ void DOSUB_SUBSET(struct runner *r, struct cell *ci, struct part *parts,
   else {
 
     /* Get the cell dimensions. */
-    const float h = fmin(ci->width[0], fmin(ci->width[1], ci->width[2]));
+    const float h = fminf(ci->width[0], fminf(ci->width[1], ci->width[2]));
 
     /* Recurse? */
     if (ci->split && cj->split &&
@@ -2862,16 +2862,17 @@ void DOSUB_SUBSET(struct runner *r, struct cell *ci, struct part *parts,
       }
 
       /* Get the sorting index. */
-      int sid = 0;
+      int new_sid = 0;
       for (int k = 0; k < 3; k++)
-        sid =
-            3 * sid + ((cj->loc[k] - ci->loc[k] + shift[k] < 0)
-                           ? 0
-                           : (cj->loc[k] - ci->loc[k] + shift[k] > 0) ? 2 : 1);
-      sid = sortlistID[sid];
+        new_sid = 3 * new_sid +
+                  ((cj->loc[k] - ci->loc[k] + shift[k] < 0)
+                       ? 0
+                       : (cj->loc[k] - ci->loc[k] + shift[k] > 0) ? 2 : 1);
+      new_sid = sortlistID[new_sid];
 
       /* Do any of the cells need to be sorted first? */
-      if (!(cj->sorted & (1 << sid))) runner_do_sort(r, cj, (1 << sid), 1);
+      if (!(cj->sorted & (1 << new_sid)))
+        runner_do_sort(r, cj, (1 << new_sid), 1);
 
       /* Compute the interactions. */
       DOPAIR_SUBSET(r, ci, parts, ind, count, cj);
diff --git a/src/scheduler.c b/src/scheduler.c
index 6a0d886bd5458028c5c05812f10c204bc8946a1a..ef4d19107fb48684ca299f286436a155a6fe0151 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -2,6 +2,7 @@
  * This file is part of SWIFT.
  * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
  *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2016 Peter W. Draper (p.w.draper@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
@@ -59,102 +60,80 @@
 
 void scheduler_addunlock(struct scheduler *s, struct task *ta,
                          struct task *tb) {
-
-  /* Lock the scheduler since re-allocating the unlocks is not
-     thread-safe. */
-  if (lock_lock(&s->lock) != 0) error("Unable to lock scheduler.");
+  /* Get an index at which to store this unlock. */
+  const int ind = atomic_inc(&s->nr_unlocks);
 
   /* Does the buffer need to be grown? */
-  if (s->nr_unlocks == s->size_unlocks) {
+  if (ind == s->size_unlocks) {
+    /* Allocate the new buffer. */
     struct task **unlocks_new;
     int *unlock_ind_new;
-    s->size_unlocks *= 2;
+    const int size_unlocks_new = s->size_unlocks * 2;
     if ((unlocks_new = (struct task **)malloc(sizeof(struct task *) *
-                                              s->size_unlocks)) == NULL ||
-        (unlock_ind_new = (int *)malloc(sizeof(int) * s->size_unlocks)) == NULL)
+                                              size_unlocks_new)) == NULL ||
+        (unlock_ind_new = (int *)malloc(sizeof(int) * size_unlocks_new)) ==
+            NULL)
       error("Failed to re-allocate unlocks.");
-    memcpy(unlocks_new, s->unlocks, sizeof(struct task *) * s->nr_unlocks);
-    memcpy(unlock_ind_new, s->unlock_ind, sizeof(int) * s->nr_unlocks);
+
+    /* Wait for all writes to the old buffer to complete. */
+    while (s->completed_unlock_writes < ind)
+      ;
+
+    /* Copy the buffers. */
+    memcpy(unlocks_new, s->unlocks, sizeof(struct task *) * ind);
+    memcpy(unlock_ind_new, s->unlock_ind, sizeof(int) * ind);
     free(s->unlocks);
     free(s->unlock_ind);
     s->unlocks = unlocks_new;
     s->unlock_ind = unlock_ind_new;
+
+    /* Publish the new buffer size. */
+    s->size_unlocks = size_unlocks_new;
   }
 
+  /* Wait for there to actually be space at my index. */
+  while (ind > s->size_unlocks)
+    ;
+
   /* Write the unlock to the scheduler. */
-  const int ind = atomic_inc(&s->nr_unlocks);
   s->unlocks[ind] = tb;
   s->unlock_ind[ind] = ta - s->tasks;
-
-  /* Release the scheduler. */
-  if (lock_unlock(&s->lock) != 0) error("Unable to unlock scheduler.");
+  atomic_inc(&s->completed_unlock_writes);
 }
 
 /**
- * @brief Split tasks that may be too large.
+ * @brief Split a task if too large.
  *
+ * @param t The #task
  * @param s The #scheduler we are working in.
  */
 
-void scheduler_splittasks(struct scheduler *s) {
+static void scheduler_splittask(struct task *t, struct scheduler *s) {
 
-  const int pts[7][8] = {
+  /* Static constants. */
+  static const int pts[7][8] = {
       {-1, 12, 10, 9, 4, 3, 1, 0},     {-1, -1, 11, 10, 5, 4, 2, 1},
       {-1, -1, -1, 12, 7, 6, 4, 3},    {-1, -1, -1, -1, 8, 7, 5, 4},
       {-1, -1, -1, -1, -1, 12, 10, 9}, {-1, -1, -1, -1, -1, -1, 11, 10},
       {-1, -1, -1, -1, -1, -1, -1, 12}};
-  const float sid_scale[13] = {0.1897, 0.4025, 0.1897, 0.4025, 0.5788,
-                               0.4025, 0.1897, 0.4025, 0.1897, 0.4025,
-                               0.5788, 0.4025, 0.5788};
-
-  /* Loop through the tasks... */
-  int tid = 0, redo = 0;
-  struct task *t_old = NULL;
-  while (1) {
-
-    /* Get a pointer on the task. */
-    struct task *t = t_old;
-    if (redo) {
-      redo = 0;
-    } else {
-      const int ind = atomic_inc(&tid);
-      if (ind < s->nr_tasks)
-        t_old = t = &s->tasks[s->tasks_ind[ind]];
-      else
-        break;
-    }
+  static const float sid_scale[13] = {
+      0.1897f, 0.4025f, 0.1897f, 0.4025f, 0.5788f, 0.4025f, 0.1897f,
+      0.4025f, 0.1897f, 0.4025f, 0.5788f, 0.4025f, 0.5788f};
 
-    /* Skip sorting tasks. */
-    if (t->type == task_type_part_sort) continue;
+  /* Iterate on this task until we're done with it. */
+  int redo = 1;
+  while (redo) {
 
-    if (t->type == task_type_gpart_sort) continue;
+    /* Reset the redo flag. */
+    redo = 0;
 
-    /* Empty task? */
-    if (t->ci == NULL || (t->type == task_type_pair && t->cj == NULL)) {
+    /* Non-splittable task? */
+    if ((t->ci == NULL || (t->type == task_type_pair && t->cj == NULL)) ||
+        ((t->type == task_type_kick) && t->ci->nodeID != s->nodeID) ||
+        ((t->type == task_type_init) && t->ci->nodeID != s->nodeID)) {
       t->type = task_type_none;
       t->skip = 1;
-      continue;
-    }
-
-    /* Non-local kick task? */
-    if ((t->type == task_type_kick) && t->ci->nodeID != s->nodeID) {
-      t->type = task_type_none;
-      t->skip = 1;
-      continue;
-    }
-
-    /* Non-local drift task? */
-    if ((t->type == task_type_drift) && t->ci->nodeID != s->nodeID) {
-      t->type = task_type_none;
-      t->skip = 1;
-      continue;
-    }
-
-    /* Non-local init task? */
-    if ((t->type == task_type_init) && t->ci->nodeID != s->nodeID) {
-      t->type = task_type_none;
-      t->skip = 1;
-      continue;
+      break;
     }
 
     /* Self-interaction? */
@@ -166,7 +145,7 @@ void scheduler_splittasks(struct scheduler *s) {
       /* Foreign task? */
       if (ci->nodeID != s->nodeID) {
         t->skip = 1;
-        continue;
+        break;
       }
 
       /* Is this cell even split? */
@@ -179,36 +158,38 @@ void scheduler_splittasks(struct scheduler *s) {
           /* convert to a self-subtask. */
           t->type = task_type_sub_self;
 
-        }
-
-        /* Otherwise, make tasks explicitly. */
-        else {
+          /* Otherwise, make tasks explicitly. */
+        } else {
 
           /* Take a step back (we're going to recycle the current task)... */
           redo = 1;
 
-          /* Add the self task. */
+          /* Add the self tasks. */
           int first_child = 0;
           while (ci->progeny[first_child] == NULL) first_child++;
           t->ci = ci->progeny[first_child];
           for (int k = first_child + 1; k < 8; k++)
             if (ci->progeny[k] != NULL)
-              scheduler_addtask(s, task_type_self, t->subtype, 0, 0,
-                                ci->progeny[k], NULL, 0);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_self, t->subtype, 0, 0,
+                                    ci->progeny[k], NULL, 0),
+                  s);
 
           /* Make a task for each pair of progeny. */
           for (int j = 0; j < 8; j++)
             if (ci->progeny[j] != NULL)
               for (int k = j + 1; k < 8; k++)
                 if (ci->progeny[k] != NULL)
-                  scheduler_addtask(s, task_type_pair, t->subtype, pts[j][k], 0,
-                                    ci->progeny[j], ci->progeny[k], 0);
+                  scheduler_splittask(
+                      scheduler_addtask(s, task_type_pair, t->subtype,
+                                        pts[j][k], 0, ci->progeny[j],
+                                        ci->progeny[k], 0),
+                      s);
         }
       }
-    }
 
-    /* Hydro Pair interaction? */
-    else if (t->type == task_type_pair && t->subtype != task_subtype_grav) {
+      /* Pair interaction? */
+    } else if (t->type == task_type_pair && t->subtype != task_subtype_grav) {
 
       /* Get a handle on the cells involved. */
       struct cell *ci = t->ci;
@@ -219,7 +200,7 @@ void scheduler_splittasks(struct scheduler *s) {
       /* Foreign task? */
       if (ci->nodeID != s->nodeID && cj->nodeID != s->nodeID) {
         t->skip = 1;
-        continue;
+        break;
       }
 
       /* Get the sort ID, use space_getsid and not t->flags
@@ -234,16 +215,14 @@ void scheduler_splittasks(struct scheduler *s) {
 
         /* Replace by a single sub-task? */
         if (scheduler_dosub &&
-            ci->count * cj->count * sid_scale[sid] < space_subsize &&
+            ci->count * sid_scale[sid] < space_subsize / cj->count &&
             sid != 0 && sid != 2 && sid != 6 && sid != 8) {
 
           /* Make this task a sub task. */
           t->type = task_type_sub_pair;
 
-        }
-
-        /* Otherwise, split it. */
-        else {
+          /* Otherwise, split it. */
+        } else {
 
           /* Take a step back (we're going to recycle the current task)... */
           redo = 1;
@@ -262,12 +241,18 @@ void scheduler_splittasks(struct scheduler *s) {
               t->cj = cj->progeny[0];
               t->flags = 1;
               t->tight = 1;
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[7], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[6], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[7], cj->progeny[0], 1);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
+                                    ci->progeny[7], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
+                                    ci->progeny[6], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
+                                    ci->progeny[7], cj->progeny[0], 1),
+                  s);
               break;
 
             case 2: /* (  1 ,  1 , -1 ) */
@@ -282,12 +267,18 @@ void scheduler_splittasks(struct scheduler *s) {
               t->cj = cj->progeny[0];
               t->flags = 3;
               t->tight = 1;
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[7], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[5], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[7], cj->progeny[0], 1);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
+                                    ci->progeny[7], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
+                                    ci->progeny[5], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
+                                    ci->progeny[7], cj->progeny[0], 1),
+                  s);
               break;
 
             case 4: /* (  1 ,  0 ,  0 ) */
@@ -295,36 +286,66 @@ void scheduler_splittasks(struct scheduler *s) {
               t->cj = cj->progeny[0];
               t->flags = 4;
               t->tight = 1;
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[5], cj->progeny[0], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[6], cj->progeny[0], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[7], cj->progeny[0], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[4], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 4, 0,
-                                    ci->progeny[5], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[6], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[7], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[4], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[5], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 4, 0,
-                                    ci->progeny[6], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[7], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[4], cj->progeny[3], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[5], cj->progeny[3], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[6], cj->progeny[3], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 4, 0,
-                                    ci->progeny[7], cj->progeny[3], 1);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
+                                    ci->progeny[5], cj->progeny[0], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
+                                    ci->progeny[6], cj->progeny[0], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
+                                    ci->progeny[7], cj->progeny[0], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
+                                    ci->progeny[4], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 4, 0,
+                                    ci->progeny[5], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
+                                    ci->progeny[6], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
+                                    ci->progeny[7], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
+                                    ci->progeny[4], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
+                                    ci->progeny[5], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 4, 0,
+                                    ci->progeny[6], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
+                                    ci->progeny[7], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
+                                    ci->progeny[4], cj->progeny[3], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
+                                    ci->progeny[5], cj->progeny[3], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
+                                    ci->progeny[6], cj->progeny[3], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 4, 0,
+                                    ci->progeny[7], cj->progeny[3], 1),
+                  s);
               break;
 
             case 5: /* (  1 ,  0 , -1 ) */
@@ -332,12 +353,18 @@ void scheduler_splittasks(struct scheduler *s) {
               t->cj = cj->progeny[1];
               t->flags = 5;
               t->tight = 1;
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[6], cj->progeny[3], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[4], cj->progeny[3], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[6], cj->progeny[1], 1);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
+                                    ci->progeny[6], cj->progeny[3], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
+                                    ci->progeny[4], cj->progeny[3], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
+                                    ci->progeny[6], cj->progeny[1], 1),
+                  s);
               break;
 
             case 6: /* (  1 , -1 ,  1 ) */
@@ -352,12 +379,18 @@ void scheduler_splittasks(struct scheduler *s) {
               t->cj = cj->progeny[3];
               t->flags = 6;
               t->tight = 1;
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[5], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[4], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[5], cj->progeny[3], 1);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
+                                    ci->progeny[5], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
+                                    ci->progeny[4], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
+                                    ci->progeny[5], cj->progeny[3], 1),
+                  s);
               break;
 
             case 8: /* (  1 , -1 , -1 ) */
@@ -372,12 +405,18 @@ void scheduler_splittasks(struct scheduler *s) {
               t->cj = cj->progeny[0];
               t->flags = 9;
               t->tight = 1;
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[7], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[3], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[7], cj->progeny[0], 1);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
+                                    ci->progeny[7], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
+                                    ci->progeny[3], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
+                                    ci->progeny[7], cj->progeny[0], 1),
+                  s);
               break;
 
             case 10: /* (  0 ,  1 ,  0 ) */
@@ -385,36 +424,66 @@ void scheduler_splittasks(struct scheduler *s) {
               t->cj = cj->progeny[0];
               t->flags = 10;
               t->tight = 1;
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[3], cj->progeny[0], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[6], cj->progeny[0], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[7], cj->progeny[0], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[2], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 10, 0,
-                                    ci->progeny[3], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[6], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[7], cj->progeny[1], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[2], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[3], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 10, 0,
-                                    ci->progeny[6], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[7], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[2], cj->progeny[5], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[3], cj->progeny[5], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[6], cj->progeny[5], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 10, 0,
-                                    ci->progeny[7], cj->progeny[5], 1);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
+                                    ci->progeny[3], cj->progeny[0], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
+                                    ci->progeny[6], cj->progeny[0], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
+                                    ci->progeny[7], cj->progeny[0], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
+                                    ci->progeny[2], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 10, 0,
+                                    ci->progeny[3], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
+                                    ci->progeny[6], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
+                                    ci->progeny[7], cj->progeny[1], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
+                                    ci->progeny[2], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
+                                    ci->progeny[3], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 10, 0,
+                                    ci->progeny[6], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
+                                    ci->progeny[7], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
+                                    ci->progeny[2], cj->progeny[5], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
+                                    ci->progeny[3], cj->progeny[5], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
+                                    ci->progeny[6], cj->progeny[5], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 10, 0,
+                                    ci->progeny[7], cj->progeny[5], 1),
+                  s);
               break;
 
             case 11: /* (  0 ,  1 , -1 ) */
@@ -422,12 +491,18 @@ void scheduler_splittasks(struct scheduler *s) {
               t->cj = cj->progeny[1];
               t->flags = 11;
               t->tight = 1;
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[6], cj->progeny[5], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[2], cj->progeny[5], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[6], cj->progeny[1], 1);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
+                                    ci->progeny[6], cj->progeny[5], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
+                                    ci->progeny[2], cj->progeny[5], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
+                                    ci->progeny[6], cj->progeny[1], 1),
+                  s);
               break;
 
             case 12: /* (  0 ,  0 ,  1 ) */
@@ -435,45 +510,73 @@ void scheduler_splittasks(struct scheduler *s) {
               t->cj = cj->progeny[0];
               t->flags = 12;
               t->tight = 1;
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[3], cj->progeny[0], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[5], cj->progeny[0], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[7], cj->progeny[0], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[1], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 12, 0,
-                                    ci->progeny[3], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[5], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[7], cj->progeny[2], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[1], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[3], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 12, 0,
-                                    ci->progeny[5], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[7], cj->progeny[4], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[1], cj->progeny[6], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[3], cj->progeny[6], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[5], cj->progeny[6], 1);
-              t = scheduler_addtask(s, task_type_pair, t->subtype, 12, 0,
-                                    ci->progeny[7], cj->progeny[6], 1);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
+                                    ci->progeny[3], cj->progeny[0], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
+                                    ci->progeny[5], cj->progeny[0], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
+                                    ci->progeny[7], cj->progeny[0], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
+                                    ci->progeny[1], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 12, 0,
+                                    ci->progeny[3], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
+                                    ci->progeny[5], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
+                                    ci->progeny[7], cj->progeny[2], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
+                                    ci->progeny[1], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
+                                    ci->progeny[3], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 12, 0,
+                                    ci->progeny[5], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
+                                    ci->progeny[7], cj->progeny[4], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
+                                    ci->progeny[1], cj->progeny[6], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
+                                    ci->progeny[3], cj->progeny[6], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
+                                    ci->progeny[5], cj->progeny[6], 1),
+                  s);
+              scheduler_splittask(
+                  scheduler_addtask(s, task_type_pair, t->subtype, 12, 0,
+                                    ci->progeny[7], cj->progeny[6], 1),
+                  s);
               break;
-          }
+          } /* switch(sid) */
         }
 
-      } /* split this task? */
-
-      /* Otherwise, break it up if it is too large? */
-      else if (scheduler_doforcesplit && ci->split && cj->split &&
-               (ci->count > space_maxsize / cj->count)) {
+        /* Otherwise, break it up if it is too large? */
+      } else if (scheduler_doforcesplit && ci->split && cj->split &&
+                 (ci->count > space_maxsize / cj->count)) {
 
         // message( "force splitting pair with %i and %i parts." , ci->count ,
         // cj->count );
@@ -485,34 +588,34 @@ void scheduler_splittasks(struct scheduler *s) {
           if (ci->progeny[j] != NULL)
             for (int k = 0; k < 8; k++)
               if (cj->progeny[k] != NULL) {
-                t = scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
+                struct task *tl =
+                    scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
                                       ci->progeny[j], cj->progeny[k], 0);
-                t->flags = space_getsid(s->space, &t->ci, &t->cj, shift);
+                scheduler_splittask(tl, s);
+                tl->flags = space_getsid(s->space, &t->ci, &t->cj, shift);
               }
 
-      }
-
-      /* Otherwise, if not spilt, stitch-up the sorting. */
-      else {
+        /* Otherwise, if not spilt, stitch-up the sorting. */
+      } else {
 
         /* Create the sort for ci. */
-        // lock_lock( &ci->lock );
+        lock_lock(&ci->lock);
         if (ci->sorts == NULL)
           ci->sorts = scheduler_addtask(s, task_type_sort, task_subtype_none,
                                         1 << sid, 0, ci, NULL, 0);
         else
           ci->sorts->flags |= (1 << sid);
-        // lock_unlock_blind( &ci->lock );
+        lock_unlock_blind(&ci->lock);
         scheduler_addunlock(s, ci->sorts, t);
 
         /* Create the sort for cj. */
-        // lock_lock( &cj->lock );
+        lock_lock(&cj->lock);
         if (cj->sorts == NULL)
           cj->sorts = scheduler_addtask(s, task_type_sort, task_subtype_none,
                                         1 << sid, 0, cj, NULL, 0);
         else
           cj->sorts->flags |= (1 << sid);
-        // lock_unlock_blind( &cj->lock );
+        lock_unlock_blind(&cj->lock);
         scheduler_addunlock(s, cj->sorts, t);
       }
 
@@ -528,8 +631,35 @@ void scheduler_splittasks(struct scheduler *s) {
       if (ci->gcount == 0) t->type = task_type_none;
 
     } /* gravity interaction? */
+  }   /* iterate over the current task. */
+}
+
+/**
+ * @brief Mapper function to split tasks that may be too large.
+ *
+ * @param map_data the tasks to process
+ * @param num_elements the number of tasks.
+ * @param extra_data The #scheduler we are working in.
+ */
 
-  } /* loop over all tasks. */
+void scheduler_splittasks_mapper(void *map_data, int num_elements,
+                                 void *extra_data) {
+
+  /* Extract the parameters. */
+  struct scheduler *s = (struct scheduler *)extra_data;
+  struct task *tasks = (struct task *)map_data;
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct task *t = &tasks[ind];
+    scheduler_splittask(t, s);
+  }
+}
+
+void scheduler_splittasks(struct scheduler *s) {
+
+  /* Call the mapper on each current task. */
+  threadpool_map(s->threadpool, scheduler_splittasks_mapper, s->tasks,
+                 s->nr_tasks, sizeof(struct task), 1000, s);
 }
 
 /**
@@ -574,7 +704,6 @@ struct task *scheduler_addtask(struct scheduler *s, enum task_types type,
   t->toc = 0;
   t->nr_unlock_tasks = 0;
   t->rid = -1;
-  t->last_rid = -1;
 
   /* Add an index for it. */
   // lock_lock( &s->lock );
@@ -626,17 +755,25 @@ void scheduler_set_unlocks(struct scheduler *s) {
   offsets[0] = 0;
   for (int k = 1; k < s->nr_tasks; k++)
     offsets[k] = offsets[k - 1] + counts[k - 1];
-  for (int k = 0; k < s->nr_tasks; k++)
-    for (int j = offsets[k]; j < offsets[k + 1]; j++) s->unlock_ind[j] = k;
 
   /* Set the unlocks in the tasks. */
   for (int k = 0; k < s->nr_tasks; k++) {
     struct task *t = &s->tasks[k];
     t->nr_unlock_tasks = counts[k];
     t->unlock_tasks = &s->unlocks[offsets[k]];
-    for (int j = offsets[k]; j < offsets[k + 1]; j++) s->unlock_ind[j] = k;
   }
 
+  /* Verify that there are no duplicate unlocks. */
+  /* for (int k = 0; k < s->nr_tasks; k++) {
+    struct task *t = &s->tasks[k];
+    for (int i = 0; i < t->nr_unlock_tasks; i++) {
+      for (int j = i + 1; j < t->nr_unlock_tasks; j++) {
+        if (t->unlock_tasks[i] == t->unlock_tasks[j])
+          error("duplicate unlock!");
+      }
+    }
+  } */
+
   /* Clean up. */
   free(counts);
   free(offsets);
@@ -655,42 +792,48 @@ void scheduler_ranktasks(struct scheduler *s) {
   const int nr_tasks = s->nr_tasks;
 
   /* Run through the tasks and get all the waits right. */
-  for (int k = 0; k < nr_tasks; k++) {
-    tid[k] = k;
-    for (int j = 0; j < tasks[k].nr_unlock_tasks; j++)
-      tasks[k].unlock_tasks[j]->wait += 1;
+  for (int i = 0; i < nr_tasks; i++) {
+    struct task *t = &tasks[i];
+
+    // Increment the waits of the dependances
+    for (int k = 0; k < t->nr_unlock_tasks; k++) {
+      t->unlock_tasks[k]->wait++;
+    }
   }
 
+  /* Load the tids of tasks with no waits. */
+  int left = 0;
+  for (int k = 0; k < nr_tasks; k++)
+    if (tasks[k].wait == 0) {
+      tid[left] = k;
+      left += 1;
+    }
+
   /* Main loop. */
-  for (int j = 0, rank = 0, left = 0; left < nr_tasks; rank++) {
-
-    /* Load the tids of tasks with no waits. */
-    for (int k = left; k < nr_tasks; k++)
-      if (tasks[tid[k]].wait == 0) {
-        int temp = tid[j];
-        tid[j] = tid[k];
-        tid[k] = temp;
-        j += 1;
-      }
+  for (int j = 0, rank = 0; left < nr_tasks; rank++) {
 
     /* Did we get anything? */
     if (j == left) error("Unsatisfiable task dependencies detected.");
+    const int left_old = left;
 
     /* Unlock the next layer of tasks. */
-    for (int i = left; i < j; i++) {
-      struct task *t = &tasks[tid[i]];
+    for (; j < left_old; j++) {
+      struct task *t = &tasks[tid[j]];
       t->rank = rank;
-      tid[i] = t - tasks;
-      if (tid[i] >= nr_tasks) error("Task index overshoot.");
       /* message( "task %i of type %s has rank %i." , i ,
           (t->type == task_type_self) ? "self" : (t->type == task_type_pair) ?
          "pair" : "sort" , rank ); */
-      for (int k = 0; k < t->nr_unlock_tasks; k++)
-        t->unlock_tasks[k]->wait -= 1;
+      for (int k = 0; k < t->nr_unlock_tasks; k++) {
+        struct task *u = t->unlock_tasks[k];
+        if (--u->wait == 0) {
+          tid[left] = u - tasks;
+          left += 1;
+        }
+      }
     }
 
-    /* The new left (no, not tony). */
-    left = j;
+    /* Move back to the old left (like Sanders). */
+    j = left_old;
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -724,9 +867,6 @@ void scheduler_reset(struct scheduler *s, int size) {
       error("Failed to allocate task lists.");
   }
 
-  /* Reset the task data. */
-  bzero(s->tasks, sizeof(struct task) * size);
-
   /* Reset the counters. */
   s->size = size;
   s->nr_tasks = 0;
@@ -735,6 +875,7 @@ void scheduler_reset(struct scheduler *s, int size) {
   s->mask = 0;
   s->submask = 0;
   s->nr_unlocks = 0;
+  s->completed_unlock_writes = 0;
 
   /* Set the task pointers in the queues. */
   for (int k = 0; k < s->nr_queues; k++) s->queues[k].tasks = s->tasks;
@@ -810,9 +951,6 @@ void scheduler_reweight(struct scheduler *s) {
         case task_type_kick:
           t->weight += wscale * t->ci->count;
           break;
-        case task_type_drift:
-          t->weight += wscale * t->ci->count;
-          break;
         case task_type_init:
           t->weight += wscale * t->ci->count;
           break;
@@ -832,6 +970,52 @@ void scheduler_reweight(struct scheduler *s) {
   message( "task weights are in [ %i , %i ]." , min , max ); */
 }
 
+/**
+ * @brief #threadpool_map function which runs through the task
+ *        graph and re-computes the task wait counters.
+ */
+
+void scheduler_rewait_mapper(void *map_data, int num_elements,
+                             void *extra_data) {
+
+  struct scheduler *s = (struct scheduler *)extra_data;
+  struct task *tasks = (struct task *)map_data;
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct task *t = &tasks[ind];
+
+    if (t->skip || !((1 << t->type) & s->mask) ||
+        !((1 << t->subtype) & s->submask))
+      continue;
+
+    /* Skip sort tasks that have already been performed */
+    if (t->type == task_type_sort && t->flags == 0) {
+      error("Empty sort task encountered.");
+    }
+
+    /* Sets the waits of the dependances */
+    for (int k = 0; k < t->nr_unlock_tasks; k++) {
+      struct task *u = t->unlock_tasks[k];
+      atomic_inc(&u->wait);
+    }
+  }
+}
+
+void scheduler_enqueue_mapper(void *map_data, int num_elements,
+                              void *extra_data) {
+  struct scheduler *s = (struct scheduler *)extra_data;
+  const int *tid = (int *)map_data;
+  struct task *tasks = s->tasks;
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct task *t = &tasks[tid[ind]];
+    if (atomic_dec(&t->wait) == 1 && !t->skip && ((1 << t->type) & s->mask) &&
+        ((1 << t->subtype) & s->submask)) {
+      scheduler_enqueue(s, t);
+    }
+  }
+  pthread_cond_broadcast(&s->sleep_cond);
+}
+
 /**
  * @brief Start the scheduler, i.e. fill the queues with ready tasks.
  *
@@ -843,88 +1027,33 @@ void scheduler_reweight(struct scheduler *s) {
 void scheduler_start(struct scheduler *s, unsigned int mask,
                      unsigned int submask) {
 
-  const int nr_tasks = s->nr_tasks;
-  int *tid = s->tasks_ind;
-  struct task *tasks = s->tasks;
-  // ticks tic;
+  // ticks tic = getticks();
 
   /* Store the masks */
-  s->mask = mask | (1 << task_type_rewait);
+  s->mask = mask;
   s->submask = submask | (1 << task_subtype_none);
 
   /* Clear all the waits and rids. */
-  // ticks tic = getticks();
   for (int k = 0; k < s->nr_tasks; k++) {
     s->tasks[k].wait = 1;
     s->tasks[k].rid = -1;
   }
-  // message( "waiting tasks took %.3f %s." ,
-  // clocks_from_ticks(getticks() - tic), clocks_getunit() );
-
-  /* Enqueue a set of extraenous tasks to set the task waits. */
-  struct task *rewait_tasks = &s->tasks[s->nr_tasks];
-  const int num_rewait_tasks = s->nr_queues > s->size - s->nr_tasks
-                                   ? s->size - s->nr_tasks
-                                   : s->nr_queues;
-
-  /* Remember that engine_launch may fiddle with this value. */
-  const int waiting_old = s->waiting;
-
-  /* We are going to use the task structure in a modified way to pass
-     information to the task. Don't do this at home !
-     - ci and cj will give the range of tasks to which the waits will be applied
-     - the flags will be used to transfer the mask
-     - the rank will be used to transfer the submask
-     - the rest is unused.
-  */
-  for (int k = 0; k < num_rewait_tasks; k++) {
-    rewait_tasks[k].type = task_type_rewait;
-    rewait_tasks[k].ci = (struct cell *)&s->tasks[k * nr_tasks / s->nr_queues];
-    rewait_tasks[k].cj =
-        (struct cell *)&s->tasks[(k + 1) * nr_tasks / s->nr_queues];
-    rewait_tasks[k].flags = s->mask;
-    rewait_tasks[k].rank = s->submask;
-    rewait_tasks[k].skip = 0;
-    rewait_tasks[k].wait = 0;
-    rewait_tasks[k].rid = -1;
-    rewait_tasks[k].weight = 1;
-    rewait_tasks[k].implicit = 0;
-    rewait_tasks[k].nr_unlock_tasks = 0;
-    scheduler_enqueue(s, &rewait_tasks[k]);
-    pthread_cond_broadcast(&s->sleep_cond);
-  }
-
-  /* Wait for the rewait tasks to have executed. */
-  pthread_mutex_lock(&s->sleep_mutex);
-  pthread_cond_broadcast(&s->sleep_cond);
-  while (s->waiting > waiting_old) {
-    pthread_cond_wait(&s->sleep_cond, &s->sleep_mutex);
-  }
-  pthread_mutex_unlock(&s->sleep_mutex);
-  /* message("waiting tasks took %.3f %s.",
-     clocks_from_ticks(getticks() - tic), clocks_getunit());*/
 
-  s->mask = mask;
-  s->submask = submask | (1 << task_subtype_none);
+  /* Re-wait the tasks. */
+  threadpool_map(s->threadpool, scheduler_rewait_mapper, s->tasks, s->nr_tasks,
+                 sizeof(struct task), 1000, s);
 
   /* Loop over the tasks and enqueue whoever is ready. */
-  // tic = getticks();
-  for (int k = 0; k < s->nr_tasks; k++) {
-    struct task *t = &tasks[tid[k]];
-    if (atomic_dec(&t->wait) == 1 && ((1 << t->type) & s->mask) &&
-        ((1 << t->subtype) & s->submask) && !t->skip) {
-      scheduler_enqueue(s, t);
-      pthread_cond_broadcast(&s->sleep_cond);
-    }
-  }
+  threadpool_map(s->threadpool, scheduler_enqueue_mapper, s->tasks_ind,
+                 s->nr_tasks, sizeof(int), 1000, s);
 
   /* To be safe, fire of one last sleep_cond in a safe way. */
   pthread_mutex_lock(&s->sleep_mutex);
   pthread_cond_broadcast(&s->sleep_cond);
   pthread_mutex_unlock(&s->sleep_mutex);
 
-  // message( "enqueueing tasks took %.3f %s." ,
-  // clocks_from_ticks( getticks() - tic ), clocks_getunit());
+  /* message("enqueueing tasks took %.3f %s." ,
+          clocks_from_ticks( getticks() - tic ), clocks_getunit()); */
 }
 
 /**
@@ -952,7 +1081,6 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
   if (t->implicit) {
     for (int j = 0; j < t->nr_unlock_tasks; j++) {
       struct task *t2 = t->unlock_tasks[j];
-
       if (atomic_dec(&t2->wait) == 1) scheduler_enqueue(s, t2);
     }
   }
@@ -971,7 +1099,6 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
       case task_type_sort:
       case task_type_ghost:
       case task_type_kick:
-      case task_type_drift:
       case task_type_init:
         qid = t->ci->super->owner;
         break;
@@ -1214,10 +1341,12 @@ struct task *scheduler_gettask(struct scheduler *s, int qid,
  * @param nr_queues The number of queues in this scheduler.
  * @param flags The #scheduler flags.
  * @param nodeID The MPI rank
+ * @param tp Parallel processing threadpool.
  */
 
 void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks,
-                    int nr_queues, unsigned int flags, int nodeID) {
+                    int nr_queues, unsigned int flags, int nodeID,
+                    struct threadpool *tp) {
 
   /* Init the lock. */
   lock_init(&s->lock);
@@ -1249,6 +1378,7 @@ void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks,
   s->flags = flags;
   s->space = space;
   s->nodeID = nodeID;
+  s->threadpool = tp;
 
   /* Init the tasks array. */
   s->size = 0;
@@ -1282,34 +1412,6 @@ void scheduler_print_tasks(const struct scheduler *s, const char *fileName) {
   fclose(file);
 }
 
-/**
- * @brief Sets the waits of the dependants of a range of task
- *
- * @param t_begin Beginning of the #task range
- * @param t_end End of the #task range
- * @param mask The scheduler task mask
- * @param submask The scheduler subtask mask
- */
-void scheduler_do_rewait(struct task *t_begin, struct task *t_end,
-                         unsigned int mask, unsigned int submask) {
-  for (struct task *t2 = t_begin; t2 != t_end; t2++) {
-
-    if (t2->skip) continue;
-
-    /* Skip tasks not in the mask */
-    if (!((1 << t2->type) & mask) || !((1 << t2->subtype) & submask)) continue;
-
-    /* Skip sort tasks that have already been performed */
-    if (t2->type == task_type_sort && t2->flags == 0) continue;
-
-    /* Sets the waits of the dependances */
-    for (int k = 0; k < t2->nr_unlock_tasks; k++) {
-      struct task *t3 = t2->unlock_tasks[k];
-      atomic_inc(&t3->wait);
-    }
-  }
-}
-
 /**
  * @brief Frees up the memory allocated for this #scheduler
  */
diff --git a/src/scheduler.h b/src/scheduler.h
index fcff27abfe7eaddead3e7c0f67ae544907ce6ce6..3e51197de148ee8f0bf3090551568715d3000e98 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -36,6 +36,7 @@
 #include "lock.h"
 #include "queue.h"
 #include "task.h"
+#include "threadpool.h"
 
 /* Some constants. */
 #define scheduler_maxwait 3
@@ -83,9 +84,9 @@ struct scheduler {
   int *tasks_ind;
 
   /* The task unlocks. */
-  struct task **unlocks;
-  int *unlock_ind;
-  int nr_unlocks, size_unlocks;
+  struct task **volatile unlocks;
+  int *volatile unlock_ind;
+  volatile int nr_unlocks, size_unlocks, completed_unlock_writes;
 
   /* Lock for this scheduler. */
   swift_lock_type lock;
@@ -97,13 +98,17 @@ struct scheduler {
   /* The space associated with this scheduler. */
   struct space *space;
 
+  /* Threadpool to use internally for mundane parallel work. */
+  struct threadpool *threadpool;
+
   /* The node we are working on. */
   int nodeID;
 };
 
 /* Function prototypes. */
 void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks,
-                    int nr_queues, unsigned int flags, int nodeID);
+                    int nr_queues, unsigned int flags, int nodeID,
+                    struct threadpool *tp);
 struct task *scheduler_gettask(struct scheduler *s, int qid,
                                const struct task *prev);
 void scheduler_enqueue(struct scheduler *s, struct task *t);
@@ -122,8 +127,6 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta, struct task *tb);
 void scheduler_set_unlocks(struct scheduler *s);
 void scheduler_dump_queue(struct scheduler *s);
 void scheduler_print_tasks(const struct scheduler *s, const char *fileName);
-void scheduler_do_rewait(struct task *t_begin, struct task *t_end,
-                         unsigned int mask, unsigned int submask);
 void scheduler_clean(struct scheduler *s);
 
 #endif /* SWIFT_SCHEDULER_H */
diff --git a/src/serial_io.c b/src/serial_io.c
index c981e5e31db1868e6cd2590c6bb36d51282f94c7..6e26be1a33fbc2c74ae1b8f7af2b83db285c962e 100644
--- a/src/serial_io.c
+++ b/src/serial_io.c
@@ -37,6 +37,7 @@
 
 /* Local includes. */
 #include "common_io.h"
+#include "dimension.h"
 #include "engine.h"
 #include "error.h"
 #include "gravity_io.h"
@@ -176,9 +177,10 @@ void readArray(hid_t grp, const struct io_props props, size_t N,
  * Routines writing an output file
  *-----------------------------------------------------------------------------*/
 
-void prepareArray(hid_t grp, char* fileName, FILE* xmfFile,
+void prepareArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile,
                   char* partTypeGroupName, const struct io_props props,
-                  long long N_total, const struct UnitSystem* internal_units,
+                  unsigned long long N_total,
+                  const struct UnitSystem* internal_units,
                   const struct UnitSystem* snapshot_units) {
 
   /* Create data space */
@@ -219,15 +221,17 @@ void prepareArray(hid_t grp, char* fileName, FILE* xmfFile,
   /* Set chunk size */
   h_err = H5Pset_chunk(h_prop, rank, chunk_shape);
   if (h_err < 0) {
-    error("Error while setting chunk size (%lld, %lld) for field '%s'.",
+    error("Error while setting chunk size (%llu, %llu) for field '%s'.",
           chunk_shape[0], chunk_shape[1], props.name);
   }
 
   /* Impose data compression */
-  h_err = H5Pset_deflate(h_prop, 4);
-  if (h_err < 0) {
-    error("Error while setting compression options for field '%s'.",
-          props.name);
+  if (e->snapshotCompression > 0) {
+    h_err = H5Pset_deflate(h_prop, e->snapshotCompression);
+    if (h_err < 0) {
+      error("Error while setting compression options for field '%s'.",
+            props.name);
+    }
   }
 
   /* Create dataset */
@@ -291,7 +295,7 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile,
 
   /* Prepare the arrays in the file */
   if (mpi_rank == 0)
-    prepareArray(grp, fileName, xmfFile, partTypeGroupName, props, N_total,
+    prepareArray(e, grp, fileName, xmfFile, partTypeGroupName, props, N_total,
                  internal_units, snapshot_units);
 
   /* Allocate temporary buffer */
@@ -426,6 +430,7 @@ void read_ic_serial(char* fileName, const struct UnitSystem* internal_units,
   size_t N[NUM_PARTICLE_TYPES] = {0};
   long long N_total[NUM_PARTICLE_TYPES] = {0};
   long long offset[NUM_PARTICLE_TYPES] = {0};
+  int dimension = 3; /* Assume 3D if nothing is specified */
   struct UnitSystem* ic_units = malloc(sizeof(struct UnitSystem));
 
   /* First read some information about the content */
@@ -453,6 +458,15 @@ void read_ic_serial(char* fileName, const struct UnitSystem* internal_units,
     h_grp = H5Gopen(h_file, "/Header", H5P_DEFAULT);
     if (h_grp < 0) error("Error while opening file header\n");
 
+    /* Check the dimensionality of the ICs (if the info exists) */
+    const hid_t hid_dim = H5Aexists(h_grp, "Dimension");
+    if (hid_dim < 0)
+      error("Error while testing existance of 'Dimension' attribute");
+    if (hid_dim > 0) readAttribute(h_grp, "Dimension", INT, &dimension);
+    if (dimension != hydro_dimension)
+      error("ICs dimensionality (%dD) does not match code dimensionality (%dD)",
+            dimension, (int)hydro_dimension);
+
     /* Read the relevant information and print status */
     int flag_entropy_temp[6];
     readAttribute(h_grp, "Flag_Entropy_ICs", INT, flag_entropy_temp);
@@ -577,26 +591,18 @@ void read_ic_serial(char* fileName, const struct UnitSystem* internal_units,
 
         int num_fields = 0;
         struct io_props list[100];
-        size_t N = 0;
+        size_t Nparticles = 0;
 
         /* Read particle fields into the particle structure */
         switch (ptype) {
 
           case GAS:
-            /* if (!dry_run) */
-            /*   hydro_read_particles(h_grp, N[ptype], N_total[ptype], */
-            /*                        offset[ptype], *parts); */
-            /* break; */
-            N = *Ngas;
+            Nparticles = *Ngas;
             hydro_read_particles(*parts, list, &num_fields);
             break;
 
           case DM:
-            /* if (!dry_run) */
-            /*   darkmatter_read_particles(h_grp, N[ptype], N_total[ptype], */
-            /*                             offset[ptype], *gparts); */
-            /* break; */
-            N = Ndm;
+            Nparticles = Ndm;
             darkmatter_read_particles(*gparts, list, &num_fields);
             break;
 
@@ -608,7 +614,7 @@ void read_ic_serial(char* fileName, const struct UnitSystem* internal_units,
         /* Read everything */
         if (!dry_run)
           for (int i = 0; i < num_fields; ++i)
-            readArray(h_grp, list[i], N, N_total[ptype], offset[ptype],
+            readArray(h_grp, list[i], Nparticles, N_total[ptype], offset[ptype],
                       internal_units, ic_units);
 
         /* Close particle group */
@@ -733,6 +739,8 @@ void write_output_serial(struct engine* e, const char* baseName,
     writeAttribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3);
     double dblTime = e->time;
     writeAttribute(h_grp, "Time", DOUBLE, &dblTime, 1);
+    int dimension = (int)hydro_dimension;
+    writeAttribute(h_grp, "Dimension", INT, &dimension, 1);
 
     /* GADGET-2 legacy values */
     /* Number of particles of each type */
@@ -873,13 +881,13 @@ void write_output_serial(struct engine* e, const char* baseName,
 
         int num_fields = 0;
         struct io_props list[100];
-        size_t N = 0;
+        size_t Nparticles = 0;
 
         /* Write particle fields from the particle structure */
         switch (ptype) {
 
           case GAS:
-            N = Ngas;
+            Nparticles = Ngas;
             hydro_write_particles(parts, list, &num_fields);
             break;
 
@@ -894,7 +902,7 @@ void write_output_serial(struct engine* e, const char* baseName,
             collect_dm_gparts(gparts, Ntot, dmparts, Ndm);
 
             /* Write DM particles */
-            N = Ndm;
+            Nparticles = Ndm;
             darkmatter_write_particles(dmparts, list, &num_fields);
 
             break;
@@ -905,9 +913,9 @@ void write_output_serial(struct engine* e, const char* baseName,
 
         /* Write everything */
         for (int i = 0; i < num_fields; ++i)
-          writeArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i], N,
-                     N_total[ptype], mpi_rank, offset[ptype], internal_units,
-                     snapshot_units);
+          writeArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i],
+                     Nparticles, N_total[ptype], mpi_rank, offset[ptype],
+                     internal_units, snapshot_units);
 
         /* Free temporary array */
         free(dmparts);
diff --git a/src/single_io.c b/src/single_io.c
index 93faab6717fb8c136559511ada2c928f185f9f42..6cb7e830209b0d58919fe6f529f675b4c611a51d 100644
--- a/src/single_io.c
+++ b/src/single_io.c
@@ -36,6 +36,7 @@
 
 /* Local includes. */
 #include "common_io.h"
+#include "dimension.h"
 #include "engine.h"
 #include "error.h"
 #include "gravity_io.h"
@@ -256,15 +257,17 @@ void writeArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile,
   /* Set chunk size */
   h_err = H5Pset_chunk(h_prop, rank, chunk_shape);
   if (h_err < 0) {
-    error("Error while setting chunk size (%lld, %lld) for field '%s'.",
+    error("Error while setting chunk size (%llu, %llu) for field '%s'.",
           chunk_shape[0], chunk_shape[1], props.name);
   }
 
   /* Impose data compression */
-  h_err = H5Pset_deflate(h_prop, 4);
-  if (h_err < 0) {
-    error("Error while setting compression options for field '%s'.",
-          props.name);
+  if (e->snapshotCompression > 0) {
+    h_err = H5Pset_deflate(h_prop, e->snapshotCompression);
+    if (h_err < 0) {
+      error("Error while setting compression options for field '%s'.",
+            props.name);
+    }
   }
 
   /* Create dataset */
@@ -339,6 +342,7 @@ void read_ic_single(char* fileName, const struct UnitSystem* internal_units,
   int numParticles[NUM_PARTICLE_TYPES] = {0};
   int numParticles_highWord[NUM_PARTICLE_TYPES] = {0};
   size_t N[NUM_PARTICLE_TYPES] = {0};
+  int dimension = 3; /* Assume 3D if nothing is specified */
   size_t Ndm;
 
   /* Open file */
@@ -364,6 +368,15 @@ void read_ic_single(char* fileName, const struct UnitSystem* internal_units,
   h_grp = H5Gopen(h_file, "/Header", H5P_DEFAULT);
   if (h_grp < 0) error("Error while opening file header\n");
 
+  /* Check the dimensionality of the ICs (if the info exists) */
+  const hid_t hid_dim = H5Aexists(h_grp, "Dimension");
+  if (hid_dim < 0)
+    error("Error while testing existance of 'Dimension' attribute");
+  if (hid_dim > 0) readAttribute(h_grp, "Dimension", INT, &dimension);
+  if (dimension != hydro_dimension)
+    error("ICs dimensionality (%dD) does not match code dimensionality (%dD)",
+          dimension, (int)hydro_dimension);
+
   /* Read the relevant information and print status */
   int flag_entropy_temp[6];
   readAttribute(h_grp, "Flag_Entropy_ICs", INT, flag_entropy_temp);
@@ -458,18 +471,18 @@ void read_ic_single(char* fileName, const struct UnitSystem* internal_units,
 
     int num_fields = 0;
     struct io_props list[100];
-    size_t N = 0;
+    size_t Nparticles = 0;
 
     /* Read particle fields into the structure */
     switch (ptype) {
 
       case GAS:
-        N = *Ngas;
+        Nparticles = *Ngas;
         hydro_read_particles(*parts, list, &num_fields);
         break;
 
       case DM:
-        N = Ndm;
+        Nparticles = Ndm;
         darkmatter_read_particles(*gparts, list, &num_fields);
         break;
 
@@ -480,7 +493,7 @@ void read_ic_single(char* fileName, const struct UnitSystem* internal_units,
     /* Read everything */
     if (!dry_run)
       for (int i = 0; i < num_fields; ++i)
-        readArray(h_grp, list[i], N, internal_units, ic_units);
+        readArray(h_grp, list[i], Nparticles, internal_units, ic_units);
 
     /* Close particle group */
     H5Gclose(h_grp);
@@ -579,6 +592,8 @@ void write_output_single(struct engine* e, const char* baseName,
   writeAttribute(h_grp, "BoxSize", DOUBLE, e->s->dim, 3);
   double dblTime = e->time;
   writeAttribute(h_grp, "Time", DOUBLE, &dblTime, 1);
+  int dimension = (int)hydro_dimension;
+  writeAttribute(h_grp, "Dimension", INT, &dimension, 1);
 
   /* GADGET-2 legacy values */
   /* Number of particles of each type */
diff --git a/src/space.c b/src/space.c
index 8b7e5f81b539fb02da44dac67a3575356c08c45e..de6ac7226f01b815d39f3c689bdf23d7ad234c20 100644
--- a/src/space.c
+++ b/src/space.c
@@ -41,17 +41,18 @@
 
 /* Local headers. */
 #include "atomic.h"
+#include "const.h"
 #include "engine.h"
 #include "error.h"
+#include "gravity.h"
+#include "hydro.h"
 #include "kernel_hydro.h"
 #include "lock.h"
 #include "minmax.h"
 #include "runner.h"
+#include "threadpool.h"
 #include "tools.h"
 
-/* Shared sort structure. */
-struct parallel_sort space_sort_struct;
-
 /* Split size. */
 int space_splitsize = space_splitsize_default;
 int space_subsize = space_subsize_default;
@@ -163,20 +164,22 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
   const size_t nr_parts = s->nr_parts;
   struct cell *restrict c;
   ticks tic = getticks();
+  const int ti_current = (s->e != NULL) ? s->e->ti_current : 0;
 
-  /* Run through the parts and get the current h_max. */
+  /* Run through the cells and get the current h_max. */
   // tic = getticks();
   float h_max = s->cell_min / kernel_gamma / space_stretch;
   if (nr_parts > 0) {
     if (s->cells != NULL) {
       for (int k = 0; k < s->nr_cells; k++) {
-        if (s->cells[k].h_max > h_max) h_max = s->cells[k].h_max;
+        if (s->cells[k].nodeID == engine_rank && s->cells[k].h_max > h_max) {
+          h_max = s->cells[k].h_max;
+        }
       }
     } else {
       for (size_t k = 0; k < nr_parts; k++) {
         if (s->parts[k].h > h_max) h_max = s->parts[k].h;
       }
-      s->h_max = h_max;
     }
   }
 
@@ -269,7 +272,7 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
 
     /* Allocate the highest level of cells. */
     s->tot_cells = s->nr_cells = cdim[0] * cdim[1] * cdim[2];
-    if (posix_memalign((void *)&s->cells, 64,
+    if (posix_memalign((void *)&s->cells, cell_align,
                        s->nr_cells * sizeof(struct cell)) != 0)
       error("Failed to allocate cells.");
     bzero(s->cells, s->nr_cells * sizeof(struct cell));
@@ -292,6 +295,7 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
           c->count = 0;
           c->gcount = 0;
           c->super = c;
+          c->ti_old = ti_current;
           lock_init(&c->lock);
         }
 
@@ -348,16 +352,18 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
       s->cells[k].sorts = NULL;
       s->cells[k].nr_tasks = 0;
       s->cells[k].nr_density = 0;
+      s->cells[k].nr_gradient = 0;
       s->cells[k].nr_force = 0;
       s->cells[k].density = NULL;
+      s->cells[k].gradient = NULL;
       s->cells[k].force = NULL;
       s->cells[k].dx_max = 0.0f;
       s->cells[k].sorted = 0;
       s->cells[k].count = 0;
       s->cells[k].gcount = 0;
       s->cells[k].init = NULL;
+      s->cells[k].extra_ghost = NULL;
       s->cells[k].ghost = NULL;
-      s->cells[k].drift = NULL;
       s->cells[k].kick = NULL;
       s->cells[k].super = &s->cells[k];
     }
@@ -391,6 +397,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) {
   size_t nr_parts = s->nr_parts;
   size_t nr_gparts = s->nr_gparts;
   struct cell *restrict cells = s->cells;
+  const int ti_current = (s->e != NULL) ? s->e->ti_current : 0;
 
   const double ih[3] = {s->iwidth[0], s->iwidth[1], s->iwidth[2]};
   const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
@@ -422,7 +429,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) {
   int *gind;
   if ((gind = (int *)malloc(sizeof(int) * gind_size)) == NULL)
     error("Failed to allocate temporary g-particle indices.");
-  for (int k = 0; k < nr_gparts; k++) {
+  for (size_t k = 0; k < nr_gparts; k++) {
     struct gpart *restrict gp = &s->gparts[k];
     for (int j = 0; j < 3; j++)
       if (gp->x[j] < 0.0)
@@ -480,7 +487,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) {
 #endif
 
   /* Move non-local gparts to the end of the list. */
-  for (int k = 0; k < nr_gparts;) {
+  for (size_t k = 0; k < nr_gparts;) {
     if (cells[gind[k]].nodeID != local_nodeID) {
       cells[gind[k]].gcount -= 1;
       nr_gparts -= 1;
@@ -589,7 +596,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) {
   }
 
   /* Assign each particle to its cell. */
-  for (int k = nr_gparts; k < s->nr_gparts; k++) {
+  for (size_t k = nr_gparts; k < s->nr_gparts; k++) {
     const struct gpart *const p = &s->gparts[k];
     gind[k] =
         cell_getid(cdim, p->x[0] * ih[0], p->x[1] * ih[1], p->x[2] * ih[2]);
@@ -642,6 +649,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) {
   struct gpart *gfinger = s->gparts;
   for (int k = 0; k < s->nr_cells; k++) {
     struct cell *restrict c = &cells[k];
+    c->ti_old = ti_current;
     c->parts = finger;
     c->xparts = xfinger;
     c->gparts = gfinger;
@@ -672,10 +680,8 @@ void space_split(struct space *s, struct cell *cells, int verbose) {
 
   const ticks tic = getticks();
 
-  for (int k = 0; k < s->nr_cells; k++)
-    scheduler_addtask(&s->e->sched, task_type_split_cell, task_subtype_none, k,
-                      0, &cells[k], NULL, 0);
-  engine_launch(s->e, s->e->nr_threads, 1 << task_type_split_cell, 0);
+  threadpool_map(&s->e->threadpool, space_split_mapper, cells, s->nr_cells,
+                 sizeof(struct cell), 1, s);
 
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
@@ -699,29 +705,32 @@ void space_parts_sort(struct space *s, int *ind, size_t N, int min, int max,
 
   const ticks tic = getticks();
 
-  /*Populate the global parallel_sort structure with the input data */
-  space_sort_struct.parts = s->parts;
-  space_sort_struct.xparts = s->xparts;
-  space_sort_struct.ind = ind;
-  space_sort_struct.stack_size = 2 * (max - min + 1) + 10 + s->e->nr_threads;
-  if ((space_sort_struct.stack = malloc(sizeof(struct qstack) *
-                                        space_sort_struct.stack_size)) == NULL)
+  /* Populate a parallel_sort structure with the input data */
+  struct parallel_sort sort_struct;
+  sort_struct.parts = s->parts;
+  sort_struct.xparts = s->xparts;
+  sort_struct.ind = ind;
+  sort_struct.stack_size = 2 * (max - min + 1) + 10 + s->e->nr_threads;
+  if ((sort_struct.stack =
+           malloc(sizeof(struct qstack) * sort_struct.stack_size)) == NULL)
     error("Failed to allocate sorting stack.");
-  for (int i = 0; i < space_sort_struct.stack_size; i++)
-    space_sort_struct.stack[i].ready = 0;
+  for (unsigned int i = 0; i < sort_struct.stack_size; i++)
+    sort_struct.stack[i].ready = 0;
 
   /* Add the first interval. */
-  space_sort_struct.stack[0].i = 0;
-  space_sort_struct.stack[0].j = N - 1;
-  space_sort_struct.stack[0].min = min;
-  space_sort_struct.stack[0].max = max;
-  space_sort_struct.stack[0].ready = 1;
-  space_sort_struct.first = 0;
-  space_sort_struct.last = 1;
-  space_sort_struct.waiting = 1;
-
-  /* Launch the sorting tasks. */
-  engine_launch(s->e, s->e->nr_threads, (1 << task_type_part_sort), 0);
+  sort_struct.stack[0].i = 0;
+  sort_struct.stack[0].j = N - 1;
+  sort_struct.stack[0].min = min;
+  sort_struct.stack[0].max = max;
+  sort_struct.stack[0].ready = 1;
+  sort_struct.first = 0;
+  sort_struct.last = 1;
+  sort_struct.waiting = 1;
+
+  /* Launch the sorting tasks with a stride of zero such that the same
+     map data is passed to each thread. */
+  threadpool_map(&s->e->threadpool, space_parts_sort_mapper, &sort_struct,
+                 s->e->threadpool.num_threads, 0, 1, NULL);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Verify space_sort_struct. */
@@ -733,37 +742,40 @@ void space_parts_sort(struct space *s, int *ind, size_t N, int min, int max,
 #endif
 
   /* Clean up. */
-  free(space_sort_struct.stack);
+  free(sort_struct.stack);
 
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
 }
 
-void space_do_parts_sort() {
+void space_parts_sort_mapper(void *map_data, int num_elements,
+                             void *extra_data) {
+
+  /* Unpack the mapping data. */
+  struct parallel_sort *sort_struct = (struct parallel_sort *)map_data;
 
   /* Pointers to the sorting data. */
-  int *ind = space_sort_struct.ind;
-  struct part *parts = space_sort_struct.parts;
-  struct xpart *xparts = space_sort_struct.xparts;
+  int *ind = sort_struct->ind;
+  struct part *parts = sort_struct->parts;
+  struct xpart *xparts = sort_struct->xparts;
 
   /* Main loop. */
-  while (space_sort_struct.waiting) {
+  while (sort_struct->waiting) {
 
     /* Grab an interval off the queue. */
-    int qid =
-        atomic_inc(&space_sort_struct.first) % space_sort_struct.stack_size;
+    int qid = atomic_inc(&sort_struct->first) % sort_struct->stack_size;
 
     /* Wait for the entry to be ready, or for the sorting do be done. */
-    while (!space_sort_struct.stack[qid].ready)
-      if (!space_sort_struct.waiting) return;
+    while (!sort_struct->stack[qid].ready)
+      if (!sort_struct->waiting) return;
 
     /* Get the stack entry. */
-    ptrdiff_t i = space_sort_struct.stack[qid].i;
-    ptrdiff_t j = space_sort_struct.stack[qid].j;
-    int min = space_sort_struct.stack[qid].min;
-    int max = space_sort_struct.stack[qid].max;
-    space_sort_struct.stack[qid].ready = 0;
+    ptrdiff_t i = sort_struct->stack[qid].i;
+    ptrdiff_t j = sort_struct->stack[qid].j;
+    int min = sort_struct->stack[qid].min;
+    int max = sort_struct->stack[qid].max;
+    sort_struct->stack[qid].ready = 0;
 
     /* Loop over sub-intervals. */
     while (1) {
@@ -813,18 +825,16 @@ void space_do_parts_sort() {
 
         /* Recurse on the left? */
         if (jj > i && pivot > min) {
-          qid = atomic_inc(&space_sort_struct.last) %
-                space_sort_struct.stack_size;
-          while (space_sort_struct.stack[qid].ready)
+          qid = atomic_inc(&sort_struct->last) % sort_struct->stack_size;
+          while (sort_struct->stack[qid].ready)
             ;
-          space_sort_struct.stack[qid].i = i;
-          space_sort_struct.stack[qid].j = jj;
-          space_sort_struct.stack[qid].min = min;
-          space_sort_struct.stack[qid].max = pivot;
-          if (atomic_inc(&space_sort_struct.waiting) >=
-              space_sort_struct.stack_size)
+          sort_struct->stack[qid].i = i;
+          sort_struct->stack[qid].j = jj;
+          sort_struct->stack[qid].min = min;
+          sort_struct->stack[qid].max = pivot;
+          if (atomic_inc(&sort_struct->waiting) >= sort_struct->stack_size)
             error("Qstack overflow.");
-          space_sort_struct.stack[qid].ready = 1;
+          sort_struct->stack[qid].ready = 1;
         }
 
         /* Recurse on the right? */
@@ -838,18 +848,16 @@ void space_do_parts_sort() {
 
         /* Recurse on the right? */
         if (pivot + 1 < max) {
-          qid = atomic_inc(&space_sort_struct.last) %
-                space_sort_struct.stack_size;
-          while (space_sort_struct.stack[qid].ready)
+          qid = atomic_inc(&sort_struct->last) % sort_struct->stack_size;
+          while (sort_struct->stack[qid].ready)
             ;
-          space_sort_struct.stack[qid].i = jj + 1;
-          space_sort_struct.stack[qid].j = j;
-          space_sort_struct.stack[qid].min = pivot + 1;
-          space_sort_struct.stack[qid].max = max;
-          if (atomic_inc(&space_sort_struct.waiting) >=
-              space_sort_struct.stack_size)
+          sort_struct->stack[qid].i = jj + 1;
+          sort_struct->stack[qid].j = j;
+          sort_struct->stack[qid].min = pivot + 1;
+          sort_struct->stack[qid].max = max;
+          if (atomic_inc(&sort_struct->waiting) >= sort_struct->stack_size)
             error("Qstack overflow.");
-          space_sort_struct.stack[qid].ready = 1;
+          sort_struct->stack[qid].ready = 1;
         }
 
         /* Recurse on the left? */
@@ -862,7 +870,7 @@ void space_do_parts_sort() {
 
     } /* loop over sub-intervals. */
 
-    atomic_dec(&space_sort_struct.waiting);
+    atomic_dec(&sort_struct->waiting);
 
   } /* main loop. */
 }
@@ -883,28 +891,31 @@ void space_gparts_sort(struct space *s, int *ind, size_t N, int min, int max,
 
   const ticks tic = getticks();
 
-  /*Populate the global parallel_sort structure with the input data */
-  space_sort_struct.gparts = s->gparts;
-  space_sort_struct.ind = ind;
-  space_sort_struct.stack_size = 2 * (max - min + 1) + 10 + s->e->nr_threads;
-  if ((space_sort_struct.stack = malloc(sizeof(struct qstack) *
-                                        space_sort_struct.stack_size)) == NULL)
+  /*Populate a global parallel_sort structure with the input data */
+  struct parallel_sort sort_struct;
+  sort_struct.gparts = s->gparts;
+  sort_struct.ind = ind;
+  sort_struct.stack_size = 2 * (max - min + 1) + 10 + s->e->nr_threads;
+  if ((sort_struct.stack =
+           malloc(sizeof(struct qstack) * sort_struct.stack_size)) == NULL)
     error("Failed to allocate sorting stack.");
-  for (int i = 0; i < space_sort_struct.stack_size; i++)
-    space_sort_struct.stack[i].ready = 0;
+  for (unsigned int i = 0; i < sort_struct.stack_size; i++)
+    sort_struct.stack[i].ready = 0;
 
   /* Add the first interval. */
-  space_sort_struct.stack[0].i = 0;
-  space_sort_struct.stack[0].j = N - 1;
-  space_sort_struct.stack[0].min = min;
-  space_sort_struct.stack[0].max = max;
-  space_sort_struct.stack[0].ready = 1;
-  space_sort_struct.first = 0;
-  space_sort_struct.last = 1;
-  space_sort_struct.waiting = 1;
-
-  /* Launch the sorting tasks. */
-  engine_launch(s->e, s->e->nr_threads, (1 << task_type_gpart_sort), 0);
+  sort_struct.stack[0].i = 0;
+  sort_struct.stack[0].j = N - 1;
+  sort_struct.stack[0].min = min;
+  sort_struct.stack[0].max = max;
+  sort_struct.stack[0].ready = 1;
+  sort_struct.first = 0;
+  sort_struct.last = 1;
+  sort_struct.waiting = 1;
+
+  /* Launch the sorting tasks with a stride of zero such that the same
+     map data is passed to each thread. */
+  threadpool_map(&s->e->threadpool, space_gparts_sort_mapper, &sort_struct,
+                 s->e->threadpool.num_threads, 0, 1, NULL);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Verify space_sort_struct. */
@@ -916,36 +927,39 @@ void space_gparts_sort(struct space *s, int *ind, size_t N, int min, int max,
 #endif
 
   /* Clean up. */
-  free(space_sort_struct.stack);
+  free(sort_struct.stack);
 
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
 }
 
-void space_do_gparts_sort() {
+void space_gparts_sort_mapper(void *map_data, int num_elements,
+                              void *extra_data) {
+
+  /* Unpack the mapping data. */
+  struct parallel_sort *sort_struct = (struct parallel_sort *)map_data;
 
   /* Pointers to the sorting data. */
-  int *ind = space_sort_struct.ind;
-  struct gpart *gparts = space_sort_struct.gparts;
+  int *ind = sort_struct->ind;
+  struct gpart *gparts = sort_struct->gparts;
 
   /* Main loop. */
-  while (space_sort_struct.waiting) {
+  while (sort_struct->waiting) {
 
     /* Grab an interval off the queue. */
-    int qid =
-        atomic_inc(&space_sort_struct.first) % space_sort_struct.stack_size;
+    int qid = atomic_inc(&sort_struct->first) % sort_struct->stack_size;
 
     /* Wait for the entry to be ready, or for the sorting do be done. */
-    while (!space_sort_struct.stack[qid].ready)
-      if (!space_sort_struct.waiting) return;
+    while (!sort_struct->stack[qid].ready)
+      if (!sort_struct->waiting) return;
 
     /* Get the stack entry. */
-    ptrdiff_t i = space_sort_struct.stack[qid].i;
-    ptrdiff_t j = space_sort_struct.stack[qid].j;
-    int min = space_sort_struct.stack[qid].min;
-    int max = space_sort_struct.stack[qid].max;
-    space_sort_struct.stack[qid].ready = 0;
+    ptrdiff_t i = sort_struct->stack[qid].i;
+    ptrdiff_t j = sort_struct->stack[qid].j;
+    int min = sort_struct->stack[qid].min;
+    int max = sort_struct->stack[qid].max;
+    sort_struct->stack[qid].ready = 0;
 
     /* Loop over sub-intervals. */
     while (1) {
@@ -992,18 +1006,16 @@ void space_do_gparts_sort() {
 
         /* Recurse on the left? */
         if (jj > i && pivot > min) {
-          qid = atomic_inc(&space_sort_struct.last) %
-                space_sort_struct.stack_size;
-          while (space_sort_struct.stack[qid].ready)
+          qid = atomic_inc(&sort_struct->last) % sort_struct->stack_size;
+          while (sort_struct->stack[qid].ready)
             ;
-          space_sort_struct.stack[qid].i = i;
-          space_sort_struct.stack[qid].j = jj;
-          space_sort_struct.stack[qid].min = min;
-          space_sort_struct.stack[qid].max = pivot;
-          if (atomic_inc(&space_sort_struct.waiting) >=
-              space_sort_struct.stack_size)
+          sort_struct->stack[qid].i = i;
+          sort_struct->stack[qid].j = jj;
+          sort_struct->stack[qid].min = min;
+          sort_struct->stack[qid].max = pivot;
+          if (atomic_inc(&sort_struct->waiting) >= sort_struct->stack_size)
             error("Qstack overflow.");
-          space_sort_struct.stack[qid].ready = 1;
+          sort_struct->stack[qid].ready = 1;
         }
 
         /* Recurse on the right? */
@@ -1017,18 +1029,16 @@ void space_do_gparts_sort() {
 
         /* Recurse on the right? */
         if (pivot + 1 < max) {
-          qid = atomic_inc(&space_sort_struct.last) %
-                space_sort_struct.stack_size;
-          while (space_sort_struct.stack[qid].ready)
+          qid = atomic_inc(&sort_struct->last) % sort_struct->stack_size;
+          while (sort_struct->stack[qid].ready)
             ;
-          space_sort_struct.stack[qid].i = jj + 1;
-          space_sort_struct.stack[qid].j = j;
-          space_sort_struct.stack[qid].min = pivot + 1;
-          space_sort_struct.stack[qid].max = max;
-          if (atomic_inc(&space_sort_struct.waiting) >=
-              space_sort_struct.stack_size)
+          sort_struct->stack[qid].i = jj + 1;
+          sort_struct->stack[qid].j = j;
+          sort_struct->stack[qid].min = pivot + 1;
+          sort_struct->stack[qid].max = max;
+          if (atomic_inc(&sort_struct->waiting) >= sort_struct->stack_size)
             error("Qstack overflow.");
-          space_sort_struct.stack[qid].ready = 1;
+          sort_struct->stack[qid].ready = 1;
         }
 
         /* Recurse on the left? */
@@ -1041,7 +1051,7 @@ void space_do_gparts_sort() {
 
     } /* loop over sub-intervals. */
 
-    atomic_dec(&space_sort_struct.waiting);
+    atomic_dec(&sort_struct->waiting);
 
   } /* main loop. */
 }
@@ -1222,126 +1232,134 @@ void space_map_cells_pre(struct space *s, int full,
 }
 
 /**
- * @brief Split cells that contain too many particles.
- *
- * @param s The #space we are working in.
- * @param c The #cell under consideration.
+ * @brief #threadpool mapper function to split cells if they contain
+ *        too many particles.
  */
 
-void space_do_split(struct space *s, struct cell *c) {
-
-  const int count = c->count;
-  const int gcount = c->gcount;
-  int maxdepth = 0;
-  float h_max = 0.0f;
-  int ti_end_min = max_nr_timesteps, ti_end_max = 0;
-  struct cell *temp;
-  struct part *parts = c->parts;
-  struct gpart *gparts = c->gparts;
-  struct xpart *xparts = c->xparts;
-
-  /* Check the depth. */
-  if (c->depth > s->maxdepth) s->maxdepth = c->depth;
-
-  /* Split or let it be? */
-  if (count > space_splitsize || gcount > space_splitsize) {
-
-    /* No longer just a leaf. */
-    c->split = 1;
-
-    /* Create the cell's progeny. */
-    for (int k = 0; k < 8; k++) {
-      temp = space_getcell(s);
-      temp->count = 0;
-      temp->gcount = 0;
-      temp->loc[0] = c->loc[0];
-      temp->loc[1] = c->loc[1];
-      temp->loc[2] = c->loc[2];
-      temp->width[0] = c->width[0] / 2;
-      temp->width[1] = c->width[1] / 2;
-      temp->width[2] = c->width[2] / 2;
-      temp->dmin = c->dmin / 2;
-      if (k & 4) temp->loc[0] += temp->width[0];
-      if (k & 2) temp->loc[1] += temp->width[1];
-      if (k & 1) temp->loc[2] += temp->width[2];
-      temp->depth = c->depth + 1;
-      temp->split = 0;
-      temp->h_max = 0.0;
-      temp->dx_max = 0.f;
-      temp->nodeID = c->nodeID;
-      temp->parent = c;
-      c->progeny[k] = temp;
-    }
+void space_split_mapper(void *map_data, int num_elements, void *extra_data) {
 
-    /* Split the cell data. */
-    cell_split(c, c->parts - s->parts);
+  /* Unpack the inputs. */
+  struct space *s = (struct space *)extra_data;
+  struct cell *cells = (struct cell *)map_data;
+  struct engine *e = s->e;
 
-    /* Remove any progeny with zero parts. */
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k]->count == 0 && c->progeny[k]->gcount == 0) {
-        space_recycle(s, c->progeny[k]);
-        c->progeny[k] = NULL;
-      } else {
-        space_do_split(s, c->progeny[k]);
-        h_max = fmaxf(h_max, c->progeny[k]->h_max);
-        ti_end_min = min(ti_end_min, c->progeny[k]->ti_end_min);
-        ti_end_max = max(ti_end_max, c->progeny[k]->ti_end_max);
-        if (c->progeny[k]->maxdepth > maxdepth)
-          maxdepth = c->progeny[k]->maxdepth;
-      }
+  for (int ind = 0; ind < num_elements; ind++) {
 
-    /* Set the values for this cell. */
-    c->h_max = h_max;
-    c->ti_end_min = ti_end_min;
-    c->ti_end_max = ti_end_max;
-    c->maxdepth = maxdepth;
+    struct cell *c = &cells[ind];
 
-  }
+    const int count = c->count;
+    const int gcount = c->gcount;
+    int maxdepth = 0;
+    float h_max = 0.0f;
+    int ti_end_min = max_nr_timesteps, ti_end_max = 0;
+    struct cell *temp;
+    struct part *parts = c->parts;
+    struct gpart *gparts = c->gparts;
+    struct xpart *xparts = c->xparts;
 
-  /* Otherwise, collect the data for this cell. */
-  else {
+    /* Check the depth. */
+    while (c->depth > (maxdepth = s->maxdepth)) {
+      atomic_cas(&s->maxdepth, maxdepth, c->depth);
+    }
+
+    /* Split or let it be? */
+    if (count > space_splitsize || gcount > space_splitsize) {
+
+      /* No longer just a leaf. */
+      c->split = 1;
+
+      /* Create the cell's progeny. */
+      for (int k = 0; k < 8; k++) {
+        temp = space_getcell(s);
+        temp->count = 0;
+        temp->gcount = 0;
+        temp->ti_old = e->ti_current;
+        temp->loc[0] = c->loc[0];
+        temp->loc[1] = c->loc[1];
+        temp->loc[2] = c->loc[2];
+        temp->width[0] = c->width[0] / 2;
+        temp->width[1] = c->width[1] / 2;
+        temp->width[2] = c->width[2] / 2;
+        temp->dmin = c->dmin / 2;
+        if (k & 4) temp->loc[0] += temp->width[0];
+        if (k & 2) temp->loc[1] += temp->width[1];
+        if (k & 1) temp->loc[2] += temp->width[2];
+        temp->depth = c->depth + 1;
+        temp->split = 0;
+        temp->h_max = 0.0;
+        temp->dx_max = 0.f;
+        temp->nodeID = c->nodeID;
+        temp->parent = c;
+        c->progeny[k] = temp;
+      }
+
+      /* Split the cell data. */
+      cell_split(c, c->parts - s->parts);
+
+      /* Remove any progeny with zero parts. */
+      for (int k = 0; k < 8; k++)
+        if (c->progeny[k]->count == 0 && c->progeny[k]->gcount == 0) {
+          space_recycle(s, c->progeny[k]);
+          c->progeny[k] = NULL;
+        } else {
+          space_split_mapper(c->progeny[k], 1, s);
+          h_max = fmaxf(h_max, c->progeny[k]->h_max);
+          ti_end_min = min(ti_end_min, c->progeny[k]->ti_end_min);
+          ti_end_max = max(ti_end_max, c->progeny[k]->ti_end_max);
+          if (c->progeny[k]->maxdepth > maxdepth)
+            maxdepth = c->progeny[k]->maxdepth;
+        }
 
-    /* Clear the progeny. */
-    bzero(c->progeny, sizeof(struct cell *) * 8);
-    c->split = 0;
-    c->maxdepth = c->depth;
-
-    /* Get dt_min/dt_max. */
-    for (int k = 0; k < count; k++) {
-      struct part *p = &parts[k];
-      struct xpart *xp = &xparts[k];
-      const float h = p->h;
-      const int ti_end = p->ti_end;
-      xp->x_diff[0] = 0.f;
-      xp->x_diff[1] = 0.f;
-      xp->x_diff[2] = 0.f;
-      if (h > h_max) h_max = h;
-      if (ti_end < ti_end_min) ti_end_min = ti_end;
-      if (ti_end > ti_end_max) ti_end_max = ti_end;
     }
-    for (int k = 0; k < gcount; k++) {
-      struct gpart *gp = &gparts[k];
-      const int ti_end = gp->ti_end;
-      gp->x_diff[0] = 0.f;
-      gp->x_diff[1] = 0.f;
-      gp->x_diff[2] = 0.f;
-      if (ti_end < ti_end_min) ti_end_min = ti_end;
-      if (ti_end > ti_end_max) ti_end_max = ti_end;
+
+    /* Otherwise, collect the data for this cell. */
+    else {
+
+      /* Clear the progeny. */
+      bzero(c->progeny, sizeof(struct cell *) * 8);
+      c->split = 0;
+      maxdepth = c->depth;
+
+      /* Get dt_min/dt_max. */
+      for (int k = 0; k < count; k++) {
+        struct part *p = &parts[k];
+        struct xpart *xp = &xparts[k];
+        const float h = p->h;
+        const int ti_end = p->ti_end;
+        xp->x_diff[0] = 0.f;
+        xp->x_diff[1] = 0.f;
+        xp->x_diff[2] = 0.f;
+        if (h > h_max) h_max = h;
+        if (ti_end < ti_end_min) ti_end_min = ti_end;
+        if (ti_end > ti_end_max) ti_end_max = ti_end;
+      }
+      for (int k = 0; k < gcount; k++) {
+        struct gpart *gp = &gparts[k];
+        const int ti_end = gp->ti_end;
+        gp->x_diff[0] = 0.f;
+        gp->x_diff[1] = 0.f;
+        gp->x_diff[2] = 0.f;
+        if (ti_end < ti_end_min) ti_end_min = ti_end;
+        if (ti_end > ti_end_max) ti_end_max = ti_end;
+      }
     }
+
+    /* Set the values for this cell. */
     c->h_max = h_max;
     c->ti_end_min = ti_end_min;
     c->ti_end_max = ti_end_max;
-  }
+    c->maxdepth = maxdepth;
 
-  /* Set ownership according to the start of the parts array. */
-  if (s->nr_parts > 0)
-    c->owner =
-        ((c->parts - s->parts) % s->nr_parts) * s->nr_queues / s->nr_parts;
-  else if (s->nr_gparts > 0)
-    c->owner =
-        ((c->gparts - s->gparts) % s->nr_gparts) * s->nr_queues / s->nr_gparts;
-  else
-    c->owner = 0; /* Ok, there is really nothing on this rank... */
+    /* Set ownership according to the start of the parts array. */
+    if (s->nr_parts > 0)
+      c->owner =
+          ((c->parts - s->parts) % s->nr_parts) * s->nr_queues / s->nr_parts;
+    else if (s->nr_gparts > 0)
+      c->owner = ((c->gparts - s->gparts) % s->nr_gparts) * s->nr_queues /
+                 s->nr_gparts;
+    else
+      c->owner = 0; /* Ok, there is really nothing on this rank... */
+  }
 }
 
 /**
@@ -1390,7 +1408,7 @@ struct cell *space_getcell(struct space *s) {
 
   /* Is the buffer empty? */
   if (s->cells_new == NULL) {
-    if (posix_memalign((void *)&s->cells_new, 64,
+    if (posix_memalign((void *)&s->cells_new, cell_align,
                        space_cellallocchunk * sizeof(struct cell)) != 0)
       error("Failed to allocate more cells.");
     bzero(s->cells_new, space_cellallocchunk * sizeof(struct cell));
@@ -1416,6 +1434,59 @@ struct cell *space_getcell(struct space *s) {
   return c;
 }
 
+/**
+ * @brief Initialises all the particles by setting them into a valid state
+ *
+ * Calls hydro_first_init_part() on all the particles
+ */
+void space_init_parts(struct space *s) {
+
+  const size_t nr_parts = s->nr_parts;
+  struct part *restrict p = s->parts;
+  struct xpart *restrict xp = s->xparts;
+
+  for (size_t i = 0; i < nr_parts; ++i) {
+
+#ifdef HYDRO_DIMENSION_2D
+    p[i].x[2] = 0.f;
+    p[i].v[2] = 0.f;
+#endif
+
+#ifdef HYDRO_DIMENSION_1D
+    p[i].x[1] = p[i].x[2] = 0.f;
+    p[i].v[1] = p[i].v[2] = 0.f;
+#endif
+
+    hydro_first_init_part(&p[i], &xp[i]);
+  }
+}
+
+/**
+ * @brief Initialises all the g-particles by setting them into a valid state
+ *
+ * Calls gravity_first_init_gpart() on all the particles
+ */
+void space_init_gparts(struct space *s) {
+
+  const size_t nr_gparts = s->nr_gparts;
+  struct gpart *restrict gp = s->gparts;
+
+  for (size_t i = 0; i < nr_gparts; ++i) {
+
+#ifdef HYDRO_DIMENSION_2D
+    gp[i].x[2] = 0.f;
+    gp[i].v_full[2] = 0.f;
+#endif
+
+#ifdef HYDRO_DIMENSION_1D
+    gp[i].x[1] = gp[i].x[2] = 0.f;
+    gp[i].v_full[1] = gp[i].v_full[2] = 0.f;
+#endif
+
+    gravity_first_init_gpart(&gp[i]);
+  }
+}
+
 /**
  * @brief Split the space into cells given the array of particles.
  *
@@ -1514,13 +1585,13 @@ void space_init(struct space *s, const struct swift_params *params,
 
     /* Check that all the part positions are reasonable, wrap if periodic. */
     if (periodic) {
-      for (int k = 0; k < Npart; k++)
+      for (size_t k = 0; k < Npart; k++)
         for (int j = 0; j < 3; j++) {
           while (parts[k].x[j] < 0) parts[k].x[j] += dim[j];
           while (parts[k].x[j] >= dim[j]) parts[k].x[j] -= dim[j];
         }
     } else {
-      for (int k = 0; k < Npart; k++)
+      for (size_t k = 0; k < Npart; k++)
         for (int j = 0; j < 3; j++)
           if (parts[k].x[j] < 0 || parts[k].x[j] >= dim[j])
             error("Not all particles are within the specified domain.");
@@ -1528,13 +1599,13 @@ void space_init(struct space *s, const struct swift_params *params,
 
     /* Same for the gparts */
     if (periodic) {
-      for (int k = 0; k < Ngpart; k++)
+      for (size_t k = 0; k < Ngpart; k++)
         for (int j = 0; j < 3; j++) {
           while (gparts[k].x[j] < 0) gparts[k].x[j] += dim[j];
           while (gparts[k].x[j] >= dim[j]) gparts[k].x[j] -= dim[j];
         }
     } else {
-      for (int k = 0; k < Ngpart; k++)
+      for (size_t k = 0; k < Ngpart; k++)
         for (int j = 0; j < 3; j++)
           if (gparts[k].x[j] < 0 || gparts[k].x[j] >= dim[j])
             error("Not all g-particles are within the specified domain.");
@@ -1549,6 +1620,10 @@ void space_init(struct space *s, const struct swift_params *params,
     bzero(s->xparts, Npart * sizeof(struct xpart));
   }
 
+  /* Set the particles in a state where they are ready for a run */
+  space_init_parts(s);
+  space_init_gparts(s);
+
   /* Init the space lock. */
   if (lock_init(&s->lock) != 0) error("Failed to create space spin-lock.");
 
diff --git a/src/space.h b/src/space.h
index 180759f2af69dd0d77e6e681229353358d554291..90313be8dbe817d65fbd0e6a8c30c156747594b1 100644
--- a/src/space.h
+++ b/src/space.h
@@ -68,8 +68,8 @@ struct space {
   /* Cell widths. */
   double width[3], iwidth[3];
 
-  /* The minimum and maximum cutoff radii. */
-  double h_max, cell_min;
+  /* The minimum cell width. */
+  double cell_min;
 
   /* Current maximum displacement for particles. */
   float dx_max;
@@ -132,7 +132,6 @@ struct parallel_sort {
   unsigned int stack_size;
   volatile unsigned int first, last, waiting;
 };
-extern struct parallel_sort space_sort_struct;
 
 /* function prototypes. */
 void space_parts_sort(struct space *s, int *ind, size_t N, int min, int max,
@@ -156,12 +155,18 @@ void space_map_parts_xparts(struct space *s,
                                         struct cell *c));
 void space_map_cells_post(struct space *s, int full,
                           void (*fun)(struct cell *c, void *data), void *data);
+void space_parts_sort_mapper(void *map_data, int num_elements,
+                             void *extra_data);
+void space_gparts_sort_mapper(void *map_data, int num_elements,
+                              void *extra_data);
 void space_rebuild(struct space *s, double h_max, int verbose);
 void space_recycle(struct space *s, struct cell *c);
 void space_split(struct space *s, struct cell *cells, int verbose);
-void space_do_split(struct space *s, struct cell *c);
+void space_split_mapper(void *map_data, int num_elements, void *extra_data);
 void space_do_parts_sort();
 void space_do_gparts_sort();
+void space_init_parts(struct space *s);
+void space_init_gparts(struct space *s);
 void space_link_cleanup(struct space *s);
 void space_clean(struct space *s);
 
diff --git a/src/task.c b/src/task.c
index 143ba3b2165656782de79d14c781df5fa71563ac..de2fbe0160258fe3fbd5945ec536600e40422677 100644
--- a/src/task.c
+++ b/src/task.c
@@ -43,24 +43,30 @@
 /* Local headers. */
 #include "atomic.h"
 #include "error.h"
+#include "inline.h"
 #include "lock.h"
 
 /* Task type names. */
 const char *taskID_names[task_type_count] = {
-    "none",    "sort",          "self",          "pair",       "sub",
-    "init",    "ghost",         "drift",         "kick",       "kick_fixdt",
-    "send",    "recv",          "grav_gather_m", "grav_fft",   "grav_mm",
-    "grav_up", "grav_external", "part_sort",     "gpart_sort", "split_cell",
-    "rewait"};
+    "none",       "sort",    "self",         "pair",          "sub_self",
+    "sub_pair",   "init",    "ghost",        "extra_ghost",   "kick",
+    "kick_fixdt", "send",    "recv",         "grav_gather_m", "grav_fft",
+    "grav_mm",    "grav_up", "grav_external"};
 
-const char *subtaskID_names[task_type_count] = {"none", "density", "force",
-                                                "grav"};
+const char *subtaskID_names[task_subtype_count] = {
+    "none", "density", "gradient", "force", "grav", "tend"};
 
 /**
  * @brief Computes the overlap between the parts array of two given cells.
+ *
+ * @param ci The first #cell.
+ * @param cj The second #cell.
  */
-size_t task_cell_overlap_part(const struct cell *ci, const struct cell *cj) {
+__attribute__((always_inline)) INLINE static size_t task_cell_overlap_part(
+    const struct cell *ci, const struct cell *cj) {
+
   if (ci == NULL || cj == NULL) return 0;
+
   if (ci->parts <= cj->parts &&
       ci->parts + ci->count >= cj->parts + cj->count) {
     return cj->count;
@@ -68,14 +74,21 @@ size_t task_cell_overlap_part(const struct cell *ci, const struct cell *cj) {
              cj->parts + cj->count >= ci->parts + ci->count) {
     return ci->count;
   }
+
   return 0;
 }
 
 /**
  * @brief Computes the overlap between the gparts array of two given cells.
+ *
+ * @param ci The first #cell.
+ * @param cj The second #cell.
  */
-size_t task_cell_overlap_gpart(const struct cell *ci, const struct cell *cj) {
+__attribute__((always_inline)) INLINE static size_t task_cell_overlap_gpart(
+    const struct cell *ci, const struct cell *cj) {
+
   if (ci == NULL || cj == NULL) return 0;
+
   if (ci->gparts <= cj->gparts &&
       ci->gparts + ci->gcount >= cj->gparts + cj->gcount) {
     return cj->gcount;
@@ -83,6 +96,7 @@ size_t task_cell_overlap_gpart(const struct cell *ci, const struct cell *cj) {
              cj->gparts + cj->gcount >= ci->gparts + ci->gcount) {
     return ci->gcount;
   }
+
   return 0;
 }
 
@@ -91,7 +105,8 @@ size_t task_cell_overlap_gpart(const struct cell *ci, const struct cell *cj) {
  *
  * @param t The #task.
  */
-enum task_actions task_acts_on(const struct task *t) {
+__attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
+    const struct task *t) {
 
   switch (t->type) {
 
@@ -101,6 +116,7 @@ enum task_actions task_acts_on(const struct task *t) {
 
     case task_type_sort:
     case task_type_ghost:
+    case task_type_extra_ghost:
     case task_type_cooling:
       return task_action_part;
       break;
@@ -112,6 +128,7 @@ enum task_actions task_acts_on(const struct task *t) {
       switch (t->subtype) {
 
         case task_subtype_density:
+        case task_subtype_gradient:
         case task_subtype_force:
           return task_action_part;
           break;
@@ -128,7 +145,6 @@ enum task_actions task_acts_on(const struct task *t) {
       break;
 
     case task_type_init:
-    case task_type_drift:
     case task_type_kick:
     case task_type_kick_fixdt:
     case task_type_send:
@@ -147,15 +163,8 @@ enum task_actions task_acts_on(const struct task *t) {
       return task_action_gpart;
       break;
 
-    case task_type_part_sort:
-    case task_type_gpart_sort:
-    case task_type_split_cell:
-    case task_type_rewait:
-      return task_action_none;
-      break;
-
     default:
-      error("Unknow task_action for task");
+      error("Unknown task_action for task");
       return task_action_none;
       break;
   }
diff --git a/src/task.h b/src/task.h
index bc657cda0f160fd7e21909206324fa50212cf62d..0b5700430cfb69a74d82dd8d3f45db2cb9c7842a 100644
--- a/src/task.h
+++ b/src/task.h
@@ -23,15 +23,15 @@
 #ifndef SWIFT_TASK_H
 #define SWIFT_TASK_H
 
+#include "../config.h"
+
 /* Includes. */
 #include "cell.h"
 #include "cycle.h"
 
-/* Some constants. */
-#define task_maxwait 3
-#define task_maxunlock 15
-
-/* The different task types. */
+/**
+ * @brief The different task types.
+ */
 enum task_types {
   task_type_none = 0,
   task_type_sort,
@@ -41,7 +41,7 @@ enum task_types {
   task_type_sub_pair,
   task_type_init,
   task_type_ghost,
-  task_type_drift,
+  task_type_extra_ghost,
   task_type_kick,
   task_type_kick_fixdt,
   task_type_send,
@@ -52,26 +52,25 @@ enum task_types {
   task_type_grav_up,
   task_type_grav_external,
   task_type_cooling,
-  task_type_part_sort,
-  task_type_gpart_sort,
-  task_type_split_cell,
-  task_type_rewait,
   task_type_count
 };
 
-extern const char *taskID_names[];
-
-/* The different task sub-types. */
+/**
+ * @brief The different task sub-types (for pairs, selfs and sub-tasks).
+ */
 enum task_subtypes {
   task_subtype_none = 0,
   task_subtype_density,
+  task_subtype_gradient,
   task_subtype_force,
   task_subtype_grav,
   task_subtype_tend,
   task_subtype_count
 };
 
-/* The kind of action the task perform */
+/**
+ * @brief The type of particles/objects this task acts upon in a given cell.
+ */
 enum task_actions {
   task_action_none,
   task_action_part,
@@ -81,29 +80,74 @@ enum task_actions {
   task_action_count
 };
 
+/**
+ * @brief Names of the task types.
+ */
+extern const char *taskID_names[];
+
+/**
+ * @brief Names of the task sub-types.
+ */
 extern const char *subtaskID_names[];
 
-/* Data of a task. */
+/**
+ * @brief A task to be run by the #scheduler.
+ */
 struct task {
 
+  /*! Type of the task */
   enum task_types type;
+
+  /*! Sub-type of the task (for the tasks that have one */
   enum task_subtypes subtype;
   char skip, tight, implicit;
   int flags, wait, rank, weight;
 
+  /*! Flags used to carry additional information (e.g. sort directions) */
+  int flags;
+
+  /*! Number of unsatisfied dependencies */
+  int wait;
+
+  /*! Rank of a task in the order */
+  int rank;
+
+  /*! Weight of the task */
+  int weight;
+
+  /*! Pointers to the cells this task acts upon */
   struct cell *ci, *cj;
 
-  void *buff;
+  /*! ID of the queue or runner owning this task */
+  int rid;
+
+  /*! Number of tasks unlocked by this one */
+  int nr_unlock_tasks;
+
+  /*! List of tasks unlocked by this one */
+  struct task **unlock_tasks;
 
 #ifdef WITH_MPI
+
+  /*! Buffer for this task's communications */
+  void *buff;
+
+  /*! MPI request corresponding to this task */
   MPI_Request req;
+
 #endif
 
-  int rid, last_rid;
+  /*! Start and end time of this task */
   ticks tic, toc;
 
-  int nr_unlock_tasks;
-  struct task **unlock_tasks;
+  /*! Should the scheduler skip this task ? */
+  char skip;
+
+  /*! Does this task require the particles to be tightly in the cell ? */
+  char tight;
+
+  /*! Is this task implicit (i.e. does not do anything) ? */
+  char implicit;
 };
 
 /* Function prototypes. */
@@ -113,6 +157,5 @@ int task_lock(struct task *t);
 void task_print_mask(unsigned int mask);
 void task_print_submask(unsigned int submask);
 void task_do_rewait(struct task *t);
-enum task_actions task_acts_on(const struct task *t);
 
 #endif /* SWIFT_TASK_H */
diff --git a/src/threadpool.c b/src/threadpool.c
new file mode 100644
index 0000000000000000000000000000000000000000..4ef75954b39603db0d442acc9be2bd95b39614d3
--- /dev/null
+++ b/src/threadpool.c
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* This object's header. */
+#include "threadpool.h"
+
+/* Local headers. */
+#include "atomic.h"
+#include "error.h"
+
+void *threadpool_runner(void *data) {
+
+  /* Our threadpool. */
+  struct threadpool *tp = (struct threadpool *)data;
+
+  /* Main loop. */
+  while (1) {
+
+    /* Let the controller know that this thread is waiting. */
+    pthread_mutex_lock(&tp->thread_mutex);
+    tp->num_threads_waiting += 1;
+    if (tp->num_threads_waiting == tp->num_threads) {
+      pthread_cond_signal(&tp->control_cond);
+    }
+
+    /* Wait for the controller. */
+    pthread_cond_wait(&tp->thread_cond, &tp->thread_mutex);
+    tp->num_threads_waiting -= 1;
+    tp->num_threads_running += 1;
+    if (tp->num_threads_running == tp->num_threads) {
+      pthread_cond_signal(&tp->control_cond);
+    }
+    pthread_mutex_unlock(&tp->thread_mutex);
+
+    /* The index of the mapping task we will work on next. */
+    size_t task_ind;
+    while ((task_ind = atomic_add(&tp->map_data_count, tp->map_data_chunk)) <
+           tp->map_data_size) {
+      const int num_elements = task_ind + tp->map_data_chunk > tp->map_data_size
+                                   ? tp->map_data_size - task_ind
+                                   : tp->map_data_chunk;
+      tp->map_function((char *)tp->map_data + (tp->map_data_stride * task_ind),
+                       num_elements, tp->map_extra_data);
+    }
+  }
+}
+
+/**
+ * @brief Initialises the #threadpool with a given number of threads.
+ *
+ * @param tp The #threadpool.
+ * @param num_threads The number of threads.
+ */
+void threadpool_init(struct threadpool *tp, int num_threads) {
+
+  /* Initialize the thread counters. */
+  tp->num_threads = num_threads;
+  tp->num_threads_waiting = 0;
+
+  /* Init the threadpool mutexes. */
+  if (pthread_mutex_init(&tp->thread_mutex, NULL) != 0)
+    error("Failed to initialize mutexex.");
+  if (pthread_cond_init(&tp->control_cond, NULL) != 0 ||
+      pthread_cond_init(&tp->thread_cond, NULL) != 0)
+    error("Failed to initialize condition variables.");
+
+  /* Set the task counter to zero. */
+  tp->map_data_size = 0;
+  tp->map_data_count = 0;
+  tp->map_data_stride = 0;
+  tp->map_data_chunk = 0;
+  tp->map_function = NULL;
+
+  /* Allocate the threads. */
+  if ((tp->threads = (pthread_t *)malloc(sizeof(pthread_t) * num_threads)) ==
+      NULL) {
+    error("Failed to allocate thread array.");
+  }
+
+  /* Create and start the threads. */
+  pthread_mutex_lock(&tp->thread_mutex);
+  for (int k = 0; k < num_threads; k++) {
+    if (pthread_create(&tp->threads[k], NULL, &threadpool_runner, tp) != 0)
+      error("Failed to create threadpool runner thread.");
+  }
+
+  /* Wait for all the threads to be up and running. */
+  while (tp->num_threads_waiting < tp->num_threads) {
+    pthread_cond_wait(&tp->control_cond, &tp->thread_mutex);
+  }
+  pthread_mutex_unlock(&tp->thread_mutex);
+}
+
+/**
+ * @brief Map a function to an array of data in parallel using a #threadpool.
+ *
+ * The function @c map_function is called on each element of @c map_data
+ * in parallel.
+ *
+ * @param tp The #threadpool on which to run.
+ * @param map_function The function that will be applied to the map data.
+ * @param map_data The data on which the mapping function will be called.
+ * @param N Number of elements in @c map_data.
+ * @param stride Size, in bytes, of each element of @c map_data.
+ * @param chunk Number of map data elements to pass to the function at a time.
+ * @param extra_data Addtitional pointer that will be passed to the mapping
+ *        function, may contain additional data.
+ */
+void threadpool_map(struct threadpool *tp, threadpool_map_function map_function,
+                    void *map_data, size_t N, int stride, int chunk,
+                    void *extra_data) {
+
+  /* Set the map data and signal the threads. */
+  pthread_mutex_lock(&tp->thread_mutex);
+  tp->map_data_stride = stride;
+  tp->map_data_size = N;
+  tp->map_data_count = 0;
+  tp->map_data_chunk = chunk;
+  tp->map_function = map_function;
+  tp->map_data = map_data;
+  tp->map_extra_data = extra_data;
+  tp->num_threads_running = 0;
+  pthread_cond_broadcast(&tp->thread_cond);
+
+  /* Wait for all the threads to be up and running. */
+  while (tp->num_threads_running < tp->num_threads) {
+    pthread_cond_wait(&tp->control_cond, &tp->thread_mutex);
+  }
+
+  /* Wait for all threads to be done. */
+  while (tp->num_threads_waiting < tp->num_threads) {
+    pthread_cond_wait(&tp->control_cond, &tp->thread_mutex);
+  }
+  pthread_mutex_unlock(&tp->thread_mutex);
+}
+
+/**
+ * @brief Frees up the memory allocated for this #threadpool.
+ */
+void threadpool_clean(struct threadpool *tp) { free(tp->threads); }
diff --git a/src/threadpool.h b/src/threadpool.h
new file mode 100644
index 0000000000000000000000000000000000000000..76aa0c119610c4d540e117f046b286095a9c676d
--- /dev/null
+++ b/src/threadpool.h
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Pedro Gonnet (pedro.gonnet@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_THREADPOOL_H
+#define SWIFT_THREADPOOL_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <pthread.h>
+
+/* Function type for mappings. */
+typedef void (*threadpool_map_function)(void *map_data, int num_elements,
+                                        void *extra_data);
+
+/* Data of a threadpool. */
+struct threadpool {
+
+  /* Number of threads in this pool. */
+  int num_threads;
+
+  /* The threads themselves. */
+  pthread_t *threads;
+
+  /* This is where threads go to rest. */
+  pthread_mutex_t thread_mutex;
+  pthread_cond_t control_cond, thread_cond;
+
+  /* Current map data and count. */
+  void *map_data, *map_extra_data;
+  volatile size_t map_data_count, map_data_size, map_data_stride,
+      map_data_chunk;
+  volatile threadpool_map_function map_function;
+
+  /* Counter for the number of threads that are done. */
+  volatile int num_threads_waiting, num_threads_running;
+};
+
+/* Function prototypes. */
+void threadpool_init(struct threadpool *tp, int num_threads);
+void threadpool_map(struct threadpool *tp, threadpool_map_function map_function,
+                    void *map_data, size_t N, int stride, int chunk,
+                    void *extra_data);
+void threadpool_clean(struct threadpool *tp);
+
+#endif /* SWIFT_THREADPOOL_H */
diff --git a/src/timers.c b/src/timers.c
index b621d27c90902f06c3760cbef6a88237a2b3b95b..c2f3b35d75ea340082acf1a5ec334bb3543bab12 100644
--- a/src/timers.c
+++ b/src/timers.c
@@ -37,11 +37,11 @@ ticks timers[timer_count];
  * To reset all timers, use the mask #timers_mask_all.
  */
 
-void timers_reset(unsigned int mask) {
+void timers_reset(unsigned long long mask) {
 
   int k;
 
   /* Loop over the timers and set the masked ones to zero. */
   for (k = 0; k < timer_count; k++)
-    if (mask & (1 << k)) timers[k] = 0;
+    if (mask & (1ull << k)) timers[k] = 0;
 }
diff --git a/src/timers.h b/src/timers.h
index 023e31a1b24dd57ac8aa215610b57aaad1a42b3d..b93a34df4d90251d4686afaef0be51b36dc9a25e 100644
--- a/src/timers.h
+++ b/src/timers.h
@@ -36,17 +36,21 @@ enum {
   timer_kick,
   timer_dosort,
   timer_doself_density,
+  timer_doself_gradient,
   timer_doself_force,
   timer_doself_grav_pp,
   timer_dopair_density,
+  timer_dopair_gradient,
   timer_dopair_force,
   timer_dopair_grav_pm,
   timer_dopair_grav_pp,
   timer_dograv_external,
   timer_dosub_self_density,
+  timer_dosub_self_gradient,
   timer_dosub_self_force,
   timer_dosub_self_grav,
   timer_dosub_pair_density,
+  timer_dosub_pair_gradient,
   timer_dosub_pair_force,
   timer_dosub_pair_grav,
   timer_dopair_subset,
@@ -65,7 +69,7 @@ enum {
 extern ticks timers[timer_count];
 
 /* Mask for all timers. */
-#define timers_mask_all ((1 << timer_count) - 1)
+#define timers_mask_all ((1ull << timer_count) - 1)
 
 /* Define the timer macros. */
 #ifdef TIMER
@@ -75,7 +79,7 @@ extern ticks timers[timer_count];
 #define TIMER_TOC(t) timers_toc(t, tic)
 #define TIMER_TIC2 ticks tic2 = getticks();
 #define TIMER_TOC2(t) timers_toc(t, tic2)
-INLINE static ticks timers_toc(int t, ticks tic) {
+INLINE static ticks timers_toc(unsigned int t, ticks tic) {
   ticks d = (getticks() - tic);
   atomic_add(&timers[t], d);
   return d;
@@ -88,6 +92,6 @@ INLINE static ticks timers_toc(int t, ticks tic) {
 #endif
 
 /* Function prototypes. */
-void timers_reset(unsigned int mask);
+void timers_reset(unsigned long long mask);
 
 #endif /* SWIFT_TIMERS_H */
diff --git a/src/timestep.h b/src/timestep.h
index f32a699f6338362c3de247e5a5abc419b9547374..d92f88d06451892ce47db4b9468db9714bb52baa 100644
--- a/src/timestep.h
+++ b/src/timestep.h
@@ -70,14 +70,16 @@ __attribute__((always_inline)) INLINE static int get_gpart_timestep(
 
   const float new_dt_external = gravity_compute_timestep_external(
       e->external_potential, e->physical_constants, gp);
-  const float new_dt_self =
-      gravity_compute_timestep_self(e->physical_constants, gp);
+  /* const float new_dt_self = */
+  /*     gravity_compute_timestep_self(e->physical_constants, gp); */
+  const float new_dt_self = FLT_MAX;  // MATTHIEU
 
-  float new_dt = fminf(new_dt_external, new_dt_self);
+  float new_dt =
+      (new_dt_external < new_dt_self) ? new_dt_external : new_dt_self;
 
   /* Limit timestep within the allowed range */
-  new_dt = fminf(new_dt, e->dt_max);
-  new_dt = fmaxf(new_dt, e->dt_min);
+  new_dt = (new_dt < e->dt_max) ? new_dt : e->dt_max;
+  new_dt = (new_dt > e->dt_min) ? new_dt : e->dt_min;
 
   /* Convert to integer time */
   const int new_dti =
@@ -106,14 +108,16 @@ __attribute__((always_inline)) INLINE static int get_part_timestep(
 
     const float new_dt_external = gravity_compute_timestep_external(
         e->external_potential, e->physical_constants, p->gpart);
-    const float new_dt_self =
-        gravity_compute_timestep_self(e->physical_constants, p->gpart);
+    /* const float new_dt_self = */
+    /*     gravity_compute_timestep_self(e->physical_constants, p->gpart); */
+    const float new_dt_self = FLT_MAX;  // MATTHIEU
 
-    new_dt_grav = fminf(new_dt_external, new_dt_self);
+    new_dt_grav =
+        (new_dt_external < new_dt_self) ? new_dt_external : new_dt_self;
   }
 
   /* Final time-step is minimum of hydro and gravity */
-  float new_dt = fminf(new_dt_hydro, new_dt_grav);
+  float new_dt = (new_dt_hydro < new_dt_grav) ? new_dt_hydro : new_dt_grav;
 
   /* Limit change in h */
   const float dt_h_change =
@@ -121,11 +125,11 @@ __attribute__((always_inline)) INLINE static int get_part_timestep(
           ? fabsf(e->hydro_properties->log_max_h_change * p->h / p->force.h_dt)
           : FLT_MAX;
 
-  new_dt = fminf(new_dt, dt_h_change);
+  new_dt = (new_dt < dt_h_change) ? new_dt : dt_h_change;
 
   /* Limit timestep within the allowed range */
-  new_dt = fminf(new_dt, e->dt_max);
-  new_dt = fmaxf(new_dt, e->dt_min);
+  new_dt = (new_dt < e->dt_max) ? new_dt : e->dt_max;
+  new_dt = (new_dt > e->dt_min) ? new_dt : e->dt_min;
 
   /* Convert to integer time */
   const int new_dti =
diff --git a/src/tools.c b/src/tools.c
index b4a37328ff7df9de76adceebb5ed6801fa417851..060bf1439f30dc6237938c060bc4ddc8d9be822b 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -144,10 +144,7 @@ void pairs_single_density(double *dim, long long int pid,
   p = parts[k];
   printf("pairs_single: part[%i].id == %lli.\n", k, pid);
 
-  p.rho = 0.0;
-  p.density.wcount = 0.0;
-  // p.icount = 0;
-  p.rho_dh = 0.0;
+  hydro_init_part(&p);
 
   /* Loop over all particle pairs. */
   for (k = 0; k < N; k++) {
@@ -445,67 +442,29 @@ void pairs_single_grav(double *dim, long long int pid,
       aabs[2]);
 }
 
-/**
- * @brief Test the density function by dumping it for two random parts.
- *
- * @param N number of intervals in [0,1].
- */
-void density_dump(int N) {
-
-  int k;
-  float r2[4] = {0.0f, 0.0f, 0.0f, 0.0f}, hi[4], hj[4];
-  struct part /**pi[4],  *pj[4],*/ Pi[4], Pj[4];
-
-  /* Init the interaction parameters. */
-  for (k = 0; k < 4; k++) {
-    Pi[k].mass = 1.0f;
-    Pi[k].rho = 0.0f;
-    Pi[k].density.wcount = 0.0f;
-    Pi[k].id = k;
-    Pj[k].mass = 1.0f;
-    Pj[k].rho = 0.0f;
-    Pj[k].density.wcount = 0.0f;
-    Pj[k].id = k + 4;
-    hi[k] = 1.0;
-    hj[k] = 1.0;
-  }
-
-  for (k = 0; k <= N; k++) {
-    r2[3] = r2[2];
-    r2[2] = r2[1];
-    r2[1] = r2[0];
-    r2[0] = ((float)k) / N;
-    Pi[0].density.wcount = 0;
-    Pj[0].density.wcount = 0;
-    runner_iact_density(r2[0], NULL, hi[0], hj[0], &Pi[0], &Pj[0]);
-    printf(" %e %e %e", r2[0], Pi[0].density.wcount, Pj[0].density.wcount);
-  }
-}
-
 /**
  * @brief Compute the force on a single particle brute-force.
  */
 void engine_single_density(double *dim, long long int pid,
                            struct part *restrict parts, int N, int periodic) {
-  int i, k;
   double r2, dx[3];
-  float fdx[3], ih;
+  float fdx[3];
   struct part p;
 
   /* Find "our" part. */
+  int k;
   for (k = 0; k < N && parts[k].id != pid; k++)
     ;
   if (k == N) error("Part not found.");
   p = parts[k];
 
   /* Clear accumulators. */
-  ih = 1.0f / p.h;
   hydro_init_part(&p);
 
   /* Loop over all particle pairs (force). */
   for (k = 0; k < N; k++) {
     if (parts[k].id == p.id) continue;
-    for (i = 0; i < 3; i++) {
+    for (int i = 0; i < 3; i++) {
       dx[i] = p.x[i] - parts[k].x[i];
       if (periodic) {
         if (dx[i] < -dim[i] / 2)
@@ -522,12 +481,9 @@ void engine_single_density(double *dim, long long int pid,
   }
 
   /* Dump the result. */
-  p.rho = ih * ih * ih * (p.rho + p.mass * kernel_root);
-  p.rho_dh = p.rho_dh * ih * ih * ih * ih;
-  p.density.wcount =
-      (p.density.wcount + kernel_root) * (4.0f / 3.0 * M_PI * kernel_gamma3);
-  message("part %lli (h=%e) has wcount=%e, rho=%e, rho_dh=%e.", p.id, p.h,
-          p.density.wcount, p.rho, p.rho_dh);
+  hydro_end_density(&p, 0);
+  message("part %lli (h=%e) has wcount=%e, rho=%e.", p.id, p.h,
+          p.density.wcount, hydro_get_density(&p));
   fflush(stdout);
 }
 
diff --git a/src/units.c b/src/units.c
index 5c262ae03639262dfa126b101402b8fbfe41259a..f598b5ddf0b1a4b165648d5378915cd6f10f0bba 100644
--- a/src/units.c
+++ b/src/units.c
@@ -316,6 +316,10 @@ void units_get_base_unit_exponants_array(float baseUnitsExp[5],
 
     case UNIT_CONV_TEMPERATURE:
       baseUnitsExp[UNIT_TEMPERATURE] = 1.f;
+      break;
+
+    case UNIT_CONV_VOLUME:
+      baseUnitsExp[UNIT_LENGTH] = -3.f;
   }
 }
 
diff --git a/src/units.h b/src/units.h
index c67f6ebbab324e3c90ae86eb1ea11dcf013c5dc7..26fa15a66528dd39ea232cdf94da2ff0230300cd 100644
--- a/src/units.h
+++ b/src/units.h
@@ -29,25 +29,25 @@
  * @brief The unit system used internally.
  *
  * This structure contains the conversion factors to the 7 cgs base units to the
- *internal units.
- * It is used everytime a conversion is performed or an i/o function is called.
- *
+ * internal units. It is used everytime a conversion is performed or an i/o
+ * function is called.
  **/
 struct UnitSystem {
-  double UnitMass_in_cgs; /*< Conversion factor from grams to internal mass
-                             units */
 
-  double UnitLength_in_cgs; /*< Conversion factor from centimeters to internal
-                               length units. */
+  /*! Conversion factor from grams to internal mass units */
+  double UnitMass_in_cgs;
+
+  /*! Conversion factor from centimeters to internal length unit */
+  double UnitLength_in_cgs;
 
-  double UnitTime_in_cgs; /*< Conversion factor from seconds to internal time
-                             units. */
+  /*! Conversion factor from seconds to internal time units */
+  double UnitTime_in_cgs;
 
-  double UnitCurrent_in_cgs; /*< Conversion factor from Ampere to internal
-                                current units. */
+  /*! Conversion factor from Ampere to internal current units */
+  double UnitCurrent_in_cgs;
 
-  double UnitTemperature_in_cgs; /*< Conversion factor from Kelvins to internal
-                                    temperature units. */
+  /*! Conversion factor from Kelvins to internal temperature units. */
+  double UnitTemperature_in_cgs;
 };
 
 /**
@@ -89,7 +89,8 @@ enum UnitConversionFactor {
   UNIT_CONV_MAGNETIC_FLUX,
   UNIT_CONV_MAGNETIC_FIELD,
   UNIT_CONV_MAGNETIC_INDUCTANCE,
-  UNIT_CONV_TEMPERATURE
+  UNIT_CONV_TEMPERATURE,
+  UNIT_CONV_VOLUME
 };
 
 void units_init_cgs(struct UnitSystem*);
diff --git a/src/version.c b/src/version.c
index 68a051fa08c53c68319c1e785c0c6503afe18f4d..8bd94e5651dbc597fcd80bc585a47c6633ee3993 100644
--- a/src/version.c
+++ b/src/version.c
@@ -218,7 +218,7 @@ const char *hdf5_version(void) {
 #ifdef HAVE_HDF5
   unsigned int majnum, minnum, relnum;
   H5get_libversion(&majnum, &minnum, &relnum);
-  sprintf(version, "%i.%i.%i", majnum, minnum, relnum);
+  sprintf(version, "%u.%u.%u", majnum, minnum, relnum);
 #else
   sprintf(version, "Unknown version");
 #endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2adce2c90985b537a668adfccec0d5241113314e..136b7ad231947574a5459298e7fb85902028a3f4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -23,12 +23,17 @@ AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS
 # List of programs and scripts to run in the test suite
 TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetry \
         testPair.sh testPairPerturbed.sh test27cells.sh test27cellsPerturbed.sh  \
-        testParser.sh testSPHStep test125cells.sh testKernelGrav testFFT
+        testParser.sh testSPHStep test125cells.sh testKernelGrav testFFT \
+        testAdiabaticIndex testRiemannExact testRiemannTRRS testRiemannHLLC \
+        testMatrixInversion testThreadpool
 
 # List of test programs to compile
 check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \
 		 testSPHStep testPair test27cells test125cells testParser \
-                 testKernel testKernelGrav testFFT testInteractions testMaths testSymmetry
+                 testKernel testKernelGrav testFFT testInteractions testMaths \
+                 testSymmetry testThreadpool \
+                 testAdiabaticIndex testRiemannExact testRiemannTRRS \
+                 testRiemannHLLC testMatrixInversion
 
 # Sources for the individual programs
 testGreetings_SOURCES = testGreetings.c
@@ -61,7 +66,21 @@ testFFT_SOURCES = testFFT.c
 
 testInteractions_SOURCES = testInteractions.c
 
+testAdiabaticIndex_SOURCES = testAdiabaticIndex.c
+
+testRiemannExact_SOURCES = testRiemannExact.c
+
+testRiemannTRRS_SOURCES = testRiemannTRRS.c
+
+testRiemannHLLC_SOURCES = testRiemannHLLC.c
+
+testMatrixInversion_SOURCES = testMatrixInversion.c
+
+testThreadpool_SOURCES = testThreadpool.c
+
 # Files necessary for distribution
 EXTRA_DIST = testReading.sh makeInput.py testPair.sh testPairPerturbed.sh \
-	     test27cells.sh test27cellsPerturbed.sh tolerance.dat testParser.sh \
-	     test125cells.sh testParserInput.yaml
+	     test27cells.sh test27cellsPerturbed.sh testParser.sh \
+	     test125cells.sh testParserInput.yaml difffloat.py \
+	     tolerance_125.dat tolerance_27_normal.dat tolerance_27_perturbed.dat \
+	     tolerance_pair_normal.dat tolerance_pair_perturbed.dat
diff --git a/tests/difffloat.py b/tests/difffloat.py
index 57707c5920997e3ef688606a0839f59a69d2e4f2..e0f0864372264899c6de1bf2f83ab678b7dd9ead 100644
--- a/tests/difffloat.py
+++ b/tests/difffloat.py
@@ -89,13 +89,12 @@ for i in range(n_lines_to_check):
         abs_diff = abs(data1[i,j] - data2[i,j])
 
         sum = abs(data1[i,j] + data2[i,j])
-        if abs(data1[i,j]) + abs(data2[i,j]) < 2.5e-7: continue
         if sum > 0:
             rel_diff = abs(data1[i,j] - data2[i,j]) / sum
         else:
             rel_diff = 0.
 
-        if( abs_diff > absTol[j]):
+        if( abs_diff > 1.1*absTol[j]):
             print "Absolute difference larger than tolerance (%e) for particle %d, column %d:"%(absTol[j], i,j)
             print "%10s:           a = %e"%("File 1", data1[i,j])
             print "%10s:           b = %e"%("File 2", data2[i,j])
@@ -103,7 +102,9 @@ for i in range(n_lines_to_check):
             print ""
             error = True
 
-        if( rel_diff > relTol[j]):
+        if abs(data1[i,j]) < 1e-6 and + abs(data2[i,j]) < 1e-6 : continue
+            
+        if( rel_diff > 1.1*relTol[j]):
             print "Relative difference larger than tolerance (%e) for particle %d, column %d:"%(relTol[j], i,j)
             print "%10s:           a = %e"%("File 1", data1[i,j])
             print "%10s:           b = %e"%("File 2", data2[i,j])
diff --git a/tests/test125cells.c b/tests/test125cells.c
index c2e80f9535801b693a3144bd9d6fe9b541c61feb..c7e01693b45f76fa21ffd397289fc06ad36af03d 100644
--- a/tests/test125cells.c
+++ b/tests/test125cells.c
@@ -99,6 +99,8 @@ void set_energy_state(struct part *part, enum pressure_field press, float size,
   part->u = pressure / (hydro_gamma_minus_one * density);
 #elif defined(MINIMAL_SPH)
   part->u = pressure / (hydro_gamma_minus_one * density);
+#elif defined(GIZMO_SPH)
+  part->primitives.P = pressure;
 #else
   error("Need to define pressure here !");
 #endif
@@ -127,7 +129,7 @@ void get_solution(const struct cell *main_cell, struct solution_part *solution,
                   float density, enum velocity_field vel,
                   enum pressure_field press, float size) {
 
-  for (size_t i = 0; i < main_cell->count; ++i) {
+  for (int i = 0; i < main_cell->count; ++i) {
 
     solution[i].id = main_cell->parts[i].id;
 
@@ -178,6 +180,38 @@ void get_solution(const struct cell *main_cell, struct solution_part *solution,
     solution[i].a_hydro[2] = -gradP[2] / solution[i].rho;
 
     solution[i].v_sig = 2.f * solution[i].c;
+
+    solution[i].S_dt = 0.f;
+    solution[i].u_dt = -(solution[i].P / solution[i].rho) * solution[i].div_v;
+  }
+}
+
+void reset_particles(struct cell *c, enum velocity_field vel,
+                     enum pressure_field press, float size, float density) {
+
+  for (int i = 0; i < c->count; ++i) {
+
+    struct part *p = &c->parts[i];
+
+    set_velocity(p, vel, size);
+    set_energy_state(p, press, size, density);
+
+#if defined(GIZMO_SPH)
+    p->geometry.volume = p->conserved.mass / density;
+    p->primitives.rho = density;
+    p->primitives.v[0] = p->v[0];
+    p->primitives.v[1] = p->v[1];
+    p->primitives.v[2] = p->v[2];
+    p->conserved.momentum[0] = p->conserved.mass * p->v[0];
+    p->conserved.momentum[1] = p->conserved.mass * p->v[1];
+    p->conserved.momentum[2] = p->conserved.mass * p->v[2];
+    p->conserved.energy =
+        p->primitives.P / hydro_gamma_minus_one * p->geometry.volume +
+        0.5f * (p->conserved.momentum[0] * p->conserved.momentum[0] +
+                p->conserved.momentum[1] * p->conserved.momentum[1] +
+                p->conserved.momentum[2] * p->conserved.momentum[2]) /
+            p->conserved.mass;
+#endif
   }
 }
 
@@ -223,7 +257,12 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
         part->x[1] = offset[1] + size * (y + 0.5) / (float)n;
         part->x[2] = offset[2] + size * (z + 0.5) / (float)n;
         part->h = size * h / (float)n;
+
+#ifdef GIZMO_SPH
+        part->conserved.mass = density * volume / count;
+#else
         part->mass = density * volume / count;
+#endif
 
         set_velocity(part, vel, size);
         set_energy_state(part, press, size, density);
@@ -232,9 +271,25 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
         part->ti_begin = 0;
         part->ti_end = 1;
 
-        xpart->v_full[0] = part->v[0];
-        xpart->v_full[1] = part->v[1];
-        xpart->v_full[2] = part->v[2];
+        hydro_first_init_part(part, xpart);
+
+#if defined(GIZMO_SPH)
+        part->geometry.volume = part->conserved.mass / density;
+        part->primitives.rho = density;
+        part->primitives.v[0] = part->v[0];
+        part->primitives.v[1] = part->v[1];
+        part->primitives.v[2] = part->v[2];
+        part->conserved.momentum[0] = part->conserved.mass * part->v[0];
+        part->conserved.momentum[1] = part->conserved.mass * part->v[1];
+        part->conserved.momentum[2] = part->conserved.mass * part->v[2];
+        part->conserved.energy =
+            part->primitives.P / hydro_gamma_minus_one * part->geometry.volume +
+            0.5f * (part->conserved.momentum[0] * part->conserved.momentum[0] +
+                    part->conserved.momentum[1] * part->conserved.momentum[1] +
+                    part->conserved.momentum[2] * part->conserved.momentum[2]) /
+                part->conserved.mass;
+#endif
+
         ++part;
         ++xpart;
       }
@@ -291,7 +346,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
   fprintf(file, "# Main cell --------------------------------------------\n");
 
   /* Write main cell */
-  for (size_t pid = 0; pid < main_cell->count; pid++) {
+  for (int pid = 0; pid < main_cell->count; pid++) {
     fprintf(file,
             "%6llu %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f "
             "%8.5f "
@@ -300,7 +355,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
             main_cell->parts[pid].x[1], main_cell->parts[pid].x[2],
             main_cell->parts[pid].v[0], main_cell->parts[pid].v[1],
             main_cell->parts[pid].v[2], main_cell->parts[pid].h,
-            main_cell->parts[pid].rho,
+            hydro_get_density(&main_cell->parts[pid]),
 #ifdef MINIMAL_SPH
             0.f,
 #else
@@ -312,15 +367,16 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
             hydro_get_soundspeed(&main_cell->parts[pid], 0.f),
             main_cell->parts[pid].a_hydro[0], main_cell->parts[pid].a_hydro[1],
             main_cell->parts[pid].a_hydro[2], main_cell->parts[pid].force.h_dt,
-            main_cell->parts[pid].force.v_sig,
 #if defined(GADGET2_SPH)
-            main_cell->parts[pid].entropy_dt, 0.f
+            main_cell->parts[pid].force.v_sig, main_cell->parts[pid].entropy_dt,
+            0.f
 #elif defined(DEFAULT_SPH)
-            0.f, main_cell->parts[pid].force.u_dt
+            main_cell->parts[pid].force.v_sig, 0.f,
+            main_cell->parts[pid].force.u_dt
 #elif defined(MINIMAL_SPH)
-            0.f, main_cell->parts[pid].u_dt
+            main_cell->parts[pid].force.v_sig, 0.f, main_cell->parts[pid].u_dt
 #else
-            0.f, 0.f
+            0.f, 0.f, 0.f
 #endif
             );
   }
@@ -329,7 +385,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
 
     fprintf(file, "# Solution ---------------------------------------------\n");
 
-    for (size_t pid = 0; pid < main_cell->count; pid++) {
+    for (int pid = 0; pid < main_cell->count; pid++) {
       fprintf(file,
               "%6llu %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f "
               "%8.5f %8.5f "
@@ -340,7 +396,8 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
               solution[pid].div_v, solution[pid].S, solution[pid].u,
               solution[pid].P, solution[pid].c, solution[pid].a_hydro[0],
               solution[pid].a_hydro[1], solution[pid].a_hydro[2],
-              solution[pid].h_dt, solution[pid].v_sig, solution[pid].S_dt, 0.f);
+              solution[pid].h_dt, solution[pid].v_sig, solution[pid].S_dt,
+              solution[pid].u_dt);
     }
   }
 
@@ -427,9 +484,10 @@ int main(int argc, char *argv[]) {
 
   /* Help users... */
   message("Adiabatic index: ga = %f", hydro_gamma);
+  message("Hydro implementation: %s", SPH_IMPLEMENTATION);
   message("Smoothing length: h = %f", h * size);
   message("Kernel:               %s", kernel_name);
-  message("Neighbour target: N = %f", h * h * h * kernel_norm);
+  message("Neighbour target: N = %f", pow_dimension(h) * kernel_norm);
   message("Density target: rho = %f", rho);
   message("div_v target:   div = %f", vel == 2 ? 3.f : 0.f);
   message("curl_v target: curl = [0., 0., %f]", vel == 3 ? -2.f : 0.f);
@@ -442,18 +500,27 @@ int main(int argc, char *argv[]) {
 
   printf("\n");
 
+#if !defined(HYDRO_DIMENSION_3D)
+  message("test125cells only useful in 3D. Change parameters in const.h !");
+  return 1;
+#endif
+
   /* Build the infrastructure */
   struct space space;
   space.periodic = 0;
-  space.h_max = h;
+
+  struct phys_const prog_const;
+  prog_const.const_newton_G = 1.f;
 
   struct hydro_props hp;
-  hp.target_neighbours = h * h * h * kernel_norm;
-  hp.delta_neighbours = 1.;
+  hp.target_neighbours = pow_dimension(h) * kernel_norm;
+  hp.delta_neighbours = 2.;
   hp.max_smoothing_iterations = 1;
+  hp.CFL_condition = 0.1;
 
   struct engine engine;
   engine.hydro_properties = &hp;
+  engine.physical_constants = &prog_const;
   engine.s = &space;
   engine.time = 0.1f;
   engine.ti_current = 1;
@@ -508,7 +575,7 @@ int main(int argc, char *argv[]) {
     for (int j = 0; j < 125; ++j) runner_do_init(&runner, cells[j], 0);
 
 /* Do the density calculation */
-#if defined(DEFAULT_SPH) || !defined(WITH_VECTORIZATION)
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
     /* Run all the pairs (only once !)*/
     for (int i = 0; i < 5; i++) {
@@ -550,7 +617,7 @@ int main(int argc, char *argv[]) {
     for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j]);
 
 /* Do the force calculation */
-#if defined(DEFAULT_SPH) || !defined(WITH_VECTORIZATION)
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
     /* Do the pairs (for the central 27 cells) */
     for (int i = 1; i < 4; i++) {
@@ -585,6 +652,9 @@ int main(int argc, char *argv[]) {
   /* Output timing */
   message("SWIFT calculation took       : %15lli ticks.", time / runs);
 
+  for (int j = 0; j < 125; ++j)
+    reset_particles(cells[j], vel, press, size, rho);
+
   /* NOW BRUTE-FORCE CALCULATION */
 
   const ticks tic = getticks();
@@ -593,7 +663,7 @@ int main(int argc, char *argv[]) {
   for (int j = 0; j < 125; ++j) runner_do_init(&runner, cells[j], 0);
 
 /* Do the density calculation */
-#if defined(DEFAULT_SPH) || !defined(WITH_VECTORIZATION)
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
   /* Run all the pairs (only once !)*/
   for (int i = 0; i < 5; i++) {
@@ -634,7 +704,7 @@ int main(int argc, char *argv[]) {
   for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j]);
 
 /* Do the force calculation */
-#if defined(DEFAULT_SPH) || !defined(WITH_VECTORIZATION)
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
   /* Do the pairs (for the central 27 cells) */
   for (int i = 1; i < 4; i++) {
@@ -650,6 +720,7 @@ int main(int argc, char *argv[]) {
 
   /* And now the self-interaction for the main cell */
   self_all_force(&runner, main_cell);
+
 #endif
 
   /* Finally, give a gentle kick */
diff --git a/tests/test125cells.sh.in b/tests/test125cells.sh.in
index 2d724438af1f32d92e3fb3ab11319818c028939e..1d3b0db75d70bf2d5047f71b183812702305df75 100755
--- a/tests/test125cells.sh.in
+++ b/tests/test125cells.sh.in
@@ -4,9 +4,18 @@ do
     for p in {0..2}
     do
 	echo ""
-	rm brute_force_125_standard.dat swift_dopair_125_standard.dat
+
+	rm -f brute_force_125_standard.dat swift_dopair_125_standard.dat
+
 	./test125cells -n 6 -r 1 -v $v -p $p -f standard
-	python @srcdir@/difffloat.py brute_force_125_standard.dat swift_dopair_125_standard.dat @srcdir@/tolerance_125.dat 6
+
+	if [ -e brute_force_125_standard.dat ]
+	then
+	    python @srcdir@/difffloat.py brute_force_125_standard.dat swift_dopair_125_standard.dat @srcdir@/tolerance_125.dat 6
+	else
+	    exit 1
+        fi
+
     done
 done
 	
diff --git a/tests/test27cells.c b/tests/test27cells.c
index 09981af2b99d050bb2db3395d19396defe3b8153..1a1ab88748d922b3e7fbb30a73a10809dca10863 100644
--- a/tests/test27cells.c
+++ b/tests/test27cells.c
@@ -104,7 +104,11 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
         }
         part->h = size * h / (float)n;
         part->id = ++(*partId);
+#ifdef GIZMO_SPH
+        part->conserved.mass = density * volume / count;
+#else
         part->mass = density * volume / count;
+#endif
         part->ti_begin = 0;
         part->ti_end = 1;
         ++part;
@@ -146,9 +150,7 @@ void clean_up(struct cell *ci) {
  * @brief Initializes all particles field to be ready for a density calculation
  */
 void zero_particle_fields(struct cell *c) {
-  for (size_t pid = 0; pid < c->count; pid++) {
-    c->parts[pid].rho = 0.f;
-    c->parts[pid].rho_dh = 0.f;
+  for (int pid = 0; pid < c->count; pid++) {
     hydro_init_part(&c->parts[pid]);
   }
 }
@@ -157,7 +159,7 @@ void zero_particle_fields(struct cell *c) {
  * @brief Ends the loop by adding the appropriate coefficients
  */
 void end_calculation(struct cell *c) {
-  for (size_t pid = 0; pid < c->count; pid++) {
+  for (int pid = 0; pid < c->count; pid++) {
     hydro_end_density(&c->parts[pid], 1);
   }
 }
@@ -179,15 +181,21 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
   fprintf(file, "# Main cell --------------------------------------------\n");
 
   /* Write main cell */
-  for (size_t pid = 0; pid < main_cell->count; pid++) {
+  for (int pid = 0; pid < main_cell->count; pid++) {
     fprintf(file,
             "%6llu %10f %10f %10f %10f %10f %10f %13e %13e %13e %13e %13e "
             "%13e %13e %13e\n",
             main_cell->parts[pid].id, main_cell->parts[pid].x[0],
             main_cell->parts[pid].x[1], main_cell->parts[pid].x[2],
             main_cell->parts[pid].v[0], main_cell->parts[pid].v[1],
-            main_cell->parts[pid].v[2], main_cell->parts[pid].rho,
-            main_cell->parts[pid].rho_dh, main_cell->parts[pid].density.wcount,
+            main_cell->parts[pid].v[2],
+            hydro_get_density(&main_cell->parts[pid]),
+#if defined(GIZMO_SPH)
+            0.f,
+#else
+            main_cell->parts[pid].rho_dh,
+#endif
+            main_cell->parts[pid].density.wcount,
             main_cell->parts[pid].density.wcount_dh,
 #if defined(GADGET2_SPH)
             main_cell->parts[pid].density.div_v,
@@ -216,14 +224,19 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
                 "# Offset: [%2d %2d %2d] -----------------------------------\n",
                 i - 1, j - 1, k - 1);
 
-        for (size_t pjd = 0; pjd < cj->count; pjd++) {
+        for (int pjd = 0; pjd < cj->count; pjd++) {
           fprintf(
               file,
               "%6llu %10f %10f %10f %10f %10f %10f %13e %13e %13e %13e %13e "
               "%13e %13e %13e\n",
               cj->parts[pjd].id, cj->parts[pjd].x[0], cj->parts[pjd].x[1],
               cj->parts[pjd].x[2], cj->parts[pjd].v[0], cj->parts[pjd].v[1],
-              cj->parts[pjd].v[2], cj->parts[pjd].rho, cj->parts[pjd].rho_dh,
+              cj->parts[pjd].v[2], hydro_get_density(&cj->parts[pjd]),
+#if defined(GIZMO_SPH)
+              0.f,
+#else
+              main_cell->parts[pjd].rho_dh,
+#endif
               cj->parts[pjd].density.wcount, cj->parts[pjd].density.wcount_dh,
 #if defined(GADGET2_SPH)
               cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0],
@@ -318,9 +331,10 @@ int main(int argc, char *argv[]) {
 
   /* Help users... */
   message("Adiabatic index: ga = %f", hydro_gamma);
+  message("Hydro implementation: %s", SPH_IMPLEMENTATION);
   message("Smoothing length: h = %f", h * size);
   message("Kernel:               %s", kernel_name);
-  message("Neighbour target: N = %f", h * h * h * kernel_norm);
+  message("Neighbour target: N = %f", pow_dimension(h) * kernel_norm);
   message("Density target: rho = %f", rho);
   message("div_v target:   div = %f", vel == 2 ? 3.f : 0.f);
   message("curl_v target: curl = [0., 0., %f]", vel == 3 ? -2.f : 0.f);
@@ -330,7 +344,6 @@ int main(int argc, char *argv[]) {
   /* Build the infrastructure */
   struct space space;
   space.periodic = 0;
-  space.h_max = h;
 
   struct engine engine;
   engine.s = &space;
@@ -366,7 +379,7 @@ int main(int argc, char *argv[]) {
 
     const ticks tic = getticks();
 
-#if defined(DEFAULT_SPH) || defined(GADGET2_SPH)
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
     /* Run all the pairs */
     for (int j = 0; j < 27; ++j)
@@ -402,7 +415,7 @@ int main(int argc, char *argv[]) {
 
   const ticks tic = getticks();
 
-#if defined(DEFAULT_SPH) || defined(GADGET2_SPH)
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
   /* Run all the brute-force pairs */
   for (int j = 0; j < 27; ++j)
diff --git a/tests/test27cells.sh.in b/tests/test27cells.sh.in
index 966c8618cfdd9f5f5042f236b76d14bed8e735a0..bf9cfeaf9a70790a321fa7ec4c63983d8cfd866c 100755
--- a/tests/test27cells.sh.in
+++ b/tests/test27cells.sh.in
@@ -3,9 +3,18 @@
 for v in {0..3}
 do
     echo ""
-    rm brute_force_27_standard.dat swift_dopair_27_standard.dat
+
+    rm -f brute_force_27_standard.dat swift_dopair_27_standard.dat
+
     ./test27cells -n 6 -r 1 -d 0 -f standard -v $v
-    python @srcdir@/difffloat.py brute_force_27_standard.dat swift_dopair_27_standard.dat @srcdir@/tolerance_27.dat 6
+
+    if [ -e brute_force_27_standard.dat ]
+    then
+	python @srcdir@/difffloat.py brute_force_27_standard.dat swift_dopair_27_standard.dat @srcdir@/tolerance_27_normal.dat 6
+    else
+	exit 1
+    fi
+
 done
 
 exit $?
diff --git a/tests/test27cellsPerturbed.sh.in b/tests/test27cellsPerturbed.sh.in
index cb64fb605f41f54729456283a2614435c8456fd3..3cdaf79ab17e705ec69a0b646949cc5a71109796 100755
--- a/tests/test27cellsPerturbed.sh.in
+++ b/tests/test27cellsPerturbed.sh.in
@@ -3,9 +3,18 @@
 for v in {0..3}
 do
     echo ""
-    rm brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat
+
+    rm -f brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat
+
     ./test27cells -n 6 -r 1 -d 0.1 -f perturbed -v $v
-    python @srcdir@/difffloat.py brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat @srcdir@/tolerance_27.dat 6
+
+    if [ -e brute_force_27_perturbed.dat ]
+    then
+	python @srcdir@/difffloat.py brute_force_27_perturbed.dat swift_dopair_27_perturbed.dat @srcdir@/tolerance_27_perturbed.dat 6
+    else
+	exit 1
+    fi
+
 done
 
 exit $?
diff --git a/tests/testAdiabaticIndex.c b/tests/testAdiabaticIndex.c
new file mode 100644
index 0000000000000000000000000000000000000000..e0c8c4f54bd2d6e5ddadb25bc44b96f1ca19aad2
--- /dev/null
+++ b/tests/testAdiabaticIndex.c
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2016 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/>.
+ *
+ ******************************************************************************/
+
+#include "adiabatic_index.h"
+#include "error.h"
+
+/**
+ * @brief Check that a and b are consistent (up to some absolute error)
+ *
+ * @param a First value
+ * @param b Second value
+ * @param s String used to identify this check in messages
+ */
+void check_value(float a, float b, const char* s) {
+  if (fabsf(a - b) > 1.e-5f) {
+    error("Values are inconsistent: %g %g (%s)!", a, b, s);
+  } else {
+    message("Values are consistent: %g %g (%s).", a, b, s);
+  }
+}
+
+/**
+ * @brief Check that the pre-defined adiabatic index constants contain correct
+ * values
+ */
+void check_constants() {
+  float val;
+
+  val = 0.5 * (hydro_gamma + 1.0f) / hydro_gamma;
+  check_value(val, hydro_gamma_plus_one_over_two_gamma, "(gamma+1)/(2 gamma)");
+
+  val = 0.5 * (hydro_gamma - 1.0f) / hydro_gamma;
+  check_value(val, hydro_gamma_minus_one_over_two_gamma, "(gamma-1)/(2 gamma)");
+
+  val = (hydro_gamma - 1.0f) / (hydro_gamma + 1.0f);
+  check_value(val, hydro_gamma_minus_one_over_gamma_plus_one,
+              "(gamma-1)/(gamma+1)");
+
+  val = 2.0f / (hydro_gamma + 1.0f);
+  check_value(val, hydro_two_over_gamma_plus_one, "2/(gamma+1)");
+
+  val = 2.0f / (hydro_gamma - 1.0f);
+  check_value(val, hydro_two_over_gamma_minus_one, "2/(gamma-1)");
+
+  val = 0.5f * (hydro_gamma - 1.0f);
+  check_value(val, hydro_gamma_minus_one_over_two, "(gamma-1)/2");
+
+  val = 2.0f * hydro_gamma / (hydro_gamma - 1.0f);
+  check_value(val, hydro_two_gamma_over_gamma_minus_one, "(2 gamma)/(gamma-1)");
+
+  val = 1.0f / hydro_gamma;
+  check_value(val, hydro_one_over_gamma, "1/gamma");
+}
+
+/**
+ * @brief Check that the adiabatic index power functions return the correct
+ * values
+ */
+void check_functions() {
+  float val_a, val_b;
+  const float x = 0.4;
+
+  val_a = pow(x, -hydro_gamma);
+  val_b = pow_minus_gamma(x);
+  check_value(val_a, val_b, "x^(-gamma)");
+
+  val_a = pow(x, 2.0f / (hydro_gamma - 1.0f));
+  val_b = pow_two_over_gamma_minus_one(x);
+  check_value(val_a, val_b, "x^(2/(gamma-1))");
+
+  val_a = pow(x, 2.0f * hydro_gamma / (hydro_gamma - 1.0f));
+  val_b = pow_two_gamma_over_gamma_minus_one(x);
+  check_value(val_a, val_b, "x^((2 gamma)/(gamma-1))");
+
+  val_a = pow(x, 0.5f * (hydro_gamma - 1.0f) / hydro_gamma);
+  val_b = pow_gamma_minus_one_over_two_gamma(x);
+  check_value(val_a, val_b, "x^((gamma-1)/(2 gamma))");
+
+  val_a = pow(x, -0.5f * (hydro_gamma + 1.0f) / hydro_gamma);
+  val_b = pow_minus_gamma_plus_one_over_two_gamma(x);
+  check_value(val_a, val_b, "x^(-(gamma+1)/(2 gamma))");
+
+  val_a = pow(x, 1.0f / hydro_gamma);
+  val_b = pow_one_over_gamma(x);
+  check_value(val_a, val_b, "x^(1/gamma)");
+}
+
+/**
+ * @brief Check adiabatic index constants and power functions
+ */
+int main() {
+
+  /* check the values of the adiabatic index constants */
+  check_constants();
+
+  /* check the adiabatic index power functions */
+  check_functions();
+
+  return 0;
+}
diff --git a/tests/testFFT.c b/tests/testFFT.c
index 7ff29dac3f0e9b7d6aec65cbfc7e4f4b85d08dbb..c4aeb2885c788bd769bda49bdd15ab121dd8e9d4 100644
--- a/tests/testFFT.c
+++ b/tests/testFFT.c
@@ -21,6 +21,12 @@
 #include <stdlib.h>
 #include <string.h>
 
+#ifndef HAVE_FFTW
+
+int main() { return 0; }
+
+#else
+
 #include <fftw3.h>
 
 /* Includes. */
@@ -187,3 +193,5 @@ int main() {
   free(gparts);
   return 0;
 }
+
+#endif
diff --git a/tests/testInteractions.c b/tests/testInteractions.c
index 55858b17a51eb37e9e6184b8cf4fc853951bfd57..9d8dca79f3f84ea06e42d61390e68467a5f1b415 100644
--- a/tests/testInteractions.c
+++ b/tests/testInteractions.c
@@ -17,6 +17,12 @@
  *
  ******************************************************************************/
 
+#include "../config.h"
+
+#ifndef WITH_VECTORIZATION
+int main() { return 0; }
+#else
+
 #include <fenv.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -138,7 +144,7 @@ void write_header(char *fileName) {
   fclose(file);
 }
 
-/*
+/**
  * @brief Calls the serial and vectorised version of the non-symmetrical density
  * interaction.
  *
@@ -324,3 +330,5 @@ int main(int argc, char *argv[]) {
 
   return 0;
 }
+
+#endif /* WITH_VECTORIZATION */
diff --git a/tests/testKernelGrav.c b/tests/testKernelGrav.c
index 42ce6066b605c8cc9ac78a6e565dd59a500112f4..2733a4a4d041149499d354ef217ae85b1cd35b7f 100644
--- a/tests/testKernelGrav.c
+++ b/tests/testKernelGrav.c
@@ -74,10 +74,11 @@ int main() {
       swift_w = 1 / (r * r * r);
     }
 
-    printf("%2d: r= %f h= %f u= %f Wg(r,h)= %f Ws(r,h)= %f\n", k, r, h, u,
-           gadget_w, swift_w);
-
     if (fabsf(gadget_w - swift_w) > 2e-7) {
+
+      printf("%2d: r= %f h= %f u= %f Wg(r,h)= %f Ws(r,h)= %f\n", k, r, h, u,
+             gadget_w, swift_w);
+
       printf("Invalid value ! Gadget= %e, SWIFT= %e\n", gadget_w, swift_w);
       return 1;
     }
@@ -99,10 +100,11 @@ int main() {
 
     float gadget_w = erfc(u / 2) + u * exp(-u * u / 4) / sqrt(M_PI);
 
-    printf("%2d: r= %f r_lr= %f u= %f Ws(r)= %f Wg(r)= %f\n", k, r, a_smooth, u,
-           swift_w, gadget_w);
-
     if (fabsf(gadget_w - swift_w) > 2e-7) {
+
+      printf("%2d: r= %f r_lr= %f u= %f Ws(r)= %f Wg(r)= %f\n", k, r, a_smooth,
+             u, swift_w, gadget_w);
+
       printf("Invalid value ! Gadget= %e, SWIFT= %e\n", gadget_w, swift_w);
       return 1;
     }
diff --git a/tests/testMaths.c b/tests/testMaths.c
index 96c75313db18edc653aa3a48cb6ac34913297806..3d8f9a8f9db0cf01276eff89aa44157008cbddc6 100644
--- a/tests/testMaths.c
+++ b/tests/testMaths.c
@@ -39,24 +39,29 @@ int main() {
     const float rel =
         0.5f * fabs(exp_correct - exp_approx) / fabs(exp_correct + exp_approx);
 
-    printf("%2d: x= %f exp(x)= %e approx_exp(x)=%e abs=%e rel=%e\n", i, x,
-           exp_correct, exp_approx, abs, rel);
+    int error = 0;
 
     if (abs > 3e-6 && fabsf(x) <= 0.2) {
       printf("Absolute difference too large !\n");
-      return 1;
+      error = 1;
     }
     if (abs > 1.2e-7 && fabsf(x) <= 0.1) {
       printf("Absolute difference too large !\n");
-      return 1;
+      error = 1;
     }
 
     if (rel > 1e-6 && fabsf(x) <= 0.2) {
       printf("Relative difference too large !\n");
-      return 1;
+      error = 1;
     }
     if (rel > 4e-8 && fabsf(x) <= 0.1) {
       printf("Relative difference too large !\n");
+      error = 1;
+    }
+
+    if (error) {
+      printf("%2d: x= %f exp(x)= %e approx_exp(x)=%e abs=%e rel=%e\n", i, x,
+             exp_correct, exp_approx, abs, rel);
       return 1;
     }
   }
diff --git a/tests/testMatrixInversion.c b/tests/testMatrixInversion.c
new file mode 100644
index 0000000000000000000000000000000000000000..9a45cd52d6f5d3ec96cc6d3f34fd683971f4cf19
--- /dev/null
+++ b/tests/testMatrixInversion.c
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2016 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/>.
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include "const.h"
+#include "dimension.h"
+#include "error.h"
+#include "tools.h"
+
+void setup_matrix(float A[3][3]) {
+  A[0][0] = random_uniform(-1.0, 1.0);
+  A[0][1] = random_uniform(-1.0, 1.0);
+  A[0][2] = random_uniform(-1.0, 1.0);
+  A[1][0] = random_uniform(-1.0, 1.0);
+  A[1][1] = random_uniform(-1.0, 1.0);
+  A[1][2] = random_uniform(-1.0, 1.0);
+  A[2][0] = random_uniform(-1.0, 1.0);
+  A[2][1] = random_uniform(-1.0, 1.0);
+  A[2][2] = random_uniform(-1.0, 1.0);
+}
+
+int is_unit_matrix(float A[3][3]) {
+  int check = 1;
+
+  check &= (fabsf(A[0][0] - 1.0f) < 1.e-6f);
+
+#if defined(HYDRO_DIMENSION_2D) && defined(HYDRO_DIMENSION_3D)
+  check &= (fabsf(A[0][1]) < 1.e-6f);
+  check &= (fabsf(A[1][0]) < 1.e-6f);
+  check &= (fabsf(A[1][1] - 1.0f) < 1.e-6f);
+#if defined(HYDRO_DIMENSION_3D)
+  check &= (fabsf(A[0][2]) < 1.e-6f);
+  check &= (fabsf(A[1][2]) < 1.e-6f);
+  check &= (fabsf(A[2][0]) < 1.e-6f);
+  check &= (fabsf(A[2][1]) < 1.e-6f);
+  check &= (fabsf(A[2][2] - 1.0f) < 1.e-6f);
+#endif  // 3D
+#endif  // 2D and 3D
+
+  return check;
+}
+
+void print_matrix(float A[3][3], const char* s) {
+  message("Matrix %s:", s);
+#if defined(HYDRO_DIMENSION_1D)
+  message("[%.3e]", A[0][0]);
+#elif defined(HYDRO_DIMENSION_2D)
+  message("[%.3e, %.3e]", A[0][0], A[0][1]);
+  message("[%.3e, %.3e]", A[1][0], A[1][1]);
+#elif defined(HYDRO_DIMENSION_3D)
+  message("[%.8e, %.8e, %.8e]", A[0][0], A[0][1], A[0][2]);
+  message("[%.8e, %.8e, %.8e]", A[1][0], A[1][1], A[1][2]);
+  message("[%.8e, %.8e, %.8e]", A[2][0], A[2][1], A[2][2]);
+#endif
+}
+
+void multiply_matrices(float A[3][3], float B[3][3], float C[3][3]) {
+#if defined(HYDRO_DIMENSION_1D)
+  C[0][0] = A[0][0] * B[0][0];
+#elif defined(HYDRO_DIMENSION_2D)
+  for (int i = 0; i < 2; ++i) {
+    for (int j = 0; j < 2; ++j) {
+      C[i][j] = 0.0f;
+      for (int k = 0; k < 2; ++k) {
+        C[i][j] += A[i][k] * B[k][j];
+      }
+    }
+  }
+#elif defined(HYDRO_DIMENSION_3D)
+  for (int i = 0; i < 3; ++i) {
+    for (int j = 0; j < 3; ++j) {
+      C[i][j] = 0.0f;
+      for (int k = 0; k < 3; ++k) {
+        C[i][j] += A[i][k] * B[k][j];
+      }
+    }
+  }
+#endif
+}
+
+int main() {
+
+  float A[3][3], B[3][3], C[3][3];
+  setup_matrix(A);
+
+  memcpy(B, A, 9 * sizeof(float));
+
+  for (int i = 0; i < 3; ++i) {
+    for (int j = 0; j < 3; ++j) {
+      if (A[i][j] != B[i][j]) {
+        error("Matrices not equal after copy!");
+      }
+    }
+  }
+
+  invert_dimension_by_dimension_matrix(A);
+
+  multiply_matrices(A, B, C);
+
+  if (!is_unit_matrix(C)) {
+    print_matrix(A, "A");
+    print_matrix(B, "B");
+    print_matrix(C, "C");
+    error("Inverted matrix is wrong!");
+  }
+
+  return 0;
+}
diff --git a/tests/testPair.c b/tests/testPair.c
index 385e9a9e709b95685d86559e34b3b4c5fb3d3884..efa1e628c2d57bf7922be8affdd5436ebca2f9cf 100644
--- a/tests/testPair.c
+++ b/tests/testPair.c
@@ -63,7 +63,11 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
         part->v[2] = random_uniform(-0.05, 0.05);
         part->h = size * h / (float)n;
         part->id = ++(*partId);
+#ifdef GIZMO_SPH
+        part->conserved.mass = density * volume / count;
+#else
         part->mass = density * volume / count;
+#endif
         part->ti_begin = 0;
         part->ti_end = 1;
         ++part;
@@ -105,9 +109,7 @@ void clean_up(struct cell *ci) {
  * @brief Initializes all particles field to be ready for a density calculation
  */
 void zero_particle_fields(struct cell *c) {
-  for (size_t pid = 0; pid < c->count; pid++) {
-    c->parts[pid].rho = 0.f;
-    c->parts[pid].rho_dh = 0.f;
+  for (int pid = 0; pid < c->count; pid++) {
     hydro_init_part(&c->parts[pid]);
   }
 }
@@ -127,15 +129,20 @@ void dump_particle_fields(char *fileName, struct cell *ci, struct cell *cj) {
 
   fprintf(file, "# ci --------------------------------------------\n");
 
-  for (size_t pid = 0; pid < ci->count; pid++) {
+  for (int pid = 0; pid < ci->count; pid++) {
     fprintf(file,
             "%6llu %10f %10f %10f %10f %10f %10f %13e %13e %13e %13e %13e "
             "%13e %13e %13e\n",
             ci->parts[pid].id, ci->parts[pid].x[0], ci->parts[pid].x[1],
             ci->parts[pid].x[2], ci->parts[pid].v[0], ci->parts[pid].v[1],
-            ci->parts[pid].v[2], ci->parts[pid].rho, ci->parts[pid].rho_dh,
+            ci->parts[pid].v[2], hydro_get_density(&ci->parts[pid]),
+#if defined(GIZMO_SPH)
+            0.f,
+#else
+            cj->parts[pid].rho_dh,
+#endif
             ci->parts[pid].density.wcount, ci->parts[pid].density.wcount_dh,
-#ifdef GADGET2_SPH
+#if defined(GADGET2_SPH) || defined(DEFAULT_SPH)
             ci->parts[pid].density.div_v, ci->parts[pid].density.rot_v[0],
             ci->parts[pid].density.rot_v[1], ci->parts[pid].density.rot_v[2]
 #else
@@ -146,15 +153,20 @@ void dump_particle_fields(char *fileName, struct cell *ci, struct cell *cj) {
 
   fprintf(file, "# cj --------------------------------------------\n");
 
-  for (size_t pjd = 0; pjd < cj->count; pjd++) {
+  for (int pjd = 0; pjd < cj->count; pjd++) {
     fprintf(file,
             "%6llu %10f %10f %10f %10f %10f %10f %13e %13e %13e %13e %13e "
             "%13e %13e %13e\n",
             cj->parts[pjd].id, cj->parts[pjd].x[0], cj->parts[pjd].x[1],
             cj->parts[pjd].x[2], cj->parts[pjd].v[0], cj->parts[pjd].v[1],
-            cj->parts[pjd].v[2], cj->parts[pjd].rho, cj->parts[pjd].rho_dh,
+            cj->parts[pjd].v[2], hydro_get_density(&cj->parts[pjd]),
+#if defined(GIZMO_SPH)
+            0.f,
+#else
+            cj->parts[pjd].rho_dh,
+#endif
             cj->parts[pjd].density.wcount, cj->parts[pjd].density.wcount_dh,
-#ifdef GADGET2_SPH
+#if defined(GADGET2_SPH) || defined(DEFAULT_SPH)
             cj->parts[pjd].density.div_v, cj->parts[pjd].density.rot_v[0],
             cj->parts[pjd].density.rot_v[1], cj->parts[pjd].density.rot_v[2]
 #else
@@ -230,7 +242,6 @@ int main(int argc, char *argv[]) {
   }
 
   space.periodic = 0;
-  space.h_max = h;
 
   engine.s = &space;
   engine.time = 0.1f;
diff --git a/tests/testPair.sh.in b/tests/testPair.sh.in
index 086db14fdd7377d92a35420dffe0ea1dcca561d5..bd7051b060c4acab6cf5a164af1914715856849b 100755
--- a/tests/testPair.sh.in
+++ b/tests/testPair.sh.in
@@ -1,8 +1,11 @@
 #!/bin/bash
-rm brute_force_standard.dat swift_dopair_standard.dat
+
+echo ""
+
+rm -f brute_force_standard.dat swift_dopair_standard.dat
 
 ./testPair -p 6 -r 1 -d 0 -f standard
 
-python @srcdir@/difffloat.py brute_force_standard.dat swift_dopair_standard.dat @srcdir@/tolerance_pair.dat
+python @srcdir@/difffloat.py brute_force_standard.dat swift_dopair_standard.dat @srcdir@/tolerance_pair_normal.dat
 
 exit $?
diff --git a/tests/testPairPerturbed.sh.in b/tests/testPairPerturbed.sh.in
index f17d7559e0084727c74a53a9582009a1867d0d0e..9f214e25a098448a906f9da307ea569e327cfdea 100755
--- a/tests/testPairPerturbed.sh.in
+++ b/tests/testPairPerturbed.sh.in
@@ -1,8 +1,11 @@
 #!/bin/bash
-rm brute_force_perturbed.dat swift_dopair_perturbed.dat
+
+echo ""
+
+rm -f brute_force_perturbed.dat swift_dopair_perturbed.dat
 
 ./testPair -p 6 -r 1 -d 0.1 -f perturbed
 
-python @srcdir@/difffloat.py brute_force_perturbed.dat swift_dopair_perturbed.dat @srcdir@/tolerance_pair.dat
+python @srcdir@/difffloat.py brute_force_perturbed.dat swift_dopair_perturbed.dat @srcdir@/tolerance_pair_perturbed.dat
 
 exit $?
diff --git a/tests/testParser.sh.in b/tests/testParser.sh.in
index 6c62bc57c041576ea6e807a0a31052ee1dffb4db..9ccfa5078eafca46de406698b4e030bf46ba912c 100755
--- a/tests/testParser.sh.in
+++ b/tests/testParser.sh.in
@@ -1,4 +1,4 @@
 #!/bin/bash
 
-rm parser_output.yml
+rm -f parser_output.yml
 ./testParser @srcdir@/testParserInput.yaml
diff --git a/tests/testReading.c b/tests/testReading.c
index 5a9707d2705ed021996859f324310c4a4926730c..2ef32a5ef11c7e24a379ce5131df9cbea153fa7c 100644
--- a/tests/testReading.c
+++ b/tests/testReading.c
@@ -28,7 +28,7 @@ int main() {
   size_t Ngas = 0, Ngpart = 0;
   int periodic = -1;
   int flag_entropy_ICs = -1;
-  int i, j, k, n;
+  int i, j, k;
   double dim[3];
   struct part *parts = NULL;
   struct gpart *gparts = NULL;
@@ -55,14 +55,14 @@ int main() {
   assert(periodic == 1);
 
   /* Check particles */
-  for (n = 0; n < Ngas; ++n) {
+  for (size_t n = 0; n < Ngas; ++n) {
 
     /* Check that indices are in a reasonable range */
     unsigned long long index = parts[n].id;
     assert(index < Ngas);
 
     /* Check masses */
-    float mass = parts[n].mass;
+    float mass = hydro_get_mass(&parts[n]);
     float correct_mass = boxSize * boxSize * boxSize * rho / Ngas;
     assert(mass == correct_mass);
 
diff --git a/tests/testRiemannExact.c b/tests/testRiemannExact.c
new file mode 100644
index 0000000000000000000000000000000000000000..1943820339ba2ac06d194a17d2d450157ded1a31
--- /dev/null
+++ b/tests/testRiemannExact.c
@@ -0,0 +1,338 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2016 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/>.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "error.h"
+#include "riemann/riemann_exact.h"
+#include "tools.h"
+
+int opposite(float a, float b) {
+  if ((a - b)) {
+    return fabs((a + b) / (a - b)) < 1.e-4;
+  } else {
+    return a == 0.0f;
+  }
+}
+
+int equal(float a, float b) {
+  if ((a + b)) {
+    return fabs((a - b) / (a + b)) < 1.e-4;
+  } else {
+    return a == 0.0f;
+  }
+}
+
+/**
+ * @brief Check that a and b are consistent (up to some error)
+ *
+ * @param a First value
+ * @param b Second value
+ * @param s String used to identify this check in messages
+ */
+void check_value(float a, float b, const char* s) {
+  if (fabsf(a - b) / fabsf(a + b) > 1.e-5f && fabsf(a - b) > 1.e-5f) {
+    error("Values are inconsistent: %g %g (%s)!", a, b, s);
+  } else {
+    message("Values are consistent: %g %g (%s).", a, b, s);
+  }
+}
+
+struct riemann_statevector {
+  /*! @brief Density */
+  float rho;
+
+  /*! @brief Fluid velocity */
+  float v;
+
+  /*! @brief Pressure */
+  float P;
+};
+
+/**
+ * @brief Check that the solution to the Riemann problem with given left and
+ * right state is consistent with the given expected solution
+ *
+ * @param WL Left state
+ * @param WR Right state
+ * @param Whalf Expected solution
+ * @param s String used to identify this check in messages
+ */
+void check_riemann_solution(struct riemann_statevector* WL,
+                            struct riemann_statevector* WR,
+                            struct riemann_statevector* Whalf, const char* s) {
+  float WLarr[5], WRarr[5], Whalfarr[5], n_unit[3];
+
+  n_unit[0] = 1.0f;
+  n_unit[1] = 0.0f;
+  n_unit[2] = 0.0f;
+
+  WLarr[0] = WL->rho;
+  WLarr[1] = WL->v;
+  WLarr[2] = 0.0f;
+  WLarr[3] = 0.0f;
+  WLarr[4] = WL->P;
+
+  WRarr[0] = WR->rho;
+  WRarr[1] = WR->v;
+  WRarr[2] = 0.0f;
+  WRarr[3] = 0.0f;
+  WRarr[4] = WR->P;
+
+  riemann_solver_solve(WLarr, WRarr, Whalfarr, n_unit);
+
+  message("Checking %s...", s);
+  check_value(Whalfarr[0], Whalf->rho, "rho");
+  check_value(Whalfarr[1], Whalf->v, "v");
+  check_value(Whalfarr[4], Whalf->P, "P");
+}
+
+/**
+ * @brief Check the exact Riemann solver on the Toro test problems
+ */
+void check_riemann_exact() {
+  struct riemann_statevector WL, WR, Whalf;
+
+  /* Test 1 */
+  WL.rho = 1.0f;
+  WL.v = 0.0f;
+  WL.P = 1.0f;
+  WR.rho = 0.125f;
+  WR.v = 0.0f;
+  WR.P = 0.1f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 0.47969f;
+  Whalf.v = 0.841194f;
+  Whalf.P = 0.293945f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 0.411437f;
+  Whalf.v = 0.953205f;
+  Whalf.P = 0.306011f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 0.534767f;
+  Whalf.v = 0.760062f;
+  Whalf.P = 0.285975f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 1");
+
+  /* Test 2 */
+  WL.rho = 1.0f;
+  WL.v = -2.0f;
+  WL.P = 0.4f;
+  WR.rho = 1.0f;
+  WR.v = 2.0f;
+  WR.P = 0.4f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 0.00617903f;
+  Whalf.v = 0.0f;
+  Whalf.P = 8.32249e-5f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 0.0257933f;
+  Whalf.v = 0.0f;
+  Whalf.P = 0.00304838f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 0.0f;
+  Whalf.v = 0.0f;
+  Whalf.P = 0.0f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 2");
+
+  /* Test 3 */
+  WL.rho = 1.0f;
+  WL.v = 0.0f;
+  WL.P = 1000.0f;
+  WR.rho = 1.0f;
+  WR.v = 0.0f;
+  WR.P = 0.01f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 0.615719f;
+  Whalf.v = 18.2812f;
+  Whalf.P = 445.626f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 0.563517f;
+  Whalf.v = 19.9735f;
+  Whalf.P = 465.453f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 0.656768f;
+  Whalf.v = 16.9572f;
+  Whalf.P = 431.345f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 3");
+
+  /* Test 4 */
+  WL.rho = 1.0f;
+  WL.v = 0.0f;
+  WL.P = 0.01f;
+  WR.rho = 1.0f;
+  WR.v = 0.0f;
+  WR.P = 100.0f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 0.61577f;
+  Whalf.v = -5.78022f;
+  Whalf.P = 44.5687f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 0.563567f;
+  Whalf.v = -6.31525f;
+  Whalf.P = 46.5508f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 0.656819f;
+  Whalf.v = -5.36146f;
+  Whalf.P = 43.1412f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 4");
+
+  /* Test 5 */
+  WL.rho = 5.99924f;
+  WL.v = 19.5975f;
+  WL.P = 460.894f;
+  WR.rho = 5.99242f;
+  WR.v = -6.19633f;
+  WR.P = 46.0950f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 12.743f;
+  Whalf.v = 8.56045f;
+  Whalf.P = 1841.82f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 5.99924f;
+  Whalf.v = 19.5975f;
+  Whalf.P = 460.894f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 11.5089f;
+  Whalf.v = 8.42099f;
+  Whalf.P = 2026.27f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 5");
+}
+
+/**
+ * @brief Check the symmetry of the TRRS Riemann solver
+ */
+void check_riemann_symmetry() {
+  float WL[5], WR[5], Whalf1[5], Whalf2[5], n_unit1[3], n_unit2[3], n_norm,
+      vij[3], totflux1[5], totflux2[5];
+
+  WL[0] = random_uniform(0.1f, 1.0f);
+  WL[1] = random_uniform(-10.0f, 10.0f);
+  WL[2] = random_uniform(-10.0f, 10.0f);
+  WL[3] = random_uniform(-10.0f, 10.0f);
+  WL[4] = random_uniform(0.1f, 1.0f);
+  WR[0] = random_uniform(0.1f, 1.0f);
+  WR[1] = random_uniform(-10.0f, 10.0f);
+  WR[2] = random_uniform(-10.0f, 10.0f);
+  WR[3] = random_uniform(-10.0f, 10.0f);
+  WR[4] = random_uniform(0.1f, 1.0f);
+
+  n_unit1[0] = random_uniform(-1.0f, 1.0f);
+  n_unit1[1] = random_uniform(-1.0f, 1.0f);
+  n_unit1[2] = random_uniform(-1.0f, 1.0f);
+
+  n_norm = sqrtf(n_unit1[0] * n_unit1[0] + n_unit1[1] * n_unit1[1] +
+                 n_unit1[2] * n_unit1[2]);
+  n_unit1[0] /= n_norm;
+  n_unit1[1] /= n_norm;
+  n_unit1[2] /= n_norm;
+
+  n_unit2[0] = -n_unit1[0];
+  n_unit2[1] = -n_unit1[1];
+  n_unit2[2] = -n_unit1[2];
+
+  riemann_solver_solve(WL, WR, Whalf1, n_unit1);
+  riemann_solver_solve(WR, WL, Whalf2, n_unit2);
+
+  if (!equal(Whalf1[0], Whalf2[0]) || !equal(Whalf1[1], Whalf2[1]) ||
+      !equal(Whalf1[2], Whalf2[2]) || !equal(Whalf1[3], Whalf2[3]) ||
+      !equal(Whalf1[4], Whalf2[4])) {
+    message(
+        "Solver asymmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        Whalf1[0], Whalf1[1], Whalf1[2], Whalf1[3], Whalf1[4], Whalf2[0],
+        Whalf2[1], Whalf2[2], Whalf2[3], Whalf2[4]);
+    message("Asymmetry in solution!\n");
+    /* This asymmetry is to be expected, since we do an iteration. Are the
+       results at least consistent? */
+    check_value(Whalf1[0], Whalf2[0], "Rho solution");
+    check_value(Whalf1[1], Whalf2[1], "V[0] solution");
+    check_value(Whalf1[2], Whalf2[2], "V[1] solution");
+    check_value(Whalf1[3], Whalf2[3], "V[2] solution");
+    check_value(Whalf1[4], Whalf2[4], "Pressure solution");
+  } else {
+    message(
+        "Solver symmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        Whalf1[0], Whalf1[1], Whalf1[2], Whalf1[3], Whalf1[4], Whalf2[0],
+        Whalf2[1], Whalf2[2], Whalf2[3], Whalf2[4]);
+  }
+
+  vij[0] = random_uniform(-10.0f, 10.0f);
+  vij[1] = random_uniform(-10.0f, 10.0f);
+  vij[2] = random_uniform(-10.0f, 10.0f);
+
+  riemann_solve_for_flux(WL, WR, n_unit1, vij, totflux1);
+  riemann_solve_for_flux(WR, WL, n_unit2, vij, totflux2);
+
+  if (!opposite(totflux1[0], totflux2[0]) ||
+      !opposite(totflux1[1], totflux2[1]) ||
+      !opposite(totflux1[2], totflux2[2]) ||
+      !opposite(totflux1[3], totflux2[3]) ||
+      !opposite(totflux1[4], totflux2[4])) {
+    message(
+        "Flux solver asymmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        totflux1[0], totflux1[1], totflux1[2], totflux1[3], totflux1[4],
+        totflux2[0], totflux2[1], totflux2[2], totflux2[3], totflux2[4]);
+    message("Asymmetry in flux solution!");
+    /* This asymmetry is to be expected, since we do an iteration. Are the
+       results at least consistent? */
+    check_value(totflux1[0], totflux2[0], "Mass flux");
+    check_value(totflux1[1], totflux2[1], "Momentum[0] flux");
+    check_value(totflux1[2], totflux2[2], "Momentum[1] flux");
+    check_value(totflux1[3], totflux2[3], "Momentum[2] flux");
+    check_value(totflux1[4], totflux2[4], "Energy flux");
+  } else {
+    message(
+        "Flux solver symmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        totflux1[0], totflux1[1], totflux1[2], totflux1[3], totflux1[4],
+        totflux2[0], totflux2[1], totflux2[2], totflux2[3], totflux2[4]);
+  }
+}
+
+/**
+ * @brief Check the exact Riemann solver
+ */
+int main() {
+
+  /* check the exact Riemann solver */
+  check_riemann_exact();
+
+  /* symmetry test */
+  int i;
+  for (i = 0; i < 100; ++i) check_riemann_symmetry();
+
+  return 0;
+}
diff --git a/tests/testRiemannHLLC.c b/tests/testRiemannHLLC.c
new file mode 100644
index 0000000000000000000000000000000000000000..4cf883b68efbcfd795d0b7894adb9e7265b14d14
--- /dev/null
+++ b/tests/testRiemannHLLC.c
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2016 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/>.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "error.h"
+#include "riemann/riemann_hllc.h"
+#include "tools.h"
+
+int consistent_with_zero(float val) { return fabs(val) < 1.e-4; }
+
+/**
+ * @brief Check the symmetry of the TRRS Riemann solver
+ */
+void check_riemann_symmetry() {
+  float WL[5], WR[5], n_unit1[3], n_unit2[3], n_norm, vij[3], totflux1[5],
+      totflux2[5];
+
+  WL[0] = random_uniform(0.1f, 1.0f);
+  WL[1] = random_uniform(-10.0f, 10.0f);
+  WL[2] = random_uniform(-10.0f, 10.0f);
+  WL[3] = random_uniform(-10.0f, 10.0f);
+  WL[4] = random_uniform(0.1f, 1.0f);
+  WR[0] = random_uniform(0.1f, 1.0f);
+  WR[1] = random_uniform(-10.0f, 10.0f);
+  WR[2] = random_uniform(-10.0f, 10.0f);
+  WR[3] = random_uniform(-10.0f, 10.0f);
+  WR[4] = random_uniform(0.1f, 1.0f);
+
+  n_unit1[0] = random_uniform(-1.0f, 1.0f);
+  n_unit1[1] = random_uniform(-1.0f, 1.0f);
+  n_unit1[2] = random_uniform(-1.0f, 1.0f);
+
+  n_norm = sqrtf(n_unit1[0] * n_unit1[0] + n_unit1[1] * n_unit1[1] +
+                 n_unit1[2] * n_unit1[2]);
+  n_unit1[0] /= n_norm;
+  n_unit1[1] /= n_norm;
+  n_unit1[2] /= n_norm;
+
+  n_unit2[0] = -n_unit1[0];
+  n_unit2[1] = -n_unit1[1];
+  n_unit2[2] = -n_unit1[2];
+
+  vij[0] = random_uniform(-10.0f, 10.0f);
+  vij[1] = random_uniform(-10.0f, 10.0f);
+  vij[2] = random_uniform(-10.0f, 10.0f);
+
+  riemann_solve_for_flux(WL, WR, n_unit1, vij, totflux1);
+  riemann_solve_for_flux(WR, WL, n_unit2, vij, totflux2);
+
+  if (!consistent_with_zero(totflux1[0] + totflux2[0]) ||
+      !consistent_with_zero(totflux1[1] + totflux2[1]) ||
+      !consistent_with_zero(totflux1[2] + totflux2[2]) ||
+      !consistent_with_zero(totflux1[3] + totflux2[3]) ||
+      !consistent_with_zero(totflux1[4] + totflux2[4])) {
+    message(
+        "Flux solver asymmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        totflux1[0], totflux1[1], totflux1[2], totflux1[3], totflux1[4],
+        totflux2[0], totflux2[1], totflux2[2], totflux2[3], totflux2[4]);
+    error("Asymmetry in flux solution!");
+  } else {
+    message(
+        "Flux solver symmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        totflux1[0], totflux1[1], totflux1[2], totflux1[3], totflux1[4],
+        totflux2[0], totflux2[1], totflux2[2], totflux2[3], totflux2[4]);
+  }
+}
+
+/**
+ * @brief Check the HLLC Riemann solver
+ */
+int main() {
+
+  int i;
+  /* symmetry test */
+  for (i = 0; i < 100; i++) check_riemann_symmetry();
+
+  return 0;
+}
diff --git a/tests/testRiemannTRRS.c b/tests/testRiemannTRRS.c
new file mode 100644
index 0000000000000000000000000000000000000000..18ecbdce9173f43674a63b21231322cb01620d29
--- /dev/null
+++ b/tests/testRiemannTRRS.c
@@ -0,0 +1,324 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2016 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/>.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "error.h"
+#include "riemann/riemann_trrs.h"
+#include "tools.h"
+
+int opposite(float a, float b) {
+  if ((a - b)) {
+    return fabs((a + b) / (a - b)) < 1.e-4;
+  } else {
+    return a == 0.0f;
+  }
+}
+
+int equal(float a, float b) {
+  if ((a + b)) {
+    return fabs((a - b) / (a + b)) < 1.e-4;
+  } else {
+    return a == 0.0f;
+  }
+}
+
+/**
+ * @brief Check that a and b are consistent (up to some error)
+ *
+ * @param a First value
+ * @param b Second value
+ * @param s String used to identify this check in messages
+ */
+void check_value(float a, float b, const char* s) {
+  if (fabsf(a - b) / fabsf(a + b) > 1.e-5f && fabsf(a - b) > 1.e-5f) {
+    error("Values are inconsistent: %g %g (%s)!", a, b, s);
+  } else {
+    message("Values are consistent: %g %g (%s).", a, b, s);
+  }
+}
+
+struct riemann_statevector {
+  /*! @brief Density */
+  float rho;
+
+  /*! @brief Fluid velocity */
+  float v;
+
+  /*! @brief Pressure */
+  float P;
+};
+
+/**
+ * @brief Check that the solution to the Riemann problem with given left and
+ * right state is consistent with the given expected solution
+ *
+ * @param WL Left state
+ * @param WR Right state
+ * @param Whalf Expected solution
+ * @param s String used to identify this check in messages
+ */
+void check_riemann_solution(struct riemann_statevector* WL,
+                            struct riemann_statevector* WR,
+                            struct riemann_statevector* Whalf, const char* s) {
+  float WLarr[5], WRarr[5], Whalfarr[5], n_unit[3];
+
+  n_unit[0] = 1.0f;
+  n_unit[1] = 0.0f;
+  n_unit[2] = 0.0f;
+
+  WLarr[0] = WL->rho;
+  WLarr[1] = WL->v;
+  WLarr[2] = 0.0f;
+  WLarr[3] = 0.0f;
+  WLarr[4] = WL->P;
+
+  WRarr[0] = WR->rho;
+  WRarr[1] = WR->v;
+  WRarr[2] = 0.0f;
+  WRarr[3] = 0.0f;
+  WRarr[4] = WR->P;
+
+  riemann_solver_solve(WLarr, WRarr, Whalfarr, n_unit);
+
+  message("Checking %s...", s);
+  check_value(Whalfarr[0], Whalf->rho, "rho");
+  check_value(Whalfarr[1], Whalf->v, "v");
+  check_value(Whalfarr[4], Whalf->P, "P");
+}
+
+/**
+ * @brief Check the TRRS Riemann solver on the Toro test problems
+ */
+void check_riemann_trrs() {
+  struct riemann_statevector WL, WR, Whalf;
+
+  /* Test 1 */
+  WL.rho = 1.0f;
+  WL.v = 0.0f;
+  WL.P = 1.0f;
+  WR.rho = 0.125f;
+  WR.v = 0.0f;
+  WR.P = 0.1f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 0.481167f;
+  Whalf.v = 0.838085f;
+  Whalf.P = 0.295456f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 0.41586f;
+  Whalf.v = 0.942546f;
+  Whalf.P = 0.310406f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 0.53478f;
+  Whalf.v = 0.760037f;
+  Whalf.P = 0.285989f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 1");
+
+  /* Test 2 */
+  WL.rho = 1.0f;
+  WL.v = -2.0f;
+  WL.P = 0.4f;
+  WR.rho = 1.0f;
+  WR.v = 2.0f;
+  WR.P = 0.4f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 0.00617903f;
+  Whalf.v = 0.0f;
+  Whalf.P = 8.32249e-5f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 0.0257933f;
+  Whalf.v = 0.0f;
+  Whalf.P = 0.00304838f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 0.013932f;
+  Whalf.v = 0.0f;
+  Whalf.P = 7.76405e-5f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 2");
+
+  /* Test 3 */
+  WL.rho = 1.0f;
+  WL.v = 0.0f;
+  WL.P = 1000.0f;
+  WR.rho = 1.0f;
+  WR.v = 0.0f;
+  WR.P = 0.01f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 0.919498f;
+  Whalf.v = 3.37884f;
+  Whalf.P = 869.464f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 0.941258f;
+  Whalf.v = 2.19945f;
+  Whalf.P = 922.454f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 0.902032f;
+  Whalf.v = 4.49417f;
+  Whalf.P = 813.662f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 3");
+
+  /* Test 4 */
+  WL.rho = 1.0f;
+  WL.v = 0.0f;
+  WL.P = 0.01f;
+  WR.rho = 1.0f;
+  WR.v = 0.0f;
+  WR.P = 100.0f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 0.857525f;
+  Whalf.v = -1.93434f;
+  Whalf.P = 77.4007f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 0.880649f;
+  Whalf.v = -1.45215f;
+  Whalf.P = 84.4119f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 0.843058f;
+  Whalf.v = -2.31417f;
+  Whalf.P = 71.0747f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 4");
+
+  /* Test 5 */
+  WL.rho = 5.99924f;
+  WL.v = 19.5975f;
+  WL.P = 460.894f;
+  WR.rho = 5.99242f;
+  WR.v = -6.19633f;
+  WR.P = 46.0950f;
+#if defined(HYDRO_GAMMA_5_3)
+  Whalf.rho = 5.99924f;
+  Whalf.v = 19.5975f;
+  Whalf.P = 460.894f;
+#elif defined(HYDRO_GAMMA_4_3)
+  Whalf.rho = 5.99924f;
+  Whalf.v = 19.5975f;
+  Whalf.P = 460.894f;
+#elif defined(HYDRO_GAMMA_2_1)
+  Whalf.rho = 5.99924f;
+  Whalf.v = 19.5975f;
+  Whalf.P = 460.894f;
+#else
+#error "Unsupported adiabatic index!"
+#endif
+  check_riemann_solution(&WL, &WR, &Whalf, "Test 5");
+}
+
+/**
+ * @brief Check the symmetry of the TRRS Riemann solver
+ */
+void check_riemann_symmetry() {
+  float WL[5], WR[5], Whalf1[5], Whalf2[5], n_unit1[3], n_unit2[3], n_norm,
+      vij[3], totflux1[5], totflux2[5];
+
+  WL[0] = random_uniform(0.1f, 1.0f);
+  WL[1] = random_uniform(-10.0f, 10.0f);
+  WL[2] = random_uniform(-10.0f, 10.0f);
+  WL[3] = random_uniform(-10.0f, 10.0f);
+  WL[4] = random_uniform(0.1f, 1.0f);
+  WR[0] = random_uniform(0.1f, 1.0f);
+  WR[1] = random_uniform(-10.0f, 10.0f);
+  WR[2] = random_uniform(-10.0f, 10.0f);
+  WR[3] = random_uniform(-10.0f, 10.0f);
+  WR[4] = random_uniform(0.1f, 1.0f);
+
+  n_unit1[0] = random_uniform(-1.0f, 1.0f);
+  n_unit1[1] = random_uniform(-1.0f, 1.0f);
+  n_unit1[2] = random_uniform(-1.0f, 1.0f);
+
+  n_norm = sqrtf(n_unit1[0] * n_unit1[0] + n_unit1[1] * n_unit1[1] +
+                 n_unit1[2] * n_unit1[2]);
+  n_unit1[0] /= n_norm;
+  n_unit1[1] /= n_norm;
+  n_unit1[2] /= n_norm;
+
+  n_unit2[0] = -n_unit1[0];
+  n_unit2[1] = -n_unit1[1];
+  n_unit2[2] = -n_unit1[2];
+
+  riemann_solver_solve(WL, WR, Whalf1, n_unit1);
+  riemann_solver_solve(WR, WL, Whalf2, n_unit2);
+
+  if (!equal(Whalf1[0], Whalf2[0]) || !equal(Whalf1[1], Whalf2[1]) ||
+      !equal(Whalf1[2], Whalf2[2]) || !equal(Whalf1[3], Whalf2[3]) ||
+      !equal(Whalf1[4], Whalf2[4])) {
+    message(
+        "Solver asymmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        Whalf1[0], Whalf1[1], Whalf1[2], Whalf1[3], Whalf1[4], Whalf2[0],
+        Whalf2[1], Whalf2[2], Whalf2[3], Whalf2[4]);
+    error("Asymmetry in solution!");
+  } else {
+    message(
+        "Solver symmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        Whalf1[0], Whalf1[1], Whalf1[2], Whalf1[3], Whalf1[4], Whalf2[0],
+        Whalf2[1], Whalf2[2], Whalf2[3], Whalf2[4]);
+  }
+
+  vij[0] = random_uniform(-10.0f, 10.0f);
+  vij[1] = random_uniform(-10.0f, 10.0f);
+  vij[2] = random_uniform(-10.0f, 10.0f);
+
+  riemann_solve_for_flux(WL, WR, n_unit1, vij, totflux1);
+  riemann_solve_for_flux(WR, WL, n_unit2, vij, totflux2);
+
+  if (!opposite(totflux1[0], totflux2[0]) ||
+      !opposite(totflux1[1], totflux2[1]) ||
+      !opposite(totflux1[2], totflux2[2]) ||
+      !opposite(totflux1[3], totflux2[3]) ||
+      !opposite(totflux1[4], totflux2[4])) {
+    message(
+        "Solver asymmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        totflux1[0], totflux1[1], totflux1[2], totflux1[3], totflux1[4],
+        totflux2[0], totflux2[1], totflux2[2], totflux2[3], totflux2[4]);
+    error("Asymmetry in solution!");
+  } else {
+    message(
+        "Solver symmetric: [%.3e,%.3e,%.3e,%.3e,%.3e] == "
+        "[%.3e,%.3e,%.3e,%.3e,%.3e]\n",
+        totflux1[0], totflux1[1], totflux1[2], totflux1[3], totflux1[4],
+        totflux2[0], totflux2[1], totflux2[2], totflux2[3], totflux2[4]);
+  }
+}
+
+/**
+ * @brief Check the TRRS Riemann solver
+ */
+int main() {
+
+  /* check the TRRS Riemann solver */
+  check_riemann_trrs();
+
+  /* symmetry test */
+  int i;
+  for (i = 0; i < 100; i++) check_riemann_symmetry();
+
+  return 0;
+}
diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c
index eb3fab6becca08e9ef87e7c60cc8c04bd2a0290c..6469d314fb8b1438cc2c9737669c1a13a97bd803 100644
--- a/tests/testSymmetry.c
+++ b/tests/testSymmetry.c
@@ -46,6 +46,55 @@ int main(int argc, char *argv[]) {
   pi.id = 1;
   pj.id = 2;
 
+#if defined(GIZMO_SPH)
+  /* Give the primitive variables sensible values, since the Riemann solver does
+     not like negative densities and pressures */
+  pi.primitives.rho = random_uniform(0.1f, 1.0f);
+  pi.primitives.v[0] = random_uniform(-10.0f, 10.0f);
+  pi.primitives.v[1] = random_uniform(-10.0f, 10.0f);
+  pi.primitives.v[2] = random_uniform(-10.0f, 10.0f);
+  pi.primitives.P = random_uniform(0.1f, 1.0f);
+  pj.primitives.rho = random_uniform(0.1f, 1.0f);
+  pj.primitives.v[0] = random_uniform(-10.0f, 10.0f);
+  pj.primitives.v[1] = random_uniform(-10.0f, 10.0f);
+  pj.primitives.v[2] = random_uniform(-10.0f, 10.0f);
+  pj.primitives.P = random_uniform(0.1f, 1.0f);
+  /* make gradients zero */
+  pi.primitives.gradients.rho[0] = 0.0f;
+  pi.primitives.gradients.rho[1] = 0.0f;
+  pi.primitives.gradients.rho[2] = 0.0f;
+  pi.primitives.gradients.v[0][0] = 0.0f;
+  pi.primitives.gradients.v[0][1] = 0.0f;
+  pi.primitives.gradients.v[0][2] = 0.0f;
+  pi.primitives.gradients.v[1][0] = 0.0f;
+  pi.primitives.gradients.v[1][1] = 0.0f;
+  pi.primitives.gradients.v[1][2] = 0.0f;
+  pi.primitives.gradients.v[2][0] = 0.0f;
+  pi.primitives.gradients.v[2][1] = 0.0f;
+  pi.primitives.gradients.v[2][2] = 0.0f;
+  pi.primitives.gradients.P[0] = 0.0f;
+  pi.primitives.gradients.P[1] = 0.0f;
+  pi.primitives.gradients.P[2] = 0.0f;
+  pj.primitives.gradients.rho[0] = 0.0f;
+  pj.primitives.gradients.rho[1] = 0.0f;
+  pj.primitives.gradients.rho[2] = 0.0f;
+  pj.primitives.gradients.v[0][0] = 0.0f;
+  pj.primitives.gradients.v[0][1] = 0.0f;
+  pj.primitives.gradients.v[0][2] = 0.0f;
+  pj.primitives.gradients.v[1][0] = 0.0f;
+  pj.primitives.gradients.v[1][1] = 0.0f;
+  pj.primitives.gradients.v[1][2] = 0.0f;
+  pj.primitives.gradients.v[2][0] = 0.0f;
+  pj.primitives.gradients.v[2][1] = 0.0f;
+  pj.primitives.gradients.v[2][2] = 0.0f;
+  pj.primitives.gradients.P[0] = 0.0f;
+  pj.primitives.gradients.P[1] = 0.0f;
+  pj.primitives.gradients.P[2] = 0.0f;
+  /* set time step to reasonable value */
+  pi.force.dt = 0.001;
+  pj.force.dt = 0.001;
+#endif
+
   /* Make an xpart companion */
   struct xpart xpi, xpj;
   bzero(&xpi, sizeof(struct xpart));
@@ -100,12 +149,54 @@ int main(int argc, char *argv[]) {
   dx[2] = -dx[2];
   runner_iact_nonsym_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2);
 
-  /* Check that the particles are the same */
+/* Check that the particles are the same */
+#if defined(GIZMO_SPH)
+  i_ok = 0;
+  j_ok = 0;
+  for (size_t i = 0; i < sizeof(struct part) / sizeof(float); ++i) {
+    float a = *(((float *)&pi) + i);
+    float b = *(((float *)&pi2) + i);
+    float c = *(((float *)&pj) + i);
+    float d = *(((float *)&pj2) + i);
+
+    int a_is_b;
+    if ((a + b)) {
+      a_is_b = (fabs((a - b) / (a + b)) > 1.e-4);
+    } else {
+      a_is_b = !(a == 0.0f);
+    }
+    int c_is_d;
+    if ((c + d)) {
+      c_is_d = (fabs((c - d) / (c + d)) > 1.e-4);
+    } else {
+      c_is_d = !(c == 0.0f);
+    }
+
+    if (a_is_b) {
+      message("%.8e, %.8e, %lu", a, b, i);
+    }
+    if (c_is_d) {
+      message("%.8e, %.8e, %lu", c, d, i);
+    }
+
+    i_ok |= a_is_b;
+    j_ok |= c_is_d;
+  }
+#else
   i_ok = memcmp(&pi, &pi2, sizeof(struct part));
   j_ok = memcmp(&pj, &pj2, sizeof(struct part));
+#endif
 
-  if (i_ok) error("Particles 'pi' do not match after force");
-  if (j_ok) error("Particles 'pj' do not match after force");
+  if (i_ok) {
+    printParticle_single(&pi, &xpi);
+    printParticle_single(&pi2, &xpi);
+    error("Particles 'pi' do not match after force");
+  }
+  if (j_ok) {
+    printParticle_single(&pj, &xpj);
+    printParticle_single(&pj2, &xpj);
+    error("Particles 'pj' do not match after force");
+  }
 
   return 0;
 }
diff --git a/tests/testThreadpool.c b/tests/testThreadpool.c
new file mode 100644
index 0000000000000000000000000000000000000000..aa65d533a29afbe4e7e8384fb887281822a31e58
--- /dev/null
+++ b/tests/testThreadpool.c
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2016 Pedro Gonnet (pedro.gonnet@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/>.
+ *
+ ******************************************************************************/
+
+// Standard includes.
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+// Local includes.
+#include "../src/atomic.h"
+#include "../src/threadpool.h"
+
+void map_function_first(void *map_data, int num_elements, void *extra_data) {
+  const int *inputs = (int *)map_data;
+  for (int ind = 0; ind < num_elements; ind++) {
+    int input = inputs[ind];
+    usleep(rand() % 1000000);
+    printf("map_function_first: got input %i.\n", input);
+    fflush(stdout);
+  }
+}
+
+void map_function_second(void *map_data, int num_elements, void *extra_data) {
+  const int *inputs = (int *)map_data;
+  for (int ind = 0; ind < num_elements; ind++) {
+    int input = inputs[ind];
+    usleep(rand() % 1000000);
+    printf("map_function_second: got input %i.\n", input);
+    fflush(stdout);
+  }
+}
+
+int main(int argc, char *argv[]) {
+
+  // Some constants for this test.
+  const int num_threads = 16;
+  const int N = 20;
+  const int num_runs = 2;
+
+  // Create a threadpool with 8 threads.
+  struct threadpool tp;
+  threadpool_init(&tp, num_threads);
+
+  // Main loop.
+  for (int run = 0; run < num_runs; run++) {
+
+    // Run over a set of integers and print them.
+    int data[N];
+    for (int k = 0; k < N; k++) data[k] = k;
+    printf("processing integers from 0..%i.\n", N);
+    fflush(stdout);
+    threadpool_map(&tp, map_function_first, data, N, sizeof(int), 1, NULL);
+
+    // Do the same thing again, with less jobs than threads.
+    printf("processing integers from 0..%i.\n", N / 2);
+    fflush(stdout);
+    threadpool_map(&tp, map_function_second, data, N / 2, sizeof(int), 1, NULL);
+
+    // Do the same thing again, with a chunk size of two.
+    printf("processing integers from 0..%i.\n", N);
+    fflush(stdout);
+    threadpool_map(&tp, map_function_first, data, N, sizeof(int), 2, NULL);
+  }
+
+  /* Be clean */
+  threadpool_clean(&tp);
+
+  return 0;
+}
diff --git a/tests/testTimeIntegration.c b/tests/testTimeIntegration.c
index 03893daf3530df040e5a5630bc6dc1d930ddcd1b..f39adaee902ac3460b01857c002659b8bb2101f4 100644
--- a/tests/testTimeIntegration.c
+++ b/tests/testTimeIntegration.c
@@ -116,9 +116,6 @@ int main() {
 
     /* Kick... */
     runner_do_kick(&run, &c, 0);
-
-    /* Drift... */
-    runner_do_drift(&run, &c, 0);
   }
 
   /* Clean-up */
diff --git a/tests/tolerance_27_normal.dat b/tests/tolerance_27_normal.dat
new file mode 100644
index 0000000000000000000000000000000000000000..141ed3baeedd5dad7a2fda0730c2e9c828ae4b2e
--- /dev/null
+++ b/tests/tolerance_27_normal.dat
@@ -0,0 +1,3 @@
+#   ID      pos_x      pos_y      pos_z        v_x        v_y        v_z           rho        rho_dh        wcount     wcount_dh         div_v       curl_vx       curl_vy       curl_vz
+    0	    1e-6       1e-6	  1e-6 	       1e-6 	  1e-6	     1e-6	   1e-6	      2e-6	    2e-5       2e-3		 2e-6	     2e-6	   2e-6		 2e-6
+    0	    1e-6       1e-6	  1e-6 	       1e-6 	  1e-6	     1e-6	   1e-6	      1e-6	    1e-5       1e-4		 2e-5	     2e-5	   2e-5	 	 2e-5
diff --git a/tests/tolerance_27_perturbed.dat b/tests/tolerance_27_perturbed.dat
new file mode 100644
index 0000000000000000000000000000000000000000..f0417d845f83d171dacb6b66024cf9a5dc41c6f1
--- /dev/null
+++ b/tests/tolerance_27_perturbed.dat
@@ -0,0 +1,3 @@
+#   ID      pos_x      pos_y      pos_z        v_x        v_y        v_z           rho        rho_dh        wcount     wcount_dh         div_v       curl_vx       curl_vy       curl_vz
+    0	    1e-6       1e-6	  1e-6 	       1e-6 	  1e-6	     1e-6	   1.2e-6     2e-6	    2.1e-5     2e-3		 2.1e-6	     2e-6	   2e-6		 2e-6
+    0	    1e-6       1e-6	  1e-6 	       1e-6 	  1e-6	     1e-6	   1e-6	      1e-6	    1e-5       1e-4		 2e-5	     4e-4	   4e-4	 	 4e-4
diff --git a/tests/tolerance_pair.dat b/tests/tolerance_pair_normal.dat
similarity index 100%
rename from tests/tolerance_pair.dat
rename to tests/tolerance_pair_normal.dat
diff --git a/tests/tolerance_27.dat b/tests/tolerance_pair_perturbed.dat
similarity index 69%
rename from tests/tolerance_27.dat
rename to tests/tolerance_pair_perturbed.dat
index 6691c7791ed71152e3684c695c885d7a13fc9cad..ca58ff45995158e031eca6b60eec498aa6c627ef 100644
--- a/tests/tolerance_27.dat
+++ b/tests/tolerance_pair_perturbed.dat
@@ -1,3 +1,3 @@
 #   ID      pos_x      pos_y      pos_z        v_x        v_y        v_z           rho        rho_dh        wcount     wcount_dh         div_v       curl_vx       curl_vy       curl_vz
-    0	    1e-6       1e-6	  1e-6 	       1e-6 	  1e-6	     1e-6	   1e-5	      1e-5	    4e-5       3e-2		 1e-5	     1e-5	   1e-5		 1e-5
-    0	    1e-6       1e-6	  1e-6 	       1e-6 	  1e-6	     1e-6	   1e-5	      1.2e-5	    1e-5       1e-2		 1e-4	     1.5e-4	   1.5e-4	 1.5e-4
+    0	    1e-6       1e-6	  1e-6 	       1e-6 	  1e-6	     1e-6	   1e-5	      1e-5	    2e-5       3e-2		 1e-5	     1e-5	   1e-5		 1e-5
+    0	    1e-6       1e-6	  1e-6 	       1e-6 	  1e-6	     1e-6	   1e-3	      4e-4	    8e-3       2e-2		 1e-4	     1.6e-4	   1.6e-4	 1.6e-4