diff --git a/.gitignore b/.gitignore
index aa3676fe292246d0ab53022dfd2a4a3ce305174e..db06575cf9f291d1fb9fa253fb5146c065523dd9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,7 @@ examples/*/*/*.xmf
 examples/*/*/*.hdf5
 examples/*/*/*.txt
 examples/*/*/used_parameters.yml
+examples/*/*.png
 
 tests/testPair
 tests/brute_force_standard.dat
@@ -72,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/configure.ac b/configure.ac
index a798372c2f219a8da71f2b79321c140aba23e790..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,7 +466,7 @@ 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 -Wunused-variable"
diff --git a/examples/Disk-Patch/GravityOnly/makeIC.py b/examples/Disk-Patch/GravityOnly/makeIC.py
index 135d1d4e34e0ec4c32d75a2c49d6e55f46db4e1c..702a50ff53b73d004ff36be8049823515675cccf 100644
--- a/examples/Disk-Patch/GravityOnly/makeIC.py
+++ b/examples/Disk-Patch/GravityOnly/makeIC.py
@@ -110,7 +110,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/Disk-Patch/HydroStatic/makeIC.py b/examples/Disk-Patch/HydroStatic/makeIC.py
index 7404f76aeaa485037b19052d84636c1656847a2b..40b1d1f1ff9e08dae0c4b0b1539ca773c93009b4 100644
--- a/examples/Disk-Patch/HydroStatic/makeIC.py
+++ b/examples/Disk-Patch/HydroStatic/makeIC.py
@@ -175,6 +175,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"] = [entropy_flag]
+grp.attrs["Dimension"] = 3
 
 #Runtime parameters
 grp = file.create_group("/RuntimePars")
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/main.c b/examples/main.c
index 561f20ae3fefa723c0fb2c0677f12b9eae724306..7dcfee85478235e48351f7b8f9cf973cf5f0fda4 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -273,18 +273,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);
 
@@ -487,6 +475,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;
   }
 
@@ -553,7 +543,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
@@ -589,7 +579,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/plot_tasks.py b/examples/plot_tasks.py
index 6e71f476a106937f43bd4bd5973af01f65218afe..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_gather_m", "grav_fft",
-             "grav_mm", "grav_up", "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,7 +67,6 @@ TASKCOLOURS = {"none": "black",
                "sub_pair": "navy",
                "init": "indigo",
                "ghost": "cyan",
-               "drift": "maroon",
                "kick": "green",
                "kick_fixdt": "green",
                "send": "yellow",
@@ -78,20 +76,17 @@ TASKCOLOURS = {"none": "black",
                "grav_mm": "mediumturquoise",
                "grav_up": "mediumvioletred",
                "grav_external": "darkred",
-               "part_sort": "steelblue",
-               "gpart_sort": "teal" ,
-               "split_cell": "seagreen",
-               "rewait": "olive",
                "count": "powerblue"}
 
-SUBTYPES = ["none", "density", "force", "grav", "tend", "count"]
+SUBTYPES = ["none", "density", "gradient", "force", "grav", "tend", "count"]
 
 SUBCOLOURS = {"none": "black",
               "density": "red",
+              "gradient": "powerblue",
               "force": "blue",
               "grav": "indigo",
-              "tend": "grey"
-              "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 7550899da2d4a34a5f73b192cbd7c348426786b7..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_gather_m", "grav_fft",
-             "grav_mm", "grav_up", "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,7 +73,6 @@ TASKCOLOURS = {"none": "black",
                "sub_pair": "navy",
                "init": "indigo",
                "ghost": "cyan",
-               "drift": "maroon",
                "kick": "green",
                "kick_fixdt": "green",
                "send": "yellow",
@@ -84,20 +82,17 @@ TASKCOLOURS = {"none": "black",
                "grav_mm": "mediumturquoise",
                "grav_up": "mediumvioletred",
                "grav_external": "darkred",
-               "part_sort": "steelblue",
-               "gpart_sort": "teal" ,
-               "split_cell": "seagreen",
-               "rewait": "olive",
                "count": "powerblue"}
 
-SUBTYPES = ["none", "density", "force", "grav", "tend", "count"]
+SUBTYPES = ["none", "density", "gradient", "force", "grav", "tend", "count"]
 
 SUBCOLOURS = {"none": "black",
               "density": "red",
+              "gradient": "powerblue",
               "force": "blue",
               "grav": "indigo",
-              "tend": "grey"
-              "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/adiabatic_index.h b/src/adiabatic_index.h
index 78d1cc9d2deaf60c5d933b74a089c77446b44414..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
@@ -34,6 +35,7 @@
 /* Local headers. */
 #include "const.h"
 #include "debug.h"
+#include "error.h"
 #include "inline.h"
 
 /* First define some constants */
@@ -42,18 +44,56 @@
 #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
 
@@ -73,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) */
@@ -103,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) */
@@ -133,6 +181,10 @@ __attribute__((always_inline)) INLINE static float pow_minus_gamma_minus_one(
   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)
 
   return 1.f / cbrtf(x); /* x^(-1/3) */
@@ -149,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/cell.c b/src/cell.c
index 9cf64b89005c497d70feccb6bc4067bdbfb8ee02..094645ba50e35cd06e3a3f0c333741cb34d4858d 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -704,6 +704,9 @@ 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;
 }
diff --git a/src/cell.h b/src/cell.h
index 605d0c85444c1ac3b4147ef2fb23df52b2694ff5..19e347225d59d6bdb86091efaa4a06bbf981422a 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -134,18 +134,20 @@ struct cell {
   int sortsize, gsortsize;
 
   /* 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, *kick;
+  struct task *extra_ghost, *ghost, *init, *kick;
 
 #ifdef WITH_MPI
-  /* Task receiving data. */
-  struct task *recv_xv, *recv_rho, *recv_ti;
+  
+/* Task receiving data. */
+  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. */
@@ -215,6 +217,8 @@ 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,
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 cbaa0054fe3bc9f829c59b19b71197264fd0c5af..5f7bf0d26431887c8e37d96b20b0eba460de55a6 100644
--- a/src/const.h
+++ b/src/const.h
@@ -46,6 +46,7 @@
 
 /* Hydrodynamical adiabatic index. */
 #define HYDRO_GAMMA_5_3
+//#define HYDRO_GAMMA_7_5
 //#define HYDRO_GAMMA_4_3
 //#define HYDRO_GAMMA_2_1
 
@@ -65,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
diff --git a/src/debug.c b/src/debug.c
index 4601f9bacc2d51d988ee0212b4ccf778bcf15f5c..15354b7d419544a8456543b79c38235eb3b68b2c 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -46,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
@@ -71,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;
@@ -100,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;
@@ -159,7 +161,7 @@ int checkSpacehmax(struct space *s) {
 
   /* Now all particles. */
   float part_h_max = 0.0f;
-  for (int k = 0; k < s->nr_parts; k++) {
+  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;
     }
@@ -178,7 +180,7 @@ int checkSpacehmax(struct space *s) {
     }
   }
 
-  for (int k = 0; k < s->nr_parts; k++) {
+  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);
diff --git a/src/dimension.h b/src/dimension.h
index 6395d4d04e50d40b733e7a74dafb7d0ab277d204..0fae2c5602b87622ff67f6f5feb325efc6422472 100644
--- a/src/dimension.h
+++ b/src/dimension.h
@@ -33,6 +33,8 @@
 #include "inline.h"
 #include "vector.h"
 
+#include <math.h>
+
 /* First define some constants */
 #if defined(HYDRO_DIMENSION_3D)
 
@@ -114,6 +116,92 @@ __attribute__((always_inline)) INLINE static float pow_dimension_plus_one(
 #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
 
diff --git a/src/drift.h b/src/drift.h
index 880595dc59e3e5174ee5e888e595a9204ad383e2..bd1b35926740d49a67291ede4676f3387cd66748 100644
--- a/src/drift.h
+++ b/src/drift.h
@@ -65,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;
@@ -78,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 = -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 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 2d4b8460c0a1373cfa67832c4437472c64394d7f..0b09e2766e52e7ac2c354f038aab8d13c0a52553 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -219,6 +219,12 @@ void engine_make_hydro_hierarchical_tasks(struct engine *e, struct cell *c,
       /* Generate the ghost task. */
       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
     }
   }
 
@@ -297,7 +303,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
 
@@ -353,7 +359,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
 
@@ -588,7 +594,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);
   }
 
@@ -676,11 +682,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;
@@ -699,21 +706,42 @@ 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);
@@ -722,6 +750,9 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj,
     /* Add them to the local cell. */
     engine_addlink(e, &ci->send_xv, t_xv);
     engine_addlink(e, &ci->send_rho, t_rho);
+#ifdef EXTRA_HYDRO_LOOP
+    engine_addlink(e, &ci->send_gradient, t_gradient);
+#endif
     if (t_ti != NULL) engine_addlink(e, &ci->send_ti, t_ti);
   }
 
@@ -729,7 +760,8 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj,
   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.");
@@ -743,11 +775,13 @@ 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;
@@ -758,19 +792,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);
@@ -780,12 +834,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.");
@@ -879,7 +934,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;
@@ -969,7 +1024,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]);
@@ -1033,7 +1088,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) {
@@ -1127,13 +1182,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.",
@@ -1141,8 +1196,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];
@@ -1152,8 +1207,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;
     }
   }
 
@@ -1471,19 +1526,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);
@@ -1491,6 +1573,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
@@ -1518,6 +1601,24 @@ 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);
@@ -1528,11 +1629,40 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
       /* 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);
@@ -1551,12 +1681,38 @@ 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,
@@ -1571,12 +1727,43 @@ 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,
@@ -1596,8 +1783,8 @@ 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 */
     else if (t->type == task_type_grav_external) {
       scheduler_addunlock(sched, t->ci->init, t);
@@ -1673,7 +1860,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;
@@ -1719,13 +1910,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
@@ -2026,8 +2217,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);
 }
 
 /**
@@ -2508,11 +2699,11 @@ void engine_step(struct engine *e) {
   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);
   }
@@ -2554,6 +2745,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 */
@@ -2727,7 +2923,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;
@@ -2749,7 +2945,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;
diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h
index da6b9aa01d7d382b1d6aa85f732f1fae1169ac34..d4249c46a3150a357aaecfb02f9251901d97a157 100644
--- a/src/gravity/Default/gravity.h
+++ b/src/gravity/Default/gravity.h
@@ -71,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;
 }
@@ -115,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;
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 021599cd2daf61ff35e5f29e3f13b2ad61c8947a..f61bff55821809fe1f5da27c95d75afbecbc04cc 100644
--- a/src/hydro/Default/hydro.h
+++ b/src/hydro/Default/hydro.h
@@ -73,6 +73,28 @@ __attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
   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
@@ -288,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;
diff --git a/src/hydro/Default/hydro_part.h b/src/hydro/Default/hydro_part.h
index a2f4453dc69ed06ca4f315b6be29844c177d0435..f42c3dc886ae1ab8f472ffdf5ff508f6735d1bb1 100644
--- a/src/hydro/Default/hydro_part.h
+++ b/src/hydro/Default/hydro_part.h
@@ -28,6 +28,8 @@ struct xpart {
   /* Velocity at the last full step. */
   float v_full[3];
 
+  float u_full;
+
   /* Old density. */
   float omega;
 
diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h
index e9d626cb8c147c0cf4fa8d27f8bab31d2471beae..09a8f50d5b2e9abd43c3b9bd43a12fad8a347258 100644
--- a/src/hydro/Gadget2/hydro.h
+++ b/src/hydro/Gadget2/hydro.h
@@ -20,6 +20,7 @@
 #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"
@@ -77,6 +78,28 @@ __attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
   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
@@ -285,13 +308,30 @@ __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;
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 a5d73aad02372aa22b840c2cb0d3100cd439e75d..0bbc77f4a2384c79f7d3329c20b92990598d5c63 100644
--- a/src/hydro/Minimal/hydro.h
+++ b/src/hydro/Minimal/hydro.h
@@ -35,6 +35,7 @@
  */
 
 #include "adiabatic_index.h"
+#include "approx_math.h"
 #include "dimension.h"
 #include "equation_of_state.h"
 #include "hydro_properties.h"
@@ -102,6 +103,28 @@ __attribute__((always_inline)) INLINE static float hydro_get_soundspeed(
   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
@@ -286,15 +309,32 @@ __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) {
+    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;
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/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_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/parallel_io.c b/src/parallel_io.c
index d543aac7e700dc008c439ffca7e45108c6c1904e..5d10cf03137937a4b89db68218bb723d3a282c59 100644
--- a/src/parallel_io.c
+++ b/src/parallel_io.c
@@ -528,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;
 
@@ -559,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 */
@@ -793,13 +784,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;
 
@@ -816,7 +807,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 */
@@ -829,9 +820,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/potentials.h b/src/potentials.h
index f0de5b4dc784be2e613c634bf687300d57f40ecd..74f0fd28566f355962c83e5d743aeae9afe09c59 100644
--- a/src/potentials.h
+++ b/src/potentials.h
@@ -78,13 +78,15 @@ struct external_potential {
  *
  * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948
  *
- * @param phys_cont The physical constants in internal units.
- * @param gp Pointer to the g-particle data.
+ * @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* potential,
-                                     const struct phys_const* const phys_const,
-                                     const struct gpart* const g) {
+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;
@@ -136,13 +138,15 @@ external_gravity_disk_patch_timestep(const struct external_potential* potential,
  *
  * See Creasey, Theuns & Bower, 2013, MNRAS, Volume 429, Issue 3, p.1922-1948
  *
- * @param phys_cont The physical constants in internal units.
+ * @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(
-    const double time, const struct external_potential* potential,
-    const struct phys_const* const phys_const, struct gpart* g) {
+    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;
@@ -178,8 +182,9 @@ external_gravity_disk_patch_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;
@@ -247,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;
@@ -284,8 +290,8 @@ external_gravity_pointmass_timestep(const struct external_potential* potential,
  * @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;
diff --git a/src/riemann.h b/src/riemann.h
index d647b021167317d14f4cd7316d09c247794f3d23..0191058e3ba856d7dcec42ba6185def3ee71d090 100644
--- a/src/riemann.h
+++ b/src/riemann.h
@@ -27,18 +27,27 @@
 #include "stdio.h"
 #include "stdlib.h"
 
-#define HLLC_SOLVER
+#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 8434b3445bf3cebf6d7f5d1110c54a9ccdefb5a1..57a6b9de7017ccb953eaaf05d9880cc440bdd974 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -82,6 +82,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
@@ -422,7 +429,49 @@ 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;
+
+  /* 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.
@@ -613,7 +662,7 @@ static void runner_do_drift(struct cell *c, struct engine *e) {
   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. */
@@ -657,7 +706,8 @@ static void runner_do_drift(struct cell *c, struct engine *e) {
       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;
@@ -1089,13 +1139,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)
@@ -1106,6 +1158,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)
@@ -1119,6 +1175,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)
@@ -1129,6 +1189,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)
@@ -1142,12 +1206,18 @@ void *runner_main(void *data) {
         case task_type_ghost:
           runner_do_ghost(r, ci);
           break;
+#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);
@@ -1161,6 +1231,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;
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 adc63e55092ff9bc69e6a2f98d6dfd5c399857f4..ef4d19107fb48684ca299f286436a155a6fe0151 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -111,14 +111,14 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta,
 static void scheduler_splittask(struct task *t, struct scheduler *s) {
 
   /* Static constants. */
-  const static int pts[7][8] = {
+  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 static 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};
+  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};
 
   /* Iterate on this task until we're done with it. */
   int redo = 1;
@@ -704,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 );
diff --git a/src/serial_io.c b/src/serial_io.c
index d0618ea93e07168274dff02f31ae95c89503859b..a9213819d4816a08614b82264ab463cecc060419 100644
--- a/src/serial_io.c
+++ b/src/serial_io.c
@@ -179,7 +179,8 @@ void readArray(hid_t grp, const struct io_props props, size_t N,
 
 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 */
@@ -220,7 +221,7 @@ void prepareArray(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);
   }
 
@@ -590,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;
 
@@ -621,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 */
@@ -886,13 +879,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;
 
@@ -907,7 +900,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;
@@ -918,9 +911,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 2d95c2b32fc4b9b2e0e696deb75c9eab1acd9158..df741ac22335fc05ec1ac334636a897e6def3e96 100644
--- a/src/single_io.c
+++ b/src/single_io.c
@@ -257,7 +257,7 @@ 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);
   }
 
@@ -471,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;
 
@@ -493,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);
diff --git a/src/space.c b/src/space.c
index def8adad3d023efe5daf552328d523d3cfc37429..2f4442c4729f69d8010278f7cf772b40d100d1f1 100644
--- a/src/space.c
+++ b/src/space.c
@@ -352,14 +352,17 @@ 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].kick = NULL;
       s->cells[k].super = &s->cells[k];
@@ -426,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)
@@ -484,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;
@@ -593,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]);
@@ -711,7 +714,7 @@ void space_parts_sort(struct space *s, int *ind, size_t N, int min, int max,
   if ((sort_struct.stack =
            malloc(sizeof(struct qstack) * sort_struct.stack_size)) == NULL)
     error("Failed to allocate sorting stack.");
-  for (int i = 0; i < sort_struct.stack_size; i++)
+  for (unsigned int i = 0; i < sort_struct.stack_size; i++)
     sort_struct.stack[i].ready = 0;
 
   /* Add the first interval. */
@@ -896,7 +899,7 @@ void space_gparts_sort(struct space *s, int *ind, size_t N, int min, int max,
   if ((sort_struct.stack =
            malloc(sizeof(struct qstack) * sort_struct.stack_size)) == NULL)
     error("Failed to allocate sorting stack.");
-  for (int i = 0; i < sort_struct.stack_size; i++)
+  for (unsigned int i = 0; i < sort_struct.stack_size; i++)
     sort_struct.stack[i].ready = 0;
 
   /* Add the first interval. */
@@ -1580,13 +1583,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.");
@@ -1594,13 +1597,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.");
diff --git a/src/task.c b/src/task.c
index 9e48f8abeb9659ea2674f48620b58da977a6775a..2cecce9f066727c9059b08764df647bd8f0f3901 100644
--- a/src/task.c
+++ b/src/task.c
@@ -46,18 +46,20 @@
 #include "inline.h"
 #include "lock.h"
 
-/* Task type names. */
 const char *taskID_names[task_type_count] = {
-    "none",     "sort",         "self",          "pair",     "sub_self",
-    "sub_pair", "init",         "ghost",         "kick",     "kick_fixdt",
-    "send",     "recv",         "grav_gather_m", "grav_fft", "grav_mm",
-    "grav_up",  "grav_external"};
+    "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", "tend"};
+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.
  */
 __attribute__((always_inline)) INLINE static size_t task_cell_overlap_part(
     const struct cell *ci, const struct cell *cj) {
@@ -77,6 +79,9 @@ __attribute__((always_inline)) INLINE static size_t task_cell_overlap_part(
 
 /**
  * @brief Computes the overlap between the gparts array of two given cells.
+ *
+ * @param ci The first #cell.
+ * @param cj The second #cell.
  */
 __attribute__((always_inline)) INLINE static size_t task_cell_overlap_gpart(
     const struct cell *ci, const struct cell *cj) {
@@ -110,6 +115,7 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
 
     case task_type_sort:
     case task_type_ghost:
+    case task_type_extra_ghost:
       return task_action_part;
       break;
 
@@ -120,6 +126,7 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
       switch (t->subtype) {
 
         case task_subtype_density:
+        case task_subtype_gradient:
         case task_subtype_force:
           return task_action_part;
           break;
diff --git a/src/task.h b/src/task.h
index db74c296bb7988d12e474368ed9abffd97ede49e..c0c9e47ee4ca221f9f960256e9208c749ae523ea 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,6 +41,7 @@ enum task_types {
   task_type_sub_pair,
   task_type_init,
   task_type_ghost,
+  task_type_extra_ghost,
   task_type_kick,
   task_type_kick_fixdt,
   task_type_send,
@@ -53,19 +54,22 @@ enum task_types {
   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,
@@ -75,29 +79,72 @@ 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. */
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 aa8455397daf1e88b709a8332e3ae63694991e94..0bf1f3f413d123c84cc30edff73ca9dfce4ea159 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,
@@ -64,7 +68,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
@@ -74,7 +78,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;
@@ -87,6 +91,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/tools.c b/src/tools.c
index b64e17849081994e1969d5f8de0636768dcb729a..060bf1439f30dc6237938c060bc4ddc8d9be822b 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -442,43 +442,6 @@ 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.
  */
@@ -499,7 +462,7 @@ void engine_single_density(double *dim, long long int pid,
   hydro_init_part(&p);
 
   /* Loop over all particle pairs (force). */
-  for (int k = 0; k < N; k++) {
+  for (k = 0; k < N; k++) {
     if (parts[k].id == p.id) continue;
     for (int i = 0; i < 3; i++) {
       dx[i] = p.x[i] - parts[k].x[i];
@@ -520,7 +483,7 @@ void engine_single_density(double *dim, long long int pid,
   /* Dump the result. */
   hydro_end_density(&p, 0);
   message("part %lli (h=%e) has wcount=%e, rho=%e.", p.id, p.h,
-          p.density.wcount, p.rho);
+          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..9adaa7dc621edafe0c1f097b8c5202a2f017e41d 100644
--- a/src/units.h
+++ b/src/units.h
@@ -29,25 +29,26 @@
  * @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 +90,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 952787823d08772b79b4d69d0c8b3728d79e4bfd..5648c9a3ca0c3f944982b31060cfe9a0e9c3e45d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -24,13 +24,16 @@ AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS
 TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetry \
         testPair.sh testPairPerturbed.sh test27cells.sh test27cellsPerturbed.sh  \
         testParser.sh testSPHStep test125cells.sh testKernelGrav testFFT \
-	testThreadpool
+        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 testThreadpool
+                 testSymmetry testThreadpool \
+                 testAdiabaticIndex testRiemannExact testRiemannTRRS \
+                 testRiemannHLLC testMatrixInversion
 
 # Sources for the individual programs
 testGreetings_SOURCES = testGreetings.c
@@ -63,6 +66,16 @@ 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
diff --git a/tests/test125cells.c b/tests/test125cells.c
index dc15c5aa2f4e00862633807bec9603d77fa4bf30..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;
 
@@ -187,12 +189,29 @@ void get_solution(const struct cell *main_cell, struct solution_part *solution,
 void reset_particles(struct cell *c, enum velocity_field vel,
                      enum pressure_field press, float size, float density) {
 
-  for (size_t i = 0; i < c->count; ++i) {
+  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
   }
 }
 
@@ -238,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);
@@ -248,6 +272,24 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
         part->ti_end = 1;
 
         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;
       }
@@ -304,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 "
@@ -313,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
@@ -343,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 "
diff --git a/tests/test27cells.c b/tests/test27cells.c
index 6d935c0a4a57b3fbbdbd9b378a4b8cb97234aa63..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,7 +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++) {
+  for (int pid = 0; pid < c->count; pid++) {
     hydro_init_part(&c->parts[pid]);
   }
 }
@@ -155,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);
   }
 }
@@ -177,14 +181,15 @@ 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].v[2],
+            hydro_get_density(&main_cell->parts[pid]),
 #if defined(GIZMO_SPH)
             0.f,
 #else
@@ -219,14 +224,14 @@ 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].v[2], hydro_get_density(&cj->parts[pjd]),
 #if defined(GIZMO_SPH)
               0.f,
 #else
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/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 f7b20faa3dc2aeb39e829ae634666e164c47b60a..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,7 +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++) {
+  for (int pid = 0; pid < c->count; pid++) {
     hydro_init_part(&c->parts[pid]);
   }
 }
@@ -125,13 +129,13 @@ 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].v[2], hydro_get_density(&ci->parts[pid]),
 #if defined(GIZMO_SPH)
             0.f,
 #else
@@ -149,13 +153,13 @@ 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].v[2], hydro_get_density(&cj->parts[pjd]),
 #if defined(GIZMO_SPH)
             0.f,
 #else
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
index 90ec58e10b16d816b52b785c87e6604ad11f61d8..aa65d533a29afbe4e7e8384fb887281822a31e58 100644
--- a/tests/testThreadpool.c
+++ b/tests/testThreadpool.c
@@ -77,4 +77,9 @@ int main(int argc, char *argv[]) {
     fflush(stdout);
     threadpool_map(&tp, map_function_first, data, N, sizeof(int), 2, NULL);
   }
+
+  /* Be clean */
+  threadpool_clean(&tp);
+
+  return 0;
 }