diff --git a/.gitignore b/.gitignore
index f6a8b1799c52929d53d8ba4f531bec52c2e9db3d..27ff2cf7ff880de2007e27978fcc1cfa22d2bd75 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,19 +26,29 @@ examples/swift_mpi
 examples/*/*.xmf
 examples/*/*.h5
 examples/*/*.png
+examples/*/*.mp4
 examples/*/*.txt
 examples/*/*.dot
+examples/*/restart/*
 examples/*/used_parameters.yml
+examples/*/unused_parameters.yml
 examples/*/*/*.xmf
 examples/*/*/*.png
+examples/*/*/*.mp4
 examples/*/*/*.txt
 examples/*/*/*.dot
+examples/*/*/*.rst
+examples/*/*/*.hdf5
+examples/*/snapshots*
+examples/*/restart/*
 examples/*/*/used_parameters.yml
 examples/*/err_file*
 examples/*/out_file*
 examples/*/stf_output*
 examples/*/stf_ouput*
 examples/*/log*
+examples/*/*/unused_parameters.yml
+examples/*/*.mpg
 examples/*/gravity_checks_*.dat
 
 tests/testActivePair
@@ -64,7 +74,7 @@ tests/swift_dopair_125_standard.dat
 tests/brute_force_125_perturbed.dat
 tests/swift_dopair_125_perturbed.dat
 tests/brute_force_pair_active.dat
-tests/brute_force_dopair2_active.dat 
+tests/brute_force_dopair2_active.dat
 tests/swift_dopair2_force_active.dat
 tests/brute_force_periodic_BC_perturbed.dat
 tests/swift_dopair_active.dat
@@ -75,7 +85,9 @@ tests/test_nonsym_density_1_vec.dat
 tests/test_nonsym_density_2_vec.dat
 tests/test_nonsym_force_1_vec.dat
 tests/test_nonsym_force_2_vec.dat
+tests/potential.dat
 tests/testGreetings
+tests/testSelectOutput
 tests/testReading
 tests/testSingle
 tests/testTimeIntegration
@@ -98,6 +110,9 @@ tests/test125cells.sh
 tests/test125cellsPerturbed.sh
 tests/testParser.sh
 tests/testReading.sh
+tests/testSelectOutput.sh
+tests/unused_parser_output.yml
+tests/used_parser_output.yml
 tests/testAdiabaticIndex
 tests/testRiemannExact
 tests/testRiemannTRRS
@@ -112,6 +127,11 @@ tests/benchmarkInteractions
 tests/testGravityDerivatives
 tests/testPotentialSelf
 tests/testPotentialPair
+tests/testEOS
+tests/testEOS*.txt
+tests/testEOS*.png
+tests/testUtilities
+tests/testCbrt
 
 theory/latex/swift.pdf
 theory/SPH/Kernels/kernels.pdf
@@ -292,4 +312,7 @@ sympy-plots-for-*.tex/
 *.xdy
 
 # macOS
-*.DS_Store
\ No newline at end of file
+*.DS_Store
+
+#ctags
+*tags
diff --git a/INSTALL.swift b/INSTALL.swift
index 1782b75e34e2028110717d7873bc2c97365f8240..999a8d3655fa14a8ba1dcbd430b5146cc55ba791 100644
--- a/INSTALL.swift
+++ b/INSTALL.swift
@@ -96,18 +96,21 @@ SWIFT depends on a number of third party libraries that should be available
 before you can build it.
 
 
- - HDF5: a HDF5 library (v. 1.8.x or higher) is required to read and
-         write particle data. One of the commands "h5cc" or "h5pcc"
-         should be available. If "h5pcc" is located them a parallel
-         HDF5 built for the version of MPI located should be
-         provided. If the command is not available then it can be
-         located using the "--with-hfd5" configure option. The value
-         should be the full path to the "h5cc" or "h5pcc" commands.
-
-
- - MPI: to run on more than one node an MPI library that fully
-        supports MPI_THREAD_MULTIPLE.  Before running configure the
-        "mpirun" command should be available in the shell. If your
+ - HDF5:
+	A HDF5 library (v. 1.8.x or higher) is required to read and
+        write particle data. One of the commands "h5cc" or "h5pcc"
+        should be available. If "h5pcc" is located then a parallel
+        HDF5 built for the version of MPI located should be
+        provided. If the command is not available then it can be
+        located using the "--with-hdf5" configure option. The value
+        should be the full path to the "h5cc" or "h5pcc" commands.
+        SWIFT makes effective use of parallel HDF5 when running on more than
+        one node, so this option is highly recommended.
+
+ - MPI:
+	To run on more than one node an MPI library that fully
+        supports MPI_THREAD_MULTIPLE is required.  Before running configure
+        the "mpirun" command should be available in the shell. If your
         command isn't called "mpirun" then define the "MPIRUN"
         environment variable, either in the shell or when running
         configure.
@@ -116,57 +119,69 @@ before you can build it.
 	much like the CC one. Use this when your MPI compiler has a
 	none-standard name.
 
- - GSL: To use cosmological time integration, a version of the GSL
-        must be available. 
+ - GSL:
+	To use cosmological time integration, a version of the GSL
+	must be available.
 
- - libtool: The build system relies on libtool as well as the other autotools.
+ - FFTW 3.x:
+	To run with periodic gravity forces, a build of the FFTW 3
+	library must be available. Note that SWIFT does not make use
+	of the parallel capability of FFTW. Calculations are done by
+	single MPI nodes independently.
 
-
-                           Optional Dependencies
-                           =====================
+- libtool:
+	The build system relies on libtool as well as the other autotools.
 
 
- - METIS: a build of the METIS library can be optionally used to
-          optimize the load between MPI nodes (requires an MPI
-          library). This should be found in the standard installation
-          directories, or pointed at using the "--with-metis"
-          configuration option.  In this case the top-level
-          installation directory of the METIS build should be
-          given. Note to use METIS you should at least supply
-          "--with-metis".
 
+                           Optional Dependencies
+                           =====================
 
- - libNUMA: a build of the NUMA library can be used to pin the threads
-            to the physical core of the machine SWIFT is running
-            on. This is not always necessary as the OS scheduler may
-            do a good job at distributing the threads among the
-            different cores on each computing node.
 
+ - METIS:
+	a build of the METIS library can be optionally used to
+        optimize the load between MPI nodes (requires an MPI
+        library). This should be found in the standard installation
+        directories, or pointed at using the "--with-metis"
+        configuration option.  In this case the top-level installation
+        directory of the METIS build should be given. Note to use
+        METIS you should supply at least "--with-metis".
 
- - TCMalloc: a build of the TCMalloc library (part of gperftools) can
-             be used to obtain faster allocations than the standard C
-             malloc function part of glibc. The option "-with-tcmalloc"
-	     should be passed to the configuration script to use it.
+- libNUMA:
+	a build of the NUMA library can be used to pin the threads to
+        the physical core of the machine SWIFT is running on. This is
+        not always necessary as the OS scheduler may do a good job at
+        distributing the threads among the different cores on each
+        computing node.
 
+ - tcmalloc / jemalloc / TBBmalloc:
+	a build of the tcmalloc library (part of gperftools), jemalloc
+	or TBBmalloc can be used be used to obtain faster and more
+	scalable allocations than the standard C malloc function part
+	of glibc. Using one of these is highly recommended on systems
+	with many cores per node. One of the options
+	"--with-tcmalloc", "--with-jemalloc" or "--with-tbbmalloc"
+	should be passed to the configuration script to use it.
 
- - gperftools: a build of gperftools can be used to obtain good
-               profiling of the code. The option "-with-profiler"
-               needs to be passed to the configuration script to use
-               it.
+ - gperftools:
+	a build of gperftools can be used to obtain good profiling of
+        the code. The option "--with-profiler" needs to be passed to
+        the configuration script to use it.
 
+ - DOXYGEN:
+	the doxygen library is required to create the SWIFT API
+        documentation.
 
- - DOXYGEN: the doxygen library is required to create the SWIFT API
-            documentation.
+ - python:
+	Examples and solution script use python and rely on the numpy
+	library version 1.8.2 or higher.
 
- - python:  Examples and solution script use python and rely on the
-   	    numpy library version 1.8.2 or higher.
 
 
                              SWIFT Coding style
                              ==================
 
-The SWIFT source code is using a variation of the 'Google' style. The
-script 'format.sh' in the root directory applies the clang-format-3.8
-tool with our style choices to all the SWIFT C source file. Please
-apply the formatting script to the files before submitting a merge
-request.
+The SWIFT source code uses a variation of 'Google' style. The script
+'format.sh' in the root directory applies the clang-format-3.8 tool with our
+style choices to all the SWIFT C source file. Please apply the formatting
+script to the files before submitting a merge request.
diff --git a/configure.ac b/configure.ac
index aa717b0d9c2d978f638ed5698cbf488e862d577d..2597d61d01b9df1fa22c1759960179c25b42e226 100644
--- a/configure.ac
+++ b/configure.ac
@@ -19,9 +19,6 @@
 AC_INIT([SWIFT],[0.7.0],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim])
 swift_config_flags="$*"
 
-#  Need to define this, instead of using fifth argument of AC_INIT, until 2.64.
-AC_DEFINE([PACKAGE_URL],["www.swiftsim.com"], [Package web pages])
-
 AC_COPYRIGHT
 AC_CONFIG_SRCDIR([src/space.c])
 AC_CONFIG_AUX_DIR([.])
@@ -41,47 +38,6 @@ AX_CHECK_ENABLE_DEBUG
 AC_PROG_CC
 AM_PROG_CC_C_O
 
-
-# Master subgrid options
-# If you add a restriction (e.g. no cooling, chemistry or hydro)
-# you will need to check for overwrite after reading the additional options.
-# As an example for this, see the call to AC_ARG_WITH for cooling.
-AC_ARG_WITH([subgrid],
-	[AS_HELP_STRING([--with-subgrid=<subgrid>],
-		[Master switch for subgrid methods. Inexperienced user should start from here @<:@none, GEAR, EAGLE default: none@:>@]
-	)],
-	[with_subgrid="$withval"],
-	[with_subgrid=none]
-)
-
-# Default values
-with_subgrid_cooling=none
-with_subgrid_chemistry=none
-with_subgrid_hydro=none
-
-case "$with_subgrid" in
-   yes)
-      AC_MSG_ERROR([Invalid option. A subgrid model must be chosen.])
-   ;;
-   none)
-   ;;
-   GEAR)
-	with_subgrid_cooling=grackle
-	with_subgrid_chemistry=GEAR
-	with_subgrid_hydro=gadget2
-   ;;
-   EAGLE)
-	with_subgrid_cooling=EAGLE
-	with_subgrid_chemistry=EAGLE
-	with_subgrid_hydro=gadget2
-   ;;
-   *)	
-      AC_MSG_ERROR([Unknown subgrid choice: $with_subgrid])
-   ;;
-esac
-
-
-
 # If debug is selected then we also define SWIFT_DEVELOP_MODE to control
 # any developer code options.
 if test "x$ax_enable_debug" != "xno"; then
@@ -276,6 +232,18 @@ if test "$enable_debugging_checks" = "yes"; then
    AC_DEFINE([SWIFT_DEBUG_CHECKS],1,[Enable expensive debugging])
 fi
 
+# Check if using our custom icbrtf is enalbled.
+AC_ARG_ENABLE([custom-icbrtf],
+   [AS_HELP_STRING([--enable-custom-icbrtf],
+     [Use SWIFT's custom icbrtf function instead of the system cbrtf @<:@yes/no@:>@]
+   )],
+   [enable_custom_icbrtf="$enableval"],
+   [enable_custom_icbrtf="no"]
+)
+if test "$enable_custom_icbrtf" = "yes"; then
+   AC_DEFINE([WITH_ICBRTF],1,[Enable custom icbrtf])
+fi
+
 # Check whether we want to default to naive cell interactions
 AC_ARG_ENABLE([naive-interactions],
    [AS_HELP_STRING([--enable-naive-interactions],
@@ -552,42 +520,6 @@ AH_VERBATIM([__STDC_FORMAT_MACROS],
 #define __STDC_FORMAT_MACROS 1
 #endif])
 
-# Check for grackle.
-have_grackle="no"
-AC_ARG_WITH([grackle],
-    [AS_HELP_STRING([--with-grackle=PATH],
-       [root directory where grackle is installed @<:@yes/no@:>@]
-    )],
-    [with_grackle="$withval"],
-    [with_grackle="no"]
-)
-if test "x$with_grackle" != "xno"; then
-   AC_PROG_FC
-   AC_FC_LIBRARY_LDFLAGS
-   if test "x$with_grackle" != "xyes" -a "x$with_grackle" != "x"; then
-      GRACKLE_LIBS="-L$with_grackle/lib -lgrackle"
-      GRACKLE_INCS="-I$with_grackle/include"
-   else
-      GRACKLE_LIBS="-lgrackle"
-      GRACKLE_INCS=""
-   fi
-
-   have_grackle="yes"
-
-   AC_CHECK_LIB(
-      [grackle],
-      [initialize_chemistry_data],
-      [AC_DEFINE([HAVE_GRACKLE],1,[The GRACKLE library appears to be present.])
-        AC_DEFINE([CONFIG_BFLOAT_8],1,[Use doubles in grackle])
-      ],
-      [AC_MSG_ERROR(Cannot find grackle library!)],
-      [$GRACKLE_LIBS $GRACKLE_INCS $FCLIBS]
-   )
-fi
-AC_SUBST([GRACKLE_LIBS])
-AC_SUBST([GRACKLE_INCS])
-AM_CONDITIONAL([HAVEGRACKLE],[test -n "$GRACKLE_LIBS"])
-
 # Check for FFTW. We test for this in the standard directories by default,
 # and only disable if using --with-fftw=no or --without-fftw. When a value
 # is given GSL must be found.
@@ -618,7 +550,7 @@ if test "x$with_fftw" != "xno"; then
          AC_DEFINE([HAVE_FFTW],1,[The FFTW library appears to be present.]),
          AC_MSG_ERROR(something is wrong with the FFTW library!), $FFTW_LIBS)
       have_fftw="yes"
-   fi      
+   fi
    if test "$have_fftw" = "no"; then
       FFTW_LIBS=""
       FFTW_INCS=""
@@ -628,15 +560,51 @@ AC_SUBST([FFTW_LIBS])
 AC_SUBST([FFTW_INCS])
 AM_CONDITIONAL([HAVEFFTW],[test -n "$FFTW_LIBS"])
 
+#  Check for -lprofiler usually part of the gperftools along with tcmalloc.
+have_profiler="no"
+AC_ARG_WITH([profiler],
+   [AS_HELP_STRING([--with-profiler=PATH],
+      [use cpu profiler library or specify the directory with lib @<:@yes/no@:>@]
+   )],
+   [with_profiler="$withval"],
+   [with_profiler="no"]
+)
+if test "x$with_profiler" != "xno"; then
+   if test "x$with_profiler" != "xyes" -a "x$with_profiler" != "x"; then
+      proflibs="-L$with_profiler -lprofiler"
+   else
+      proflibs="-lprofiler"
+   fi
+   AC_CHECK_LIB([profiler],[ProfilerFlush],
+    [have_profiler="yes" 
+      AC_DEFINE([WITH_PROFILER],1,[Link against the gperftools profiling library.])],
+    [have_profiler="no"], $proflibs)
+
+   if test "$have_profiler" = "yes"; then
+      PROFILER_LIBS="$proflibs"
+   else
+      PROFILER_LIBS=""
+   fi
+fi
+AC_SUBST([PROFILER_LIBS])
+AM_CONDITIONAL([HAVEPROFILER],[test -n "$PROFILER_LIBS"])
+
+# Check for special allocators
+have_special_allocator="no"
+
 #  Check for tcmalloc a fast malloc that is part of the gperftools.
 have_tcmalloc="no"
 AC_ARG_WITH([tcmalloc],
-   [AS_HELP_STRING([--with-tcmalloc],
+   [AS_HELP_STRING([--with-tcmalloc=PATH],
       [use tcmalloc library or specify the directory with lib @<:@yes/no@:>@]
    )],
    [with_tcmalloc="$withval"],
    [with_tcmalloc="no"]
 )
+if test "x$with_tcmalloc" != "xno" -a "x$have_special_allocator" != "xno"; then
+   AC_MSG_ERROR("Cannot activate more than one alternative malloc library")
+fi
+
 if test "x$with_tcmalloc" != "xno"; then
    if test "x$with_tcmalloc" != "xyes" -a "x$with_tcmalloc" != "x"; then
       tclibs="-L$with_tcmalloc -ltcmalloc"
@@ -660,10 +628,17 @@ if test "x$with_tcmalloc" != "xno"; then
    if test "$have_tcmalloc" = "yes"; then
       TCMALLOC_LIBS="$tclibs"
 
-      # These are recommended for GCC.
-      if test "$ax_cv_c_compiler_vendor" = "gnu"; then
-         CFLAGS="$CFLAGS -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free"
-      fi
+      AC_DEFINE([HAVE_TCMALLOC],1,[The tcmalloc library appears to be present.])
+
+      have_special_allocator="tcmalloc"
+
+      # Prevent compilers that replace the calls with built-ins (GNU 99) from doing so.
+      case "$ax_cv_c_compiler_vendor" in
+        intel | gnu | clang)
+             CFLAGS="$CFLAGS -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free"
+          ;;
+      esac
+
    else
       TCMALLOC_LIBS=""
    fi
@@ -671,42 +646,19 @@ fi
 AC_SUBST([TCMALLOC_LIBS])
 AM_CONDITIONAL([HAVETCMALLOC],[test -n "$TCMALLOC_LIBS"])
 
-#  Check for -lprofiler usually part of the gperftools along with tcmalloc.
-have_profiler="no"
-AC_ARG_WITH([profiler],
-   [AS_HELP_STRING([--with-profiler],
-      [use cpu profiler library or specify the directory with lib @<:@yes/no@:>@]
-   )],
-   [with_profiler="$withval"],
-   [with_profiler="yes"]
-)
-if test "x$with_profiler" != "xno"; then
-   if test "x$with_profiler" != "xyes" -a "x$with_profiler" != "x"; then
-      proflibs="-L$with_profiler -lprofiler"
-   else
-      proflibs="-lprofiler"
-   fi
-   AC_CHECK_LIB([profiler],[ProfilerFlush],[have_profiler="yes"],[have_profiler="no"],
-                $proflibs)
-
-   if test "$have_profiler" = "yes"; then
-      PROFILER_LIBS="$proflibs"
-   else
-      PROFILER_LIBS=""
-   fi
-fi
-AC_SUBST([PROFILER_LIBS])
-AM_CONDITIONAL([HAVEPROFILER],[test -n "$PROFILER_LIBS"])
-
 #  Check for jemalloc another fast malloc that is good with contention.
 have_jemalloc="no"
 AC_ARG_WITH([jemalloc],
-   [AS_HELP_STRING([--with-jemalloc],
+   [AS_HELP_STRING([--with-jemalloc=PATH],
       [use jemalloc library or specify the directory with lib @<:@yes/no@:>@]
    )],
    [with_jemalloc="$withval"],
    [with_jemalloc="no"]
 )
+if test "x$with_jemalloc" != "xno" -a "x$have_special_allocator" != "xno"; then
+   AC_MSG_ERROR("Cannot activate more than one alternative malloc library")
+fi
+
 if test "x$with_jemalloc" != "xno"; then
    if test "x$with_jemalloc" != "xyes" -a "x$with_jemalloc" != "x"; then
       jelibs="-L$with_jemalloc -ljemalloc"
@@ -718,6 +670,18 @@ if test "x$with_jemalloc" != "xno"; then
 
    if test "$have_jemalloc" = "yes"; then
       JEMALLOC_LIBS="$jelibs"
+
+      AC_DEFINE([HAVE_JEMALLOC],1,[The jemalloc library appears to be present.])
+
+      have_special_allocator="jemalloc"
+
+      # Prevent compilers that replace the regular calls with built-ins (GNU 99) from doing so.
+      case "$ax_cv_c_compiler_vendor" in
+        intel | gnu | clang)
+             CFLAGS="$CFLAGS -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free"
+          ;;
+      esac
+
    else
       JEMALLOC_LIBS=""
    fi
@@ -725,10 +689,48 @@ fi
 AC_SUBST([JEMALLOC_LIBS])
 AM_CONDITIONAL([HAVEJEMALLOC],[test -n "$JEMALLOC_LIBS"])
 
-#  Don't allow both tcmalloc and jemalloc.
-if test "x$have_tcmalloc" != "xno" -a "x$have_jemalloc" != "xno"; then
-   AC_MSG_ERROR([Cannot use tcmalloc at same time as jemalloc])
+#  Check for tbbmalloc, Intel's fast and parallel allocator
+have_tbbmalloc="no"
+AC_ARG_WITH([tbbmalloc],
+   [AS_HELP_STRING([--with-tbbmalloc=PATH],
+      [use tbbmalloc library or specify the directory with lib @<:@yes/no@:>@]
+   )],
+   [with_tbbmalloc="$withval"],
+   [with_tbbmalloc="no"]
+)
+if test "x$with_tbbmalloc" != "xno" -a "x$have_special_allocator" != "xno"; then
+   AC_MSG_ERROR("Cannot activate more than one alternative malloc library")
+fi
+
+if test "x$with_tbbmalloc" != "xno"; then
+   if test "x$with_tbbmalloc" != "xyes" -a "x$with_tbbmalloc" != "x"; then
+      tbblibs="-L$with_tbbmalloc -ltbbmalloc_proxy -ltbbmalloc"
+   else
+      tbblibs="-ltbbmalloc_proxy -ltbbmalloc"
+   fi
+   AC_CHECK_LIB([tbbmalloc],[scalable_malloc],[have_tbbmalloc="yes"],[have_tbbmalloc="no"],
+                $tbblibs)
+
+   if test "$have_tbbmalloc" = "yes"; then
+      TBBMALLOC_LIBS="$tbblibs"
+
+      AC_DEFINE([HAVE_TBBMALLOC],1,[The TBBmalloc library appears to be present.])
+
+      have_special_allocator="TBBmalloc"
+
+      # Prevent compilers that replace the calls with built-ins (GNU 99) from doing so.
+      case "$ax_cv_c_compiler_vendor" in
+        intel | gnu | clang)
+             CFLAGS="$CFLAGS -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free"
+          ;;
+      esac
+
+   else
+      TBBMALLOC_LIBS=""
+   fi
 fi
+AC_SUBST([TBBMALLOC_LIBS])
+AM_CONDITIONAL([HAVETBBMALLOC],[test -n "$TBBMALLOC_LIBS"])
 
 # Check for HDF5. This is required.
 AX_LIB_HDF5
@@ -751,7 +753,8 @@ if test "$with_hdf5" = "yes"; then
 
     if test "$enable_parallel_hdf5" = "yes"; then
         AC_MSG_CHECKING([for HDF5 parallel support])
-# Check if the library is capable, the header should define H5_HAVE_PARALLEL.
+
+	# Check if the library is capable, the header should define H5_HAVE_PARALLEL.
 
         AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
         #include "hdf5.h"
@@ -812,6 +815,27 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM(
 [AC_DEFINE(HAVE__RTC,1,[Define if you have the UNICOS _rtc() intrinsic.])],[rtc_ok=no])
 AC_MSG_RESULT($rtc_ok)
 
+# Special timers for the ARM v7 and ARM v8 platforms (taken from FFTW-3 to match their cycle.h)
+AC_ARG_ENABLE(armv8-pmccntr-el0, [AC_HELP_STRING([--enable-armv8-pmccntr-el0],[enable the cycle counter on ARMv8 via the PMCCNTR_EL0 register])], have_armv8pmccntrel0=$enableval)
+if test "$have_armv8pmccntrel0"x = "yes"x; then
+	AC_DEFINE(HAVE_ARMV8_PMCCNTR_EL0,1,[Define if you have enabled the PMCCNTR_EL0 cycle counter on ARMv8])
+fi
+
+AC_ARG_ENABLE(armv8-cntvct-el0, [AC_HELP_STRING([--enable-armv8-cntvct-el0],[enable the cycle counter on ARMv8 via the CNTVCT_EL0 register])], have_armv8cntvctel0=$enableval)
+if test "$have_armv8cntvctel0"x = "yes"x; then
+	AC_DEFINE(HAVE_ARMV8_CNTVCT_EL0,1,[Define if you have enabled the CNTVCT_EL0 cycle counter on ARMv8])
+fi
+
+AC_ARG_ENABLE(armv7a-cntvct, [AC_HELP_STRING([--enable-armv7a-cntvct],[enable the cycle counter on Armv7a via the CNTVCT register])], have_armv7acntvct=$enableval)
+if test "$have_armv7acntvct"x = "yes"x; then
+	AC_DEFINE(HAVE_ARMV7A_CNTVCT,1,[Define if you have enabled the CNTVCT cycle counter on ARMv7a])
+fi
+
+AC_ARG_ENABLE(armv7a-pmccntr, [AC_HELP_STRING([--enable-armv7a-pmccntr],[enable the cycle counter on Armv7a via the PMCCNTR register])], have_armv7apmccntr=$enableval)
+if test "$have_armv7apmccntr"x = "yes"x; then
+	AC_DEFINE(HAVE_ARMV7A_PMCCNTR,1,[Define if you have enabled the PMCCNTR cycle counter on ARMv7a])
+fi
+
 # Add warning flags by default, if these can be used. Option =error adds
 # -Werror to GCC, clang and Intel.  Note do this last as compiler tests may
 # become errors, if that's an issue don't use CFLAGS for these, use an AC_SUBST().
@@ -846,14 +870,59 @@ if test "$enable_warn" != "no"; then
           ;;
        esac
     fi
+
+    # We want strict-prototypes, but this must still work even if warnings
+    # are an error.
+    AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes],[CFLAGS="$CFLAGS -Wstrict-prototypes"],
+                          [CFLAGS="$CFLAGS"],[$CFLAGS],[AC_LANG_SOURCE([int main(void){return 0;}])])
 fi
 
 # Various package configuration options.
 
+# Master subgrid options
+# If you add a restriction (e.g. no cooling, chemistry or hydro)
+# you will need to check for overwrite after reading the additional options.
+# As an example for this, see the call to AC_ARG_WITH for cooling.
+AC_ARG_WITH([subgrid],
+	[AS_HELP_STRING([--with-subgrid=<subgrid>],
+		[Master switch for subgrid methods. Inexperienced user should start from here @<:@none, GEAR, EAGLE default: none@:>@]
+	)],
+	[with_subgrid="$withval"],
+	[with_subgrid=none]
+)
+
+# Default values
+with_subgrid_cooling=none
+with_subgrid_chemistry=none
+with_subgrid_hydro=none
+
+case "$with_subgrid" in
+   yes)
+      AC_MSG_ERROR([Invalid option. A subgrid model must be chosen.])
+   ;;
+   none)
+   ;;
+   GEAR)
+	with_subgrid_cooling=grackle
+	with_subgrid_chemistry=GEAR
+	with_subgrid_hydro=gadget2
+   ;;
+   EAGLE)
+	with_subgrid_cooling=EAGLE
+	with_subgrid_chemistry=EAGLE
+	with_subgrid_hydro=gadget2
+   ;;
+   *)
+      AC_MSG_ERROR([Unknown subgrid choice: $with_subgrid])
+   ;;
+esac
+
+
+
 # Hydro scheme.
 AC_ARG_WITH([hydro],
    [AS_HELP_STRING([--with-hydro=<scheme>],
-      [Hydro dynamics to use @<:@gadget2, minimal, hopkins, default, gizmo, shadowfax debug default: gadget2@:>@]
+      [Hydro dynamics to use @<:@gadget2, minimal, pressure-entropy, pressure-energy, default, gizmo-mfv, gizmo-mfm, shadowfax, minimal-multi-mat, debug default: gadget2@:>@]
    )],
    [with_hydro="$withval"],
    [with_hydro="gadget2"]
@@ -874,18 +943,27 @@ case "$with_hydro" in
    minimal)
       AC_DEFINE([MINIMAL_SPH], [1], [Minimal SPH])
    ;;
-   hopkins)
+   pressure-entropy)
       AC_DEFINE([HOPKINS_PE_SPH], [1], [Pressure-Entropy SPH])
    ;;
+   pressure-energy)
+      AC_DEFINE([HOPKINS_PU_SPH], [1], [Pressure-Energy SPH])
+   ;;
    default)
       AC_DEFINE([DEFAULT_SPH], [1], [Default SPH])
    ;;
-   gizmo)
-      AC_DEFINE([GIZMO_SPH], [1], [GIZMO SPH])
+   gizmo-mfv)
+      AC_DEFINE([GIZMO_MFV_SPH], [1], [GIZMO MFV SPH])
+   ;;
+   gizmo-mfm)
+      AC_DEFINE([GIZMO_MFM_SPH], [1], [GIZMO MFM SPH])
    ;;
    shadowfax)
       AC_DEFINE([SHADOWFAX_SPH], [1], [Shadowfax SPH])
    ;;
+   minimal-multi-mat)
+      AC_DEFINE([MINIMAL_MULTI_MAT_SPH], [1], [Minimal Multiple Material SPH])
+   ;;
 
    *)
       AC_MSG_ERROR([Unknown hydrodynamics scheme: $with_hydro])
@@ -973,7 +1051,7 @@ esac
 #  Equation of state
 AC_ARG_WITH([equation-of-state],
    [AS_HELP_STRING([--with-equation-of-state=<EoS>],
-      [equation of state @<:@ideal-gas, isothermal-gas default: ideal-gas@:>@]
+      [equation of state @<:@ideal-gas, isothermal-gas, planetary default: ideal-gas@:>@]
    )],
    [with_eos="$withval"],
    [with_eos="ideal-gas"]
@@ -985,6 +1063,9 @@ case "$with_eos" in
    isothermal-gas)
       AC_DEFINE([EOS_ISOTHERMAL_GAS], [1], [Isothermal gas equation of state])
    ;;
+   planetary)
+      AC_DEFINE([EOS_PLANETARY], [1], [All planetary equations of state])
+   ;;
    *)
       AC_MSG_ERROR([Unknown equation of state: $with_eos])
    ;;
@@ -1042,6 +1123,42 @@ case "$with_riemann" in
    ;;
 esac
 
+# Check for grackle.
+have_grackle="no"
+AC_ARG_WITH([grackle],
+    [AS_HELP_STRING([--with-grackle=PATH],
+       [root directory where grackle is installed @<:@yes/no@:>@]
+    )],
+    [with_grackle="$withval"],
+    [with_grackle="no"]
+)
+if test "x$with_grackle" != "xno"; then
+   AC_PROG_FC
+   AC_FC_LIBRARY_LDFLAGS
+   if test "x$with_grackle" != "xyes" -a "x$with_grackle" != "x"; then
+      GRACKLE_LIBS="-L$with_grackle/lib -lgrackle"
+      GRACKLE_INCS="-I$with_grackle/include"
+   else
+      GRACKLE_LIBS="-lgrackle"
+      GRACKLE_INCS=""
+   fi
+
+   have_grackle="yes"
+
+   AC_CHECK_LIB(
+      [grackle],
+      [initialize_chemistry_data],
+      [AC_DEFINE([HAVE_GRACKLE],1,[The GRACKLE library appears to be present.])
+        AC_DEFINE([CONFIG_BFLOAT_8],1,[Use doubles in grackle])
+      ],
+      [AC_MSG_ERROR(Cannot find grackle library!)],
+      [$GRACKLE_LIBS $GRACKLE_INCS $FCLIBS]
+   )
+fi
+AC_SUBST([GRACKLE_LIBS])
+AC_SUBST([GRACKLE_INCS])
+AM_CONDITIONAL([HAVEGRACKLE],[test -n "$GRACKLE_LIBS"])
+
 #  Cooling function
 AC_ARG_WITH([cooling],
    [AS_HELP_STRING([--with-cooling=<function>],
@@ -1072,7 +1189,7 @@ case "$with_cooling" in
    grackle)
       AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
       AC_DEFINE([COOLING_GRACKLE_MODE], [0], [Grackle chemistry network, mode 0])
-   ;; 
+   ;;
    grackle1)
       AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
       AC_DEFINE([COOLING_GRACKLE_MODE], [1], [Grackle chemistry network, mode 1])
@@ -1080,11 +1197,11 @@ case "$with_cooling" in
    grackle2)
       AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
       AC_DEFINE([COOLING_GRACKLE_MODE], [2], [Grackle chemistry network, mode 2])
-   ;; 
+   ;;
    grackle3)
       AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
       AC_DEFINE([COOLING_GRACKLE_MODE], [3], [Grackle chemistry network, mode 3])
-   ;; 
+   ;;
    EAGLE)
       AC_DEFINE([COOLING_EAGLE], [1], [Cooling following the EAGLE model])
    ;;
@@ -1163,10 +1280,10 @@ esac
 #  Gravity multipole order
 AC_ARG_WITH([multipole-order],
    [AS_HELP_STRING([--with-multipole-order=<order>],
-      [order of the multipole and gravitational field expansion @<:@ default: 5@:>@]
+      [order of the multipole and gravitational field expansion @<:@ default: 4@:>@]
    )],
    [with_multipole_order="$withval"],
-   [with_multipole_order="5"]
+   [with_multipole_order="4"]
 )
 AC_DEFINE_UNQUOTED([SELF_GRAVITY_MULTIPOLE_ORDER], [$with_multipole_order], [Multipole order])
 
@@ -1190,6 +1307,7 @@ AC_CONFIG_FILES([tests/testPeriodicBC.sh], [chmod +x tests/testPeriodicBC.sh])
 AC_CONFIG_FILES([tests/testPeriodicBCPerturbed.sh], [chmod +x tests/testPeriodicBCPerturbed.sh])
 AC_CONFIG_FILES([tests/testInteractions.sh], [chmod +x tests/testInteractions.sh])
 AC_CONFIG_FILES([tests/testParser.sh], [chmod +x tests/testParser.sh])
+AC_CONFIG_FILES([tests/testSelectOutput.sh], [chmod +x tests/testSelectOutput.sh])
 
 # Save the compilation options
 AC_DEFINE_UNQUOTED([SWIFT_CONFIG_FLAGS],["$swift_config_flags"],[Flags passed to configure])
@@ -1197,6 +1315,11 @@ AC_DEFINE_UNQUOTED([SWIFT_CONFIG_FLAGS],["$swift_config_flags"],[Flags passed to
 # Make sure the latest git revision string gets included
 touch src/version.c
 
+#  Need to define this, instead of using fifth argument of AC_INIT, until
+#  2.64. Defer until now as this redefines PACKAGE_URL, which can emit a
+#  compilation error when testing with -Werror.
+AC_DEFINE([PACKAGE_URL],["www.swiftsim.com"], [Package web pages])
+
 # Generate output.
 AC_OUTPUT
 
@@ -1206,22 +1329,21 @@ AC_MSG_RESULT([
 
    $PACKAGE_NAME v.$PACKAGE_VERSION
 
-   Compiler         : $CC
-    - vendor        : $ax_cv_c_compiler_vendor
-    - version       : $ax_cv_c_compiler_version
-    - flags         : $CFLAGS
-   MPI enabled      : $enable_mpi
-   HDF5 enabled     : $with_hdf5
-    - parallel      : $have_parallel_hdf5
-   Metis enabled    : $have_metis
-   FFTW3 enabled    : $have_fftw
-   GSL enabled      : $have_gsl
-   libNUMA enabled  : $have_numa
-   GRACKLE enabled  : $have_grackle
-   Using tcmalloc   : $have_tcmalloc
-   Using jemalloc   : $have_jemalloc
-   CPU profiler     : $have_profiler
-   Pthread barriers : $have_pthread_barrier
+   Compiler           : $CC
+    - vendor          : $ax_cv_c_compiler_vendor
+    - version         : $ax_cv_c_compiler_version
+    - flags           : $CFLAGS
+   MPI enabled        : $enable_mpi
+   HDF5 enabled       : $with_hdf5
+    - parallel        : $have_parallel_hdf5
+   Metis enabled      : $have_metis
+   FFTW3 enabled      : $have_fftw
+   GSL enabled        : $have_gsl
+   libNUMA enabled    : $have_numa
+   GRACKLE enabled    : $have_grackle
+   Special allocators : $have_special_allocator
+   CPU profiler       : $have_profiler
+   Pthread barriers   : $have_pthread_barrier
 
    Hydro scheme       : $with_hydro
    Dimensionality     : $with_dimension
@@ -1243,5 +1365,6 @@ AC_MSG_RESULT([
    Interaction debugging : $enable_debug_interactions
    Naive interactions    : $enable_naive_interactions
    Gravity checks        : $gravity_force_checks
+   Custom icbrtf         : $enable_custom_icbrtf
 
  ------------------------])
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 844a6fa68029af3c119d7c03e09e6dec957c5da4..cba52250ccc37f50ed130c70d8a5039d8c786474 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -765,8 +765,9 @@ INPUT		       += @top_srcdir@/src/gravity/Default
 INPUT		       += @top_srcdir@/src/stars/Default
 INPUT		       += @top_srcdir@/src/riemann
 INPUT		       += @top_srcdir@/src/potential/point_mass
-INPUT		       += @top_srcdir@/src/cooling/none
-INPUT		       += @top_srcdir@/src/chemistry/none
+INPUT		       += @top_srcdir@/src/equation_of_state/ideal_gas
+INPUT		       += @top_srcdir@/src/cooling/EAGLE
+INPUT		       += @top_srcdir@/src/chemistry/EAGLE
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -817,7 +818,7 @@ EXCLUDE_SYMLINKS       = NO
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories for example use the pattern */test/*
 
-EXCLUDE_PATTERNS       =
+EXCLUDE_PATTERNS       = *.md
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the
@@ -1956,7 +1957,7 @@ MACRO_EXPANSION        = YES
 # The default value is: NO.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-EXPAND_ONLY_PREDEF     = YES
+EXPAND_ONLY_PREDEF     = NO
 
 # If the SEARCH_INCLUDES tag is set to YES, the include files in the
 # INCLUDE_PATH will be searched if a #include is found.
@@ -1990,8 +1991,10 @@ INCLUDE_FILE_PATTERNS  =
 
 PREDEFINED             = "__attribute__(x)= "
 PREDEFINED             += HAVE_HDF5
+PREDEFINED             += HAVE_FFTW
 PREDEFINED             += WITH_MPI
 PREDEFINED             += WITH_VECTORIZATION
+PREDEFINED             += __GNUC__
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
diff --git a/doc/RTD/DeveloperGuide/AddingTasks/addingtasks.rst b/doc/RTD-Legacy/DeveloperGuide/AddingTasks/addingtasks.rst
similarity index 100%
rename from doc/RTD/DeveloperGuide/AddingTasks/addingtasks.rst
rename to doc/RTD-Legacy/DeveloperGuide/AddingTasks/addingtasks.rst
diff --git a/doc/RTD/DeveloperGuide/Examples/Cooling/cooling.rst b/doc/RTD-Legacy/DeveloperGuide/Examples/Cooling/cooling.rst
similarity index 100%
rename from doc/RTD/DeveloperGuide/Examples/Cooling/cooling.rst
rename to doc/RTD-Legacy/DeveloperGuide/Examples/Cooling/cooling.rst
diff --git a/doc/RTD/DeveloperGuide/Examples/ExternalGravity/externalgravity.rst b/doc/RTD-Legacy/DeveloperGuide/Examples/ExternalGravity/externalgravity.rst
similarity index 100%
rename from doc/RTD/DeveloperGuide/Examples/ExternalGravity/externalgravity.rst
rename to doc/RTD-Legacy/DeveloperGuide/Examples/ExternalGravity/externalgravity.rst
diff --git a/doc/RTD/DeveloperGuide/developerguide.rst b/doc/RTD-Legacy/DeveloperGuide/developerguide.rst
similarity index 100%
rename from doc/RTD/DeveloperGuide/developerguide.rst
rename to doc/RTD-Legacy/DeveloperGuide/developerguide.rst
diff --git a/doc/RTD/FAQ/index.rst b/doc/RTD-Legacy/FAQ/index.rst
similarity index 100%
rename from doc/RTD/FAQ/index.rst
rename to doc/RTD-Legacy/FAQ/index.rst
diff --git a/doc/RTD/Innovation/AsynchronousComms/index.rst b/doc/RTD-Legacy/Innovation/AsynchronousComms/index.rst
similarity index 100%
rename from doc/RTD/Innovation/AsynchronousComms/index.rst
rename to doc/RTD-Legacy/Innovation/AsynchronousComms/index.rst
diff --git a/doc/RTD/Innovation/Caching/index.rst b/doc/RTD-Legacy/Innovation/Caching/index.rst
similarity index 100%
rename from doc/RTD/Innovation/Caching/index.rst
rename to doc/RTD-Legacy/Innovation/Caching/index.rst
diff --git a/doc/RTD/Innovation/HeirarchicalCellDecomposition/InitialDecomp.png b/doc/RTD-Legacy/Innovation/HeirarchicalCellDecomposition/InitialDecomp.png
similarity index 100%
rename from doc/RTD/Innovation/HeirarchicalCellDecomposition/InitialDecomp.png
rename to doc/RTD-Legacy/Innovation/HeirarchicalCellDecomposition/InitialDecomp.png
diff --git a/doc/RTD/Innovation/HeirarchicalCellDecomposition/SplitCell.png b/doc/RTD-Legacy/Innovation/HeirarchicalCellDecomposition/SplitCell.png
similarity index 100%
rename from doc/RTD/Innovation/HeirarchicalCellDecomposition/SplitCell.png
rename to doc/RTD-Legacy/Innovation/HeirarchicalCellDecomposition/SplitCell.png
diff --git a/doc/RTD/Innovation/HeirarchicalCellDecomposition/SplitPair.png b/doc/RTD-Legacy/Innovation/HeirarchicalCellDecomposition/SplitPair.png
similarity index 100%
rename from doc/RTD/Innovation/HeirarchicalCellDecomposition/SplitPair.png
rename to doc/RTD-Legacy/Innovation/HeirarchicalCellDecomposition/SplitPair.png
diff --git a/doc/RTD/Innovation/HeirarchicalCellDecomposition/index.rst b/doc/RTD-Legacy/Innovation/HeirarchicalCellDecomposition/index.rst
similarity index 100%
rename from doc/RTD/Innovation/HeirarchicalCellDecomposition/index.rst
rename to doc/RTD-Legacy/Innovation/HeirarchicalCellDecomposition/index.rst
diff --git a/doc/RTD/Innovation/HybridParallelism/index.rst b/doc/RTD-Legacy/Innovation/HybridParallelism/index.rst
similarity index 100%
rename from doc/RTD/Innovation/HybridParallelism/index.rst
rename to doc/RTD-Legacy/Innovation/HybridParallelism/index.rst
diff --git a/doc/RTD/Innovation/TaskBasedParallelism/OMPScaling.png b/doc/RTD-Legacy/Innovation/TaskBasedParallelism/OMPScaling.png
similarity index 100%
rename from doc/RTD/Innovation/TaskBasedParallelism/OMPScaling.png
rename to doc/RTD-Legacy/Innovation/TaskBasedParallelism/OMPScaling.png
diff --git a/doc/RTD/Innovation/TaskBasedParallelism/TasksExample.png b/doc/RTD-Legacy/Innovation/TaskBasedParallelism/TasksExample.png
similarity index 100%
rename from doc/RTD/Innovation/TaskBasedParallelism/TasksExample.png
rename to doc/RTD-Legacy/Innovation/TaskBasedParallelism/TasksExample.png
diff --git a/doc/RTD/Innovation/TaskBasedParallelism/TasksExampleConflicts.png b/doc/RTD-Legacy/Innovation/TaskBasedParallelism/TasksExampleConflicts.png
similarity index 100%
rename from doc/RTD/Innovation/TaskBasedParallelism/TasksExampleConflicts.png
rename to doc/RTD-Legacy/Innovation/TaskBasedParallelism/TasksExampleConflicts.png
diff --git a/doc/RTD/Innovation/TaskBasedParallelism/index.rst b/doc/RTD-Legacy/Innovation/TaskBasedParallelism/index.rst
similarity index 100%
rename from doc/RTD/Innovation/TaskBasedParallelism/index.rst
rename to doc/RTD-Legacy/Innovation/TaskBasedParallelism/index.rst
diff --git a/doc/RTD/Innovation/TaskGraphPartition/index.rst b/doc/RTD-Legacy/Innovation/TaskGraphPartition/index.rst
similarity index 100%
rename from doc/RTD/Innovation/TaskGraphPartition/index.rst
rename to doc/RTD-Legacy/Innovation/TaskGraphPartition/index.rst
diff --git a/doc/RTD/Innovation/Vectorisation/index.rst b/doc/RTD-Legacy/Innovation/Vectorisation/index.rst
similarity index 100%
rename from doc/RTD/Innovation/Vectorisation/index.rst
rename to doc/RTD-Legacy/Innovation/Vectorisation/index.rst
diff --git a/doc/RTD/Innovation/index.rst b/doc/RTD-Legacy/Innovation/index.rst
similarity index 96%
rename from doc/RTD/Innovation/index.rst
rename to doc/RTD-Legacy/Innovation/index.rst
index da3f2474b4c71f8030634b2f669d3d8093757171..65a9cb82230b5661f332e25ea05644ec8e2cdbf8 100644
--- a/doc/RTD/Innovation/index.rst
+++ b/doc/RTD-Legacy/Innovation/index.rst
@@ -1,5 +1,3 @@
-.. _GettingStarted:
-
 What makes SWIFT different?
 ===========================
 
diff --git a/doc/RTD-Legacy/Makefile b/doc/RTD-Legacy/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b1dfebb01c2f55530f7a8efad3c5e5bf96484c18
--- /dev/null
+++ b/doc/RTD-Legacy/Makefile
@@ -0,0 +1,177 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  xml        to make Docutils-native XML files"
+	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SWIFT.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SWIFT.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/SWIFT"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SWIFT"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through platex and dvipdfmx..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+	@echo
+	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+	@echo
+	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/doc/RTD/Motivation/index.rst b/doc/RTD-Legacy/Motivation/index.rst
similarity index 100%
rename from doc/RTD/Motivation/index.rst
rename to doc/RTD-Legacy/Motivation/index.rst
diff --git a/doc/RTD/Physics/Gravity/gravity.rst b/doc/RTD-Legacy/Physics/Gravity/gravity.rst
similarity index 100%
rename from doc/RTD/Physics/Gravity/gravity.rst
rename to doc/RTD-Legacy/Physics/Gravity/gravity.rst
diff --git a/doc/RTD/Physics/SPH/sph.rst b/doc/RTD-Legacy/Physics/SPH/sph.rst
similarity index 100%
rename from doc/RTD/Physics/SPH/sph.rst
rename to doc/RTD-Legacy/Physics/SPH/sph.rst
diff --git a/doc/RTD/Physics/index.rst b/doc/RTD-Legacy/Physics/index.rst
similarity index 100%
rename from doc/RTD/Physics/index.rst
rename to doc/RTD-Legacy/Physics/index.rst
diff --git a/doc/RTD/conf.py b/doc/RTD-Legacy/conf.py
similarity index 98%
rename from doc/RTD/conf.py
rename to doc/RTD-Legacy/conf.py
index b4eab3d354322f8ff5e060b1795c2654eb879f90..6b65aabe54a480a29a7930f6e6f24a35b0204dcf 100644
--- a/doc/RTD/conf.py
+++ b/doc/RTD-Legacy/conf.py
@@ -25,7 +25,7 @@ import sys, os
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.pngmath', 'sphinx.ext.mathjax']
+extensions = ['sphinx.ext.todo', 'sphinx.ext.mathjax']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
diff --git a/doc/RTD/index.rst b/doc/RTD-Legacy/index.rst
similarity index 100%
rename from doc/RTD/index.rst
rename to doc/RTD-Legacy/index.rst
diff --git a/doc/RTD/.gitignore b/doc/RTD/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..50239858cbc1b86a270ae88903139d91b943acd3
--- /dev/null
+++ b/doc/RTD/.gitignore
@@ -0,0 +1,2 @@
+build/*
+make.bat
diff --git a/doc/RTD/Makefile b/doc/RTD/Makefile
index b1dfebb01c2f55530f7a8efad3c5e5bf96484c18..22ff80ae32739df7d7ca97d8d2a6c6c8159b8248 100644
--- a/doc/RTD/Makefile
+++ b/doc/RTD/Makefile
@@ -1,177 +1,20 @@
-# Makefile for Sphinx documentation
+# Minimal makefile for Sphinx documentation
 #
 
 # You can set these variables from the command line.
 SPHINXOPTS    =
 SPHINXBUILD   = sphinx-build
-PAPER         =
-BUILDDIR      = _build
-
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+SPHINXPROJ    = SWIFTSPHWIthFine-grainedinter-dependentTasking
+SOURCEDIR     = source
+BUILDDIR      = build
 
+# Put it first so that "make" without argument is like "make help".
 help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html       to make standalone HTML files"
-	@echo "  dirhtml    to make HTML files named index.html in directories"
-	@echo "  singlehtml to make a single large HTML file"
-	@echo "  pickle     to make pickle files"
-	@echo "  json       to make JSON files"
-	@echo "  htmlhelp   to make HTML files and a HTML help project"
-	@echo "  qthelp     to make HTML files and a qthelp project"
-	@echo "  devhelp    to make HTML files and a Devhelp project"
-	@echo "  epub       to make an epub"
-	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
-	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
-	@echo "  text       to make text files"
-	@echo "  man        to make manual pages"
-	@echo "  texinfo    to make Texinfo files"
-	@echo "  info       to make Texinfo files and run them through makeinfo"
-	@echo "  gettext    to make PO message catalogs"
-	@echo "  changes    to make an overview of all changed/added/deprecated items"
-	@echo "  xml        to make Docutils-native XML files"
-	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
-	@echo "  linkcheck  to check all external links for integrity"
-	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
-
-clean:
-	rm -rf $(BUILDDIR)/*
-
-html:
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
-	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
-	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
-	@echo
-	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files."
-
-json:
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-	@echo
-	@echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
-	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-	@echo
-	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
-	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SWIFT.qhcp"
-	@echo "To view the help file:"
-	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SWIFT.qhc"
-
-devhelp:
-	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
-	@echo
-	@echo "Build finished."
-	@echo "To view the help file:"
-	@echo "# mkdir -p $$HOME/.local/share/devhelp/SWIFT"
-	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SWIFT"
-	@echo "# devhelp"
-
-epub:
-	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
-	@echo
-	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
-	@echo "Run \`make' in that directory to run these through (pdf)latex" \
-	      "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through pdflatex..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-latexpdfja:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through platex and dvipdfmx..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
-	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
-	@echo
-	@echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
-	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
-	@echo
-	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo
-	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
-	@echo "Run \`make' in that directory to run these through makeinfo" \
-	      "(use \`make info' here to do that automatically)."
-
-info:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo "Running Texinfo files through makeinfo..."
-	make -C $(BUILDDIR)/texinfo info
-	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
-	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
-	@echo
-	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-	@echo
-	@echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
-	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-	@echo "Testing of doctests in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/doctest/output.txt."
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 
-xml:
-	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
-	@echo
-	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+.PHONY: help Makefile
 
-pseudoxml:
-	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
-	@echo
-	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
diff --git a/doc/RTD/README.md b/doc/RTD/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3394ce7b8b97a71a7f14fb235e49e2efb51b9f9f
--- /dev/null
+++ b/doc/RTD/README.md
@@ -0,0 +1,15 @@
+SWIFT Documentation
+===================
+
+This is the main documentation for SWIFT that can be found on ReadTheDocs.
+
+You will need the `sphinx` and `sphinx-autobuild` python packages (pip install
+them!) to build the documentation to html, as well as the `sphinx_rtd_theme`
+package which is used as the theme.
+
+To build the documentation, `make html` and then it is available in
+`build/html`.
+
+Please consider adding documentation when you add code!
+
+
diff --git a/doc/RTD/source/Cooling/index.rst b/doc/RTD/source/Cooling/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..46a01b2a054629b7fc13f0ea190c2a5a0fdd6d9c
--- /dev/null
+++ b/doc/RTD/source/Cooling/index.rst
@@ -0,0 +1,67 @@
+.. Equation of State
+   Loic Hausammann, 7th April 2018
+
+.. _cooling:
+
+Cooling
+=======
+
+Currently, we have 5 different cooling (EAGLE, Grackle, const-lambda, const-du
+and none).  Three of them are easily solved analytically (const-lambda,
+const-du and none) while the two last requires complex chemical networks.
+
+
+Equations
+---------
+
+The first table compares the different analytical cooling while the next ones
+are specific to a given cooling.  The quantities are the internal energy (\\( u
+\\)), the density \\( rho \\), the element mass fraction (\\( X_i \\)), the
+cooling function (\\(\\Lambda\\), the proton mass (\\( m_H \\)) and the time
+step condition (\\( t\_\\text{step}\\)).  If not specified otherwise, all
+cooling contains a temperature floor avoiding negative temperature.
+
+.. csv-table:: Analytical Cooling
+   :header: "Variable", "Const-Lambda", "Const-du", "None"
+
+   "\\( \\frac{ \\mathrm{d}u }{ \\mathrm{d}t } \\)", "\\( -\\Lambda \\frac{\\rho^2 X_H^2}{\\rho m_H^2} \\)", "const", "0"
+   "\\( \\Delta t\_\\text{max} \\)", "\\( t\_\\text{step} \\frac{u}{\\left|\\frac{ \\mathrm{d}u }{ \\mathrm{d}t }\\right|} \\)", "\\( t\_\\text{step} \\frac{u}{\\ \\left| \\frac{ \\mathrm{d}u }{ \\mathrm{d}t }\\right|} \\)", "None"
+
+
+Grackle
+~~~~~~~
+   
+Grackle is a chemistry and cooling library presented in B. Smith et al. 2016
+(do not forget to cite if used).  Four different modes are available:
+equilibrium, 6 species network (H, H\\( ^+ \\), e\\( ^- \\), He, He\\( ^+ \\)
+and He\\( ^{++} \\)), 9 species network (adds H\\(^-\\), H\\(_2\\) and
+H\\(_2^+\\)) and 12 species (adds D, D\\(^+\\) and HD).  Following the same
+order, the swift cooling options are ``grackle``, ``grackle1``, ``grackle2``
+and ``grackle3`` (the numbers correspond to the value of
+``primordial_chemistry`` in Grackle).  It also includes some self-shielding
+methods and UV background.  In order to use the Grackle cooling, you will need
+to provide an HDF5 table computed by Cloudy.
+
+When starting a simulation without providing the different fractions, the code
+supposes an equilibrium and computes the fractions automatically.
+
+Eagle
+~~~~~
+
+TODO
+
+How to Implement a New Cooling
+------------------------------
+
+The developper should provide at least one function for:
+ * writing the cooling name in HDF5
+ * cooling a particle
+ * the maximal time step possible
+ * initializing a particle
+ * computing the total energy radiated by a particle
+ * initializing the cooling parameters
+ * printing the cooling type
+
+For implementation details, see ``src/cooling/none/cooling.h``
+
+See :ref:`new_option` for the full list of changes required.
diff --git a/doc/RTD/source/EquationOfState/index.rst b/doc/RTD/source/EquationOfState/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..3558041e9513b967a2530165acec5e5f4f11a364
--- /dev/null
+++ b/doc/RTD/source/EquationOfState/index.rst
@@ -0,0 +1,50 @@
+.. Equation of State
+   Loic Hausammann, 6th April 2018
+
+.. _equation_of_state:
+
+Equation of State
+=================
+
+Currently (if the documentation was well updated), we have two different
+equation of states implemented: ideal gas and isothermal.  They describe the
+relations between our main thermodynamical variables: the internal energy
+(\\(u\\)), the density (\\(\\rho\\)), the entropy (\\(A\\)) and the pressure
+(\\(P\\)).
+
+Equations
+---------
+
+In the following section, the variables not yet defined are: \\(\\gamma\\) for
+the adiabatic index and \\( c_s \\) for the speed of sound.
+
+.. csv-table:: Ideal Gas
+   :header: "Variable", "A", "u", "P"
+	   
+   "A", "", "\\( \\left( \\gamma - 1 \\right) u \\rho^{1-\\gamma} \\)", "\\(P \\rho^{-\\gamma} \\)"
+   "u", "\\( A \\frac{ \\rho^{ \\gamma - 1 } }{\\gamma - 1 } \\)", "", "\\(\\frac{1}{\\gamma - 1} \\frac{P}{\\rho}\\)"
+   "P", "\\( A \\rho^\\gamma \\)", "\\( \\left( \\gamma - 1\\right) u \\rho \\)", ""
+   "\\(c_s\\)", "\\(\\sqrt{ \\gamma \\rho^{\\gamma - 1} A}\\)", "\\(\\sqrt{ u \\gamma \\left( \\gamma - 1 \\right) } \\)", "\\(\\sqrt{ \\frac{\\gamma P}{\\rho} }\\)"
+
+
+.. csv-table:: Isothermal Gas
+   :header: "Variable", "A", "u", "P"
+
+	    
+   "A", "", "\\(\\left( \\gamma - 1 \\right) u \\rho^{1-\\gamma}\\)", "" 
+   "u", "", "const", ""
+   "P", "", "\\(\\left( \\gamma - 1\\right) u \\rho \\)", ""
+   "\\( c_s\\)", "", "\\(\\sqrt{ u \\gamma \\left( \\gamma - 1 \\right) } \\)", ""
+
+
+How to Implement a New Equation of State
+----------------------------------------
+
+See :ref:`new_option` for a full list of required changes.
+
+You will need to provide a ``equation_of_state.h`` file containing: the
+definition of ``eos_parameters``, IO functions and transformations between the
+different variables: \\(u(\\rho, A)\\), \\(u(\\rho, P)\\), \\(P(\\rho,A)\\),
+\\(P(\\rho, u)\\), \\(A(\\rho, P)\\), \\(A(\\rho, u)\\), \\(c_s(\\rho, A)\\),
+\\(c_s(\\rho, u)\\) and \\(c_s(\\rho, P)\\). See other equation of state files
+to have implementation details.
diff --git a/doc/RTD/source/GettingStarted/compiling_code.rst b/doc/RTD/source/GettingStarted/compiling_code.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c40f06965e15146c41bf210aec3b195032cef0e7
--- /dev/null
+++ b/doc/RTD/source/GettingStarted/compiling_code.rst
@@ -0,0 +1,108 @@
+.. Compiling the Code
+   Josh Borrow, 5th April 2018
+
+
+Compiling SWIFT
+===============
+
+Dependencies
+------------
+
+To compile SWIFT, you will need the following libraries:
+
+HDF5
+~~~~
+
+Version 1.8.x or higher is required. Input and output files are stored as HDF5
+and are compatible with the existing GADGET-2 specification. Please consider
+using a build of parallel-HDF5, as SWIFT can leverage this when writing and
+reading snapshots. We recommend using HDF5 > 1.10.x as this is `vastly superior`
+in parallel.
+
+MPI
+~~~
+A recent implementation of MPI, such as Open MPI (v2.x or higher), is required,
+or any library that implements at least the MPI 3 standard.
+
+Libtool
+~~~~~~~
+The build system depends on libtool.
+
+FFTW
+~~~~
+Version 3.3.x or higher is required for periodic gravity.
+
+METIS
+~~~~~
+METIS is used for domain decomposition and load balancing.
+
+libNUMA
+~~~~~~~
+libNUMA is used to pin threads.
+
+GSL
+~~~
+The GSL is required for cosmological integration.
+
+
+Optional Dependencies
+---------------------
+
+There are also the following _optional_ dependencies.
+
+TCmalloc/Jemalloc
+~~~~~~~~~~~~~~~~~
+TCmalloc/Jemalloc are used for faster memory allocations when available.
+
+DOXYGEN
+~~~~~~~
+You can build documentation for SWIFT with DOXYGEN.
+
+Python
+~~~~~~
+To run the examples, you will need python and some of the standard scientific libraries (numpy, matplotlib). Some examples use Python 2 scripts, but the more recent ones use Python 3 (this is specified in individual READMEs).
+
+GRACKLE
+~~~~~~~
+GRACKLE cooling is implemented in SWIFT. If you wish to take advantage of it, you will need it installed.
+
+
+Initial Setup
+-------------
+
+We use autotools for setup. To get a basic running version of the code
+(the binary is created in swiftsim/examples) on most platforms, run
+
+.. code-block:: bash
+
+  ./autogen.sh
+  ./configure
+  make
+
+
+MacOS Specific Oddities
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To build on MacOS you will need to disable compiler warnings due to an
+incomplete implementation of pthread barriers. DOXYGEN also has some issues on
+MacOS, so it is best to leave it out. To configure:
+
+.. code-block:: bash
+
+  ./configure --disable-compiler-warnings --disable-doxygen-doc
+
+
+Trouble Finding Libraries
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If the configure script is having trouble finding your libraries for you, it
+may be that they are in nonstandard locations. You can link the specific
+library locations by using ``--with-<LIBRARY>=<PATH>``. For example for the
+HDF5 library,
+
+.. code-block:: bash
+   
+   ./configure --with-hdf5=/path/to/h5cc
+
+More information about what needs to be provided to these flags is given in
+``./configure --help``.
diff --git a/doc/RTD/source/GettingStarted/configuration_options.rst b/doc/RTD/source/GettingStarted/configuration_options.rst
new file mode 100644
index 0000000000000000000000000000000000000000..e37384cfd1c29cb1df82cc180a763f4859650b2e
--- /dev/null
+++ b/doc/RTD/source/GettingStarted/configuration_options.rst
@@ -0,0 +1,50 @@
+.. Configuration Options
+   Josh Borrow, 5th April 2018
+
+Configuration Options
+=====================
+
+There are many configuration options that SWIFT makes available; a few key
+ones are summarised here.
+
+Note that these need to be ran with ``./configure x`` where ``x`` is the
+configuration flag.
+
+A description of the available options of the below flags can be found by using
+``./configure  --help``.
+
+``--with-hydro=gadget2``
+~~~~~~~~~~~~~~~~~~~~~~~~
+There are several hydrodynamical schemes available in SWIFT. You can choose
+between them at compile-time with this option.
+
+``--with-riemann-solver=none``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Some hydrodynamical schemes, for example GIZMO, require a Riemann solver.
+
+``--with-kernel=cubic-spline``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Several kernels are made available for use with the hydrodynamical schemes.
+Choose between them with this compile-time flag.
+
+``--with-hydro-dimension=3``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Run problems in 1, 2, and 3 (default) dimensions.
+
+``--with-equation-of-state=ideal-gas``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Several equations of state are made available with this flag. Also consider
+``--with-adiabatic-index``.
+
+``--with-cooling=none``
+~~~~~~~~~~~~~~~~~~~~~~~
+Several cooling implementations (including GRACKLE) are available.
+
+``--with-ext-potential=none``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Many external potentials are available for use with SWIFT. You can choose
+between them at compile time. Some examples include a central potential, a
+softened central potential, and a sinusoidal potential. You will need to
+configure, for example, the mass in your parameterfile at runtime.
+
+
diff --git a/doc/RTD/source/GettingStarted/index.rst b/doc/RTD/source/GettingStarted/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d15a8eee2f3b9089a1c8ee033f9aa3ee7ad92a5f
--- /dev/null
+++ b/doc/RTD/source/GettingStarted/index.rst
@@ -0,0 +1,25 @@
+.. Getting Started
+   Josh Borrow, 4th April 2018
+
+Getting Started
+===============
+
+So, you want to use SWIFT? Below you should find all of the information you need
+to get up and running with some examples, and then build your own initial conditions
+for running.
+
+Also, you might want to consult our onboarding guide (available at
+http://www.swiftsim.com/onboarding.pdf) if you would like something to print out
+and keep on your desk.
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   compiling_code
+   running_example
+   runtime_options
+   configuration_options
+   parameter_file
+   what_about_mpi
+   running_on_large_systems
diff --git a/doc/RTD/source/GettingStarted/parameter_file.rst b/doc/RTD/source/GettingStarted/parameter_file.rst
new file mode 100644
index 0000000000000000000000000000000000000000..cecf2fa085b23946b8bcbebd324372b01f4940c2
--- /dev/null
+++ b/doc/RTD/source/GettingStarted/parameter_file.rst
@@ -0,0 +1,27 @@
+.. Parameter File
+   Loic Hausammann, 1 june 2018
+
+.. _Parameter_File_label:
+
+Parameter File
+==============
+
+To run SWIFT, you will need to provide a ``yaml`` parameter file.  An example is
+given in ``examples/parameter_file.yml`` which should contain all possible
+parameters.  Each section in this file corresponds to a different option in
+SWIFT and are not always required depending on the configuration options and
+the run time parameters.
+
+
+Output Selection
+~~~~~~~~~~~~~~~~
+
+With SWIFT, you can select the particle fields to output in snapshot using the parameter file.
+In section ``SelectOutput``, you can remove a field by adding a parameter formatted in the
+following way ``field_parttype`` where ``field`` is the name of the field that you
+want to remove (e.g. ``Masses``) and ``parttype`` is the type of particles that
+contains this field (e.g. ``Gas``, ``DM`` or ``Star``).  For a parameter, the only
+values accepted are 0 (skip this field when writing) or 1 (default, do not skip
+this field when writing).
+
+You can generate a ``yaml`` file containing all the possible fields with ``./swift -o output.yml``. By default, all the fields are written.
diff --git a/doc/RTD/source/GettingStarted/running_example.rst b/doc/RTD/source/GettingStarted/running_example.rst
new file mode 100644
index 0000000000000000000000000000000000000000..854e74cf830d58e51cf866d59a93ede6dceb57b6
--- /dev/null
+++ b/doc/RTD/source/GettingStarted/running_example.rst
@@ -0,0 +1,37 @@
+.. Running an Example
+   Josh Borrow, 5th April 2018
+
+Running an Example
+==================
+
+Now that you have built the code, you will want to run an example! To do that,
+you need to follow the following instructions (requires ``python2`` or
+``python3`` with the ``h5py`` and other standard scientific packages, as well
+as ``wget`` for grabbing the glass).
+
+.. code-block:: bash
+   
+   cd examples/SodShock_3D
+   ./getGlass.sh
+   python makeIC.py
+   ../swift -s -t 4 sodShock.yml
+   python plotSolution.py 1
+
+
+This will run the 'SodShock' in 3D and produce a nice plot that shows you
+how the density has varied. Try running with GIZMO (this will take
+_significantly_ longer than with SPH) to see the difference. For that, you
+will need to reconfigure with the following options:
+
+.. code-block:: bash
+   
+   ./configure \
+   --with-hydro=gizmo \
+   --with-riemann-solver=hllc
+
+
+To see the results that you should get, you should check out our developer
+wiki at https://gitlab.cosma.dur.ac.uk/swift/swiftsim/wikis/Sod_3D.
+
+If you don't get these results, please contact us on our GitHub page at
+https://github.com/SWIFTSIM/swiftsim/issues.
diff --git a/doc/RTD/source/GettingStarted/running_on_large_systems.rst b/doc/RTD/source/GettingStarted/running_on_large_systems.rst
new file mode 100644
index 0000000000000000000000000000000000000000..55eb812cef21474045931490591b3978841a4085
--- /dev/null
+++ b/doc/RTD/source/GettingStarted/running_on_large_systems.rst
@@ -0,0 +1,42 @@
+.. Running on Large Systems
+   Josh Borrow, 5th April 2018
+
+Running on Large Systems
+========================
+
+There are a few extra things to keep in mind when running SWIFT on a large
+system (i.e. over MPI on several nodes). Here are some recommendations:
+
++ Compile and run with
+  `tbbmalloc <https://www.threadingbuildingblocks.org>`_.  You can add this
+  to the configuration of SWIFT by running configure with the
+  ``--with-tbbmalloc`` flag. Using this allocator, over the one included in the
+  standard library, is particularly important on systems with large core counts
+  per node. Alternatives include
+  `jemalloc <https://github.com/jemalloc/jemalloc>`_ and
+  `tcmalloc <https://github.com/gperftools/gperftools>`_, and using these
+  other allocation tools also improves performance on single-node jobs.
++ Run with one MPI rank per NUMA region, usually a socket, rather than per node.
+  Typical HPC clusters now use two chips per node. Consult with your local system
+  manager if you are unsure about your system configuration. This can be done
+  by invoking ``mpirun -np <NUMBER OF CHIPS> swift_mpi -t <NUMBER OF CORES PER CHIP>``.
+  You should also be careful to include this in your batch script, for example
+  with the `SLURM <https://slurm.schedmd.com>`_ batch system you will need to
+  include ``#SBATCH --tasks-per-node=2``.
++ Run with threads pinned. You can do this by passing the ``-a`` flag to the
+  SWIFT binary. This ensures that processes stay on the same core that spawned
+  them, ensuring that cache is accessed more efficiently.
++ Ensure that you compile with METIS. More information is available in an
+  upcoming paper, but using METIS allows for work to be distributed in a
+  more efficient way between your nodes.
+
+Your batch script should look something like the following (to run on 16 nodes
+each with 2x16 core processors for a total of 512 cores):
+
+.. code-block:: bash
+  
+   #SBATCH -N 16  # Number of nodes to run on
+   #SBATCH --tasks-per-node=2  # This system has 2 chips per node
+   
+   mpirun -np 32 swift_mpi -t 16 -a parameter.yml
+
diff --git a/doc/RTD/source/GettingStarted/runtime_options.rst b/doc/RTD/source/GettingStarted/runtime_options.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b2ca10640d8830b9b5ecb8e117bf047af738889c
--- /dev/null
+++ b/doc/RTD/source/GettingStarted/runtime_options.rst
@@ -0,0 +1,41 @@
+.. Runtime Options
+   Josh Borrow, 5th April 2018
+
+Runtime Options
+===============
+
+SWIFT requires a number of runtime options to run and get any sensible output.
+For instance, just running the ``swift`` binary will not use any SPH or gravity;
+the particles will just sit still!
+
+Below is a list of the runtime options and when they should be used. The same list
+can be found by typing ``./swift -h``.
+
++ ``-a``: Pin runners using processor affinity.
++ ``-c``: Run with cosmological time integration.
++ ``-C``: Run with cooling.
++ ``-d``: Dry run. Read the parameter file, allocate memory but does not read
+  the particles from ICs and exit before the start of time integration. Allows
+  user to check validity of parameter and IC files as well as memory limits.
++ ``-D``: Always drift all particles even the ones far from active particles.
+  This emulates Gadget-[23] and GIZMO's default behaviours.
++ ``-e``: Enable floating-point exceptions (debugging mode).
++ ``-f``: {int} Overwrite the CPU frequency (Hz) to be used for time measurements.
++ ``-g``: Run with an external gravitational potential.
++ ``-G``: Run with self-gravity.
++ ``-M``: Reconstruct the multipoles every time-step.
++ ``-n``: {int} Execute a fixed number of time steps. When unset use the
+  time_end parameter to stop.
++ ``-o``: {str} Generate a default output parameter file.
++ ``-P``: {sec:par:val} Set parameter value and overwrites values read from the
+  parameters file. Can be used more than once.
++ ``-s``: Run with hydrodynamics.
++ ``-S``: Run with stars.
++ ``-t``: {int} The number of threads to use on each MPI rank. Defaults to 1 if
+  not specified.
++ ``-T``: Print timers every time-step.
++ ``-v``: [12] Increase the level of verbosity: 1, MPI-rank 0 writes, 2, All
+  MPI-ranks write.
++ ``-y``: {int} Time-step frequency at which task graphs are dumped.
++ ``-Y``: {int} Time-step frequency at which threadpool tasks are dumped.
++ ``-h``: Print a help message and exit.
diff --git a/doc/RTD/source/GettingStarted/what_about_mpi.rst b/doc/RTD/source/GettingStarted/what_about_mpi.rst
new file mode 100644
index 0000000000000000000000000000000000000000..098fd35d80d71866cb86d2342d5d54710cd73a82
--- /dev/null
+++ b/doc/RTD/source/GettingStarted/what_about_mpi.rst
@@ -0,0 +1,12 @@
+.. What about MPI? Running SWIFT on more than one node
+   Josh Borrow, 5th April 2018
+
+What about MPI? Running SWIFT on more than one node
+===================================================
+
+After compilation, you will be left with two binaries. One is called ``swift``,
+and the other ``swift_mpi``. Current wisdom is to run ``swift`` if you are only
+using one node (i.e. without any interconnect), and one MPI rank per NUMA
+region using ``swift_mpi`` for anything larger. You will need some GADGET-2
+HDF5 initial conditions to run SWIFT, as well as a compatible yaml
+parameterfile.
diff --git a/doc/RTD/source/HydroSchemes/adding_your_own.rst b/doc/RTD/source/HydroSchemes/adding_your_own.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2d7e640f66153a17e19f4e4c456cd37eed19a95a
--- /dev/null
+++ b/doc/RTD/source/HydroSchemes/adding_your_own.rst
@@ -0,0 +1,119 @@
+.. Adding Hydro Schemes
+   Josh Borrow, 5th April 2018
+
+
+Adding Hydro Schemes
+====================
+
+.. toctree::
+   :maxdepth: 2
+   :hidden:
+   :caption: Contents:
+
+SWIFT is engineered to enable you to add your own hydrodynamics schemes easily.
+We enable this through the use of header files to encapsulate each scheme.
+
+Note that it's unlikely you will ever have to consider paralellism or 'loops over
+neighbours' for SWIFT; all of this is handled by the tasking system. All we ask
+for is the interaction functions that tell us how to a) compute the density
+and b) compute forces.
+
+
+Getting Started
+---------------
+
+The hydro schemes are stored in ``src/hydro``. You will need to create a folder
+with a sensible name that you are going to store your scheme-specific information
+in. Then, you will need to create the following files:
+
++ ``hydro.h``, which includes functions that are applied to particles at the end
+  of the density loop and beginning of the force loop, along with helper functions
++ ``hydro_debug.h``, which includes a quick function that prints out your particle
+  properties for debugging purposes
++ ``hydro_iact.h`` that includes the interaction functions
++ ``hydro_io.h`` which includes the information on what should be read from the
+  initial conditions file, as well as written to the output files
++ ``hydro_part.h`` which includes your particle definition. SWIFT uses an array-of
+  -structures scheme.
+
+
+``hydro.h``
+-----------
+
+As previously noted, ``hydro.h`` includes the helper functions for your scheme. You
+will need to 'fill out' the following:
+
++ ``hydro_get_comoving_internal_energy(p)`` which returns the comoving internal energy
+  of your particles (typically this will just be ``p->u``).
++ ``hydro_get_physical_internal_energy(p, cosmo)`` which returns the physical internal
+  energy. You can use the ``a_factor_internal_energy`` from the ``cosmology`` struct.
++ ``hydro_get_comoving_pressure(p)`` which returns the comoving pressure.
++ ``hydro_get_comoving_entropy(p)`` which returns the comoving entropy.
++ ``hydro_get_physical_entropy(p, cosmo)`` which returns the physical entropy. In our
+  formalism, usually there is no conversion factor here so it is the same as the
+  comoving version.
++ ``hydro_get_comoving_soundspeed(p)`` which returns the comoving sound speed.
++ ``hydro_get_physical_soundspeed(p, cosmo)`` which returns the physical sound
+  speed. You can use the ``a_factor_sound_speed``.
++ ``hydro_get_comoving_density(p)`` which returns the comoving density.
++ ``hydro_get_physical_density(p, cosmo)`` which returns the physical density.
+  You can use the ``a3_inv`` member of the ``cosmology`` struct.
++ ``hydro_get_mass(p)`` returns the mass of particle ``p``.
++ ``hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, v[3])`` gets
+  the drifted velocities; this is just ``a_hydro * dt_kick_hydro`` + ``a_grav *
+  dt_kick_grav`` in most implementations.
++ ``hydro_get_energy_dt(p)`` returns the time derivative of the (comoving) internal
+  energy of the particle.
++ ``hydro_set_energy_dt(p)`` sets the time derivative of the (comoving) internal
+  energy of the particle.
++ ``hydro_compute_timestep(p, xp, hydro_props, cosmo)`` returns the timestep for 
+  the hydrodynamics particles.
++ ``hydro_timestep_extra(p, dt)`` does some extra hydro operations once the
+  physical timestel for the particle is known.
++ ``hydro_init_part(p, hydro_space)`` initialises the particle in preparation for
+  the density calculation. This essentially sets properties, such as the density,
+  to zero.
++ ``hydro_end_density(p, cosmo)`` performs operations directly after the density
+  loop on each particle. Note that you will have to add a particle's self-contribution
+  at this stage as particles are never 'interacted' with themselves.
++ ``hydro_part_has_no_neighbours(p, xp, cosmo)`` resets properties to a sensible
+  value if a particle is found to have no neighbours.
++ ``hydro_prepare_force(p, xp, cosmo)`` is computed for each particle before the
+  force loop. You can use this to pre-compute particle properties that are used
+  in the force loop, but only depend on the particle itself.
++ ``hydro_reset_acceleration(p)`` resets the acceleration variables of the particles
+  to zero in preparation for the force loop.
++ ``hydro_predict_extra(p, xp, dt_drift, dt_therm)`` predicts extra particle properties
+  when drifting, such as the smoothing length.
++ ``hydro_end_force(p, cosmo)`` is called after the force loop for each particle and 
+  can be used to e.g. include overall factors of the smoothing length.
++ ``hydro_kick_extra(p, xp, dt_therm)`` kicks extra variables.
++ ``hydro_convert_quantities(p, xp)`` converts quantities at the start of a run (e.g.
+  internal energy to entropy).
++ ``hydro_first_init_part(p, xp)`` is called for every particle at the start of a run
+  and is used to initialise variables.
+
+
+``hydro_debug.h``
+-----------------
+
+TBD
+
+
+``hydro_iact.h``
+----------------
+
+TBD
+
+
+``hydro_io.h``
+--------------
+
+TBD
+
+
+``hydro_part.h``
+----------------
+
+TBD
+
diff --git a/doc/RTD/source/HydroSchemes/gizmo.rst b/doc/RTD/source/HydroSchemes/gizmo.rst
new file mode 100644
index 0000000000000000000000000000000000000000..365e1dc41c27f7c92bfb33859bedad2d96f35248
--- /dev/null
+++ b/doc/RTD/source/HydroSchemes/gizmo.rst
@@ -0,0 +1,27 @@
+.. GIZMO (MFV)
+   Josh Borrow, 5th April 2018
+
+GIZMO-Like Scheme
+=================
+
+.. toctree::
+   :maxdepth: 2
+   :hidden:
+   :caption: Contents:
+
+
+There is a meshless finite volume (MFV) GIZMO-like scheme implemented in SWIFT
+(see Hopkins 2015 for more information). You will need a Riemann solver to run
+this, and configure as follows:
+
+.. code-block:: bash
+   
+   ./configure --with-hydro="gizmo-mfv" --with-riemann-solver="hllc"
+
+
+We also have the meshless finite mass (MFM) GIZMO-like scheme. You can select
+this at compile-time with the following configuration flags:
+
+.. code-block:: bash
+   
+   ./configure --with-hydro="gizmo-mfm" --with-riemann-solver="hllc"
diff --git a/doc/RTD/source/HydroSchemes/hopkins_sph.rst b/doc/RTD/source/HydroSchemes/hopkins_sph.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bcc51e0ad96b18956f1c8e54f7bf2bf3b352c138
--- /dev/null
+++ b/doc/RTD/source/HydroSchemes/hopkins_sph.rst
@@ -0,0 +1,30 @@
+.. 'Hopkins'-SPH
+   Josh Borrow 5th April 2018
+
+Pressure-Entropy SPH
+====================
+
+.. toctree::
+   :maxdepth: 2
+   :hidden:
+   :caption: Contents:
+
+A pressure-entropy SPH scheme is available in SWIFT, inspired by Hopkins 2013.
+This includes a Monaghan AV scheme and a Balsara switch.
+
+
+.. code-block:: bash
+   
+   ./configure --with-hydro="pressure-entropy"
+
+
+Pressure-Energy SPH
+===================
+
+Pressure-energy SPH is now implemented in SWIFT, and like the pressure-entropy
+scheme it includes a Monaghan AV scheme and a Balsara switch.
+
+
+.. code-block:: bash
+   
+   ./configure --with-hydro="pressure-energy"
diff --git a/doc/RTD/source/HydroSchemes/index.rst b/doc/RTD/source/HydroSchemes/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..cd6c169245e83440a1258d216991763488586c0c
--- /dev/null
+++ b/doc/RTD/source/HydroSchemes/index.rst
@@ -0,0 +1,21 @@
+.. Hydrodynamics Schemes
+   Josh Borrow 4th April 2018
+
+.. _hydro:
+   
+Hydrodynamics Schemes
+=====================
+
+This section of the documentation includes information on the hydrodynamics
+schemes available in SWIFT, as well as how to implement your own.
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   traditional_sph
+   minimal_sph
+   hopkins_sph
+   gizmo
+   adding_your_own
+
diff --git a/doc/RTD/source/HydroSchemes/minimal_sph.rst b/doc/RTD/source/HydroSchemes/minimal_sph.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1a16a23360aaba8b28920150af0d4f4b05c74c2f
--- /dev/null
+++ b/doc/RTD/source/HydroSchemes/minimal_sph.rst
@@ -0,0 +1,20 @@
+.. Minimal SPH
+   Josh Borrow 4th April 2018
+
+Minimal (Density-Energy) SPH
+============================
+
+.. toctree::
+   :maxdepth: 2
+   :hidden:
+   :caption: Contents:
+
+This scheme is a textbook implementation of Density-Energy SPH, and can be used
+as a pedagogical example. It also implements a Monaghan AV scheme, like the
+GADGET-2 scheme. It uses very similar equations, but differs in implementation
+details; namely it tracks the internal energy \(u\) as the thermodynamic
+variable, rather than entropy \(A\). To use the minimal scheme, use
+
+.. code-block:: bash
+
+    ./configure --with-hydro="minimal"
diff --git a/doc/RTD/source/HydroSchemes/traditional_sph.rst b/doc/RTD/source/HydroSchemes/traditional_sph.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c69ea5f60644119b8590414ffe00a75246de49a6
--- /dev/null
+++ b/doc/RTD/source/HydroSchemes/traditional_sph.rst
@@ -0,0 +1,17 @@
+.. Traditional SPH (GADGET-2)
+   Josh Borrow 4th April 2018
+
+Traditional (Density-Entropy) SPH
+=================================
+
+.. toctree::
+   :maxdepth: 2
+   :hidden:
+   :caption: Contents:
+
+Traditional, GADGET-2-like, Density-Entropy SPH is available in SWIFT with
+a Monaghan artificial viscosity scheme and Balsara switch.
+
+To use this hydro scheme, you need no extra configuration options -- it is the
+default!
+
diff --git a/doc/RTD/source/InitialConditions/index.rst b/doc/RTD/source/InitialConditions/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..eba438c722fbf4ffd78984aa55d6bfa5efcd71ad
--- /dev/null
+++ b/doc/RTD/source/InitialConditions/index.rst
@@ -0,0 +1,238 @@
+.. Initial Conditions
+   Josh Borrow, 5th April 2018
+
+Initial Conditions
+==================
+
+To run anything more than examples from our suite, you will need to be able to
+produce your own initial conditions for SWIFT. We use the same initial
+conditions format as the popular `GADGET-2
+<https://wwwmpa.mpa-garching.mpg.de/~volker/gadget/>`_ code, which uses HDF5 for
+its type 3 format. Note that we do not support the GADGET-2 types 1 and 2
+formats.
+
+The original GADGET-2 file format only contains 2 types of particles: gas
+particles and 5 sorts of collisionless particles that allow users to run with 5
+separate particle masses and softenings. In SWIFT, we expand on this by using
+two of these types for stars and black holes.
+
+GADGET-2 can have initial conditions split over many files. This allow multiple
+ones to be read in parallel and is the only way the code can handle more than
+2^31 particles. This limitation is not in place in SWIFT. A single file can
+contain any number of particles (well... up to 2^64...) and the file is read in
+parallel by HDF5 when running on more than one compute node.
+
+As the original documentation for the GADGET-2 initial conditions format is
+quite sparse, we lay out here all of the necessary components. If you are
+generating your initial conditions from python, we recommend you use the h5py
+package. We provide a writing wrapper for this for our initial conditions in
+``examples/KeplerianRing/write_gadget.py``.
+
+You can find out more about the HDF5 format on their `webpages
+<https://support.hdfgroup.org/HDF5/doc/H5.intro.html>`_.
+
+
+Structure of the File
+---------------------
+
+There are several groups that contain 'auxilliary' information, such as
+``Header``.  Particle data is placed in separate groups depending of the type of
+the particles. Some types are currently ignored by SWIFT but are kept in the
+file format for compatibility reasons.
+
++---------------------+------------------------+----------------------------+
+| HDF5 Group Name     | Physical Particle Type | In code ``enum part_type`` |
++=====================+========================+============================+
+| ``/PartType0/``     | Gas                    | ``swift_type_gas``         |
++---------------------+------------------------+----------------------------+
+| ``/PartType1/``     | Dark Matter            | ``swift_type_dark_matter`` |
++---------------------+------------------------+----------------------------+
+| ``/PartType2/``     | Ignored                |                            |
++---------------------+------------------------+----------------------------+
+| ``/PartType3/``     | Ignored                |                            |
++---------------------+------------------------+----------------------------+
+| ``/PartType4/``     | Stars                  | ``swift_type_star``        |
++---------------------+------------------------+----------------------------+
+| ``/PartType5/``     | Black Holes            | ``swift_type_black_hole``  |
++---------------------+------------------------+----------------------------+
+
+The last column in the table gives the ``enum`` value from ``part_type.h``
+corresponding to a given entry in the files.
+
+Note that the only particles that have hydrodynamical forces calculated between
+them are those in ``PartType0``.
+
+
+Necessary Components
+--------------------
+
+There are several necessary components (in particular header information) in a
+SWIFT initial conditions file. Again, we recommend that you use the ``write_gadget``
+script.
+
+Header
+~~~~~~
+
+In the ``/Header/`` group, the following attributes are required:
+
++ ``Dimension``, an integer indicating the dimensionality of the ICs (1,2 or 3).
+  Note that this parameter is an addition to the GADGET-2 format and will be
+  ignored by GADGET. SWIFT will use this value to verify that the dimensionality
+  of the code matches the ICs. If this parameter is not provided, it defaults
+  to 3.
++ ``BoxSize``, a floating point number or N-dimensional (usually 3) array that
+  describes the size of the box. If only one number is provided (as per the
+  GADGET-2 standard) then the box is assumed have the same size along all the
+  axis. In cosmological runs, this is the comoving box-size expressed in the
+  units specified in the ``/Units`` group (see below). Note that, unlike GADGET,
+  we express all quantities in "h-free" units. So that, for instance, we express
+  the box side-length in ``Mpc`` and not ``Mpc/h``. 
++ ``NumPart_Total``, a length 6 array of integers that tells the code how many
+  particles of each type are in the initial conditions file. Unlike traditional
+  GADGET-2 files, these can be >2^31.
++ ``NumPart_Total_HighWord``, a historical length-6 array that tells the code
+  the number of high word particles in the initial conditions there are. If you
+  are unsure, just set this to ``[0, 0, 0, 0, 0, 0]``. This does have to be
+  present but can be a set of 0s unless you have more than 2^31 particles and
+  want to be fully compliant with GADGET-2. Note that, as SWIFT supports
+  ``NumPart_Total`` to be >2^31, the use of ``NumPart_Total_HighWord`` is only
+  here for compatibility reasons.
++ ``Flag_Entropy_ICs``, a historical value that tells the code if you have
+  included entropy or internal energy values in your intial conditions files.
+  Acceptable values are 0 or 1. We recommend using internal energies over
+  entropy in the ICs and hence have this flag set to 0.
+
+You may want to include the following for backwards-compatibility with many
+GADGET-2 based analysis programs:
+
++ ``MassTable``, an array of length 6 which gives the masses of each particle
+  type. SWIFT ignores this and uses the individual particle masses, but some
+  programs will crash if it is not included.
++ ``NumPart_ThisFile``, a length 6 array of integers describing the number of
+  particles in this file. If you have followed the above advice, this will be
+  exactly the same as the ``NumPart_Total`` array. As SWIFT only uses ICs
+  contained in a single file, this is not necessary for SWIFT-only ICs.
++ ``NumFilesPerSnapshot``, again a historical integer value that tells the code
+  how many files there are per snapshot. You will probably want to set this to 1.
++ ``Time``, time of the start of the simulation in internal units or expressed
+  as a scale-factor for cosmological runs. SWIFT ignores this and reads it from
+  the parameter file.
+  
+RuntimePars
+~~~~~~~~~~~
+
+In the ``/RuntimePars/``, the following attributes are required:
+
++ ``PeriodicBoundaryConditionsOn``, a flag to tell the code whether or not you
+  have periodic boundaries switched on. Again, this is historical; it should be
+  set to 1 (default) if you have the code running in periodic mode, or 0 otherwise.
+
+
+Particle Data
+~~~~~~~~~~~~~
+
+Now for the interesting part! You can include particle data groups for each
+individual particle type (e.g. ``/PartType0/``) that have the following *datasets*:
+
++ ``Coordinates``, an array of shape (N, 3) where N is the number of particles
+  of that type, that are the cartesian co-ordinates of the
+  particles. Co-ordinates must be within the box so, in the case of a cube
+  within [0, L)^3 where L is the side-length of the simulation volume. In the
+  case of cosmological simulations, these are the co-moving positions.
++ ``Velocities``, an array of shape (N, 3) that is the cartesian velocities of
+  the particles. When running cosmological simulations, these are the peculiar
+  velocities. Note that this is different from GADGET which uses peculiar
+  velocities divided by ``sqrt(a)`` (see below for a fix).
++ ``ParticleIDs``, an array of length N that are unique identifying numbers for
+  each particle. Note that these have to be unique to a particle, and cannot be
+  the same even between particle types. The **IDs must be >1**. 0 or negative
+  IDs will be rejected by the code.
++ ``Masses``, an array of length N that gives the masses of the particles.
+
+For ``PartType0`` (i.e. particles that interact through hydro-dynamics), you will
+need the following auxilliary items:
+
++ ``SmoothingLength``, the smoothing lengths of the particles. These will be
+  tidied up a bit, but it is best if you provide accurate numbers. In
+  cosmological runs, these are the co-moving smoothing lengths.
++ ``InternalEnergy``, an array of length N that gives the internal energies per
+  unit mass of the particles. If the hydro-scheme used in the code is based on
+  another thermodynamical quantity (entropy or total energy, etc.), the
+  conversion will happen inside the code. In cosmological runs, this is the
+  **physical** internal energy per unit mass. This has the dimension of velocity
+  squared.
+
+  
+Note that for cosmological runs, all quantities have to be expressed in "h-free"
+dimensions. This means ``Mpc`` and not ``Mpc/h`` for instance. If the ICs have
+been generated for GADGET (where h-full values are expected), the parameter
+``InitialConditions:cleanup_h_factors`` can be set to ``1`` in the
+:ref:`Parameter_File_label` to make SWIFT convert the quantities read in to
+h-free quantities. Switching this parameter on will also affect the box size
+read from the ``/Header/`` group (see above).
+
+Similarly, GADGET cosmological ICs have traditionally used velocities expressed
+as peculiar velocities divided by ``sqrt(a)``. This can be undone by swicthing
+on the parameter ``InitialConditions:cleanup_velocity_factors`` in the
+:ref:`Parameter_File_label`.
+
+     
+Optional Components
+-------------------
+
+In the ``/Units/`` HDF5 group, you cans specify what units your initial conditions are
+in. If this group is not present, the code assumes that you are using the same
+units for your initial conditions as in your :ref:`Parameter_File_label`
+(i.e. as the internal units system used by the code), but it is best to include
+them to be on the safe side. You will need:
+
++ ``Unit length in cgs (U_L)``
++ ``Unit mass in cgs (U_M)``
++ ``Unit time in cgs (U_t)``
++ ``Unit current in cgs (U_I)``
++ ``Unit temperature in cgs (U_T)``
+
+These are all floating point numbers. Note that we specify the time units and
+not the velocity units.
+
+If the units specified in the initial conditions are different from the internal
+units (specified in the parameter file), SWIFT will perform a conversion of all
+the quantities when reading in the ICs. This includes a conversion of the box
+size read from the ``/Header/`` group.
+
+
+     
+Summary
+-------
+
+You should have an HDF5 file with the following structure:
+
+.. code-block:: bash
+
+   Header/
+     BoxSize=[x, y, z]
+     Flag_Entropy_ICs=0
+     NumPart_Total=[0, 1, 0, 0, 4, 5]
+     NumPart_Total_HighWord=[0, 0, 0, 0, 0, 0]
+   RuntimePars/
+     PeriodicBoundariesOn=1
+   Units/
+     Unit current in cgs (U_I)=1.0
+     Unit length in cgs (U_L)=1.0
+     Unit mass in cgs (U_M)=1.0
+     Unit temperature in cgs (U_T)=1.0
+     Unit time in cgs (U_t)=1.0
+   PartType0/
+     Coordinates=[[x, y, z]]
+     Velocities=[[vx, vy, vz]]
+     ParticleIDs=[...]
+     Masses=[...]
+     InternalEnergy=[...]
+     SmoothingLength=[...]
+   PartType1/
+     Coordinates=[[x, y, z]]
+     Velocities=[[vx, vy, vz]]
+     ParticleIDs=[...]
+     Masses=[...]
+
+
diff --git a/doc/RTD/source/NewOption/index.rst b/doc/RTD/source/NewOption/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a7445524017fefd99d76c80a4a1ecc646874bd7a
--- /dev/null
+++ b/doc/RTD/source/NewOption/index.rst
@@ -0,0 +1,36 @@
+.. Equation of State
+   Loic Hausammann, 7th April 2018
+
+.. _new_option:
+   
+General information for adding new schemes
+==========================================
+
+The following steps are required for any new options (such as new
+:ref:`hydro`, :ref:`chemistry`, :ref:`cooling`,
+:ref:`equation_of_state`, :ref:`stars` or :ref:`gravity`)
+   
+In order to add a new scheme, you will need to:
+
+1. Create a new subdirectory inside the option directory (e.g.
+   ``src/equation_of_state`` or ``src/hydro``) with an explicit name.
+
+2. Create the required new files (depending on your option, you will need
+   different files).  Copy the structure of the most simple option (e.g.
+   ``src/hydro/Gadget2``, ``src/gravity/Default``, ``src/stars/Default``,
+   ``src/cooling/none``, ``src/chemistry/none`` or
+   ``src/equation_of_state/ideal_gas``)
+
+3. Add the right includes in the option file (e.g. ``src/hydro.h``,
+   ``src/gravity.h``, ``src/stars.h``, ``src/cooling.h``, ``src/chemistry.h``
+   or ``src/equation_of_state.h``) and the corresponding io file if present.
+
+4. Add the new option in ``configure.ac``.  This file generates the
+   ``configure`` script and you just need to add a new option under the right
+   ``case``.
+
+5. Add your files in ``src/Makefile.am``.  In order to generate the Makefiles
+   during the configuration step, a list of files is required. In
+   ``nobase_noinst_HEADERS``, add your new header files.
+
+6. Update the documentation.  Add your equations/documentation to ``doc/RTD``.
diff --git a/doc/RTD/source/conf.py b/doc/RTD/source/conf.py
new file mode 100644
index 0000000000000000000000000000000000000000..031687ea5228252e2d2e44ec0bd6f53b1b64d732
--- /dev/null
+++ b/doc/RTD/source/conf.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/stable/config
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- Project information -----------------------------------------------------
+
+project = 'SWIFT: SPH WIth Fine-grained inter-dependent Tasking'
+copyright = '2018, SWIFT Collaboration'
+author = 'SWIFT Team'
+
+# The short X.Y version
+version = '0.7'
+# The full version, including alpha/beta/rc tags
+release = '0.7.0'
+
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.todo',
+    'sphinx.ext.mathjax',
+    'sphinx.ext.githubpages',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path .
+exclude_patterns = []
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['.static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself.  Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'SWIFTSPHWIthFine-grainedinter-dependentTaskingdoc'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'SWIFTSPHWIthFine-grainedinter-dependentTasking.tex', 'SWIFT: SPH WIth Fine-grained inter-dependent Tasking Documentation',
+     'Josh Borrow', 'manual'),
+]
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'swiftsphwithfine-grainedinter-dependenttasking', 'SWIFT: SPH WIth Fine-grained inter-dependent Tasking Documentation',
+     [author], 1)
+]
+
+
+# -- Options for Texinfo output ----------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'SWIFTSPHWIthFine-grainedinter-dependentTasking', 'SWIFT: SPH WIth Fine-grained inter-dependent Tasking Documentation',
+     author, 'SWIFTSPHWIthFine-grainedinter-dependentTasking', 'One line description of project.',
+     'Miscellaneous'),
+]
+
+
+# -- Extension configuration -------------------------------------------------
+
+# -- Options for todo extension ----------------------------------------------
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = True
diff --git a/doc/RTD/source/index.rst b/doc/RTD/source/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..888945a5c0101bb6f59b574a30f1f736ad134079
--- /dev/null
+++ b/doc/RTD/source/index.rst
@@ -0,0 +1,22 @@
+.. Welcome!
+   sphinx-quickstart on Wed Apr  4 15:03:50 2018.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to SWIFT: SPH WIth Fine-grained inter-dependent Tasking's documentation!
+================================================================================
+
+Want to get started using SWIFT? Check out the on-boarding guide available
+here. SWIFT can be used as a drop-in replacement for Gadget-2 and initial
+conditions in hdf5 format for Gadget can directly be read by SWIFT. The only
+difference is the parameter file that will need to be adapted for SWIFT.
+
+.. toctree::
+   :maxdepth: 2
+
+   GettingStarted/index
+   InitialConditions/index
+   HydroSchemes/index
+   Cooling/index
+   EquationOfState/index
+   NewOption/index
diff --git a/examples/AgoraDisk/agora_disk.yml b/examples/AgoraDisk/agora_disk.yml
new file mode 100644
index 0000000000000000000000000000000000000000..598f1db2858c3e420c55ae31c9ca10d37c2f48b7
--- /dev/null
+++ b/examples/AgoraDisk/agora_disk.yml
@@ -0,0 +1,69 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.989e43      # 10^10 M_sun in grams
+  UnitLength_in_cgs:   3.085e21   # kpc in centimeters
+  UnitVelocity_in_cgs: 1e5           # km/s in centimeters per second
+  UnitCurrent_in_cgs:  1             # Amperes
+  UnitTemp_in_cgs:     1             # Kelvin
+
+Scheduler:
+  max_top_level_cells: 8
+  
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   0.5  # The end time of the simulation (in internal units).
+  dt_min:     1e-10 # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            agora_disk # Common part of the name of output files
+  time_first:          0.    # Time of the first output (in internal units)
+  delta_time:          1e-2  # Time difference between consecutive outputs (in internal units)
+  compression:         4
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-3 # Time between statistics output
+
+# Parameters for the self-gravity scheme
+Gravity:
+  eta:                    0.025    # Constant dimensionless multiplier for time integration.
+  theta:                  0.7     # Opening angle (Multipole acceptance criterion)
+  comoving_softening:     0.08 # Comoving softening length (in internal units).
+  max_physical_softening: 0.08    # Physical softening length (in internal units).
+  mesh_side_length:       32        # Number of cells along each axis for the periodic gravity mesh.
+  
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:   10      # (internal units)
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./agora_disk.hdf5     # The file to read
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  shift:    [674.1175, 674.1175, 674.1175]   # (Optional) A shift to apply to all particles read from the ICs (in internal units).
+
+# Dimensionless pre-factor for the time-step condition
+LambdaCooling:
+  lambda_cgs:                  1.0e-22    # Cooling rate (in cgs units)
+  minimum_temperature:         1.0e2      # Minimal temperature (Kelvin)
+  mean_molecular_weight:       0.59       # Mean molecular weight
+  hydrogen_mass_abundance:     0.75       # Hydrogen mass abundance (dimensionless)
+  cooling_tstep_mult:          1.0        # Dimensionless pre-factor for the time-step condition
+
+# Cooling with Grackle 2.0
+GrackleCooling:
+  CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
+  WithUVbackground: 1 # Enable or not the UV background
+  Redshift: 0 # Redshift to use (-1 means time based redshift)
+  WithMetalCooling: 1 # Enable or not the metal cooling
+  ProvideVolumetricHeatingRates: 0 # User provide volumetric heating rates
+  ProvideSpecificHeatingRates: 0 # User provide specific heating rates
+  SelfShieldingMethod: 0 # Grackle (<= 3) or Gear self shielding method
+  OutputMode: 1 # Write in output corresponding primordial chemistry mode
+  MaxSteps: 1000
+  ConvergenceLimit: 1e-2
diff --git a/examples/AgoraDisk/changeType.py b/examples/AgoraDisk/changeType.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a0eba9def3a07b01f18f727de220d8f7193858f
--- /dev/null
+++ b/examples/AgoraDisk/changeType.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+
+from h5py import File
+from sys import argv
+import numpy as np
+
+"""
+Change particle types in order to match the implemented types
+"""
+
+# number of particle type
+N_type = 6
+
+debug = 0
+
+
+def getOption():
+    if len(argv) != 2:
+        raise IOError("You need to provide a filename")
+
+    # get filename and read it
+    filename = argv[-1]
+
+    return filename
+
+
+def groupName(part_type):
+    return "PartType%i" % part_type
+
+
+def changeType(f, old, new):
+    # check if directory exists
+    old_group = groupName(old)
+    if old_group not in f:
+        raise IOError("Cannot find group '%s'" % old)
+    old = f[old_group]
+
+    new_group = groupName(new)
+    if new_group not in f:
+        f.create_group(new_group)
+    new = f[new_group]
+
+    for name in old:
+        if debug:
+            print("Moving '%s' from '%s' to '%s'"
+                  % (name, old_group, new_group))
+
+        tmp = old[name][:]
+        del old[name]
+        if name in new:
+            new_tmp = new[name][:]
+            if debug:
+                print("Found previous data:", tmp.shape, new_tmp.shape)
+            tmp = np.append(tmp, new_tmp, axis=0)
+            del new[name]
+
+        if debug:
+            print("With new shape:", tmp.shape)
+        new.create_dataset(name, tmp.shape)
+        new[name][:] = tmp
+
+    del f[old_group]
+
+
+def countPart(f):
+    npart = []
+    for i in range(N_type):
+        name = groupName(i)
+        if name in f:
+            grp = f[groupName(i)]
+            N = grp["Masses"].shape[0]
+        else:
+            N = 0
+        npart.append(N)
+
+    f["Header"].attrs["NumPart_ThisFile"] = npart
+    f["Header"].attrs["NumPart_Total"] = npart
+    f["Header"].attrs["NumPart_Total_HighWord"] = [0]*N_type
+
+
+if __name__ == "__main__":
+    filename = getOption()
+
+    f = File(filename)
+
+    changeType(f, 2, 1)
+    changeType(f, 3, 1)
+    changeType(f, 4, 1)
+
+    countPart(f)
+
+    f.close()
diff --git a/examples/AgoraDisk/cleanupSwift.py b/examples/AgoraDisk/cleanupSwift.py
new file mode 100644
index 0000000000000000000000000000000000000000..e3c364081fc82986968727695e1142690781bb67
--- /dev/null
+++ b/examples/AgoraDisk/cleanupSwift.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+
+# ./translate_particles.py filename output_name
+from h5py import File
+import sys
+from shutil import copyfile
+
+NPartType = 1
+filename = sys.argv[-2]
+out = sys.argv[-1]
+
+copyfile(filename, out)
+
+f = File(out)
+
+name = "PartType0/ElementAbundance"
+if name in f:
+    del f[name]
+
+for i in range(NPartType):
+    name = "PartType%i" % i
+    if name not in f:
+        continue
+
+    grp = f[name + "/SmoothingLength"]
+    grp[:] *= 1.823
+
+f.close()
diff --git a/examples/AgoraDisk/getIC.sh b/examples/AgoraDisk/getIC.sh
new file mode 100644
index 0000000000000000000000000000000000000000..620a751bedaf6c646119247270fad6dd3f740fde
--- /dev/null
+++ b/examples/AgoraDisk/getIC.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+if [ "$#" -ne 1 ]; then
+    echo "You need to provide the resolution (e.g. ./getIC.sh low)."
+    echo "The possible options are low, med and high."
+    exit
+fi
+
+wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/$1
diff --git a/examples/AgoraDisk/getSolution.sh b/examples/AgoraDisk/getSolution.sh
new file mode 100644
index 0000000000000000000000000000000000000000..41fbab800098bfaa381ca2e83cab0210c8dcf924
--- /dev/null
+++ b/examples/AgoraDisk/getSolution.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+OPTIND=1
+
+with_cooling=0
+
+function show_help {
+    echo "Valid options are:"
+    echo "\t -h \t Show this help"
+    echo "\t -C \t Download solution with cooling"
+}
+
+while getopts "h?C" opt; do
+    case "$opt" in
+	h|\?)
+	    show_help
+	    exit
+	    ;;
+	C)
+	    with_cooling=1
+	    ;;
+    esac
+done
+
+# cleanup work space
+rm snapshot_0000.hdf5 snapshot_0500.hdf5
+
+if [ $with_cooling -eq 1 ]; then
+    wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/with_cooling/snapshot_0000.hdf5
+    wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/with_cooling/snapshot_0500.hdf5
+else
+    wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/without_cooling/snapshot_0000.hdf5
+    wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/without_cooling/snapshot_0500.hdf5
+fi
+
+
diff --git a/examples/AgoraDisk/plotSolution.py b/examples/AgoraDisk/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c3de1728eb6f22bee72361db1e7f0c77613eb25
--- /dev/null
+++ b/examples/AgoraDisk/plotSolution.py
@@ -0,0 +1,2634 @@
+#!/usr/bin/env python2
+#######################################################################
+#
+#  UNIFIED ANALYSIS SCRIPT FOR DISK SIMULATION FOR THE AGORA PROJECT
+#
+#  FOR SCRIPT HISTORY SEE VERSION CONTROL CHANGELOG
+#
+#  Note: This script requires yt-3.2 or yt-dev. Older versions may
+#        yield incorrect results.
+#
+#######################################################################
+# This script is a copy of the AGORA project (https://bitbucket.org/mornkr/agora-analysis-script/)
+# modified in order to take into account SWIFT
+import matplotlib
+matplotlib.use('Agg')
+import sys
+import math
+import copy
+import csv
+import numpy as np
+import matplotlib.colorbar as cb
+import matplotlib.lines as ln
+import yt.utilities.physical_constants as constants
+import matplotlib.pyplot as plt
+import matplotlib.gridspec as gridspec
+from yt.mods import *
+from yt.units.yt_array import YTQuantity
+from mpl_toolkits.axes_grid1 import AxesGrid
+from matplotlib.offsetbox import AnchoredText
+from matplotlib.ticker import MaxNLocator
+from yt.fields.particle_fields import add_volume_weighted_smoothed_field
+from yt.fields.particle_fields import add_nearest_neighbor_field
+from yt.analysis_modules.star_analysis.api import StarFormationRate
+from yt.analysis_modules.halo_analysis.api import *
+from yt.data_objects.particle_filters import add_particle_filter
+from scipy.stats import kde
+from subprocess import call
+#mylog.setLevel(1)
+
+import sys
+
+if "-C" in sys.argv:
+    with_cooling = 1
+else:
+    with_cooling = 0
+    
+draw_density_map                 = 1#1           # 0/1         = OFF/ON
+draw_temperature_map             = 1#1           # 0/1         = OFF/ON
+draw_cellsize_map                = 2#2           # 0/1/2/3     = OFF/ON/ON now with resolution map where particle_size is defined as [M/rho]^(1/3) for SPH/ON with both cell_size and resolution maps
+draw_elevation_map               = 1#1           # 0/1         = OFF/ON
+draw_metal_map                   = 0#0           # 0/1/2       = OFF/ON/ON with total metal mass in the simulation annotated (this will be inaccurate for SPH)
+draw_zvel_map                    = 0#0           # 0/1         = OFF/ON
+draw_star_map                    = 0#0           # 0/1         = OFF/ON
+draw_star_clump_stats            = 0#0           # 0/1/2/3     = OFF/ON/ON with additional star_map with annotated clumps/ON with addtional star_map + extra clump mass function with GIZMO-ps2
+draw_SFR_map                     = 0###0         # 0/1/2       = OFF/ON/ON with local K-S plot using patches
+draw_PDF                         = 0###0         # 0/1/2/3     = OFF/ON/ON with constant pressure/entropy lines/ON with additional annotations such as 1D profile from a specific code (CHANGA)
+draw_pos_vel_PDF                 = 0##0          # 0/1/2/3/4   = OFF/ON/ON with 1D profile/ON also with 1D dispersion profile/ON also with separate 1D vertical dispersion profiles
+draw_star_pos_vel_PDF            = 0##0          # 0/1/2/3/4   = OFF/ON/ON with 1D profile/ON also with 1D dispersion profile/ON also with separate 1D vertical dispersion profiles
+draw_rad_height_PDF              = 0##0          # 0/1/2/3     = OFF/ON/ON with 1D profile/ON with analytic ftn subtracted
+draw_metal_PDF                   = 0##0          # 0/1         = OFF/ON
+draw_density_DF                  = 0#0           # 0/1/2       = OFF/ON/ON with difference plot between 1st and 2nd datasets (when 2, dataset_num should be set to 2)
+draw_radius_DF                   = 0#0           # 0/1         = OFF/ON
+draw_star_radius_DF              = 0#0           # 0/1/2       = OFF/ON/ON with SFR profile and K-S plot (when 2, this automatically turns on draw_radius_DF)
+draw_height_DF                   = 0#0           # 0/1         = OFF/ON
+draw_SFR                         = 0#0           # 0/1/2       = OFF/ON/ON with extra line with GIZMO-ps2
+draw_cut_through                 = 0#0           # 0/1         = OFF/ON
+add_nametag                      = 1#1           # 0/1         = OFF/ON
+add_mean_fractional_dispersion   = 0#0           # 0/1         = OFF/ON
+
+dataset_num                      = 2             # 1/2         = 1st dataset(Grackle+noSF)/2nd dataset(Grackle+SF+ThermalFbck) for AGORA Paper 4
+yt_version_pre_3_2_3             = 0             # 0/1         = NO/YES to "Is the yt version being used pre yt-dev-3.2.3?"
+times                            = [0, 500]      # in Myr
+figure_width                     = 30            # in kpc
+n_ref                            = 64            # 8; for SPH codes
+over_refine_factor               = 1             # 2; for SPH codes
+aperture_size_SFR_map            = 750           # in pc       = Used if draw_SFR_map = 1, 750 matches Bigiel et al. (2008)
+young_star_cutoff_SFR_map        = 20            # in Myr      = Used if draw_SFR_map = 1
+young_star_cutoff_star_radius_DF = 20            # in Myr      = Used if draw_star_radius_DF = 1
+mean_dispersion_radius_range     = [2, 10]       # in kpc      = Used if add_mean_fractional_dispersion = 1, for draw_pos_vel_PDF, draw_star_pos_vel_PDF, draw_rad_height_PDF, draw_radius_DF, draw_star_radius_DF
+mean_dispersion_height_range     = [0, 0.6]      # in kpc      = Used if add_mean_fractional_dispersion = 1, for draw_height_DF
+mean_dispersion_time_range       = [50, 500]     # in Myr      = Used if add_mean_fractional_dispersion = 1, for draw_SFR
+mean_dispersion_density_range    = [1e-25, 1e-22]# in g/cm3    = Used if add_mean_fractional_dispersion = 1, for draw_density_DF
+disk_normal_vector               = [0., 0., 1.]
+
+gadget_default_unit_base = {'UnitLength_in_cm'         : 3.08568e+21,
+                            'UnitMass_in_g'            :   1.989e+43,
+                            'UnitVelocity_in_cm_per_s' :      100000}
+color_names              = ['red', 'magenta', 'orange', 'gold', 'green', 'cyan', 'blue', 'blueviolet', 'black']
+linestyle_names          = ['-']
+marker_names             = ['s', 'o', 'p', 'v', '^', '<', '>', 'h', '*']
+
+# file_location = ['/lustre/ki/pfs/mornkr/080112_CHaRGe/pfs-hyades/AGORA-DISK-repository-for-use/Grackle+noSF/',
+#                '/lustre/ki/pfs/mornkr/080112_CHaRGe/pfs-hyades/AGORA-DISK-repository-for-use/Grackle+SF+ThermalFbck/']
+# file_location = ['/global/project/projectdirs/agora/AGORA-DISK-repository-for-use/Grackle+noSF/',
+#                '/global/project/projectdirs/agora/AGORA-DISK-repository-for-use/Grackle+SF+ThermalFbck/']
+# codes = ['ART-I', 'ART-II', 'ENZO', 'RAMSES', 'CHANGA', 'GASOLINE', 'GADGET-3', 'GEAR', 'GIZMO']
+# filenames = [[[file_location[0]+'ART-I/IC/AGORA_Galaxy_LOW.d', file_location[0]+'ART-I/t0.5Gyr/10MpcBox_csf512_02350.d'],
+#             [file_location[0]+'ART-II/noSF_def_2p/OUT/AGORA_LOW_000000.art', file_location[0]+'ART-II/noSF_def_2p/OUT/AGORA_LOW_000098.art'],
+#             [file_location[0]+'ENZO/DD0000/DD0000', file_location[0]+'ENZO/DD0100/DD0100'],
+#             [file_location[0]+'RAMSES/output_00001/info_00001.txt', file_location[0]+'RAMSES/output_00068/info_00068.txt'],
+#             [file_location[0]+'CHANGA/disklow/disklow.000000', file_location[0]+'CHANGA/disklow/disklow.000500'],
+#             [file_location[0]+'GASOLINE/LOW_dataset1.00001', file_location[0]+'GASOLINE/LOW_dataset1.00335'],
+#             [file_location[0]+'GADGET-3/AGORA_ISO_LOW_DRY/snap_iso_dry_000.hdf5', file_location[0]+'GADGET-3/AGORA_ISO_LOW_DRY/snap_iso_dry_010.hdf5'],
+#             [file_location[0]+'GEAR/snapshot_0000', file_location[0]+'GEAR/snapshot_0500'],
+#             [file_location[0]+'GIZMO/snapshot_temp_000', file_location[0]+'GIZMO/snapshot_temp_100']],
+#            [[file_location[1]+'ART-I/IC/AGORA_Galaxy_LOW.d', file_location[1]+'ART-I/t0.5Gyr/10MpcBox_csf512_02350.d'],
+#             [file_location[1]+'ART-II/SF_FBth_def_2p/OUT/AGORA_LOW_000000.art', file_location[1]+'ART-II/SF_FBth_def_2p/OUT/AGORA_LOW_000311.art'],
+#             [file_location[1]+'ENZO/DD0000/DD0000', file_location[1]+'ENZO/DD0050/DD0050'],
+#             [file_location[1]+'RAMSES/output_00001/info_00001.txt', file_location[1]+'RAMSES/output_00216/info_00216.txt'],
+#             [file_location[1]+'CHANGA/disklow/disklow.000000', file_location[1]+'CHANGA/disklow/disklow.000500'],
+#             [file_location[1]+'GASOLINE/LOW_dataset1.00001', file_location[1]+'GASOLINE/LOW_dataset2.00335'],
+#             [file_location[1]+'GADGET-3/AGORA_ISO_LOW_SF_SNII_Thermal_Chevalier_SFT10/snap_iso_sf_000.hdf5', file_location[1]+'GADGET-3/AGORA_ISO_LOW_SF_SNII_Thermal_Chevalier_SFT10/snap_iso_sf_010.hdf5'],
+#             [file_location[1]+'GEAR/snapshot_0000', file_location[1]+'GEAR/snapshot_0500'],
+#             [file_location[1]+'GIZMO/snapshot_temp_000', file_location[1]+'GIZMO/snapshot_temp_100']]]
+codes = ['SWIFT', 'GEAR']
+filenames = [[["./agora_disk_IC.hdf5", "./agora_disk_500Myr.hdf5"],
+              ["./snapshot_0000.hdf5", "./snapshot_0500.hdf5"]],
+             [["./agora_disk_IC.hdf5", "./agora_disk_500Myr.hdf5"],
+              ["./snapshot_0000.hdf5", "./snapshot_0500.hdf5"]]]
+
+# codes = ["SWIFT"]
+# filenames = [[["./agora_disk_0000.hdf5", "./agora_disk_0050.hdf5"]],
+#             [["./agora_disk_0000.hdf5", "./agora_disk_0050.hdf5"]]]
+# codes = ['ART-I']
+# filenames = [[[file_location[0]+'ART-I/IC/AGORA_Galaxy_LOW.d', file_location[0]+'ART-I/t0.5Gyr/10MpcBox_csf512_02350.d']],
+#            [[file_location[1]+'ART-I/IC/AGORA_Galaxy_LOW.d', file_location[1]+'ART-I/t0.5Gyr/10MpcBox_csf512_02350.d']]]
+# codes = ['ART-II']
+# filenames = [[[file_location[0]+'ART-II/noSF_def_2p/OUT/AGORA_LOW_000000.art', file_location[0]+'ART-II/noSF_def_2p/OUT/AGORA_LOW_000098.art']],
+#            [[file_location[1]+'ART-II/SF_FBth_def_2p/OUT/AGORA_LOW_000000.art', file_location[1]+'ART-II/SF_FBth_def_2p/OUT/AGORA_LOW_000311.art']]]
+# codes = ['ENZO']
+# filenames = [[[file_location[0]+'ENZO/DD0000/DD0000', file_location[0]+'ENZO/DD0100/DD0100']],
+#            [[file_location[1]+'ENZO/DD0000/DD0000', file_location[1]+'ENZO/DD0050/DD0050']]]
+# codes = ['RAMSES']
+# filenames = [[[file_location[0]+'RAMSES/output_00001/info_00001.txt', file_location[0]+'RAMSES/output_00068/info_00068.txt']],
+#            [[file_location[1]+'RAMSES/output_00001/info_00001.txt', file_location[1]+'RAMSES/output_00216/info_00216.txt']]]
+# codes = ['CHANGA']
+# filenames = [[[file_location[0]+'CHANGA/disklow/disklow.000000', file_location[0]+'CHANGA/disklow/disklow.000500']],
+#            [[file_location[1]+'CHANGA/disklow/disklow.000000', file_location[1]+'CHANGA/disklow/disklow.000500']]]
+# codes = ['GASOLINE']
+# filenames = [[[file_location[0]+'GASOLINE/LOW_dataset1.00001', file_location[0]+'GASOLINE/LOW_dataset1.00335']],
+#            [[file_location[1]+'GASOLINE/LOW_dataset1.00001', file_location[1]+'GASOLINE/LOW_dataset2.00335']]]
+# codes = ['GADGET-3']
+# filenames = [[[file_location[0]+'GADGET-3/AGORA_ISO_LOW_DRY/snap_iso_dry_000.hdf5', file_location[0]+'GADGET-3/AGORA_ISO_LOW_DRY/snap_iso_dry_010.hdf5']],
+#            [[file_location[1]+'GADGET-3/AGORA_ISO_LOW_SF_SNII_Thermal_Chevalier_SFT10/snap_iso_sf_000.hdf5', file_location[1]+'GADGET-3/AGORA_ISO_LOW_SF_SNII_Thermal_Chevalier_SFT10/snap_iso_sf_010.hdf5']]]
+# codes = ['GEAR']
+# filenames = [[['snapshot_0000', 'snapshot_0500']],
+#            [['snapshot_0000', 'snapshot_0500']]]
+# codes = ['GIZMO']
+# filenames = [[[file_location[0]+'GIZMO/snapshot_temp_000', file_location[0]+'GIZMO/snapshot_temp_100']],
+#            [[file_location[1]+'GIZMO/snapshot_temp_000', file_location[1]+'GIZMO/snapshot_temp_100']]]
+
+def load_dataset(dataset_num, time, code, codes, filenames_entry):
+        if codes[code] == 'ART-I': # ART frontend doesn't find the accompanying files, so we specify them; see http://yt-project.org/docs/dev/examining/loading_data.html#art-data
+                dirnames = filenames_entry[code][time][:filenames_entry[code][time].rfind('/')+1]
+                if time == 0:
+                        timestamp = ''
+                else:
+                        timestamp = filenames_entry[code][time][filenames_entry[code][time].rfind('_'):filenames_entry[code][time].rfind('.')]
+                pf = load(filenames_entry[code][time], file_particle_header=dirnames+'PMcrd'+timestamp+'.DAT', file_particle_data=dirnames+'PMcrs0'+timestamp+'.DAT', file_particle_stars=dirnames+'stars'+timestamp+'.dat')
+        elif codes[code] == 'CHANGA' or codes[code] == 'GASOLINE': # For TIPSY frontend, always make sure to place your parameter file in the same directory as your datasets
+                pf = load(filenames_entry[code][time], n_ref=n_ref, over_refine_factor=over_refine_factor)
+        elif codes[code] == 'GADGET-3': # For GADGET-3 2nd dataset, there somehow exist particles very far from the center; so we use [-2000, 2000] for a bounding_box
+                pf = load(filenames_entry[code][time], unit_base = gadget_default_unit_base, bounding_box=[[-2000.0, 2000.0], [-2000.0, 2000.0], [-2000.0, 2000.0]], n_ref=n_ref, over_refine_factor=over_refine_factor)
+        elif codes[code] == "SWIFT":
+                pf = load(filenames_entry[code][time], unit_base = gadget_default_unit_base, bounding_box=[[-2000.0, 2000.0], [-2000.0, 2000.0], [-2000.0, 2000.0]], n_ref=n_ref, over_refine_factor=over_refine_factor)
+        elif codes[code] == 'GEAR':
+                pf = load(filenames_entry[code][time], unit_base = gadget_default_unit_base, bounding_box=[[-2000.0, 2000.0], [-2000.0, 2000.0], [-2000.0, 2000.0]], n_ref=n_ref, over_refine_factor=over_refine_factor) 
+               # from yt.frontends.gadget.definitions import gadget_header_specs
+               #  from yt.frontends.gadget.definitions import gadget_ptype_specs
+               #  from yt.frontends.gadget.definitions import gadget_field_specs
+               #  if with_cooling:
+               #          gadget_header_specs["chemistry"] = (('h1',  4, 'c'),('h2',  4, 'c'),('empty',  256, 'c'),)
+               #          header_spec = "default+chemistry"
+               #  else:
+               #          header_spec = "default"
+               #  gear_ptype_specs = ("Gas", "Stars", "Halo", "Disk", "Bulge", "Bndry")
+               #  if dataset_num == 1:
+               #          pf = GadgetDataset(filenames_entry[code][time], unit_base = gadget_default_unit_base, bounding_box=[[-1000.0, 1000.0], [-1000.0, 1000.0], [-1000.0, 1000.0]], header_spec=header_spec,
+               #                             ptype_spec=gear_ptype_specs, n_ref=n_ref, over_refine_factor=over_refine_factor)
+               #  elif dataset_num == 2:
+               #          # For GEAR 2nd dataset (Grackle+SF+ThermalFbck), "Metals" is acutally 10-species field; check how metallicity field is added below.
+               #          if with_cooling:
+               #                  agora_gear  = ( "Coordinates",
+               #                                  "Velocities",
+               #                                  "ParticleIDs",
+               #                                  "Mass",
+               #                                  ("InternalEnergy", "Gas"),
+               #                                  ("Density", "Gas"),
+               #                                  ("SmoothingLength", "Gas"),
+               #                                  ("Metals", "Gas"),
+               #                                  ("StellarFormationTime", "Stars"),
+               #                                  ("StellarInitMass", "Stars"),
+               #                                  ("StellarIDs", "Stars"),
+               #                                  ("StellarDensity", "Stars"),
+               #                                  ("StellarSmoothingLength", "Stars"),
+               #                                  ("StellarMetals", "Stars"),
+               #                                  ("Opt1", "Stars"),
+               #                                  ("Opt2", "Stars"),
+               #                  )
+               #          else:
+               #                  agora_gear  = ( "Coordinates",
+               #                                  "Velocities",
+               #                                  "ParticleIDs",
+               #                                  "Mass",
+               #                                  ("InternalEnergy", "Gas"),
+               #                                  ("Density", "Gas"),
+               #                                  ("SmoothingLength", "Gas"),
+               #                  )
+               #          gadget_field_specs["agora_gear"] = agora_gear
+               #          pf = GadgetDataset(filenames_entry[code][time], unit_base = gadget_default_unit_base, bounding_box=[[-1000.0, 1000.0], [-1000.0, 1000.0], [-1000.0, 1000.0]], header_spec=header_spec,
+               #                             ptype_spec=gear_ptype_specs, field_spec="agora_gear", n_ref=n_ref, over_refine_factor=over_refine_factor)
+        elif codes[code] == 'GIZMO':
+                from yt.frontends.gadget.definitions import gadget_field_specs
+                if dataset_num == 1:
+                        agora_gizmo = ( "Coordinates",
+                                        "Velocities",
+                                        "ParticleIDs",
+                                        "Mass",
+                                        ("Temperature", "Gas"),
+                                        ("Density", "Gas"),
+                                        ("Electron_Number_Density", "Gas"),
+                                        ("HI_NumberDensity", "Gas"),
+                                        ("SmoothingLength", "Gas"),
+                                        )
+                elif dataset_num == 2:
+                        agora_gizmo = ( "Coordinates",
+                                        "Velocities",
+                                        "ParticleIDs",
+                                        "Mass",
+                                        ("Temperature", "Gas"),
+                                        ("Density", "Gas"),
+                                        ("ElectronAbundance", "Gas"),
+                                        ("NeutralHydrogenAbundance", "Gas"),
+                                        ("SmoothingLength", "Gas"),
+                                        ("StarFormationRate", "Gas"),
+                                        ("StellarFormationTime", "Stars"),
+                                        ("Metallicity", ("Gas", "Stars")),
+                                        ("DelayTime", "Stars"),
+                                        ("StellarInitMass", "Stars"),
+                                        )
+                gadget_field_specs["agora_gizmo"] = agora_gizmo
+                pf = load(filenames_entry[code][time], unit_base = gadget_default_unit_base, bounding_box=[[-1000.0, 1000.0], [-1000.0, 1000.0], [-1000.0,1000.0]], field_spec="agora_gizmo",
+                          n_ref=n_ref, over_refine_factor=over_refine_factor)
+        else:
+                pf = load(filenames_entry[code][time])
+        return pf
+
+fig_density_map             = []
+fig_temperature_map         = []
+fig_cellsize_map            = []
+fig_cellsize_map_2          = []
+fig_elevation_map           = []
+fig_metal_map               = []
+fig_zvel_map                = []
+fig_star_map                = []
+fig_star_map_2              = []
+fig_star_map_3              = []
+fig_degr_sfr_map            = []
+fig_degr_density_map        = []
+fig_PDF                     = []
+fig_pos_vel_PDF             = []
+fig_star_pos_vel_PDF        = []
+fig_rad_height_PDF          = []
+fig_metal_PDF               = []
+grid_density_map            = []
+grid_temperature_map        = []
+grid_cellsize_map           = []
+grid_cellsize_map_2         = []
+grid_elevation_map          = []
+grid_metal_map              = []
+grid_zvel_map               = []
+grid_star_map               = []
+grid_star_map_2             = []
+grid_star_map_3             = []
+grid_degr_sfr_map           = []
+grid_degr_density_map       = []
+grid_PDF                    = []
+grid_pos_vel_PDF            = []
+grid_star_pos_vel_PDF       = []
+grid_rad_height_PDF         = []
+grid_metal_PDF              = []
+star_clump_masses_hop       = []
+star_clump_masses_fof       = []
+star_clump_masses_hop_ref   = []
+star_clump_masses_fof_ref   = []
+pos_vel_xs                  = []
+pos_vel_profiles            = []
+pos_disp_xs                 = []
+pos_disp_profiles           = []
+pos_disp_vert_xs            = []
+pos_disp_vert_profiles      = []
+star_pos_vel_xs             = []
+star_pos_vel_profiles       = []
+star_pos_disp_xs            = []
+star_pos_disp_profiles      = []
+star_pos_disp_vert_xs       = []
+star_pos_disp_vert_profiles = []
+rad_height_xs               = []
+rad_height_profiles         = []
+density_DF_xs               = []
+density_DF_profiles         = []
+density_DF_1st_xs           = []
+density_DF_1st_profiles     = []
+radius_DF_xs                = []
+radius_DF_profiles          = []
+surface_density             = []
+star_radius_DF_xs           = []
+star_radius_DF_profiles     = []
+star_surface_density        = []
+sfr_radius_DF_xs            = []
+sfr_radius_DF_profiles      = []
+sfr_surface_density         = []
+height_DF_xs                = []
+height_DF_profiles          = []
+height_surface_density      = []
+sfr_ts                      = []
+sfr_cum_masses              = []
+sfr_sfrs                    = []
+surf_dens_SFR_map           = []
+sfr_surf_dens_SFR_map       = []
+cut_through_zs              = []
+cut_through_zvalues         = []
+cut_through_xs              = []
+cut_through_xvalues         = []
+
+for time in range(len(times)):
+        if draw_density_map == 1:
+                fig_density_map      += [plt.figure(figsize=(100,20))]
+                grid_density_map     += [AxesGrid(fig_density_map[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.02)]
+        if draw_temperature_map == 1:
+                fig_temperature_map  += [plt.figure(figsize=(100,20))]
+                grid_temperature_map += [AxesGrid(fig_temperature_map[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.02)]
+        if draw_cellsize_map >= 1:
+                fig_cellsize_map     += [plt.figure(figsize=(100,20))]
+                grid_cellsize_map    += [AxesGrid(fig_cellsize_map[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.02)]
+                fig_cellsize_map_2   += [plt.figure(figsize=(100,20))]
+                grid_cellsize_map_2  += [AxesGrid(fig_cellsize_map_2[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.02)]
+        if draw_elevation_map == 1:
+                fig_elevation_map    += [plt.figure(figsize=(100,20))]
+                grid_elevation_map   += [AxesGrid(fig_elevation_map[time], (0.01,0.01,0.99,0.99), nrows_ncols = (1, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "6%", cbar_pad = 0.02)]
+        if draw_metal_map >= 1:
+                fig_metal_map        += [plt.figure(figsize=(100,20))]
+                grid_metal_map       += [AxesGrid(fig_metal_map[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.02)]
+        if draw_zvel_map == 1:
+                fig_zvel_map         += [plt.figure(figsize=(100,20))]
+                grid_zvel_map        += [AxesGrid(fig_zvel_map[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.02)]
+        if draw_star_map == 1:
+                fig_star_map         += [plt.figure(figsize=(100,20))]
+                grid_star_map        += [AxesGrid(fig_star_map[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.02)]
+        if draw_star_clump_stats >= 1:
+                star_clump_masses_hop.append([])
+                star_clump_masses_fof.append([])
+                fig_star_map_2       += [plt.figure(figsize=(100,20))]
+                grid_star_map_2      += [AxesGrid(fig_star_map_2[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.02)]
+                fig_star_map_3       += [plt.figure(figsize=(100,20))]
+                grid_star_map_3      += [AxesGrid(fig_star_map_3[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.02)]
+        if draw_SFR_map >= 1:
+                fig_degr_density_map += [plt.figure(figsize=(100,20))]
+                grid_degr_density_map+= [AxesGrid(fig_degr_density_map[time], (0.01,0.01,0.99,0.99), nrows_ncols = (1, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "6%", cbar_pad = 0.02)]
+                fig_degr_sfr_map     += [plt.figure(figsize=(100,20))]
+                grid_degr_sfr_map    += [AxesGrid(fig_degr_sfr_map[time], (0.01,0.01,0.99,0.99), nrows_ncols = (1, len(codes)), axes_pad = 0.02, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "6%", cbar_pad = 0.02)]
+                surf_dens_SFR_map.append([])
+                sfr_surf_dens_SFR_map.append([])
+        if draw_SFR_map >= 2 or draw_star_radius_DF >= 2:
+                def import_text(filename, separator):
+                        for line in csv.reader(open(filename), delimiter=separator, skipinitialspace=True):
+                                if line:
+                                        yield line
+        if draw_PDF >= 1:
+                fig_PDF              += [plt.figure(figsize=(50, 80))]
+                grid_PDF             += [AxesGrid(fig_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (3, int(math.ceil(len(codes)/3.0))), axes_pad = 0.05, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.05, aspect = False)]
+        if draw_pos_vel_PDF >= 1:
+                fig_pos_vel_PDF      += [plt.figure(figsize=(50, 80))]
+                grid_pos_vel_PDF     += [AxesGrid(fig_pos_vel_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (3, int(math.ceil(len(codes)/3.0))), axes_pad = 0.05, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.05, aspect = False)]
+                pos_vel_xs.append([])
+                pos_vel_profiles.append([])
+                pos_disp_xs.append([])
+                pos_disp_profiles.append([])
+                pos_disp_vert_xs.append([])
+                pos_disp_vert_profiles.append([])
+        if draw_star_pos_vel_PDF >= 1:
+                fig_star_pos_vel_PDF += [plt.figure(figsize=(50, 80))]
+                grid_star_pos_vel_PDF+= [AxesGrid(fig_star_pos_vel_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (3, int(math.ceil(len(codes)/3.0))), axes_pad = 0.05, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.05, aspect = False)]
+                star_pos_vel_xs.append([])
+                star_pos_vel_profiles.append([])
+                star_pos_disp_xs.append([])
+                star_pos_disp_profiles.append([])
+                star_pos_disp_vert_xs.append([])
+                star_pos_disp_vert_profiles.append([])
+        if draw_rad_height_PDF >= 1:
+                fig_rad_height_PDF   += [plt.figure(figsize=(50, 80))]
+                grid_rad_height_PDF  += [AxesGrid(fig_rad_height_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (3, int(math.ceil(len(codes)/3.0))), axes_pad = 0.05, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.05, aspect = False)]
+                rad_height_xs.append([])
+                rad_height_profiles.append([])
+        if draw_metal_PDF == 1:
+                fig_metal_PDF        += [plt.figure(figsize=(50, 80))]
+                grid_metal_PDF       += [AxesGrid(fig_metal_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (3, int(math.ceil(len(codes)/3.0))), axes_pad = 0.05, add_all = True, share_all = True,
+                                                  label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.05, aspect = False)]
+        if draw_density_DF >= 1:
+                density_DF_xs.append([])
+                density_DF_profiles.append([])
+                density_DF_1st_xs.append([])
+                density_DF_1st_profiles.append([])
+                if draw_density_DF == 2 and dataset_num == 1:
+                        print("This won't work; resetting draw_density_DF to 1...")
+                        draw_density_DF == 1
+        if draw_star_radius_DF >= 1:
+                star_radius_DF_xs.append([])
+                star_radius_DF_profiles.append([])
+                star_surface_density.append([])
+                sfr_radius_DF_xs.append([])
+                sfr_radius_DF_profiles.append([])
+                sfr_surface_density.append([])
+        if draw_star_radius_DF == 2:
+                draw_radius_DF = 1
+        if draw_radius_DF == 1:
+                radius_DF_xs.append([])
+                radius_DF_profiles.append([])
+                surface_density.append([])
+        if draw_height_DF == 1:
+                height_DF_xs.append([])
+                height_DF_profiles.append([])
+                height_surface_density.append([])
+        if draw_SFR >= 1:
+                sfr_ts.append([])
+                sfr_cum_masses.append([])
+                sfr_sfrs.append([])
+        if draw_cut_through == 1:
+                cut_through_zs.append([])
+                cut_through_zvalues.append([])
+                cut_through_xs.append([])
+                cut_through_xvalues.append([])
+
+for time in range(len(times)):
+
+        for code in range(len(codes)):
+
+                if (dataset_num != 1 and dataset_num != 2) or (filenames[dataset_num-1][code] == []):
+                        continue
+
+                ####################################
+                #        PRE-ANALYSIS STEPS        #
+                ####################################
+
+                # LOAD DATASETS
+                pf = load_dataset(dataset_num, time, code, codes, filenames[dataset_num-1])
+
+                # PARTICLE FILED NAMES FOR SPH CODES, AND STELLAR PARTICLE FILTERS
+                PartType_Gas_to_use = "Gas"
+                PartType_Star_to_use = "Stars"
+                PartType_StarBeforeFiltered_to_use = "Stars"
+                MassType_to_use = "Mass"
+                MetallicityType_to_use = "Metallicity"
+                FormationTimeType_to_use = "StellarFormationTime" # for GADGET/GEAR/GIZMO, this field has to be added in frontends/sph/fields.py, in which only "FormationTime" can be recognized
+                SmoothingLengthType_to_use = "SmoothingLength"
+
+                if codes[code] == 'CHANGA' or codes[code] == 'GASOLINE':
+                        MetallicityType_to_use = "Metals"
+                        PartType_Star_to_use = "NewStars"
+                        PartType_StarBeforeFiltered_to_use = "Stars"
+                        FormationTimeType_to_use = "FormationTime"
+                        SmoothingLengthType_to_use = "smoothing_length"
+                        def NewStars(pfilter, data): # see http://yt-project.org/docs/dev/analyzing/filtering.html#filtering-particle-fields
+                                return (data[(pfilter.filtered_type, FormationTimeType_to_use)] > 0)
+                        add_particle_filter(PartType_Star_to_use, function=NewStars, filtered_type=PartType_StarBeforeFiltered_to_use, requires=[FormationTimeType_to_use])
+                        pf.add_particle_filter(PartType_Star_to_use)
+                        pf.periodicity = (True, True, True) # this is needed especially when bPeriodic = 0 in GASOLINE, to avoid RuntimeError in geometry/selection_routines.pyx:855
+                elif codes[code] == 'ART-I':
+                        PartType_StarBeforeFiltered_to_use = "stars"
+                        FormationTimeType_to_use = "particle_creation_time"
+                        def Stars(pfilter, data):
+                                return (data[(pfilter.filtered_type, FormationTimeType_to_use)] > 0)
+#                               return ((data[(pfilter.filtered_type, FormationTimeType_to_use)] > 0) & (data[(pfilter.filtered_type, "particle_index")] >= 212500)) # without the fix metioned above, the above line won't work because all "stars"="specie1" have the same wrong particle_creation_time of 6.851 Gyr; so you will have to use this quick and dirty patch
+                        add_particle_filter(PartType_Star_to_use, function=Stars, filtered_type=PartType_StarBeforeFiltered_to_use, requires=[FormationTimeType_to_use, "particle_index"])
+                        pf.add_particle_filter(PartType_Star_to_use)
+                elif codes[code] == 'ART-II':
+                        PartType_StarBeforeFiltered_to_use = "STAR"
+                        if time != 0: # BIRTH_TIME field exists in ART-II but as a dimensionless quantity for some reason in frontends/artio/fields.py; so we create StellarFormationTime field
+                                def _FormationTime(field, data):
+                                        return pf.arr(data["STAR", "BIRTH_TIME"].d, 'code_time')
+                                pf.add_field(("STAR", FormationTimeType_to_use), function=_FormationTime, particle_type=True, take_log=False, units="code_time")
+                        def Stars(pfilter, data):
+                                return (data[(pfilter.filtered_type, FormationTimeType_to_use)] > 0)
+                        add_particle_filter(PartType_Star_to_use, function=Stars, filtered_type=PartType_StarBeforeFiltered_to_use, requires=[FormationTimeType_to_use])
+                        pf.add_particle_filter(PartType_Star_to_use)
+                elif codes[code] == 'ENZO':
+                        PartType_StarBeforeFiltered_to_use = "all"
+                        FormationTimeType_to_use = "creation_time"
+                        def Stars(pfilter, data):
+                                return ((data[(pfilter.filtered_type, "particle_type")] == 2) & (data[(pfilter.filtered_type, FormationTimeType_to_use)] > 0))
+                        add_particle_filter(PartType_Star_to_use, function=Stars, filtered_type=PartType_StarBeforeFiltered_to_use, requires=["particle_type", FormationTimeType_to_use])
+                        pf.add_particle_filter(PartType_Star_to_use)
+                elif codes[code] == "GADGET-3":
+                        PartType_Gas_to_use = "PartType0"
+                        PartType_Star_to_use = "PartType4"
+                        PartType_StarBeforeFiltered_to_use = "PartType4"
+                        MassType_to_use = "Masses"
+                elif codes[code] == "SWIFT":
+                        PartType_Gas_to_use = "PartType0"
+                        PartType_Star_to_use = "PartType2"
+                        PartType_StarBeforeFiltered_to_use = "PartType2"
+                        MassType_to_use = "Masses"
+                elif codes[code] == "GEAR":
+                        PartType_Gas_to_use = "PartType0"
+                        PartType_Star_to_use = "PartType1"
+                        PartType_StarBeforeFiltered_to_use = "PartType1"
+                        MassType_to_use = "Masses"
+                elif codes[code] == 'RAMSES':
+                        PartType_StarBeforeFiltered_to_use = "all"
+                        pf.current_time = pf.arr(pf.parameters['time'], 'code_time') # reset pf.current_time because it is incorrectly set up in frontends/ramses/data_structure.py, and I don't wish to mess with units there
+                        FormationTimeType_to_use = "particle_age" # particle_age field actually means particle creation time, at least for this particular dataset, so the new field below is not needed
+                        # if time != 0: # Only particle_age field exists in RAMSES (only for new stars + IC stars), so we create StellarFormationTime field
+                        #       def _FormationTime(field, data):
+                        #               return pf.current_time - data["all", "particle_age"].in_units("s")
+                        #       pf.add_field(("all", FormationTimeType_to_use), function=_FormationTime, particle_type=True, take_log=False, units="code_time")
+                        def Stars(pfilter, data):
+                                return (data[(pfilter.filtered_type, "particle_age")] > 0)
+                        add_particle_filter(PartType_Star_to_use, function=Stars, filtered_type=PartType_StarBeforeFiltered_to_use, requires=["particle_age"])
+                        pf.add_particle_filter(PartType_Star_to_use)
+
+                # AXIS SWAP FOR PLOT COLLECTION
+                pf.coordinates.x_axis[1] = 0
+                pf.coordinates.y_axis[1] = 2
+                pf.coordinates.x_axis['y'] = 0
+                pf.coordinates.y_axis['y'] = 2
+
+                # ADDITIONAL FIELDS I: GLOBALLY USED FIELDS
+                def _density_squared(field, data):
+                        return data[("gas", "density")]**2
+                pf.add_field(("gas", "density_squared"), function=_density_squared, units="g**2/cm**6")
+
+                # ADDITIONAL FIELDS II: FOR CELL SIZE AND RESOLUTION MAPS
+                def _CellSizepc(field,data):
+                        return (data[("index", "cell_volume")].in_units('pc**3'))**(1/3.)
+                pf.add_field(("index", "cell_size"), function=_CellSizepc, units='pc', display_name="Resolution $\Delta$ x", take_log=True )
+                def _Inv2CellVolumeCode(field,data):
+                        return data[("index", "cell_volume")]**-2
+                pf.add_field(("index", "cell_volume_inv2"), function=_Inv2CellVolumeCode, units='code_length**(-6)', display_name="Inv2CellVolumeCode", take_log=True)
+                if draw_cellsize_map >= 2:
+                        if codes[code] == 'CHANGA' or codes[code] == 'GEAR' or codes[code] == 'GADGET-3' or codes[code] == 'GASOLINE' or codes[code] == 'GIZMO' or codes[code] == "SWIFT":
+                                def _ParticleSizepc(field, data):
+                                        return (data[(PartType_Gas_to_use, MassType_to_use)]/data[(PartType_Gas_to_use, "Density")])**(1./3.)
+                                pf.add_field((PartType_Gas_to_use, "particle_size"), function=_ParticleSizepc, units="pc", display_name="Resolution $\Delta$ x", particle_type=True, take_log=True)
+                                def _Inv2ParticleVolumepc(field, data):
+                                        return (data[(PartType_Gas_to_use, MassType_to_use)]/data[(PartType_Gas_to_use, "Density")])**(-2.)
+                                pf.add_field((PartType_Gas_to_use, "particle_volume_inv2"), function=_Inv2ParticleVolumepc, units="pc**(-6)", display_name="Inv2ParticleVolumepc", particle_type=True, take_log=True)
+                                # Also creating smoothed field following an example in yt-project.org/docs/dev/cookbook/calculating_information.html; use hardcoded num_neighbors as in frontends/gadget/fields.py
+                                fn = add_volume_weighted_smoothed_field(PartType_Gas_to_use, "Coordinates", MassType_to_use, SmoothingLengthType_to_use, "Density", "particle_size", pf.field_info, nneighbors=64)
+                                fn = add_volume_weighted_smoothed_field(PartType_Gas_to_use, "Coordinates", MassType_to_use, SmoothingLengthType_to_use, "Density", "particle_volume_inv2", pf.field_info, nneighbors=64)
+
+                                # Alias doesn't work -- e.g. pf.field_info.alias(("gas", "particle_size"), fn[0]) -- check alias below; so I simply add ("gas", "particle_size")
+                                def _ParticleSizepc_2(field, data):
+                                        return data["deposit", PartType_Gas_to_use+"_smoothed_"+"particle_size"]
+                                pf.add_field(("gas", "particle_size"), function=_ParticleSizepc_2, units="pc", force_override=True, display_name="Resolution $\Delta$ x", particle_type=False, take_log=True)
+                                def _Inv2ParticleVolumepc_2(field, data):
+                                        return data["deposit", PartType_Gas_to_use+"_smoothed_"+"particle_volume_inv2"]
+                                pf.add_field(("gas", "particle_volume_inv2"), function=_Inv2ParticleVolumepc_2, units="pc**(-6)", force_override=True, display_name="Inv2ParticleVolumepc", particle_type=False, take_log=True)
+
+                # ADDITIONAL FIELDS III: TEMPERATURE
+                if codes[code] == 'GEAR' or codes[code] == 'GADGET-3' or codes[code] == 'RAMSES' or codes[code] == "SWIFT":
+                        # From grackle/src/python/utilities/convenience.py: Calculate a tabulated approximation to mean molecular weight (valid for data that used Grackle 2.0 or below)
+                        def calc_mu_table_local(temperature):
+                                tt = np.array([1.0e+01, 1.0e+02, 1.0e+03, 1.0e+04, 1.3e+04, 2.1e+04, 3.4e+04, 6.3e+04, 1.0e+05, 1.0e+09])
+                                mt = np.array([1.18701555, 1.15484424, 1.09603514, 0.9981496, 0.96346395, 0.65175895, 0.6142901, 0.6056833, 0.5897776, 0.58822635])
+                                logttt= np.log(temperature)
+                                logmu = np.interp(logttt,np.log(tt),np.log(mt)) # linear interpolation in log-log space
+                                return np.exp(logmu)
+                        temperature_values = []
+                        mu_values = []
+                        T_over_mu_values = []
+                        current_temperature = 1e1
+                        final_temperature = 1e7
+                        dlogT = 0.1
+                        while current_temperature < final_temperature:
+                                temperature_values.append(current_temperature)
+                                current_mu = calc_mu_table_local(current_temperature)
+                                mu_values.append(current_mu)
+                                T_over_mu_values.append(current_temperature/current_mu)
+                                current_temperature = np.exp(np.log(current_temperature)+dlogT)
+                        def convert_T_over_mu_to_T(T_over_mu):
+                                logT_over_mu = np.log(T_over_mu)
+                                logT = np.interp(logT_over_mu, np.log(T_over_mu_values), np.log(temperature_values)) # linear interpolation in log-log space
+                                return np.exp(logT)
+                        if codes[code] == 'GEAR' or codes[code] == 'GADGET-3' or codes[code] == "SWIFT":
+                                def _Temperature_3(field, data):
+                                        gamma = 5.0/3.0
+                                        T_over_mu = (data[PartType_Gas_to_use, "InternalEnergy"] * (gamma-1) * constants.mass_hydrogen_cgs / constants.boltzmann_constant_cgs).in_units('K').d # T/mu
+                                        return YTArray(convert_T_over_mu_to_T(T_over_mu), 'K') # now T
+                                pf.add_field((PartType_Gas_to_use, "Temperature"), function=_Temperature_3, particle_type=True, force_override=True, units="K")
+                        elif codes[code] == 'RAMSES':
+                                # The pressure field includes the artificial pressure support term, so one needs to be careful (compare with the exsiting frontends/ramses/fields.py)
+                                def _temperature_3(field, data):
+                                        T_J = 1800.0  # in K
+                                        n_J = 8.0     # in H/cc
+                                        gamma_0 = 2.0
+                                        x_H = 0.76
+                                        mH = 1.66e-24      # from pymses/utils/constants/__init__.py  (vs. in yt, mass_hydrogen_cgs = 1.007947*amu_cgs = 1.007947*1.660538921e-24 = 1.6737352e-24)
+                                        kB = 1.3806504e-16 # from pymses/utils/constants/__init__.py  (vs. in yt, boltzmann_constant_cgs = 1.3806488e-16)
+                                        if time != 0:
+                                                T_over_mu = data["gas", "pressure"].d/data["gas", "density"].d * mH / kB - T_J * (data["gas", "density"].d * x_H / mH / n_J)**(gamma_0 - 1.0) # T/mu = T2 in Ramses
+                                        else:
+                                                T_over_mu = data["gas", "pressure"].d/data["gas", "density"].d * mH / kB # IC: no pressure support
+                                        return YTArray(convert_T_over_mu_to_T(T_over_mu), 'K') # now T
+                                pf.add_field(("gas", "temperature"), function=_temperature_3, force_override=True, units="K")
+
+                # ADDITIONAL FIELDS IV: METALLICITY (IN MASS FRACTION, NOT IN ZSUN)
+                if draw_metal_map >= 1 or draw_metal_PDF == 1:
+                        if codes[code] == 'ART-I': # metallicity field in ART-I has a different meaning (see frontends/art/fields.py), and metallicity field in ART-II is missing
+                                def _metallicity_2(field, data):
+                                        return (data["gas", "metal_ii_density"] + data["gas", "metal_ia_density"]) / data["gas", "density"] # metal_ia_density needs to be added to account for initial 0.5 Zsun, even though we don't have SNe Ia
+                                pf.add_field(("gas", "metallicity"), function=_metallicity_2, force_override=True, display_name="Metallicity", take_log=True, units="")
+                        elif codes[code] == 'ART-II':
+                                def _metallicity_2(field, data):
+                                        return data["gas", "metal_ii_density"] / data["gas", "density"]
+                                pf.add_field(("gas", "metallicity"), function=_metallicity_2, force_override=True, display_name="Metallicity", take_log=True, units="")
+                        elif codes[code] == 'ENZO': # metallicity field in ENZO is in Zsun, so we create a new field
+                                def _metallicity_2(field, data):
+                                        return data["gas", "metal_density"] / data["gas", "density"]
+                                pf.add_field(("gas", "metallicity"), function=_metallicity_2, force_override=True, display_name="Metallicity", take_log=True, units="")
+                        elif codes[code] == 'GEAR': # "Metals" in GEAR is 10-species field ([:,9] is the total metal fraction), so requires a change in _vector_fields in frontends/gadget/io.py: added ("Metals", 10)
+                                def _metallicity_2(field, data):
+                                        if len(data[PartType_Gas_to_use, "Metals"].shape) == 1:
+                                                return data[PartType_Gas_to_use, "Metals"]
+                                        else:
+                                                return data[PartType_Gas_to_use, "Metals"][:,9].in_units("") # in_units("") turned out to be crucial!; otherwise code_metallicity will be used and it will mess things up
+                                # We are creating ("Gas", "Metallicity") here, different from ("Gas", "metallicity") which is auto-generated by yt but doesn't work properly
+                                pf.add_field((PartType_Gas_to_use, MetallicityType_to_use), function=_metallicity_2, display_name="Metallicity", particle_type=True, take_log=True, units="")
+                                # Also creating smoothed field following an example in yt-project.org/docs/dev/cookbook/calculating_information.html; use hardcoded num_neighbors as in frontends/gadget/fields.py
+                                fn = add_volume_weighted_smoothed_field(PartType_Gas_to_use, "Coordinates", MassType_to_use, "SmoothingLength", "Density", MetallicityType_to_use, pf.field_info, nneighbors=64)
+                                # Alias doesn't work -- e.g. pf.field_info.alias(("gas", "metallicity"), fn[0]) -- probably because pf=GadgetDataset()?, not load()?; so I add and replace existing ("gas", "metallicity")
+                                def _metallicity_3(field, data):
+                                        return data["deposit", PartType_Gas_to_use+"_smoothed_"+MetallicityType_to_use]
+                                pf.add_field(("gas", "metallicity"), function=_metallicity_3, force_override=True, display_name="Metallicity", particle_type=False, take_log=True, units="")
+                        if draw_metal_map >= 2:
+                                def _metal_mass(field, data):
+                                        return data["gas", "cell_mass"] * data["gas", "metallicity"]
+                                pf.add_field(("gas", "metal_mass"), function=_metal_mass, force_override=True, display_name="Metal Mass", take_log=True, units="Msun")
+
+                # ADDITIONAL FIELDS V: FAKE PARTICLE FIELDS, CYLINDRICAL COORDINATES, etc.
+                def rho_agora_disk(r, z):
+                        r_d = YTArray(3.432, 'kpc')
+                        z_d = 0.1*r_d
+                        M_d = YTArray(4.297e10, 'Msun')
+                        f_gas = 0.2
+                        r_0 = (f_gas*M_d / (4.0*np.pi*r_d**2*z_d)).in_units('g/cm**3')
+                        return r_0*numpy.exp(-r/r_d)*numpy.exp(-z/z_d)
+
+                if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                        def _cylindrical_z_abs(field, data):
+                                return numpy.abs(data[("index", "cylindrical_z")])
+                        pf.add_field(("index", "cylindrical_z_abs"), function=_cylindrical_z_abs, take_log=False, particle_type=False, units="cm")
+                        def _density_minus_analytic(field, data):
+                                return data[("gas", "density")] - rho_agora_disk(data[("index", "cylindrical_r")], data[("index", "cylindrical_z_abs")])
+                        pf.add_field(("gas", "density_minus_analytic"), function=_density_minus_analytic, take_log=False, particle_type=False, display_name="density residual abs", units="g/cm**3")
+                else:
+                        def _particle_position_cylindrical_z_abs(field, data):
+                                return numpy.abs(data[(PartType_Gas_to_use, "particle_position_cylindrical_z")])
+                        pf.add_field((PartType_Gas_to_use, "particle_position_cylindrical_z_abs"), function=_particle_position_cylindrical_z_abs, take_log=False, particle_type=True, units="cm")
+                        # particle_type=False doesn't make sense, but is critical for PhasePlot/ProfilePlot to work for particle fields
+                        # requires a change in data_objects/data_container.py: remove raise YTFieldTypeNotFound(ftype)
+                        def _Density_2(field, data):
+                                return data[(PartType_Gas_to_use, "Density")].in_units('g/cm**3')
+                        pf.add_field((PartType_Gas_to_use, "Density_2"), function=_Density_2, take_log=True, particle_type=False, display_name="Density", units="g/cm**3")
+                        def _Temperature_2(field, data):
+                                return data[(PartType_Gas_to_use, "Temperature")].in_units('K')
+                        pf.add_field((PartType_Gas_to_use, "Temperature_2"), function=_Temperature_2, take_log=True, particle_type=False, display_name="Temperature", units="K")
+                        def _Mass_2(field, data):
+                                return data[(PartType_Gas_to_use, MassType_to_use)].in_units('Msun')
+                        pf.add_field((PartType_Gas_to_use, "Mass_2"), function=_Mass_2, take_log=True, particle_type=False, display_name="Mass", units="Msun")
+                        if draw_metal_map >= 1 or draw_metal_PDF == 1:
+                                def _Metallicity_2(field, data):
+                                        return data[(PartType_Gas_to_use, MetallicityType_to_use)]
+                                pf.add_field((PartType_Gas_to_use, "Metallicity_2"), function=_Metallicity_2, take_log=True, particle_type=False, display_name="Metallicity", units="")
+                        def _Density_2_minus_analytic(field, data):
+                                return data[(PartType_Gas_to_use, "density")] - rho_agora_disk(data[(PartType_Gas_to_use, "particle_position_cylindrical_radius")], data[(PartType_Gas_to_use, "particle_position_cylindrical_z_abs")])
+                        pf.add_field((PartType_Gas_to_use, "Density_2_minus_analytic"), function=_Density_2_minus_analytic, take_log=False, particle_type=False, display_name="Density Residual", units="g/cm**3")
+
+                ####################################
+                #      MAIN ANALYSIS ROUTINES      #
+                ####################################
+
+                # FIND CENTER AND PROJ_REGION
+                v, cen = pf.h.find_max(("gas", "density")) # find the center to keep the galaxy at the center of all the images; here we assume that the gas disk is no bigger than 30 kpc in radius
+                sp = pf.sphere(cen, (30.0, "kpc"))
+                cen2 = sp.quantities.center_of_mass(use_gas=True, use_particles=False).in_units("kpc")
+                sp2 = pf.sphere(cen2, (1.0, "kpc"))
+                cen3 = sp2.quantities.max_location(("gas", "density"))
+                center = pf.arr([cen3[1].d, cen3[2].d, cen3[3].d], 'code_length') # naive usage such as YTArray([cen3[1], cen3[2], cen3[3]]) doesn't work somehow for ART-II data
+                if yt_version_pre_3_2_3 == 1:
+                        center = pf.arr([cen3[2].d, cen3[3].d, cen3[4].d], 'code_length') # for yt-3.2.3 or before
+                if codes[code] == "GASOLINE" and dataset_num == 2 and time == 1:
+                        #center = pf.arr([2.7912903206399826e+20, 1.5205303149849894e+21, 1.5398968883245956e+21], 'cm') # a temporary hack for GASOLINE (center of the most massive stellar clump)
+                        center = pf.arr([ 0.09045956,  0.49277032,  0.49904659], 'code_length')
+                # if codes[code] == "GADGET-3" and dataset_num == 2 and time == 1:
+                #       #center = pf.arr([6.184520935812296e+21, 4.972678132728082e+21, 6.559067311284074e+21], 'cm') # a temporary hack for GADGET-3 (center of the most massive stellar clump)
+                #       center = pf.arr([ 2.00426673674,  1.61153523084,  2.12564895041], 'code_length')
+                if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                        proj_region = pf.box(center - YTArray([figure_width, figure_width, figure_width], 'kpc'),
+                                             center + YTArray([figure_width, figure_width, figure_width], 'kpc')) # projected images made using a (2*figure_width)^3 box for AMR codes
+                else:
+                        proj_region = pf.all_data()
+
+                # DENSITY MAPS
+                if draw_density_map == 1:
+                        my_cmap = copy.copy(matplotlib.cm.get_cmap('algae'))
+                        my_cmap.set_bad(my_cmap(0)) # cmap range [0, 1)
+                        my_cmap.set_under(my_cmap(0))
+                        for ax in range(1, 3):
+                                p = ProjectionPlot(pf, ax, ("gas", "density"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = None, fontsize=9)
+                                p.set_zlim(("gas", "density"), 1e-5, 1e-1)
+                                p.set_cmap(("gas", "density"), my_cmap)
+                                plot = p.plots[("gas", "density")]
+
+                                plot.figure = fig_density_map[time]
+                                plot.axes = grid_density_map[time][(ax-1)*len(codes)+code].axes
+                                if code == 0: plot.cax = grid_density_map[time].cbar_axes[0]
+                                p._setup_plots()
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                grid_density_map[time][code].axes.add_artist(at)
+
+                # TEMPERATURE MAPS
+                if draw_temperature_map == 1:
+                        my_cmap = copy.copy(matplotlib.cm.get_cmap('algae'))
+                        my_cmap.set_bad(my_cmap(0.6)) # (log(1e4) - log(1e1)) / (log(1e6) - log(1e1)) = 0.6
+                        my_cmap.set_under(my_cmap(0))
+                        for ax in range(1, 3):
+                                p2 = ProjectionPlot(pf, ax, ("gas", "temperature"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = ("gas", "density_squared"), fontsize=9)
+                                p2.set_zlim(("gas", "temperature"), 1e1, 1e6)
+                                p2.set_cmap(("gas", "temperature"), my_cmap)
+                                plot2 = p2.plots[("gas", "temperature")]
+
+                                plot2.figure = fig_temperature_map[time]
+                                plot2.axes = grid_temperature_map[time][(ax-1)*len(codes)+code].axes
+                                if code == 0: plot2.cax = grid_temperature_map[time].cbar_axes[0]
+                                p2._setup_plots()
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                grid_temperature_map[time][code].axes.add_artist(at)
+
+                # CELL-SIZE MAPS
+                if draw_cellsize_map >= 1:
+                        my_cmap = copy.copy(matplotlib.cm.get_cmap('algae'))
+                        my_cmap.set_bad(my_cmap(0.99999)) # 1 doesn't work!
+                        my_cmap.set_under(my_cmap(0))
+                        if draw_cellsize_map == 1 or draw_cellsize_map == 3:
+                                for ax in range(1, 3):
+                                        p25 = ProjectionPlot(pf, ax, ("index", "cell_size"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = ("index", "cell_volume_inv2"), fontsize=9)
+                                        p25.set_zlim(("index", "cell_size"), 10, 1e3)
+                                        p25.set_cmap(("index", "cell_size"), my_cmap)
+                                        plot25 = p25.plots[("index", "cell_size")]
+
+                                        plot25.figure = fig_cellsize_map[time]
+                                        plot25.axes = grid_cellsize_map[time][(ax-1)*len(codes)+code].axes
+                                        if code == 0: plot25.cax = grid_cellsize_map[time].cbar_axes[0]
+                                        p25._setup_plots()
+
+                        # Create another map with a different resolution definition if requested
+                        if draw_cellsize_map == 2 or draw_cellsize_map == 3:
+                                for ax in range(1, 3):
+                                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                                p251 = ProjectionPlot(pf, ax, ("index", "cell_size"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), \
+                                                                             weight_field = ("index", "cell_volume_inv2"), fontsize=9)
+                                                p251.set_zlim(("index", "cell_size"), 10, 1e3)
+                                                p251.set_cmap(("index", "cell_size"), my_cmap)
+                                                plot251 = p251.plots[("index", "cell_size")]
+                                        else:
+                                                p251 = ProjectionPlot(pf, ax, ("gas", "particle_size"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), \
+                                                                             weight_field = ("gas", "particle_volume_inv2"), fontsize=9)
+                                                p251.set_zlim(("gas", "particle_size"), 10, 1e3)
+                                                p251.set_cmap(("gas", "particle_size"), my_cmap)
+                                                plot251 = p251.plots[("gas", "particle_size")]
+
+                                        plot251.figure = fig_cellsize_map_2[time]
+                                        plot251.axes = grid_cellsize_map_2[time][(ax-1)*len(codes)+code].axes
+                                        if code == 0: plot251.cax = grid_cellsize_map_2[time].cbar_axes[0]
+                                        p251._setup_plots()
+
+                        if add_nametag == 1:
+                                if draw_cellsize_map == 1 or draw_cellsize_map == 3:
+                                        at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                        grid_cellsize_map[time][code].axes.add_artist(at)
+                                if draw_cellsize_map == 2 or draw_cellsize_map == 3:
+                                        at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                        grid_cellsize_map_2[time][code].axes.add_artist(at)
+
+                # ELEVATION MAPS
+                if draw_elevation_map == 1:
+                        def _CellzElevationpc(field,data):
+                                return ((data[("index", "z")] - center[2]).in_units('pc'))
+                        pf.add_field(("index", "z_elevation"), function=_CellzElevationpc, units='kpc', display_name="$z$ Elevation", take_log=False)
+                        my_cmap = copy.copy(matplotlib.cm.get_cmap('algae'))
+                        my_cmap.set_bad(my_cmap(0.5))
+                        my_cmap.set_under(my_cmap(0))
+                        for ax in range(2, 3):
+                                p255 = ProjectionPlot(pf, ax, ("index", "z_elevation"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = ("gas", "density"), fontsize=9)
+                                p255.set_zlim(("index", "z_elevation"), -1, 1)
+                                p255.set_cmap(("index", "z_elevation"), my_cmap)
+                                plot255 = p255.plots[("index", "z_elevation")]
+
+                                plot255.figure = fig_elevation_map[time]
+                                plot255.axes = grid_elevation_map[time][(ax-2)*len(codes)+code].axes
+                                if code == 0: plot255.cax = grid_elevation_map[time].cbar_axes[0]
+                                p255._setup_plots()
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                grid_elevation_map[time][code].axes.add_artist(at)
+
+                # Z-VELOCITY MAPS
+                if draw_zvel_map == 1:
+                        my_cmap = copy.copy(matplotlib.cm.get_cmap('algae'))
+                        my_cmap.set_bad(my_cmap(0.5))
+                        my_cmap.set_under(my_cmap(0))
+                        for ax in range(1, 3):
+                                p26 = ProjectionPlot(pf, ax, ("gas", "velocity_z"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = ("gas", "density_squared"), fontsize=9)
+                                p26.set_zlim(("gas", "velocity_z"), -1e7, 1e7)
+                                p26.set_cmap(("gas", "velocity_z"), my_cmap)
+                                plot26 = p26.plots[("gas", "velocity_z")]
+
+                                plot26.figure = fig_zvel_map[time]
+                                plot26.axes = grid_zvel_map[time][(ax-1)*len(codes)+code].axes
+                                if code == 0: plot26.cax = grid_zvel_map[time].cbar_axes[0]
+                                p26._setup_plots()
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                grid_zvel_map[time][code].axes.add_artist(at)
+
+                # METAL MAPS
+                if draw_metal_map >= 1:
+                        my_cmap = copy.copy(matplotlib.cm.get_cmap('algae'))
+                        my_cmap.set_bad(my_cmap(0.347)) # (0.02041 - 0.01) / (0.04 - 0.01) = 0.347
+                        my_cmap.set_under(my_cmap(0))
+                        for ax in range(1, 3):
+                                pf.field_info[("gas", "metallicity")].take_log = False
+                                p26 = ProjectionPlot(pf, ax, ("gas", "metallicity"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = ("gas", "density_squared"), fontsize=9)
+                                p26.set_zlim(("gas", "metallicity"), 0.01, 0.04)
+                                p26.set_cmap(("gas", "metallicity"), my_cmap)
+                                plot26 = p26.plots[("gas", "metallicity")]
+
+                                plot26.figure = fig_metal_map[time]
+                                plot26.axes = grid_metal_map[time][(ax-1)*len(codes)+code].axes
+                                if code == 0: plot26.cax = grid_metal_map[time].cbar_axes[0]
+                                p26._setup_plots()
+
+                        if add_nametag == 1:
+                                if draw_metal_map == 2:
+                                        sp = pf.sphere(center, (1, "Mpc"))
+                                        total_metal_mass = sp.quantities.total_quantity([("gas", "metal_mass")])
+                                        at = AnchoredText("%s - %e" % (codes[code], total_metal_mass), loc=2, prop=dict(size=6), frameon=True)
+                                else:
+                                        at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                grid_metal_map[time][code].axes.add_artist(at)
+
+                # STELLAR MAPS
+                if draw_star_map == 1 and time != 0:
+                        for ax in range(1, 3):
+                                p27 = ParticleProjectionPlot(pf, ax, (PartType_Star_to_use, "particle_mass"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = None, fontsize=9)
+                                p27.set_unit((PartType_Star_to_use, "particle_mass"), 'Msun')
+                                p27.set_zlim((PartType_Star_to_use, "particle_mass"), 1e4, 1e7)
+                                p27.set_cmap((PartType_Star_to_use, "particle_mass"), 'algae')
+                                p27.set_buff_size(400) # default is 800
+                                p27.set_colorbar_label((PartType_Star_to_use, "particle_mass"), "Stellar Mass Per Pixel ($\mathrm{M}_{\odot}$)")
+                                plot27 = p27.plots[(PartType_Star_to_use, "particle_mass")]
+                                # p27 = ParticleProjectionPlot(pf, ax, (PartType_Star_to_use, "particle_velocity_cylindrical_theta"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = (PartType_Star_to_use, "particle_mass"), fontsize=9)
+                                # p27.set_unit((PartType_Star_to_use, "particle_velocity_cylindrical_theta"), 'km/s')
+                                # p27.set_buff_size(400)
+                                # p27.set_colorbar_label((PartType_Star_to_use, "particle_velocity_cylindrical_theta"), "Rotational Velocity (km/s)")
+                                # plot27 = p27.plots[(PartType_Star_to_use, "particle_velocity_cylindrical_theta")]
+
+                                plot27.figure = fig_star_map[time]
+                                plot27.axes = grid_star_map[time][(ax-1)*len(codes)+code].axes
+                                if code == 0: plot27.cax = grid_star_map[time].cbar_axes[0]
+                                p27._setup_plots()
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                grid_star_map[time][code].axes.add_artist(at)
+
+                # STELLAR CLUMP STATISTICS AND ANNOTATED STELLAR MAPS
+                if draw_star_clump_stats >= 1 and time != 0:
+                        # For make yt's HaloCatalog to work with non-cosmological dataset, a fix needed to be applied to analysis_modules/halo_finding/halo_objects.py: self.period = ds.arr([3.254, 3.254, 3.254], 'Mpc')
+                        pf.hubble_constant = 0.71; pf.omega_lambda = 0.73; pf.omega_matter = 0.27; pf.omega_curvature = 0.0; pf.current_redshift = 0.0 # another trick to make HaloCatalog work especially for ART-I dataset
+                        if os.path.exists("./halo_catalogs/hop_%s_%05d/hop_%s_%05d.0.h5" % (codes[code], times[time], codes[code], times[time])) == False:
+                                hc = HaloCatalog(data_ds=pf, finder_method='hop', output_dir="./halo_catalogs/hop_%s_%05d" % (codes[code], times[time]), \
+                                                         finder_kwargs={'threshold': 2e8, 'dm_only': False, 'ptype': PartType_Star_to_use})
+#                                                        finder_kwargs={'threshold': 2e5, 'dm_only': False, 'ptype': "all"})
+                                hc.add_filter('quantity_value', 'particle_mass', '>', 2.6e6, 'Msun') # exclude halos with less than 30 particles
+                                hc.add_filter('quantity_value', 'particle_mass', '<', 2.6e8, 'Msun') # exclude the most massive halo (threshold 1e8.4 is hand-picked, so one needs to be careful!)
+                                hc.create()
+                        halo_ds = load("./halo_catalogs/hop_%s_%05d/hop_%s_%05d.0.h5" % (codes[code], times[time], codes[code], times[time]))
+                        hc = HaloCatalog(halos_ds=halo_ds, output_dir="./halo_catalogs/hop_%s_%05d" % (codes[code], times[time]))
+                        hc.load()
+
+                        halo_ad = hc.halos_ds.all_data()
+                        star_clump_masses_hop[time].append(np.log10(halo_ad['particle_mass'][:].in_units("Msun")))
+
+                        if os.path.exists("./halo_catalogs/fof_%s_%05d/fof_%s_%05d.0.h5" % (codes[code], times[time], codes[code], times[time])) == False:
+                                hc2 = HaloCatalog(data_ds=pf, finder_method='fof', output_dir="./halo_catalogs/fof_%s_%05d" % (codes[code], times[time]), \
+                                                          finder_kwargs={'link': 0.0025, 'dm_only': False, 'ptype': PartType_Star_to_use})
+#                                                         finder_kwargs={'link': 0.02, 'dm_only': False, 'ptype': "all"})
+                                hc2.add_filter('quantity_value', 'particle_mass', '>', 2.6e6, 'Msun') # exclude halos with less than 30 particles
+                                hc2.add_filter('quantity_value', 'particle_mass', '<', 2.6e8, 'Msun') # exclude the most massive halo (threshold 1e8.4 is hand-picked, so one needs to be careful!)
+                                hc2.create()
+                        halo_ds2 = load("./halo_catalogs/fof_%s_%05d/fof_%s_%05d.0.h5" % (codes[code], times[time], codes[code], times[time]))
+                        hc2 = HaloCatalog(halos_ds=halo_ds2, output_dir="./halo_catalogs/fof_%s_%05d" % (codes[code], times[time]))
+                        hc2.load()
+
+                        halo_ad2 = hc2.halos_ds.all_data()
+                        star_clump_masses_fof[time].append(np.log10(halo_ad2['particle_mass'][:].in_units("Msun")))
+                        # most_massive = halo_ad['particle_mass'].max().in_units('Msun')
+                        # cen4 = [halo_ad['particle_position_x'][halo_ad['particle_mass'] == most_massive][0].in_units('code_length').d,
+                        #       halo_ad['particle_position_y'][halo_ad['particle_mass'] == most_massive][0].in_units('code_length').d,
+                        #       halo_ad['particle_position_z'][halo_ad['particle_mass'] == most_massive][0].in_units('code_length').d]
+
+
+                        # Add additional star_map with annotated clumps if requested
+                        if draw_star_clump_stats >= 2:
+                                for ax in range(1, 3):
+                                        p271 = ParticleProjectionPlot(pf, ax, (PartType_Star_to_use, "particle_mass"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = None, fontsize=9)
+                                        p271.set_unit((PartType_Star_to_use, "particle_mass"), 'Msun')
+                                        p271.set_zlim((PartType_Star_to_use, "particle_mass"), 1e4, 1e7)
+                                        p271.set_cmap((PartType_Star_to_use, "particle_mass"), 'algae')
+                                        p271.set_buff_size(400) # default is 800
+                                        p271.set_colorbar_label((PartType_Star_to_use, "particle_mass"), "Stellar Mass Per Pixel ($\mathrm{M}_{\odot}$)")
+                                        plot271 = p271.plots[(PartType_Star_to_use, "particle_mass")]
+                                        if ax == 2:
+                                                p271.annotate_halos(hc, factor=1.0, circle_args={'linewidth':0.7, 'alpha':0.8, 'facecolor':'none', 'edgecolor':'k'})
+
+                                        plot271.figure = fig_star_map_2[time]
+                                        plot271.axes = grid_star_map_2[time][(ax-1)*len(codes)+code].axes
+                                        if code == 0: plot271.cax = grid_star_map_2[time].cbar_axes[0]
+                                        p271._setup_plots()
+
+                                if add_nametag == 1:
+                                        at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                        grid_star_map_2[time][code].axes.add_artist(at)
+
+                                for ax in range(1, 3):
+                                        p272 = ParticleProjectionPlot(pf, ax, (PartType_Star_to_use, "particle_mass"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = None, fontsize=9)
+                                        p272.set_unit((PartType_Star_to_use, "particle_mass"), 'Msun')
+                                        p272.set_zlim((PartType_Star_to_use, "particle_mass"), 1e4, 1e7)
+                                        p272.set_cmap((PartType_Star_to_use, "particle_mass"), 'algae')
+                                        p272.set_buff_size(400) # default is 800
+                                        p272.set_colorbar_label((PartType_Star_to_use, "particle_mass"), "Stellar Mass Per Pixel ($\mathrm{M}_{\odot}$)")
+                                        plot272 = p272.plots[(PartType_Star_to_use, "particle_mass")]
+                                        # p272 = ParticleProjectionPlot(pf, ax, ("all", "particle_mass"), center = center, data_source=proj_region, width = (figure_width, 'kpc'), weight_field = None, fontsize=9)
+                                        # p272.set_unit(("all", "particle_mass"), 'Msun')
+                                        # p272.set_zlim(("all", "particle_mass"), 1e4, 1e9)
+                                        # p272.set_cmap(("all", "particle_mass"), 'algae')
+                                        # p272.set_buff_size(400) # default is 800
+                                        # p272.set_colorbar_label(("all", "particle_mass"), "Mass Per Pixel ($\mathrm{M}_{\odot}$)")
+                                        # plot272 = p272.plots[("all", "particle_mass")]
+                                        if ax == 2:
+                                                p272.annotate_halos(hc2, factor=1.0, circle_args={'linewidth':0.7, 'alpha':0.8, 'facecolor':'none', 'edgecolor':'k'})#, annotate_field='particle_mass')
+
+                                        plot272.figure = fig_star_map_3[time]
+                                        plot272.axes = grid_star_map_3[time][(ax-1)*len(codes)+code].axes
+                                        if code == 0: plot272.cax = grid_star_map_3[time].cbar_axes[0]
+                                        p272._setup_plots()
+
+                                if add_nametag == 1:
+                                        at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                        grid_star_map_3[time][code].axes.add_artist(at)
+
+                # SFR MAPS
+                if draw_SFR_map >= 1 and time != 0:
+                        sp = pf.sphere(center, (1.0*figure_width, "kpc"))
+                        xy_bins = int(float(figure_width) / (float(aperture_size_SFR_map/1000.)))
+                        my_cmap = copy.copy(matplotlib.cm.get_cmap('algae'))
+                        my_cmap.set_bad(my_cmap(0))
+                        my_cmap.set_under(my_cmap(0))
+                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                def _position_x_from_center(field, data):
+                                        return (data[("index", "x")] - center[0]).in_units('kpc')
+                                pf.add_field(("index", "position_x_from_center"), function=_position_x_from_center, take_log=False, particle_type=False, units="kpc")
+                                def _position_y_from_center(field, data):
+                                        return (data[("index", "y")] - center[1]).in_units('kpc')
+                                pf.add_field(("index", "position_y_from_center"), function=_position_y_from_center, take_log=False, particle_type=False, units="kpc")
+                                def _cell_mass_surface_density_SFR_map(field, data):
+                                        trans = np.zeros(data[("gas", "cell_mass")].shape)
+                                        ind = np.where(data[("gas", "cell_mass")] > 0)
+                                        trans[ind] = data[("gas", "cell_mass")][ind].in_units('Msun').d/(float(aperture_size_SFR_map))**2
+                                        return data.ds.arr(trans, "Msun/pc**2").in_base(data.ds.unit_system.name)
+                                pf.add_field(("gas", "cell_mass_surface_density_SFR_map"), function=_cell_mass_surface_density_SFR_map, \
+                                                     display_name="$\mathrm{\Sigma}_\mathrm{gas}$", units='Msun/pc**2', particle_type=False, take_log=True)
+
+                                p28 = PhasePlot(sp, ("index", "position_x_from_center"), ("index", "position_y_from_center"), \
+                                                        ("gas", "cell_mass_surface_density_SFR_map"), weight_field=None, fontsize=9, x_bins=xy_bins, y_bins=xy_bins)
+                                p28.set_zlim(("gas", "cell_mass_surface_density_SFR_map"), 1e0, 1e3)
+                                p28.set_cmap(("gas", "cell_mass_surface_density_SFR_map"), my_cmap)
+                                plot28 = p28.plots[("gas", "cell_mass_surface_density_SFR_map")]
+                        else:
+                                def _particle_position_x_from_center(field, data):
+                                        return (data[(PartType_Gas_to_use, "particle_position_x")] - center[0]).in_units('kpc')
+                                pf.add_field((PartType_Gas_to_use, "particle_position_x_from_center"), function=_particle_position_x_from_center, take_log=False, particle_type=True, units="kpc")
+                                def _particle_position_y_from_center(field, data):
+                                        return (data[(PartType_Gas_to_use, "particle_position_y")] - center[0]).in_units('kpc')
+                                pf.add_field((PartType_Gas_to_use, "particle_position_y_from_center"), function=_particle_position_y_from_center, take_log=False, particle_type=True, units="kpc")
+                                def _particle_mass_surface_density_SFR_map(field, data):
+                                        trans = np.zeros(data[(PartType_Gas_to_use, "particle_mass")].shape)
+                                        ind = np.where(data[(PartType_Gas_to_use, "particle_mass")] > 0)
+                                        trans[ind] = data[(PartType_Gas_to_use, "particle_mass")][ind].in_units('Msun').d/(float(aperture_size_SFR_map))**2
+                                        return data.ds.arr(trans, "Msun/pc**2").in_base(data.ds.unit_system.name)
+                                pf.add_field((PartType_Gas_to_use, "particle_mass_surface_density_SFR_map"), function=_particle_mass_surface_density_SFR_map, \
+                                                     display_name="$\mathrm{\Sigma}_\mathrm{gas}$", units='Msun/pc**2', particle_type=True, take_log=True)
+
+                                p28 = ParticlePhasePlot(sp, (PartType_Gas_to_use, "particle_position_x_from_center"), (PartType_Gas_to_use, "particle_position_y_from_center"), \
+                                                                (PartType_Gas_to_use, "particle_mass_surface_density_SFR_map"), weight_field=None, deposition='cic', fontsize=9, x_bins=xy_bins, y_bins=xy_bins)
+                                p28.set_zlim((PartType_Gas_to_use, "particle_mass_surface_density_SFR_map"), 1e0, 1e3)
+                                p28.set_cmap((PartType_Gas_to_use, "particle_mass_surface_density_SFR_map"), my_cmap)
+                                plot28 = p28.plots[(PartType_Gas_to_use, "particle_mass_surface_density_SFR_map")]
+
+                        p28.set_xlabel("x (kpc)")
+                        p28.set_ylabel("y (kpc)")
+                        p28.set_xlim(-15, 15)
+                        p28.set_ylim(-15, 15)
+                        plot28.figure = fig_degr_density_map[time]
+                        plot28.axes = grid_degr_density_map[time][code].axes
+                        if code == 0: plot28.cax = grid_degr_density_map[time].cbar_axes[0]
+                        p28._setup_plots()
+
+                        def _particle_position_x_from_center_2(field, data):
+                                return (data[(PartType_StarBeforeFiltered_to_use, "particle_position_x")] - center[0]).in_units('kpc')
+                        pf.add_field((PartType_StarBeforeFiltered_to_use, "particle_position_x_from_center"), function=_particle_position_x_from_center_2, take_log=False, particle_type=True, units="kpc")
+                        pf.add_particle_filter(PartType_Star_to_use) # This is needed for a filtered particle type PartType_Star_to_use to work, because we have just created new particle fields.
+                        def _particle_position_y_from_center_2(field, data):
+                                return (data[(PartType_StarBeforeFiltered_to_use, "particle_position_y")] - center[0]).in_units('kpc')
+                        pf.add_field((PartType_StarBeforeFiltered_to_use, "particle_position_y_from_center"), function=_particle_position_y_from_center_2, take_log=False, particle_type=True, units="kpc")
+                        pf.add_particle_filter(PartType_Star_to_use)
+                        def _particle_mass_young_stars_SFR_surface_density_SFR_map(field, data):
+                                trans = np.zeros(data[(PartType_StarBeforeFiltered_to_use, "particle_mass")].shape)
+                                ind = np.where(data[(PartType_StarBeforeFiltered_to_use, FormationTimeType_to_use)].in_units('Myr') > (pf.current_time.in_units('Myr').d - young_star_cutoff_SFR_map)) # mass for young stars only
+                                trans[ind] = data[(PartType_StarBeforeFiltered_to_use, "particle_mass")][ind].in_units('Msun').d/(young_star_cutoff_SFR_map*1e6)/(float(aperture_size_SFR_map)/1000.)**2
+                                return data.ds.arr(trans, "Msun/yr/kpc**2").in_base(data.ds.unit_system.name)
+                        pf.add_field((PartType_StarBeforeFiltered_to_use, "particle_mass_young_stars_SFR_surface_density_SFR_map"), function=_particle_mass_young_stars_SFR_surface_density_SFR_map, \
+                                             display_name="$\mathrm{\Sigma}_\mathrm{SFR}$", units='Msun/yr/kpc**2', particle_type=True, take_log=True)
+                        pf.add_particle_filter(PartType_Star_to_use)
+
+                        p281 = ParticlePhasePlot(sp, (PartType_Star_to_use, "particle_position_x_from_center"), (PartType_Star_to_use, "particle_position_y_from_center"), \
+                                                         (PartType_Star_to_use, "particle_mass_young_stars_SFR_surface_density_SFR_map"), deposition='cic', weight_field=None, fontsize=9, x_bins=xy_bins, y_bins=xy_bins)
+                        p281.set_zlim((PartType_Star_to_use, "particle_mass_young_stars_SFR_surface_density_SFR_map"), 3e-4, 3e-1)
+                        p281.set_cmap((PartType_Star_to_use, "particle_mass_young_stars_SFR_surface_density_SFR_map"), my_cmap)
+                        plot281 = p281.plots[(PartType_Star_to_use, "particle_mass_young_stars_SFR_surface_density_SFR_map")]
+
+                        p281.set_xlabel("x (kpc)")
+                        p281.set_ylabel("y (kpc)")
+                        p281.set_xlim(-15, 15)
+                        p281.set_ylim(-15, 15)
+                        plot281.figure = fig_degr_sfr_map[time]
+                        plot281.axes = grid_degr_sfr_map[time][code].axes
+                        if code == 0: plot281.cax = grid_degr_sfr_map[time].cbar_axes[0]
+                        p281._setup_plots()
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                grid_degr_density_map[time][code].axes.add_artist(at)
+                                at2 = AnchoredText("%s" % codes[code], loc=2, prop=dict(size=6), frameon=True)
+                                grid_degr_sfr_map[time][code].axes.add_artist(at2)
+
+                        # Record degraded maps for the later use in a local K-S plot
+                        if draw_SFR_map == 2 and time != 0:
+                                fn = p28.profile.save_as_dataset() # see http://yt-project.org/docs/dev/reference/api/generated/yt.data_objects.profiles.ProfileND.save_as_dataset.html
+                                phaseplot_ds = load(fn)
+                                if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                        phaseplot_surf_dens = phaseplot_ds.data[('data', "cell_mass_surface_density_SFR_map")]
+                                else:
+                                        phaseplot_surf_dens = phaseplot_ds.data[('data', "particle_mass_surface_density_SFR_map")]
+                                fn_1 = p281.profile.save_as_dataset()
+                                phaseplot_ds_1 = load(fn_1)
+                                phaseplot_sfr_surf_dens = phaseplot_ds_1.data[('data', "particle_mass_young_stars_SFR_surface_density_SFR_map")]
+                                xy_shape = phaseplot_surf_dens.shape[0]
+                                surf_dens_SFR_map[time].append(phaseplot_surf_dens.reshape(1, xy_shape**2)[0])
+                                sfr_surf_dens_SFR_map[time].append(phaseplot_sfr_surf_dens.reshape(1, xy_shape**2)[0])
+                                call(["rm", "%s_%s.h5" % (str(pf), 'ParticleProfile')]) # Remove unwanted .h5 files saved
+                                if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                        call(["rm", "%s_%s.h5" % (str(pf), 'Profile2D')])
+
+                # DENSITY-TEMPERATURE PDF
+                if draw_PDF >= 1:
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        my_cmap2 = copy.copy(matplotlib.cm.get_cmap('algae'))
+                        my_cmap2.set_under(my_cmap2(0))
+                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                p3 = PhasePlot(sp, ("gas", "density"), ("gas", "temperature"), ("gas", "cell_mass"), weight_field=None, fontsize=12, x_bins=300, y_bins=300)
+                                p3.set_unit("cell_mass", 'Msun')
+                                p3.set_zlim(("gas", "cell_mass"), 1e3, 1e8)
+                                p3.set_cmap(("gas", "cell_mass"), my_cmap2)
+                                p3.set_colorbar_label(("gas", "cell_mass"), "Mass ($\mathrm{M}_{\odot}$)")
+                                plot3 = p3.plots[("gas", "cell_mass")]
+                        else:
+                                # Because ParticlePhasePlot doesn't yet work for a log-log PDF for some reason, I will do the following trick.
+                                p3 = PhasePlot(sp, (PartType_Gas_to_use, "Density_2"), (PartType_Gas_to_use, "Temperature_2"), (PartType_Gas_to_use, "Mass_2"), weight_field=None, fontsize=12, x_bins=300, y_bins=300)
+                                p3.set_zlim((PartType_Gas_to_use, "Mass_2"), 1e3, 1e8)
+                                p3.set_cmap((PartType_Gas_to_use, "Mass_2"), my_cmap2)
+                                plot3 = p3.plots[(PartType_Gas_to_use, "Mass_2")]
+
+                        p3.set_xlim(1e-29, 1e-21)
+                        p3.set_ylim(10, 1e7)
+
+                        plot3.figure = fig_PDF[time]
+                        plot3.axes = grid_PDF[time][code].axes
+                        if code == 0: plot3.cax = grid_PDF[time].cbar_axes[0]
+                        p3._setup_plots()
+
+                        # Add constant pressure and constant entropy lines to guide the eyes
+                        if draw_PDF >= 2:
+                                dummy_density = np.logspace(-29, -21, num=4)
+                                for constant_i in range(1, 100):
+                                        constant = 1e-100*100**constant_i
+                                        line = ln.Line2D(dummy_density, constant/dummy_density, linestyle=":", linewidth=0.6, color='k', alpha=0.7) # constant pressure P ~ n*T
+                                        grid_PDF[time][code].axes.add_line(line)
+                                        line2 = ln.Line2D(dummy_density, constant*dummy_density**(2./3.), linestyle="-.", linewidth=0.6, color='k', alpha=0.7) # constant entropy S ~ n**(-2/3)*T
+                                        grid_PDF[time][code].axes.add_line(line2)
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=3, prop=dict(size=10), frameon=True)
+                                grid_PDF[time][code].axes.add_artist(at)
+
+                # POSITION-VELOCITY PDF FOR GAS
+                if draw_pos_vel_PDF >= 1:
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        sp.set_field_parameter("normal", disk_normal_vector)
+                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                sp_dense = sp.cut_region(["obj['gas', 'density'].in_units('g/cm**3') > 1.e-25"]) # For AMR codes, consider only the cells that are dense enough
+                                pf.field_info[("index", "cylindrical_r")].take_log = False
+                                pf.field_info[("gas", "cylindrical_tangential_velocity")].take_log = False
+                                p4 = PhasePlot(sp_dense, ("index", "cylindrical_r"), ("gas", "cylindrical_tangential_velocity"), ("gas", "cell_mass"), weight_field=None, fontsize=12, x_bins=300, y_bins=300)
+                                p4.set_unit("cylindrical_r", 'kpc')
+                                p4.set_unit("cylindrical_tangential_velocity", 'km/s')
+                                p4.set_unit("cell_mass", 'Msun')
+                                p4.set_zlim(("gas", "cell_mass"), 1e3, 1e8)
+                                p4.set_cmap(("gas", "cell_mass"), 'algae')
+                                p4.set_colorbar_label(("gas", "cell_mass"), "Mass ($\mathrm{M}_{\odot}$)")
+                                plot4 = p4.plots[("gas", "cell_mass")]
+                        else:
+                                pf.field_info[(PartType_Gas_to_use, "particle_position_cylindrical_radius")].take_log = False
+                                pf.field_info[(PartType_Gas_to_use, "particle_velocity_cylindrical_theta")].take_log = False
+                                p4 = ParticlePhasePlot(sp, (PartType_Gas_to_use, "particle_position_cylindrical_radius"), (PartType_Gas_to_use, "particle_velocity_cylindrical_theta"), \
+                                                               (PartType_Gas_to_use, MassType_to_use), deposition='ngp', weight_field=None, fontsize=12, x_bins=300, y_bins=300)
+                                p4.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                p4.set_unit("particle_velocity_cylindrical_theta", 'km/s')
+                                p4.set_unit(MassType_to_use, 'Msun')
+                                p4.set_zlim((PartType_Gas_to_use, MassType_to_use), 1e3, 1e8)
+                                p4.set_cmap((PartType_Gas_to_use, MassType_to_use), 'algae')
+                                p4.set_colorbar_label((PartType_Gas_to_use, MassType_to_use), "Mass ($\mathrm{M}_{\odot}$)")
+                                plot4 = p4.plots[(PartType_Gas_to_use, MassType_to_use)]
+
+                        p4.set_xlabel("Cylindrical Radius (kpc)")
+                        p4.set_ylabel("Rotational Velocity (km/s)")
+                        p4.set_xlim(0, 14)
+                        p4.set_ylim(-100, 350)
+
+                        plot4.figure = fig_pos_vel_PDF[time]
+                        plot4.axes = grid_pos_vel_PDF[time][code].axes
+                        if code == 0: plot4.cax = grid_pos_vel_PDF[time].cbar_axes[0]
+                        p4._setup_plots()
+
+                        # Add 1D profile line if requested
+                        if draw_pos_vel_PDF >= 2 and time != 0:
+                                if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                        p5 = ProfilePlot(sp_dense, ("index", "cylindrical_r"),  ("gas", "cylindrical_tangential_velocity"), \
+                                                                 weight_field=("gas", "cell_mass"), n_bins=50, x_log=False)
+                                        p5.set_log("cylindrical_tangential_velocity", False)
+                                        p5.set_log("cylindrical_r", False)
+                                        p5.set_unit("cylindrical_r", 'kpc')
+                                        p5.set_xlim(1e-3, 14)
+                                        p5.set_ylim("cylindrical_tangential_velocity", -100, 350)
+                                        line = ln.Line2D(p5.profiles[0].x.in_units('kpc'), p5.profiles[0]["cylindrical_tangential_velocity"].in_units('km/s'), linestyle="-", linewidth=2, color='k', alpha=0.7)
+                                        pos_vel_xs[time].append(p5.profiles[0].x.in_units('kpc').d)
+                                        pos_vel_profiles[time].append(p5.profiles[0]["cylindrical_tangential_velocity"].in_units('km/s').d)
+                                else:
+                                        p5 = ProfilePlot(sp, (PartType_Gas_to_use, "particle_position_cylindrical_radius"), (PartType_Gas_to_use, "particle_velocity_cylindrical_theta"), \
+                                                                 weight_field=(PartType_Gas_to_use, MassType_to_use), n_bins=50, x_log=False)
+                                        p5.set_log("particle_velocity_cylindrical_theta", False)
+                                        p5.set_log("particle_position_cylindrical_radius", False)
+                                        p5.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                        p5.set_xlim(1e-3, 14)
+                                        p5.set_ylim("particle_velocity_cylindrical_theta", -100, 350)
+                                        line = ln.Line2D(p5.profiles[0].x.in_units('kpc'), p5.profiles[0]["particle_velocity_cylindrical_theta"].in_units('km/s'), linestyle="-", linewidth=2, color='k', alpha=0.7)
+                                        pos_vel_xs[time].append(p5.profiles[0].x.in_units('kpc').d)
+                                        pos_vel_profiles[time].append(p5.profiles[0]["particle_velocity_cylindrical_theta"].in_units('km/s').d)
+                                grid_pos_vel_PDF[time][code].axes.add_line(line)
+
+                        # Add velocity dispersion profile if requested
+                        if draw_pos_vel_PDF >= 3 and time != 0:
+                                if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                        # To calculate velocity dispersion -- residual velocity components other than the rotational velocity found above -- we will need a mass-weighted average of [v_i - v_rot_local]**2
+                                        # Here we assumed that the disk is on x-y plane; i.e. the variable disk_normal_vector above needs to be [0.0, 0.0, 1.0]
+                                        def _local_rotational_velocity_x(field, data):
+                                                trans = np.zeros(data[("gas", "velocity_x")].shape)
+                                                dr = 0.5*(pos_vel_xs[time][code][1] - pos_vel_xs[time][code][0])
+                                                for radius, v_rot_local in zip(pos_vel_xs[time][code], pos_vel_profiles[time][code]):
+                                                        ind = np.where((data[("index", "cylindrical_r")].in_units("kpc") >= (radius - dr)) & (data[("index", "cylindrical_r")].in_units("kpc") < (radius + dr)))
+                                                        trans[ind] = -np.sin(data["index", 'cylindrical_theta'][ind]) * v_rot_local * 1e5 # in cm/s
+                                                return data.ds.arr(trans, "cm/s").in_base(data.ds.unit_system.name)
+                                        pf.add_field(("gas", "local_rotational_velocity_x"), function=_local_rotational_velocity_x, take_log=False, particle_type=False, units="cm/s")
+                                        def _local_rotational_velocity_y(field, data):
+                                                trans = np.zeros(data[("gas", "velocity_y")].shape)
+                                                dr = 0.5*(pos_vel_xs[time][code][1] - pos_vel_xs[time][code][0])
+                                                for radius, v_rot_local in zip(pos_vel_xs[time][code], pos_vel_profiles[time][code]):
+                                                        ind = np.where((data[("index", "cylindrical_r")].in_units("kpc") >= (radius - dr)) & (data[("index", "cylindrical_r")].in_units("kpc") < (radius + dr)))
+                                                        trans[ind] =  np.cos(data["index", 'cylindrical_theta'][ind]) * v_rot_local * 1e5
+                                                return data.ds.arr(trans, "cm/s").in_base(data.ds.unit_system.name)
+                                        pf.add_field(("gas", "local_rotational_velocity_y"), function=_local_rotational_velocity_y, take_log=False, particle_type=False, units="cm/s")
+                                        def _velocity_minus_local_rotational_velocity_squared(field, data):
+                                                return (data[("gas", "velocity_x")] - data[("gas", "local_rotational_velocity_x")])**2 + \
+                                                    (data[("gas", "velocity_y")] - data[("gas", "local_rotational_velocity_y")])**2 + \
+                                                    (data[("gas", "velocity_z")])**2
+                                        pf.add_field(("gas", "velocity_minus_local_rotational_velocity_squared"), function=_velocity_minus_local_rotational_velocity_squared,
+                                                     take_log=False, particle_type=False, units="cm**2/s**2")
+#					slc = SlicePlot(pf, 'z', ("gas", "local_rotational_velocity_y"), center = center, width = (figure_width, 'kpc'))
+#					slc = SlicePlot(pf, 'z', ("gas", "velocity_minus_local_rotational_velocity_squared"), center = center, width = (figure_width, 'kpc')) # this should give zeros everywhere for IC at t=0
+#					slc.save()
+
+                                        p55 = ProfilePlot(sp_dense, ("index", "cylindrical_r"),  ("gas", "velocity_minus_local_rotational_velocity_squared"), \
+                                                                  weight_field=("gas", "cell_mass"), n_bins=50, x_log=False)
+                                        p55.set_log("cylindrical_r", False)
+                                        p55.set_unit("cylindrical_r", 'kpc')
+                                        p55.set_xlim(1e-3, 14)
+                                        pos_disp_xs[time].append(p55.profiles[0].x.in_units('kpc').d)
+                                        pos_disp_profiles[time].append(np.sqrt(p55.profiles[0]["velocity_minus_local_rotational_velocity_squared"]).in_units('km/s').d)
+                                else:
+                                        def _particle_local_rotational_velocity_x(field, data):
+                                                trans = np.zeros(data[(PartType_Gas_to_use, "particle_velocity_x")].shape)
+                                                dr = 0.5*(pos_vel_xs[time][code][1] - pos_vel_xs[time][code][0])
+                                                for radius, v_rot_local in zip(pos_vel_xs[time][code], pos_vel_profiles[time][code]):
+                                                        ind = np.where((data[(PartType_Gas_to_use, "particle_position_cylindrical_radius")].in_units("kpc") >= (radius - dr)) & \
+                                                                               (data[(PartType_Gas_to_use, "particle_position_cylindrical_radius")].in_units("kpc") < (radius + dr)))
+                                                        trans[ind] = -np.sin(data[(PartType_Gas_to_use, "particle_position_cylindrical_theta")][ind]) * v_rot_local * 1e5
+                                                return data.ds.arr(trans, "cm/s").in_base(data.ds.unit_system.name)
+                                        pf.add_field((PartType_Gas_to_use, "particle_local_rotational_velocity_x"), function=_particle_local_rotational_velocity_x, take_log=False, particle_type=True, units="cm/s")
+                                        def _particle_local_rotational_velocity_y(field, data):
+                                                trans = np.zeros(data[(PartType_Gas_to_use, "particle_velocity_y")].shape)
+                                                dr = 0.5*(pos_vel_xs[time][code][1] - pos_vel_xs[time][code][0])
+                                                for radius, v_rot_local in zip(pos_vel_xs[time][code], pos_vel_profiles[time][code]):
+                                                        ind = np.where((data[(PartType_Gas_to_use, "particle_position_cylindrical_radius")].in_units("kpc") >= (radius - dr)) & \
+                                                                               (data[(PartType_Gas_to_use, "particle_position_cylindrical_radius")].in_units("kpc") < (radius + dr)))
+                                                        trans[ind] = np.cos(data[(PartType_Gas_to_use, "particle_position_cylindrical_theta")][ind]) * v_rot_local * 1e5
+                                                return data.ds.arr(trans, "cm/s").in_base(data.ds.unit_system.name)
+                                        pf.add_field((PartType_Gas_to_use, "particle_local_rotational_velocity_y"), function=_particle_local_rotational_velocity_y, take_log=False, particle_type=True, units="cm/s")
+                                        def _particle_velocity_minus_local_rotational_velocity_squared(field, data):
+                                                return (data[(PartType_Gas_to_use, "particle_velocity_x")] - data[(PartType_Gas_to_use, "particle_local_rotational_velocity_x")])**2 + \
+                                                    (data[(PartType_Gas_to_use, "particle_velocity_y")] - data[(PartType_Gas_to_use, "particle_local_rotational_velocity_y")])**2 + \
+                                                    (data[(PartType_Gas_to_use, "particle_velocity_z")])**2
+                                        pf.add_field((PartType_Gas_to_use, "particle_velocity_minus_local_rotational_velocity_squared"), function=_particle_velocity_minus_local_rotational_velocity_squared,
+                                                     take_log=False, particle_type=True, units="cm**2/s**2")
+
+                                        p55 = ProfilePlot(sp, (PartType_Gas_to_use, "particle_position_cylindrical_radius"), (PartType_Gas_to_use, "particle_velocity_minus_local_rotational_velocity_squared"), \
+                                                                 weight_field=(PartType_Gas_to_use, MassType_to_use), n_bins=50, x_log=False)
+                                        p55.set_log("particle_position_cylindrical_radius", False)
+                                        p55.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                        p55.set_xlim(1e-3, 14)
+                                        pos_disp_xs[time].append(p55.profiles[0].x.in_units('kpc').d)
+                                        pos_disp_profiles[time].append(np.sqrt(p55.profiles[0]["particle_velocity_minus_local_rotational_velocity_squared"]).in_units('km/s').d)
+
+                                # Add vertical velocity dispersion profile if requested
+                                if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                        def _velocity_z_squared(field, data):
+                                                return (data[("gas", "velocity_z")])**2
+                                        pf.add_field(("gas", "velocity_z_squared"), function=_velocity_z_squared, take_log=False, particle_type=False, units="cm**2/s**2")
+
+                                        p551 = ProfilePlot(sp_dense, ("index", "cylindrical_r"),  ("gas", "velocity_z_squared"), weight_field=("gas", "cell_mass"), n_bins=50, x_log=False)
+                                        p551.set_log("cylindrical_r", False)
+                                        p551.set_unit("cylindrical_r", 'kpc')
+                                        p551.set_xlim(1e-3, 14)
+                                        pos_disp_vert_xs[time].append(p551.profiles[0].x.in_units('kpc').d)
+                                        pos_disp_vert_profiles[time].append(np.sqrt(p551.profiles[0]["velocity_z_squared"]).in_units('km/s').d)
+                                else:
+                                        def _particle_velocity_z_squared(field, data):
+                                                return (data[(PartType_Gas_to_use, "particle_velocity_z")])**2
+                                        pf.add_field((PartType_Gas_to_use, "particle_velocity_z_squared"), function=_particle_velocity_z_squared, take_log=False, particle_type=True, units="cm**2/s**2")
+
+                                        p551 = ProfilePlot(sp, (PartType_Gas_to_use, "particle_position_cylindrical_radius"), (PartType_Gas_to_use, "particle_velocity_z_squared"), \
+                                                                 weight_field=(PartType_Gas_to_use, MassType_to_use), n_bins=50, x_log=False)
+                                        p551.set_log("particle_position_cylindrical_radius", False)
+                                        p551.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                        p551.set_xlim(1e-3, 14)
+                                        pos_disp_vert_xs[time].append(p551.profiles[0].x.in_units('kpc').d)
+                                        pos_disp_vert_profiles[time].append(np.sqrt(p551.profiles[0]["particle_velocity_z_squared"]).in_units('km/s').d)
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=4, prop=dict(size=10), frameon=True)
+                                grid_pos_vel_PDF[time][code].axes.add_artist(at)
+
+                # POSITION-VELOCITY PDF FOR NEW STARS
+                if draw_star_pos_vel_PDF >= 1 and time != 0:
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        sp.set_field_parameter("normal", disk_normal_vector)
+                        pf.field_info[(PartType_Star_to_use, "particle_position_cylindrical_radius")].take_log = False
+                        pf.field_info[(PartType_Star_to_use, "particle_velocity_cylindrical_theta")].take_log = False
+                        pf.field_info[(PartType_Star_to_use, "particle_mass")].output_units = 'code_mass' # this turned out to be crucial!; otherwise wrong output_unit 'g' is assumed in ParticlePhasePlot->create_profile in visualizaiton/particle_plots.py for ART-I/ENZO/RAMSES
+                        p41 = ParticlePhasePlot(sp, (PartType_Star_to_use, "particle_position_cylindrical_radius"), (PartType_Star_to_use, "particle_velocity_cylindrical_theta"), \
+                                                       (PartType_Star_to_use, "particle_mass"), deposition='ngp', weight_field=None, fontsize=12, x_bins=300, y_bins=300)
+                        p41.set_unit("particle_position_cylindrical_radius", 'kpc')
+                        p41.set_unit("particle_velocity_cylindrical_theta", 'km/s')
+                        p41.set_unit("particle_mass", 'Msun') # requires a change in set_unit in visualization/profile_plotter.py: remove self.plots[field].zmin, self.plots[field].zmax = (None, None)
+#			p41.set_unit((PartType_Star_to_use, "particle_mass"), 'Msun') # Neither this nor above works without such change
+                        p41.set_zlim((PartType_Star_to_use, "particle_mass"), 1e3, 1e7)
+                        p41.set_cmap((PartType_Star_to_use, "particle_mass"), 'algae')
+
+                        p41.set_colorbar_label((PartType_Star_to_use, "particle_mass"), "Newly Formed Stellar Mass ($\mathrm{M}_{\odot}$)")
+                        plot41 = p41.plots[(PartType_Star_to_use, "particle_mass")]
+
+                        p41.set_xlabel("Cylindrical Radius (kpc)")
+                        p41.set_ylabel("Rotational Velocity (km/s)")
+                        p41.set_xlim(0, 14)
+                        p41.set_ylim(-100, 350)
+
+                        plot41.figure = fig_star_pos_vel_PDF[time]
+                        plot41.axes = grid_star_pos_vel_PDF[time][code].axes
+                        if code == 0: plot41.cax = grid_star_pos_vel_PDF[time].cbar_axes[0]
+                        p41._setup_plots()
+
+                        # Add 1D profile line if requested
+                        if draw_star_pos_vel_PDF >= 2 and time != 0:
+                                p51 = ProfilePlot(sp, (PartType_Star_to_use, "particle_position_cylindrical_radius"), (PartType_Star_to_use, "particle_velocity_cylindrical_theta"), \
+                                                         weight_field=(PartType_Star_to_use, "particle_mass"), n_bins=50, x_log=False)
+                                p51.set_log((PartType_Star_to_use, "particle_velocity_cylindrical_theta"), False)
+                                p51.set_log((PartType_Star_to_use, "particle_position_cylindrical_radius"), False)
+                                p51.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                p51.set_xlim(1e-3, 14)
+                                p51.set_ylim("particle_velocity_cylindrical_theta", -100, 350)
+                                line = ln.Line2D(p51.profiles[0].x.in_units('kpc'), p51.profiles[0]["particle_velocity_cylindrical_theta"].in_units('km/s'), linestyle="-", linewidth=2, color='k', alpha=0.7)
+                                star_pos_vel_xs[time].append(p51.profiles[0].x.in_units('kpc').d)
+                                star_pos_vel_profiles[time].append(p51.profiles[0]["particle_velocity_cylindrical_theta"].in_units('km/s').d)
+                                grid_star_pos_vel_PDF[time][code].axes.add_line(line)
+
+                        # Add velocity dispersion profile if requested
+                        if draw_star_pos_vel_PDF >= 3 and time != 0:
+                                def _particle_local_rotational_velocity_x(field, data):
+                                        trans = np.zeros(data[(PartType_StarBeforeFiltered_to_use, "particle_velocity_x")].shape)
+                                        dr = 0.5*(star_pos_vel_xs[time][code][1] - star_pos_vel_xs[time][code][0])
+                                        for radius, v_rot_local in zip(star_pos_vel_xs[time][code], star_pos_vel_profiles[time][code]):
+                                                ind = np.where((data[(PartType_StarBeforeFiltered_to_use, "particle_position_cylindrical_radius")].in_units("kpc") >= (radius - dr)) & \
+                                                                       (data[(PartType_StarBeforeFiltered_to_use, "particle_position_cylindrical_radius")].in_units("kpc") < (radius + dr)))
+                                                trans[ind] = -np.sin(data[(PartType_StarBeforeFiltered_to_use, "particle_position_cylindrical_theta")][ind]) * v_rot_local * 1e5
+                                        return data.ds.arr(trans, "cm/s").in_base(data.ds.unit_system.name)
+                                pf.add_field((PartType_StarBeforeFiltered_to_use, "particle_local_rotational_velocity_x"), function=_particle_local_rotational_velocity_x, take_log=False, particle_type=True, units="cm/s")
+                                def _particle_local_rotational_velocity_y(field, data):
+                                        trans = np.zeros(data[(PartType_StarBeforeFiltered_to_use, "particle_velocity_y")].shape)
+                                        dr = 0.5*(star_pos_vel_xs[time][code][1] - star_pos_vel_xs[time][code][0])
+                                        for radius, v_rot_local in zip(star_pos_vel_xs[time][code], star_pos_vel_profiles[time][code]):
+                                                ind = np.where((data[(PartType_StarBeforeFiltered_to_use, "particle_position_cylindrical_radius")].in_units("kpc") >= (radius - dr)) & \
+                                                                       (data[(PartType_StarBeforeFiltered_to_use, "particle_position_cylindrical_radius")].in_units("kpc") < (radius + dr)))
+                                                trans[ind] = np.cos(data[(PartType_StarBeforeFiltered_to_use, "particle_position_cylindrical_theta")][ind]) * v_rot_local * 1e5
+                                        return data.ds.arr(trans, "cm/s").in_base(data.ds.unit_system.name)
+                                pf.add_field((PartType_StarBeforeFiltered_to_use, "particle_local_rotational_velocity_y"), function=_particle_local_rotational_velocity_y, take_log=False, particle_type=True, units="cm/s")
+                                def _particle_velocity_minus_local_rotational_velocity_squared(field, data):
+                                        return (data[(PartType_StarBeforeFiltered_to_use, "particle_velocity_x")] - data[(PartType_StarBeforeFiltered_to_use, "particle_local_rotational_velocity_x")])**2 + \
+                                            (data[(PartType_StarBeforeFiltered_to_use, "particle_velocity_y")] - data[(PartType_StarBeforeFiltered_to_use, "particle_local_rotational_velocity_y")])**2 + \
+                                            (data[(PartType_StarBeforeFiltered_to_use, "particle_velocity_z")])**2
+                                pf.add_field((PartType_StarBeforeFiltered_to_use, "particle_velocity_minus_local_rotational_velocity_squared"), function=_particle_velocity_minus_local_rotational_velocity_squared,
+                                             take_log=False, particle_type=True, units="cm**2/s**2")
+                                pf.add_particle_filter(PartType_Star_to_use) # This is needed for a filtered particle type PartType_Star_to_use to work, because we have just created new particle fields.
+
+                                p515 = ProfilePlot(sp, (PartType_Star_to_use, "particle_position_cylindrical_radius"), (PartType_Star_to_use, "particle_velocity_minus_local_rotational_velocity_squared"), \
+                                                          weight_field=(PartType_Star_to_use, "particle_mass"), n_bins=50, x_log=False)
+                                p515.set_log((PartType_Star_to_use, "particle_position_cylindrical_radius"), False)
+                                p515.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                p515.set_xlim(1e-3, 14)
+                                star_pos_disp_xs[time].append(p515.profiles[0].x.in_units('kpc').d)
+                                star_pos_disp_profiles[time].append(np.sqrt(p515.profiles[0]["particle_velocity_minus_local_rotational_velocity_squared"]).in_units('km/s').d)
+
+                        # Add vertical velocity dispersion profile if requested
+                        if draw_star_pos_vel_PDF >= 4 and time != 0:
+                                def _particle_velocity_z_squared(field, data):
+                                        return (data[(PartType_StarBeforeFiltered_to_use, "particle_velocity_z")])**2
+                                pf.add_field((PartType_StarBeforeFiltered_to_use, "particle_velocity_z_squared"), function=_particle_velocity_z_squared,
+                                             take_log=False, particle_type=True, units="cm**2/s**2")
+                                pf.add_particle_filter(PartType_Star_to_use) # This is needed for a filtered particle type PartType_Star_to_use to work, because we have just created new particle fields.
+
+                                p5151 = ProfilePlot(sp, (PartType_Star_to_use, "particle_position_cylindrical_radius"), (PartType_Star_to_use, "particle_velocity_z_squared"), \
+                                                          weight_field=(PartType_Star_to_use, "particle_mass"), n_bins=50, x_log=False)
+                                p5151.set_log((PartType_Star_to_use, "particle_position_cylindrical_radius"), False)
+                                p5151.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                p5151.set_xlim(1e-3, 14)
+                                star_pos_disp_vert_xs[time].append(p5151.profiles[0].x.in_units('kpc').d)
+                                star_pos_disp_vert_profiles[time].append(np.sqrt(p5151.profiles[0]["particle_velocity_z_squared"]).in_units('km/s').d)
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=4, prop=dict(size=10), frameon=True)
+                                grid_star_pos_vel_PDF[time][code].axes.add_artist(at)
+
+                # RADIUS-HEIGHT PDF
+                if draw_rad_height_PDF >= 1:
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                if draw_rad_height_PDF == 1 or draw_rad_height_PDF == 2:
+                                        p55 = PhasePlot(sp, ("index", "cylindrical_r"), ("index", "cylindrical_z_abs"), ("gas", "density"), weight_field=("gas", "cell_mass"), fontsize=12, x_bins=200, y_bins=200)
+                                        p55.set_zlim(("gas", "density"), 1e-26, 1e-21)
+                                        p55.set_cmap(("gas", "density"), 'algae')
+                                        p55.set_log("cylindrical_r", False)
+                                        p55.set_log("cylindrical_z_abs", False)
+                                        p55.set_unit("cylindrical_r", 'kpc')
+                                        p55.set_unit("cylindrical_z_abs", 'kpc')
+                                        plot55 = p55.plots[("gas", "density")]
+                                elif draw_rad_height_PDF == 3:
+                                        p55 = PhasePlot(sp, ("index", "cylindrical_r"), ("index", "cylindrical_z_abs"), ("gas", "density_minus_analytic"), weight_field=("gas", "cell_mass"), fontsize=12, x_bins=200, y_bins=200)
+                                        p55.set_zlim(("gas", "density_minus_analytic"), -1e-24, 1e-24)
+                                        p55.set_cmap(("gas", "density_minus_analytic"), 'algae')
+                                        p55.set_log("cylindrical_r", False)
+                                        p55.set_log("cylindrical_z_abs", False)
+                                        p55.set_unit("cylindrical_r", 'kpc')
+                                        p55.set_unit("cylindrical_z_abs", 'kpc')
+                                        plot55 = p55.plots[("gas", "density_minus_analytic")]
+                        else:
+                                # Because ParticlePhasePlot doesn't work for these newly created fields for some reason, I will do the following trick.
+                                if draw_rad_height_PDF == 1 or draw_rad_height_PDF == 2:
+                                        p55 = PhasePlot(sp, (PartType_Gas_to_use, "particle_position_cylindrical_radius"), (PartType_Gas_to_use, "particle_position_cylindrical_z_abs"), (PartType_Gas_to_use, "Density_2"), weight_field=(PartType_Gas_to_use, "Mass_2"), fontsize=12, x_bins=200, y_bins=200)
+                                        p55.set_zlim((PartType_Gas_to_use, "Density_2"), 1e-26, 1e-21)
+                                        p55.set_cmap((PartType_Gas_to_use, "Density_2"), 'algae')
+                                        p55.set_log("particle_position_cylindrical_radius", False)
+                                        p55.set_log("particle_position_cylindrical_z_abs", False)
+                                        p55.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                        p55.set_unit("particle_position_cylindrical_z_abs", 'kpc')
+                                        plot55 = p55.plots[(PartType_Gas_to_use, "Density_2")]
+                                elif draw_rad_height_PDF == 3:
+                                        p55 = PhasePlot(sp, (PartType_Gas_to_use, "particle_position_cylindrical_radius"), (PartType_Gas_to_use, "particle_position_cylindrical_z_abs"), (PartType_Gas_to_use, "Density_2_minus_analytic"), weight_field=(PartType_Gas_to_use, "Mass_2"), fontsize=12, x_bins=200, y_bins=200)
+                                        p55.set_zlim((PartType_Gas_to_use, "Density_2_minus_analytic"), -1e-24, 1e-24)
+                                        p55.set_cmap((PartType_Gas_to_use, "Density_2_minus_analytic"), 'algae')
+                                        p55.set_log("particle_position_cylindrical_radius", False)
+                                        p55.set_log("particle_position_cylindrical_z_abs", False)
+                                        p55.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                        p55.set_unit("particle_position_cylindrical_z_abs", 'kpc')
+                                        plot55 = p55.plots[(PartType_Gas_to_use, "Density_2_minus_analytic")]
+
+                        p55.set_xlabel("Cylindrical Radius (kpc)")
+                        p55.set_ylabel("Vertical Height (kpc)")
+                        p55.set_xlim(0, 14)
+                        p55.set_ylim(0, 1.4)
+
+                        plot55.figure = fig_rad_height_PDF[time]
+                        plot55.axes = grid_rad_height_PDF[time][code].axes
+                        if code == 0: plot55.cax = grid_rad_height_PDF[time].cbar_axes[0]
+                        p55._setup_plots()
+
+                        # Add 1D profile line if requested
+                        if draw_rad_height_PDF == 2:
+                                if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                        p56 = ProfilePlot(sp, ("index", "cylindrical_r"),  ("index", "cylindrical_z_abs"), \
+                                                                  weight_field=("gas", "cell_mass"), n_bins=50, x_log=False)
+                                        p56.set_log("cylindrical_r", False)
+                                        p56.set_log("cylindrical_z_abs", False)
+                                        p56.set_unit("cylindrical_r", 'kpc')
+                                        p56.set_unit("cylindrical_z_abs", 'kpc')
+                                        p56.set_xlim(0, 14)
+                                        p56.set_ylim("cylindrical_z_abs", 0, 1.4)
+                                        line = ln.Line2D(p56.profiles[0].x.in_units('kpc'), p56.profiles[0]["cylindrical_z_abs"].in_units('kpc'), linestyle="-", linewidth=2, color='k', alpha=0.7)
+                                        rad_height_xs[time].append(p56.profiles[0].x.in_units('kpc').d)
+                                        rad_height_profiles[time].append(p56.profiles[0]["cylindrical_z_abs"].in_units('kpc').d)
+                                else:
+                                        p56 = ProfilePlot(sp, (PartType_Gas_to_use, "particle_position_cylindrical_radius"), (PartType_Gas_to_use, "particle_position_cylindrical_z_abs"), \
+                                                                  weight_field=(PartType_Gas_to_use, MassType_to_use), n_bins=50, x_log=False)
+                                        p56.set_log("particle_position_cylindrical_radius", False)
+                                        p56.set_log("particle_position_cylindrical_z_abs", False)
+                                        p56.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                        p56.set_unit("particle_position_cylindrical_z_abs", 'kpc')
+                                        p56.set_xlim(0, 14)
+                                        p56.set_ylim("particle_position_cylindrical_z_abs", 0, 1.4)
+                                        line = ln.Line2D(p56.profiles[0].x.in_units('kpc'), p56.profiles[0]["particle_position_cylindrical_z_abs"].in_units('kpc'), linestyle="-", linewidth=2, color='k', alpha=0.7)
+                                        rad_height_xs[time].append(p56.profiles[0].x.in_units('kpc').d)
+                                        rad_height_profiles[time].append(p56.profiles[0]["particle_position_cylindrical_z_abs"].in_units('kpc').d)
+                                grid_rad_height_PDF[time][code].axes.add_line(line)
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=1, prop=dict(size=10), frameon=True)
+                                grid_rad_height_PDF[time][code].axes.add_artist(at)
+
+                # DENSITY-TEMPERATURE-METALLICITY PDF
+                if draw_metal_PDF == 1:
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        my_cmap2 = copy.copy(matplotlib.cm.get_cmap('algae'))
+                        my_cmap2.set_under('w')
+                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                pf.field_info[("gas", "metallicity")].take_log = False
+                                p3 = PhasePlot(sp, ("gas", "density"), ("gas", "temperature"), ("gas", "metallicity"), weight_field=("gas", "cell_mass"), fontsize=12, x_bins=300, y_bins=300)
+                                p3.set_zlim(("gas", "metallicity"), 0.01, 0.04)
+                                p3.set_cmap(("gas", "metallicity"), my_cmap2)
+                                p3.set_colorbar_label(("gas", "metallicity"), "Metallicity (Mass-weighted average of mass fraction)")
+                                plot3 = p3.plots[("gas", "metallicity")]
+                        else:
+                                # Because ParticlePhasePlot doesn't yet work for a log-log PDF for some reason, I will do the following trick.
+                                pf.field_info[(PartType_Gas_to_use, "Metallicity_2")].take_log = False
+                                p3 = PhasePlot(sp, (PartType_Gas_to_use, "Density_2"), (PartType_Gas_to_use, "Temperature_2"), (PartType_Gas_to_use, "Metallicity_2"), weight_field=(PartType_Gas_to_use, "Mass_2"), fontsize=12, x_bins=300, y_bins=300)
+                                p3.set_zlim((PartType_Gas_to_use, "Metallicity_2"), 0.01, 0.04)
+                                p3.set_cmap((PartType_Gas_to_use, "Metallicity_2"), my_cmap2)
+                                plot3 = p3.plots[(PartType_Gas_to_use, "Metallicity_2")]
+
+                        p3.set_xlim(1e-29, 1e-21)
+                        p3.set_ylim(10, 1e7)
+
+                        plot3.figure = fig_metal_PDF[time]
+                        plot3.axes = grid_metal_PDF[time][code].axes
+                        if code == 0: plot3.cax = grid_metal_PDF[time].cbar_axes[0]
+                        p3._setup_plots()
+
+                        if add_nametag == 1:
+                                at = AnchoredText("%s" % codes[code], loc=3, prop=dict(size=10), frameon=True)
+                                grid_metal_PDF[time][code].axes.add_artist(at)
+
+                # DENSITY DF (DISTRIBUTION FUNCTION)
+                if draw_density_DF >= 1:
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                p6 = ProfilePlot(sp, ("gas", "density"),  ("gas", "cell_mass"), weight_field=None, n_bins=50, x_log=True, accumulation=False)
+#				p6 = ProfilePlot(sp, ("gas", "density"),  ("gas", "cell_mass"), weight_field=None, n_bins=50, x_log=True, accumulation=True)
+                                p6.set_log("cell_mass", True)
+                                p6.set_xlim(1e-29, 1e-21)
+                                density_DF_xs[time].append(p6.profiles[0].x.in_units('g/cm**3').d)
+                                density_DF_profiles[time].append(p6.profiles[0]["cell_mass"].in_units('Msun').d)
+                        else:
+                                # Because ParticleProfilePlot doesn't exist, I will do the following trick.
+                                p6 = ProfilePlot(sp, (PartType_Gas_to_use, "Density_2"),  (PartType_Gas_to_use, "Mass_2"), weight_field=None, n_bins=50, x_log=True, accumulation=False)
+#				p6 = ProfilePlot(sp, (PartType_Gas_to_use, "Density_2"),  (PartType_Gas_to_use, "Mass_2"), weight_field=None, n_bins=50, x_log=True, accumulation=True)
+                                p6.set_log("Mass_2", True)
+                                p6.set_xlim(1e-29, 1e-21)
+                                density_DF_xs[time].append(p6.profiles[0].x.in_units('g/cm**3').d)
+                                density_DF_profiles[time].append(p6.profiles[0]["Mass_2"].in_units('Msun').d)
+
+                        # Add difference plot between 1st and 2nd datasets, if requested
+                        if draw_density_DF == 2 and time != 0:
+                                if dataset_num == 2:
+                                        pf_1st = load_dataset(1, time, code, codes, filenames[0]) # load 1st datasets
+                                        v, cen = pf_1st.h.find_max(("gas", "density"))
+                                        sp = pf_1st.sphere(cen, (30.0, "kpc"))
+                                        cen2 = sp.quantities.center_of_mass(use_gas=True, use_particles=False).in_units("kpc")
+                                        sp2 = pf_1st.sphere(cen2, (1.0, "kpc"))
+                                        cen3 = sp2.quantities.max_location(("gas", "density"))
+                                        center_1st = pf_1st.arr([cen3[1].d, cen3[2].d, cen3[3].d], 'code_length')
+                                        if yt_version_pre_3_2_3 == 1:
+                                                center_1st = pf_1st.arr([cen3[2].d, cen3[3].d, cen3[4].d], 'code_length') # for yt-3.2.3 or before
+                                        sp_1st = pf_1st.sphere(center_1st, (0.5*figure_width, "kpc"))
+                                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                                p61 = ProfilePlot(sp_1st, ("gas", "density"),  ("gas", "cell_mass"), weight_field=None, n_bins=50, x_log=True, accumulation=False)
+                                                p61.set_log("cell_mass", True)
+                                                p61.set_xlim(1e-29, 1e-21)
+                                                density_DF_1st_xs[time].append(p61.profiles[0].x.in_units('g/cm**3').d)
+                                                density_DF_1st_profiles[time].append(p61.profiles[0]["cell_mass"].in_units('Msun').d)
+                                        else:
+                                                def _Density_2(field, data):
+                                                        return data[(PartType_Gas_to_use, "Density")].in_units('g/cm**3')
+                                                pf_1st.add_field((PartType_Gas_to_use, "Density_2"), function=_Density_2, take_log=True, particle_type=False, display_name="Density", units="g/cm**3")
+                                                def _Mass_2(field, data):
+                                                        return data[(PartType_Gas_to_use, MassType_to_use)].in_units('Msun')
+                                                pf_1st.add_field((PartType_Gas_to_use, "Mass_2"), function=_Mass_2, take_log=True, particle_type=False, display_name="Mass", units="Msun")
+                                                p61 = ProfilePlot(sp_1st, (PartType_Gas_to_use, "Density_2"),  (PartType_Gas_to_use, "Mass_2"), weight_field=None, n_bins=50, x_log=True, accumulation=False)
+                                                p61.set_log("Mass_2", True)
+                                                p61.set_xlim(1e-29, 1e-21)
+                                                density_DF_1st_xs[time].append(p61.profiles[0].x.in_units('g/cm**3').d)
+                                                density_DF_1st_profiles[time].append(p61.profiles[0]["Mass_2"].in_units('Msun').d)
+                                else:
+                                        print("This won't work; consider setting dataset_num to 2...")
+                                        continue
+
+                # CYLINDRICAL RADIUS DF + RADIALLY-BINNED GAS SURFACE DENSITY
+                if draw_radius_DF == 1:
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        sp.set_field_parameter("normal", disk_normal_vector)
+                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                p7 = ProfilePlot(sp, ("index", "cylindrical_r"),  ("gas", "cell_mass"), weight_field=None, n_bins=50, x_log=False, accumulation=False)
+                                p7.set_log("cell_mass", True)
+                                p7.set_log("cylindrical_r", False)
+                                p7.set_unit("cylindrical_r", 'kpc')
+                                p7.set_xlim(1e-3, 15)
+                                radius_DF_xs[time].append(p7.profiles[0].x.in_units('kpc').d)
+                                radius_DF_profiles[time].append(p7.profiles[0]["cell_mass"].in_units('Msun').d)
+                        else:
+                                p7 = ProfilePlot(sp, (PartType_Gas_to_use, "particle_position_cylindrical_radius"),  (PartType_Gas_to_use, "Mass_2"), weight_field=None, n_bins=50, x_log=False, accumulation=False)
+                                p7.set_log("Mass_2", True)
+                                p7.set_log("particle_position_cylindrical_radius", False)
+                                p7.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                p7.set_xlim(1e-3, 15)
+                                radius_DF_xs[time].append(p7.profiles[0].x.in_units('kpc').d)
+                                radius_DF_profiles[time].append(p7.profiles[0]["Mass_2"].in_units('Msun').d)
+
+                # CYLINDRICAL RADIUS DF + RADIALLY-BINNED SURFACE DENSITY FOR NEW STARS
+                if draw_star_radius_DF >= 1 and time != 0:
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        sp.set_field_parameter("normal", disk_normal_vector)
+                        pf.field_info[(PartType_Star_to_use, "particle_mass")].take_log = True
+                        pf.field_info[(PartType_Star_to_use, "particle_mass")].output_units = 'code_mass' # this turned out to be crucial!; check output_units above
+                        pf.field_info[(PartType_Star_to_use, "particle_position_cylindrical_radius")].take_log = False
+                        p71 = ProfilePlot(sp, (PartType_Star_to_use, "particle_position_cylindrical_radius"),  (PartType_Star_to_use, "particle_mass"), weight_field=None, n_bins=50, x_log=False, accumulation=False)
+                        p71.set_unit("particle_position_cylindrical_radius", 'kpc')
+                        p71.set_xlim(1e-3, 15)
+                        star_radius_DF_xs[time].append(p71.profiles[0].x.in_units('kpc').d)
+                        star_radius_DF_profiles[time].append(p71.profiles[0]["particle_mass"].in_units('Msun').d)
+
+                        # Add RADIALLY-BINNED SFR SURFACE DENSITY PROFILE if requested (SFR estimated using stars younger than 20 Myrs old)
+                        if draw_star_radius_DF == 2 and time != 0:
+                                def _particle_mass_young_stars_star_radius_DF(field, data):
+                                        trans = np.zeros(data[(PartType_StarBeforeFiltered_to_use, "particle_mass")].shape)
+                                        ind = np.where(data[(PartType_StarBeforeFiltered_to_use, FormationTimeType_to_use)].in_units('Myr') > (pf.current_time.in_units('Myr').d - young_star_cutoff_star_radius_DF))
+                                        trans[ind] = data[(PartType_StarBeforeFiltered_to_use, "particle_mass")][ind].in_units('code_mass')
+                                        return data.ds.arr(trans, "code_mass").in_base(data.ds.unit_system.name)
+                                pf.add_field((PartType_StarBeforeFiltered_to_use, "particle_mass_young_stars_star_radius_DF"), function=_particle_mass_young_stars_star_radius_DF, \
+                                                     display_name="Young Stellar Mass", units='code_mass', particle_type=True, take_log=True)
+                                pf.add_particle_filter(PartType_Star_to_use)
+
+                                pf.field_info[(PartType_Star_to_use, "particle_mass_young_stars_star_radius_DF")].take_log = True
+                                pf.field_info[(PartType_Star_to_use, "particle_mass_young_stars_star_radius_DF")].output_units = 'code_mass' # this turned out to be crucial!; check output_units above
+                                pf.field_info[(PartType_Star_to_use, "particle_position_cylindrical_radius")].take_log = False
+                                p72 = ProfilePlot(sp, (PartType_Star_to_use, "particle_position_cylindrical_radius"), (PartType_Star_to_use, "particle_mass_young_stars_star_radius_DF"), \
+                                                          weight_field=None, n_bins=50, x_log=False, accumulation=False)
+                                p72.set_unit("particle_position_cylindrical_radius", 'kpc')
+                                p72.set_xlim(1e-3, 15)
+                                sfr_radius_DF_xs[time].append(p72.profiles[0].x.in_units('kpc').d)
+                                sfr_radius_DF_profiles[time].append(p72.profiles[0]["particle_mass_young_stars_star_radius_DF"].in_units('Msun').d/young_star_cutoff_star_radius_DF/1e6) # in Msun/yr
+
+                # VERTICAL HEIGHT DF + VERTICALLY-BINNED GAS SURFACE DENSITY
+                if draw_height_DF == 1:
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        sp.set_field_parameter("normal", disk_normal_vector)
+                        if codes[code] == "ART-I" or codes[code] == "ART-II" or codes[code] == "ENZO"  or codes[code] == "RAMSES":
+                                p8 = ProfilePlot(sp, ("index", "cylindrical_z_abs"),  ("gas", "cell_mass"), weight_field=None, n_bins=10, x_log=False, accumulation=False)
+                                p8.set_log("cell_mass", True)
+                                p8.set_log("cylindrical_z_abs", False)
+                                p8.set_unit("cylindrical_z_abs", 'kpc')
+                                p8.set_xlim(1e-3, 1.4)
+                                height_DF_xs[time].append(p8.profiles[0].x.in_units('kpc').d)
+                                height_DF_profiles[time].append(p8.profiles[0]["cell_mass"].in_units('Msun').d)
+                        else:
+                                p8 = ProfilePlot(sp, (PartType_Gas_to_use, "particle_position_cylindrical_z_abs"),  (PartType_Gas_to_use, "Mass_2"), weight_field=None, n_bins=10, x_log=False, accumulation=False)
+                                p8.set_log("Mass_2", True)
+                                p8.set_log("particle_position_cylindrical_z_abs", False)
+                                p8.set_unit("particle_position_cylindrical_z_abs", 'kpc')
+                                p8.set_xlim(1e-3, 1.4)
+                                height_DF_xs[time].append(p8.profiles[0].x.in_units('kpc').d)
+                                height_DF_profiles[time].append(p8.profiles[0]["Mass_2"].in_units('Msun').d)
+
+                # STAR FORMATION RATE + CUMULATIVE STELLAR MASS GROWTH IN TIME
+                if draw_SFR >= 1 and time != 0:
+                        from yt.units.dimensions import length # Below are tricks to make StarFormationRate() work, particularly with "volume" argument, as it currently works only with comoving datasets
+                        pf.unit_registry.add('pccm', pf.unit_registry.lut['pc'][0], length, "\\rm{pc}/(1+z)")
+                        pf.hubble_constant = 0.71; pf.omega_lambda = 0.73; pf.omega_matter = 0.27; pf.omega_curvature = 0.0
+
+                        sp = pf.sphere(center, (0.5*figure_width, "kpc"))
+                        draw_SFR_mass = sp[(PartType_Star_to_use, "particle_mass")].in_units('Msun')
+                        draw_SFR_ct   = sp[(PartType_Star_to_use, FormationTimeType_to_use)].in_units('Myr')
+                        sfr = StarFormationRate(pf, star_mass = draw_SFR_mass, star_creation_time = draw_SFR_ct,
+                                                volume = sp.volume(), bins = 25) # 25 bins hardcoded; see: http://yt-project.org/docs/dev/analyzing/analysis_modules/star_analysis.html
+
+                        sfr_ts[time].append(sfr.time.in_units('Myr')) # in Myr
+                        sfr_cum_masses[time].append(sfr.Msol_cumulative) # in Msun
+                        sfr_sfrs[time].append(sfr.Msol_yr) # in Msun/yr
+
+                # DENSITY ALONG THE ORTHO-RAY OBJECT CUTTING THROUGH THE CENTER
+                if draw_cut_through == 1:
+                        ray = pf.ortho_ray(2, (center[0].in_units('code_length'), center[1].in_units('code_length'))) # see: http://yt-project.org/doc/visualizing/manual_plotting.html#line-plots
+                        srt = np.argsort(ray['z'])
+                        cut_through_zs[time].append(np.array(ray['z'][srt].in_units('kpc').d - center[2].in_units('kpc').d))
+                        cut_through_zvalues[time].append(np.array(ray[("gas", "density")][srt].in_units('g/cm**3').d))
+
+                        ray = pf.ortho_ray(0, (center[1].in_units('code_length'), center[2].in_units('code_length')))
+                        srt = np.argsort(ray['x'])
+                        cut_through_xs[time].append(np.array(ray['x'][srt].in_units('kpc').d - center[2].in_units('kpc').d))
+                        cut_through_xvalues[time].append(np.array(ray[("gas", "density")][srt].in_units('g/cm**3').d))
+
+        ####################################
+        #        POST-ANALYSIS STEPS       #
+        ####################################
+
+        # SAVE FIGURES
+        if draw_density_map == 1:
+                fig_density_map[time].savefig("Sigma_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_temperature_map == 1:
+                fig_temperature_map[time].savefig("Temp_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_cellsize_map == 1 or draw_cellsize_map == 3:
+                fig_cellsize_map[time].savefig("Cell_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_cellsize_map == 2 or draw_cellsize_map == 3:
+                fig_cellsize_map_2[time].savefig("Resolution_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_elevation_map == 1:
+                fig_elevation_map[time].savefig("Elevation_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_metal_map >= 1:
+                fig_metal_map[time].savefig("Metal_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_zvel_map == 1:
+                fig_zvel_map[time].savefig("z-vel_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_star_map == 1 and time != 0:
+                fig_star_map[time].savefig("Star_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_star_clump_stats >= 1 and time != 0:
+                if draw_star_clump_stats >= 2:
+                        fig_star_map_2[time].savefig("Star_with_clumps_HOP_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        fig_star_map_3[time].savefig("Star_with_clumps_FOF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                if draw_star_clump_stats == 3:
+                        pf_clump_ref = load_dataset(2, 1, 0, ['GIZMO'],
+                                                    [['dummy', '/lustre/ki/pfs/mornkr/080112_CHaRGe/pfs-hyades/AGORA-DISK-repository/Grackle+SF+ThermalFbck/GIZMO/AGORA_disk_second_ps1.5/snapshot_temp_100_last']])
+                        PartType_Star_to_use = "Stars"
+                        if os.path.exists("./halo_catalogs/hop_%s_%05d_high_floor/hop_%s_%05d_high_floor.0.h5" % ('GIZMO', 500, 'GIZMO', 500)) == False:
+                                hc_clump_ref = HaloCatalog(data_ds=pf_clump_ref, finder_method='hop', output_dir="./halo_catalogs/hop_%s_%05d_high_floor" % ('GIZMO', 500), \
+                                                         finder_kwargs={'threshold': 2e8, 'dm_only': False, 'ptype': PartType_Star_to_use})
+                                hc_clump_ref.add_filter('quantity_value', 'particle_mass', '>', 2.6e6, 'Msun')
+                                hc_clump_ref.add_filter('quantity_value', 'particle_mass', '<', 2.6e8, 'Msun')
+                                hc_clump_ref.create()
+                        halo_ds_clump_ref = load("./halo_catalogs/hop_%s_%05d_high_floor/hop_%s_%05d_high_floor.0.h5" % ('GIZMO', 500, 'GIZMO', 500))
+                        hc_clump_ref = HaloCatalog(halos_ds=halo_ds_clump_ref, output_dir="./halo_catalogs/hop_%s_%05d_high_floor" % ('GIZMO', 500))
+                        hc_clump_ref.load()
+
+                        halo_ad_clump_ref = hc_clump_ref.halos_ds.all_data()
+                        star_clump_masses_hop_ref.append(np.log10(halo_ad_clump_ref['particle_mass'][:].in_units("Msun")))
+
+                        if os.path.exists("./halo_catalogs/fof_%s_%05d_high_floor/fof_%s_%05d_high_floor.0.h5" % ('GIZMO', 500, 'GIZMO', 500)) == False:
+                                hc2_clump_ref = HaloCatalog(data_ds=pf_clump_ref, finder_method='fof', output_dir="./halo_catalogs/fof_%s_%05d_high_floor" %  ('GIZMO', 500), \
+                                                          finder_kwargs={'link': 0.0025, 'dm_only': False, 'ptype': PartType_Star_to_use})
+                                hc2_clump_ref.add_filter('quantity_value', 'particle_mass', '>', 2.6e6, 'Msun')
+                                hc2_clump_ref.add_filter('quantity_value', 'particle_mass', '<', 2.6e8, 'Msun')
+                                hc2_clump_ref.create()
+                        halo_ds2_clump_ref = load("./halo_catalogs/fof_%s_%05d_high_floor/fof_%s_%05d_high_floor.0.h5" % ('GIZMO', 500, 'GIZMO', 500))
+                        hc2_clump_ref = HaloCatalog(halos_ds=halo_ds2_clump_ref, output_dir="./halo_catalogs/fof_%s_%05d_high_floor" % ('GIZMO', 500))
+                        hc2_clump_ref.load()
+
+                        halo_ad2_clump_ref = hc2_clump_ref.halos_ds.all_data()
+                        star_clump_masses_fof_ref.append(np.log10(halo_ad2_clump_ref['particle_mass'][:].in_units("Msun")))
+                plt.clf()
+                fig = plt.figure(figsize=(8, 4))
+                gridspec.GridSpec(1, 2)
+                for star_clump_stats_i in range(1,3,1):
+                        codes_plotted = []
+#			plt.subplot(1,2,star_clump_stats_i, aspect=0.25)
+                        plt.subplot2grid((1, 2), (0, star_clump_stats_i-1))
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                if (star_clump_stats_i == 1 and (codes[code] == "ART-I" or codes[code] == "ART-II"  or codes[code] == "ENZO" or codes[code] == "RAMSES")) or \
+                                   (star_clump_stats_i == 2 and (codes[code] == "CHANGA" or codes[code] == "GASOLINE" or codes[code] == "GADGET-3" or codes[code] == "GEAR" or codes[code] == "GIZMO" or codes[code] == "SWIFT")):
+                                        hist = np.histogram(star_clump_masses_hop[time][code], bins=10, range=(6., 8.5))
+                                        dbin = 0.5*(hist[1][1] - hist[1][0])
+                                        lines = plt.plot(hist[1][:-1]+dbin, hist[0], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                                        codes_plotted.append(codes[code])
+                        plt.xlim(6, 8.5)
+                        plt.ylim(-0.1, 15)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{log[Newly\ Formed\ Stellar\ Clump\ Mass\ (M_{\odot})]}$")
+                        if star_clump_stats_i == 1:
+                                plt.ylabel("$\mathrm{Stellar\ Clump\ Counts, \ \ N_{clump}(M)}$")
+                        plt.legend(codes_plotted, loc=1, frameon=True, ncol=1, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='xx-small')
+                plt.savefig("star_clump_stats_HOP_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+                # Reiterate for cumulative plots
+                fig = plt.figure(figsize=(8, 4))
+                gridspec.GridSpec(1, 2)
+                for star_clump_stats_i in range(1,3,1):
+                        codes_plotted = []
+#			plt.subplot(1,2,star_clump_stats_i, aspect=0.15)
+                        plt.subplot2grid((1, 2), (0, star_clump_stats_i-1))
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                if (star_clump_stats_i == 1 and (codes[code] == "ART-I" or codes[code] == "ART-II"  or codes[code] == "ENZO" or codes[code] == "RAMSES")) or \
+                                   (star_clump_stats_i == 2 and (codes[code] == "CHANGA" or codes[code] == "GASOLINE" or codes[code] == "GADGET-3" or codes[code] == "GEAR" or codes[code] == "GIZMO" or codes[code] == "SWIFT")):
+                                        hist = np.histogram(star_clump_masses_hop[time][code], bins=10, range=(6., 8.5))
+                                        dbin = 0.5*(hist[1][1] - hist[1][0])
+                                        lines = plt.plot(hist[1][:-1]+dbin, np.cumsum(hist[0][::-1])[::-1], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], \
+                                                                 marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                                        codes_plotted.append(codes[code])
+                        if draw_star_clump_stats == 3 and star_clump_stats_i == 2:
+                                hist_clump_ref = np.histogram(star_clump_masses_hop_ref, bins=10, range=(6., 8.5))
+                                dbin = 0.5*(hist[1][1] - hist[1][0])
+                                lines = plt.plot(hist_clump_ref[1][:-1]+dbin, np.cumsum(hist_clump_ref[0][::-1])[::-1], color='k', linestyle='--', marker='*', markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                                codes_plotted.append('GIZMO-ps2')
+                        plt.xlim(6, 8.5)
+#			plt.ylim(-0.1, 30)
+                        plt.ylim(0.9, 50)
+                        plt.semilogy()
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{log[Newly\ Formed\ Stellar\ Clump\ Mass\ (M_{\odot})]}$")
+                        if star_clump_stats_i == 1:
+                                plt.ylabel("$\mathrm{Cumulative\ Clump\ Counts, \ \ N_{clump}(>M)}}$")
+                        plt.legend(codes_plotted, loc=1, frameon=True, ncol=1, fancybox=True, labelspacing=0.15, borderpad=0.3)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='xx-small')
+                plt.savefig("star_clump_stats_HOP_cumulative_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+
+                fig = plt.figure(figsize=(8, 4))
+                gridspec.GridSpec(1, 2)
+                for star_clump_stats_i in range(1,3,1):
+                        codes_plotted = []
+#			plt.subplot(1,2,star_clump_stats_i, aspect=0.25)
+                        plt.subplot2grid((1, 2), (0, star_clump_stats_i-1))
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                if (star_clump_stats_i == 1 and (codes[code] == "ART-I" or codes[code] == "ART-II"  or codes[code] == "ENZO" or codes[code] == "RAMSES")) or \
+                                   (star_clump_stats_i == 2 and (codes[code] == "CHANGA" or codes[code] == "GASOLINE" or codes[code] == "GADGET-3" or codes[code] == "GEAR" or codes[code] == "GIZMO" or codes[code] == "SWIFT")):
+                                        hist = np.histogram(star_clump_masses_fof[time][code], bins=10, range=(6., 8.5))
+                                        dbin = 0.5*(hist[1][1] - hist[1][0])
+                                        lines = plt.plot(hist[1][:-1]+dbin, hist[0], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                                        codes_plotted.append(codes[code])
+                        plt.xlim(6, 8.5)
+                        plt.ylim(-0.1, 15)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{log[Newly\ Formed\ Stellar\ Clump\ Mass\ (M_{\odot})]}$")
+                        if star_clump_stats_i == 1:
+                                plt.ylabel("$\mathrm{Stellar\ Clump\ Counts, \ \ N_{clump}(M)}$")
+                        plt.legend(codes_plotted, loc=1, frameon=True, ncol=1, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='xx-small')
+                plt.savefig("star_clump_stats_FOF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+                # Reiterate for cumulative plots
+                fig = plt.figure(figsize=(8, 4))
+                gridspec.GridSpec(1, 2)
+                for star_clump_stats_i in range(1,3,1):
+                        codes_plotted = []
+#			plt.subplot(1,2,star_clump_stats_i, aspect=0.15)
+                        plt.subplot2grid((1, 2), (0, star_clump_stats_i-1))
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                if (star_clump_stats_i == 1 and (codes[code] == "ART-I" or codes[code] == "ART-II"  or codes[code] == "ENZO" or codes[code] == "RAMSES")) or \
+                                   (star_clump_stats_i == 2 and (codes[code] == "CHANGA" or codes[code] == "GASOLINE" or codes[code] == "GADGET-3" or codes[code] == "GEAR" or codes[code] == "GIZMO" or codes[code] == "SWIFT")):
+                                        hist = np.histogram(star_clump_masses_fof[time][code], bins=10, range=(6., 8.5))
+                                        dbin = 0.5*(hist[1][1] - hist[1][0])
+                                        lines = plt.plot(hist[1][:-1]+dbin, np.cumsum(hist[0][::-1])[::-1], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], \
+                                                                 marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                                        codes_plotted.append(codes[code])
+                        if draw_star_clump_stats == 3 and star_clump_stats_i == 2:
+                                hist_clump_ref = np.histogram(star_clump_masses_fof_ref, bins=10, range=(6., 8.5))
+                                dbin = 0.5*(hist[1][1] - hist[1][0])
+                                lines = plt.plot(hist_clump_ref[1][:-1]+dbin, np.cumsum(hist_clump_ref[0][::-1])[::-1], color='k', linestyle='--', marker='*', markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                                codes_plotted.append('GIZMO-ps2')
+                        plt.xlim(6, 8.5)
+#			plt.ylim(-0.1, 30)
+                        plt.ylim(0.9, 50)
+                        plt.semilogy()
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{log[Newly\ Formed\ Stellar\ Clump\ Mass\ (M_{\odot})]}$")
+                        if star_clump_stats_i == 1:
+                                plt.ylabel("$\mathrm{Cumulative\ Clump\ Counts, \ \ N_{clump}(>M)}}$")
+                        plt.legend(codes_plotted, loc=1, frameon=True, ncol=1, fancybox=True, labelspacing=0.15, borderpad=0.3)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='xx-small')
+                plt.savefig("star_clump_stats_FOF_cumulative_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+        if draw_SFR_map >= 1 and time != 0:
+                fig_degr_density_map[time].savefig("degraded_Sigma_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                fig_degr_sfr_map[time].savefig("degraded_SFR_map_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                if draw_SFR_map == 2 and time != 0:
+                        # If requested, draw the local K-S plot using the degraded maps created above
+                        plt.clf()
+#			plt.subplot(111)
+                        fig = plt.figure(figsize=(8, 7))
+                        Bigiel_surface_density = []
+                        Bigiel_sfr_surface_density = []
+                        for code in range(len(codes)):
+                                # Remove bins where SFR surface density is zero
+                                KS_x = np.array(surf_dens_SFR_map[time][code])
+                                KS_x[np.where(np.array(sfr_surf_dens_SFR_map[time][code]) < 1e-10)] = 0
+                                KS_x = np.log10(KS_x)
+                                KS_y = np.log10(np.array(sfr_surf_dens_SFR_map[time][code]))
+                                KS_x = KS_x[~np.isinf(KS_x)]
+                                KS_y = KS_y[~np.isinf(KS_y)]
+                                if codes[code] == "CHANGA":
+                                        plt.scatter(KS_x, KS_y, color='k', edgecolor='k', s=20, linewidth=0.7, marker=marker_names[code], alpha=0.1)
+                                # Draw a 80% contour rather than scattering all the datapoints; see http://stackoverflow.com/questions/19390320/scatterplot-contours-in-matplotlib
+                                Gaussian_density_estimation_nbins = 20
+                                kernel = kde.gaussian_kde(np.vstack([KS_x, KS_y]))
+                                xi, yi = np.mgrid[KS_x.min():KS_x.max():Gaussian_density_estimation_nbins*1j, KS_y.min():KS_y.max():Gaussian_density_estimation_nbins*1j]
+                                zi = np.reshape(kernel(np.vstack([xi.flatten(), yi.flatten()])), xi.shape)
+                                contours = plt.contour(xi, yi, zi, np.array([0.2]), colors=color_names[code], linewidths=1.2, alpha=1.0)    # 80%    percentile contour
+#				contours = plt.contour(xi, yi, zi, np.array([0.3173]), colors=color_names[code], linewidths=1.2, alpha=1.0) # 68.27% percentile contour (1-sigma)
+                                contours.collections[0].set_label(codes[code]) # setting names for legend
+                                plt.clabel(contours, fmt=codes[code], inline=True, fontsize=7) # setting labels
+                        plt.xlim(0, 3)
+                        plt.ylim(-4, 1)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{log[Gas\ Surface\ Density\ (M_{\odot}/pc^2)]}$")
+                        plt.ylabel("$\mathrm{log[Star\ Formation\ Rate\ Surface\ Density\ (M_{\odot}/yr/kpc^2)]}$")
+                        plt.legend(loc=2, frameon=True, ncol=2, fancybox=True) # note difference from others since we want the legends to list contours, not scattered points
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+                        t = np.arange(-2, 5, 0.01)
+                        for line in import_text("./Bigieletal2008_Fig8_Contour.txt", " "):
+                                Bigiel_surface_density.append(float(line[0]) + np.log10(1.36)) # for the factor 1.36, see Section 2.3.1 of Bigiel et al. 2008
+                                Bigiel_sfr_surface_density.append(float(line[1]))
+                        plt.fill(Bigiel_surface_density, Bigiel_sfr_surface_density, fill=True, color='b', alpha = 0.1, hatch='\\') # contour by Bigiel et al. 2008 (Fig 8), or Feldmann et al. 2012 (Fig 1)
+                        plt.plot(t, 1.37*t - 3.78, 'k--', linewidth = 2, alpha = 0.7) # observational fit by Kennicutt et al. 2007
+#			plt.axhline(y = np.log10(8.593e4/young_star_cutoff_SFR_map/1e6/(float(aperture_size_SFR_map)/1000.)**2),
+#                                   color='k', linestyle ='-.', linewidth=1, alpha=0.7) # SFR surface density cutoff due to limited star particle mass resolution
+                        plt.savefig("K-S_local_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+        if draw_PDF >= 1:
+                if draw_PDF == 3 and time != 0:
+                        # If requested, find a 1D profile line from a specific snapshot as a reference (in this case ENZO or CHANGA noSF run at 500 Myr), then we add it to all panels
+                        # pf_profile_ref = load_dataset(1, 1, 0, ['ENZO'], [['dummy', file_location[0]+'ENZO/DD0100/DD0100']])
+                        pf_profile_ref = load_dataset(1, 1, 0, ['CHANGA'], [['dummy', file_location[0]+'CHANGA/disklow/disklow.000500']])
+                        def _Density_2(field, data):
+                                return data[(PartType_Gas_to_use, "Density")].in_units('g/cm**3')
+                        pf_profile_ref.add_field((PartType_Gas_to_use, "Density_2"), function=_Density_2, take_log=True, particle_type=False, display_name="Density", units="g/cm**3")
+                        def _Temperature_2(field, data):
+                                return data[(PartType_Gas_to_use, "Temperature")].in_units('K')
+                        pf_profile_ref.add_field((PartType_Gas_to_use, "Temperature_2"), function=_Temperature_2, take_log=True, particle_type=False, display_name="Temperature", units="K")
+                        def _Mass_2(field, data):
+                                return data[(PartType_Gas_to_use, MassType_to_use)].in_units('Msun')
+                        pf_profile_ref.add_field((PartType_Gas_to_use, "Mass_2"), function=_Mass_2, take_log=True, particle_type=False, display_name="Mass", units="Msun")
+
+                        v, cen = pf_profile_ref.h.find_max(("gas", "density"))
+                        sp = pf_profile_ref.sphere(cen, (30.0, "kpc"))
+                        cen2 = sp.quantities.center_of_mass(use_gas=True, use_particles=False).in_units("kpc")
+                        sp2 = pf_profile_ref.sphere(cen2, (1.0, "kpc"))
+                        cen3 = sp2.quantities.max_location(("gas", "density"))
+                        center_profile_ref = pf_profile_ref.arr([cen3[1].d, cen3[2].d, cen3[3].d], 'code_length')
+                        if yt_version_pre_3_2_3 == 1:
+                                center_profile_ref = pf_profile_ref.arr([cen3[2].d, cen3[3].d, cen3[4].d], 'code_length')
+                        sp_profile_ref = pf_profile_ref.sphere(center_profile_ref, (0.5*figure_width, "kpc"))
+
+                        # p35 = ProfilePlot(sp_profile_ref, ("gas", "density"),  ("gas", "temperature"), weight_field=("gas", "cell_mass"), n_bins=30)
+                        # p35.set_xlim(1e-29, 1e-21)
+                        # p35.set_ylim("temperature", 10, 1e7)
+                        p35 = ProfilePlot(sp_profile_ref, (PartType_Gas_to_use, "Density_2"), (PartType_Gas_to_use, "Temperature_2"), weight_field=(PartType_Gas_to_use, "Mass_2"), n_bins=30)
+                        p35.set_xlim(1e-26, 1e-21)
+                        p35.set_ylim("Temperature_2", 10, 1e7)
+                        for code in range(len(codes)):
+#				line_profile_ref = ln.Line2D(p35.profiles[0].x.in_units('g/cm**3'), p35.profiles[0]["temperature"].in_units('K'), linestyle="--", linewidth=2, color='k', alpha=0.7)
+                                line_profile_ref = ln.Line2D(p35.profiles[0].x.in_units('g/cm**3'), p35.profiles[0]["Temperature_2"].in_units('K'), linestyle="--", linewidth=2, color='k', alpha=0.7)
+                                grid_PDF[time][code].axes.add_line(line_profile_ref)
+                fig_PDF[time].savefig("PDF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_pos_vel_PDF >= 1:
+                fig_pos_vel_PDF[time].savefig("pos_vel_PDF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                if draw_pos_vel_PDF >= 2 and time != 0:
+                        plt.clf()
+#			plt.subplot(111, aspect=0.04)
+                        fig = plt.figure(figsize=(8, 8))
+                        gridspec.GridSpec(4, 1)
+                        plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                lines = plt.plot(pos_vel_xs[time][code], pos_vel_profiles[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(0, 270)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel("$\mathrm{Rotational\ Velocity\ (km/s)}$", fontsize='large')
+                        plt.legend(codes, loc=4, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+
+                        plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                        ave_profiles = np.mean(np.array(pos_vel_profiles[time]), axis=0)
+                        for code in range(len(codes)):
+                                lines = plt.plot(pos_vel_xs[time][code], (pos_vel_profiles[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(-0.5, 0.5)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{v} - \mathrm{\bar v})/\mathrm{\bar v}$", fontsize='large')
+                        if add_mean_fractional_dispersion == 1:
+                                mean_fractional_dispersion = (np.std(np.array(pos_vel_profiles[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < pos_vel_xs[time][0]) & (pos_vel_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                                mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                                plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.35, 0.08), size=10, xycoords='axes fraction')
+#				plt.text(12, 0.3, "mean dispersion = %.3f or %.3f dex " % (mean_fractional_dispersion, mean_fractional_dispersion_in_dex), ha="center", va="center", size=8, bbox=dict(boxstyle="round", fc="w", ec="0.5", alpha=0.9))
+                        plt.savefig("pos_vel_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+                if draw_pos_vel_PDF >= 3 and time != 0:
+                        plt.clf()
+#			plt.subplot(111, aspect=0.064)
+                        fig = plt.figure(figsize=(8, 10))
+                        gridspec.GridSpec(5, 1)
+                        plt.subplot2grid((5, 1), (0, 0), rowspan=3)
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                lines = plt.plot(pos_disp_xs[time][code], pos_disp_profiles[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(0, 170)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel("$\mathrm{Velocity\ Dispersion\ (km/s)}$", fontsize='large')
+                        plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+
+                        plt.subplot2grid((5, 1), (3, 0), rowspan=1)
+                        ave_profiles = np.mean(np.array(pos_disp_profiles[time]), axis=0)
+                        for code in range(len(codes)):
+                                lines = plt.plot(pos_disp_xs[time][code], (pos_disp_profiles[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(-1.0, 1.0)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{\sigma} - \mathrm{\bar \sigma})/\mathrm{\bar \sigma}$", fontsize='large')
+                        if add_mean_fractional_dispersion == 1:
+                                mean_fractional_dispersion = (np.std(np.array(pos_disp_profiles[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < pos_disp_xs[time][0]) & (pos_disp_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                                mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                                plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.35, 0.08), size=10, xycoords='axes fraction')
+                        plt.savefig("pos_disp_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.subplot2grid((5, 1), (4, 0), rowspan=1)
+                        for code in range(len(codes)):
+                                lines = plt.plot(pos_disp_xs[time][code], pos_disp_vert_profiles[time][code]/pos_disp_profiles[time][code], color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(0.0, 1.0)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Ratio,}\/\mathrm{\sigma}_{\perp}/\mathrm{\sigma}$", fontsize='large')
+                        plt.savefig("pos_disp_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+                if draw_pos_vel_PDF == 4 and time != 0:
+                        plt.clf()
+#			plt.subplot(111, aspect=0.064)
+                        fig = plt.figure(figsize=(8, 8))
+                        gridspec.GridSpec(4, 1)
+                        plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                lines = plt.plot(pos_disp_vert_xs[time][code], pos_disp_vert_profiles[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(0, 170)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel("$\mathrm{Vertical\ Velocity\ Dispersion\ (km/s)}$", fontsize='large')
+                        plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+
+                        plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                        ave_profiles = np.mean(np.array(pos_disp_vert_profiles[time]), axis=0)
+                        for code in range(len(codes)):
+                                lines = plt.plot(pos_disp_vert_xs[time][code], (pos_disp_vert_profiles[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(-1.0, 1.0)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{\sigma} - \mathrm{\bar \sigma})/\mathrm{\bar \sigma}$", fontsize='large')
+                        if add_mean_fractional_dispersion == 1:
+                                mean_fractional_dispersion = (np.std(np.array(pos_disp_vert_profiles[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < pos_disp_vert_xs[time][0]) & (pos_disp_vert_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                                mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                                plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.35, 0.08), size=10, xycoords='axes fraction')
+                        plt.savefig("pos_disp_vert_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+        if draw_star_pos_vel_PDF >= 1 and time != 0:
+                fig_star_pos_vel_PDF[time].savefig("star_pos_vel_PDF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                if draw_star_pos_vel_PDF >= 2 and time != 0:
+                        plt.clf()
+#			plt.subplot(111, aspect=0.04)
+                        fig = plt.figure(figsize=(8, 8))
+                        gridspec.GridSpec(4, 1)
+                        plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                lines = plt.plot(star_pos_vel_xs[time][code], star_pos_vel_profiles[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(0, 270)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel("$\mathrm{Rotational\ Velocity\ (km/s)}$", fontsize='large')
+                        plt.legend(codes, loc=4, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+
+                        plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                        ave_profiles = np.mean(np.array(star_pos_vel_profiles[time]), axis=0)
+                        for code in range(len(codes)):
+                                lines = plt.plot(star_pos_vel_xs[time][code], (star_pos_vel_profiles[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(-0.5, 0.5)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{v} - \mathrm{\bar v})/\mathrm{\bar v}$", fontsize='large')
+                        if add_mean_fractional_dispersion == 1:
+                                mean_fractional_dispersion = (np.std(np.array(star_pos_vel_profiles[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < star_pos_vel_xs[time][0]) & (star_pos_vel_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                                mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                                plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.35, 0.08), size=10, xycoords='axes fraction')
+                        plt.savefig("star_pos_vel_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+                if draw_star_pos_vel_PDF >= 3 and time != 0:
+                        plt.clf()
+#			plt.subplot(111, aspect=0.064)
+                        fig = plt.figure(figsize=(8, 10))
+                        gridspec.GridSpec(5, 1)
+                        plt.subplot2grid((5, 1), (0, 0), rowspan=3)
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                lines = plt.plot(star_pos_disp_xs[time][code], star_pos_disp_profiles[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(0, 170)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel("$\mathrm{Velocity\ Dispersion\ (km/s)}$", fontsize='large')
+                        plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+
+                        plt.subplot2grid((5, 1), (3, 0), rowspan=1)
+                        ave_profiles = np.mean(np.array(star_pos_disp_profiles[time]), axis=0)
+                        for code in range(len(codes)):
+                                lines = plt.plot(star_pos_disp_xs[time][code], (star_pos_disp_profiles[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(-1.0, 1.0)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{\sigma} - \mathrm{\bar \sigma})/\mathrm{\bar \sigma}$", fontsize='large')
+                        if add_mean_fractional_dispersion == 1:
+                                mean_fractional_dispersion = (np.std(np.array(star_pos_disp_profiles[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < star_pos_disp_xs[time][0]) & (star_pos_disp_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                                mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                                plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.35, 0.08), size=10, xycoords='axes fraction')
+                        plt.savefig("star_pos_disp_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.subplot2grid((5, 1), (4, 0), rowspan=1)
+                        for code in range(len(codes)):
+                                lines = plt.plot(star_pos_disp_xs[time][code], star_pos_disp_vert_profiles[time][code]/star_pos_disp_profiles[time][code], color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(0.0, 1.0)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Ratio,}\/\mathrm{\sigma}_{\perp}/\mathrm{\sigma}$", fontsize='large')
+                        plt.savefig("star_pos_disp_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+                if draw_star_pos_vel_PDF == 4 and time != 0:
+                        plt.clf()
+#			plt.subplot(111, aspect=0.064)
+                        fig = plt.figure(figsize=(8, 8))
+                        gridspec.GridSpec(4, 1)
+                        plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                lines = plt.plot(star_pos_disp_vert_xs[time][code], star_pos_disp_vert_profiles[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(0, 170)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel("$\mathrm{Vertical\ Velocity\ Dispersion\ (km/s)}$", fontsize='large')
+                        plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+
+                        plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                        ave_profiles = np.mean(np.array(star_pos_disp_vert_profiles[time]), axis=0)
+                        for code in range(len(codes)):
+                                lines = plt.plot(star_pos_disp_vert_xs[time][code], (star_pos_disp_vert_profiles[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(-1.0, 1.0)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{\sigma} - \mathrm{\bar \sigma})/\mathrm{\bar \sigma}$", fontsize='large')
+                        if add_mean_fractional_dispersion == 1:
+                                mean_fractional_dispersion = (np.std(np.array(star_pos_disp_vert_profiles[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < star_pos_disp_vert_xs[time][0]) & (star_pos_disp_vert_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                                mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                                plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.35, 0.08), size=10, xycoords='axes fraction')
+                        plt.savefig("star_pos_disp_vert_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+        if draw_rad_height_PDF >= 1:
+                fig_rad_height_PDF[time].savefig("rad_height_PDF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                if draw_rad_height_PDF == 2 and time != 0:
+                        plt.clf()
+#			plt.subplot(111, aspect=18)
+                        fig = plt.figure(figsize=(8, 8))
+                        gridspec.GridSpec(4, 1)
+                        plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                lines = plt.plot(rad_height_xs[time][code], rad_height_profiles[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(0, 0.45)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel("$\mathrm{Average\ Vertical\ Height\ (kpc)}$", fontsize='large')
+                        plt.legend(codes, loc=2, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+
+                        plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                        ave_profiles = np.mean(np.array(rad_height_profiles[time]), axis=0)
+                        for code in range(len(codes)):
+                                lines = plt.plot(rad_height_xs[time][code], (rad_height_profiles[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(-1.0, 1.0)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{h} - \mathrm{\bar h})/\mathrm{\bar h}$", fontsize='large')
+                        if add_mean_fractional_dispersion == 1:
+                                mean_fractional_dispersion = (np.std(np.array(rad_height_profiles[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < rad_height_xs[time][0]) & (rad_height_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                                mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                                plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.35, 0.08), size=10, xycoords='axes fraction')
+                        plt.savefig("rad_height_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+        if draw_metal_PDF == 1:
+                fig_metal_PDF[time].savefig("metal_PDF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+        if draw_density_DF >= 1:
+                plt.clf()
+#		plt.subplot(111, aspect=1)
+                fig = plt.figure(figsize=(8, 8))
+                gridspec.GridSpec(4, 1)
+                plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                for code in range(len(codes)):
+                        lines = plt.plot(density_DF_xs[time][code], density_DF_profiles[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogx()
+                plt.semilogy()
+                plt.xlim(1e-28, 1e-21)
+                plt.ylim(1e4, 1e9) # accumulation=False
+                #plt.ylim(1e5, 2e10) # accumulation=True
+                if time != 0 and dataset_num == 2:
+                        plt.axvline(x = 1.67e-24*10, color='k', linestyle ='--', linewidth=2, alpha=0.7) # SF threshold density
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Density\ (g/cm^3)}$", fontsize='large')
+                plt.ylabel(r"$\mathrm{Mass,}\/\mathrm{d}M\mathrm{/dlog}\/\mathrm{\rho}\/\mathrm{(M_{\odot})}$", fontsize='large')
+                plt.legend(codes, loc=4, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+
+                plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                ave_profiles = np.mean(np.array(density_DF_profiles[time]), axis=0)
+                for code in range(len(codes)):
+                        lines = plt.plot(density_DF_xs[time][code], (density_DF_profiles[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogx()
+                plt.xlim(1e-28, 1e-21)
+                plt.ylim(-1.0, 3.0)
+                plt.grid(True)
+                plt.locator_params(axis='y',nbins=4)
+                plt.xlabel("$\mathrm{Density\ (g/cm^3)}$", fontsize='large')
+                plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{m} - \mathrm{\bar m})/\mathrm{\bar m}$", fontsize='large')
+                if add_mean_fractional_dispersion == 1:
+                        mean_fractional_dispersion = (np.std(np.array(density_DF_profiles[time]), axis=0)/ave_profiles)[(mean_dispersion_density_range[0] < density_DF_xs[time][0]) & (density_DF_xs[time][0] < mean_dispersion_density_range[1])].mean()
+                        mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                        plt.annotate("Mean fractional dispersion for %.1e < rho < %.1e = %.3f (%.3f dex) " % (mean_dispersion_density_range[0], mean_dispersion_density_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.27, 0.84), size=10, xycoords='axes fraction')
+                plt.savefig("density_DF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+                if draw_density_DF == 2 and time != 0 and dataset_num == 2:
+                        fig = plt.figure(figsize=(8, 8))
+                        gridspec.GridSpec(16, 1)
+                        plt.subplot2grid((16, 1), (0, 0), rowspan=9)
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                lines = plt.plot(density_DF_xs[time][code], (density_DF_profiles[time][code] - density_DF_1st_profiles[time][code])/1e8, color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xscale('log')
+                        plt.xlim(1e-28, 1e-21)
+                        plt.ylim(-7, 3)
+                        plt.axvline(x = 1.67e-24*10, color='k', linestyle ='--', linewidth=2, alpha=0.7) # SF threshold density
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Density\ (g/cm^3)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Mass\ Change,}\/\mathrm{\Delta}(\mathrm{d}M\mathrm{/dlog}\/\mathrm{\rho})\/\mathrm{(10^8 M_{\odot})}$", fontsize='large')
+                        plt.legend(codes, loc=3, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+
+                        plt.subplot2grid((16, 1), (9, 0), rowspan=7)
+                        for code in range(len(codes)):
+                                lines = plt.plot(density_DF_xs[time][code], (density_DF_profiles[time][code] - density_DF_1st_profiles[time][code])/1e8, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlabel("$\mathrm{Density\ (g/cm^3)}$", fontsize='large')
+                        plt.ylabel(r"$\mathtt{symlog}[\/\mathrm{\Delta}(\mathrm{d}M\mathrm{/dlog}\/\mathrm{\rho})\/\mathrm{(10^8 M_{\odot})}\/]$", fontsize='large')
+                        plt.xscale('log')
+                        plt.yscale('symlog', linthreshy=0.01)
+                        plt.xlim(1e-28, 1e-21)
+                        plt.ylim(-10, 10)
+                        plt.axvline(x = 1.67e-24*10, color='k', linestyle ='--', linewidth=2, alpha=0.7) # SF threshold density
+                        plt.grid(True)
+                        plt.savefig("density_DF_change_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+        if draw_radius_DF == 1:
+                plt.clf()
+#		plt.subplot(111, aspect=1)
+                fig = plt.figure(figsize=(8, 6))
+                for code in range(len(codes)):
+                        lines = plt.plot(radius_DF_xs[time][code], np.add.accumulate(radius_DF_profiles[time][code]), color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogy()
+                plt.xlim(0, 14)
+                plt.ylim(1e7, 2e10)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Mass\ (M_{\odot})}$", fontsize='large')
+                plt.legend(codes, loc=4, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+                plt.savefig("radius_DF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+
+                plt.clf()
+#		plt.subplot(111, aspect=1)
+                fig = plt.figure(figsize=(8, 8))
+                gridspec.GridSpec(4, 1)
+                plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                for code in range(len(codes)):
+                        temp = []
+                        dr = 0.5*(radius_DF_xs[time][code][1] - radius_DF_xs[time][code][0]) # Here we assume that ProfilePlot was made with linearly binned radius_DF_xs
+                        for radius in range(len(radius_DF_profiles[time][code])):
+                                surface_area = np.pi*(((radius_DF_xs[time][code][radius]+dr)*1e3)**2 - ((radius_DF_xs[time][code][radius]-dr)*1e3)**2)
+                                temp.append(radius_DF_profiles[time][code][radius] / surface_area)
+                        surface_density[time].append(temp)
+                        lines = plt.plot(radius_DF_xs[time][code], surface_density[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogy()
+                plt.xlim(0, 14)
+                plt.ylim(1e-1, 2e3)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Surface\ Density\ (M_{\odot}/pc^2)}$", fontsize='large')
+                plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+
+                plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                ave_profiles = np.mean(np.array(surface_density[time]), axis=0)
+                for code in range(len(codes)):
+                        lines = plt.plot(radius_DF_xs[time][code], (surface_density[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.xlim(0, 14)
+                plt.ylim(-1.0, 1.0)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{\Sigma} - \mathrm{\bar \Sigma})/\mathrm{\bar \Sigma}$", fontsize='large')
+                if add_mean_fractional_dispersion == 1:
+                        mean_fractional_dispersion = (np.std(np.array(surface_density[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < radius_DF_xs[time][0]) & (radius_DF_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                        mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                        plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.35, 0.08), size=10, xycoords='axes fraction')
+                plt.savefig("gas_surface_density_radial_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+        if draw_star_radius_DF >= 1 and time != 0:
+                plt.clf()
+#		plt.subplot(111, aspect=1)
+                fig = plt.figure(figsize=(8, 6))
+                for code in range(len(codes)):
+                        lines = plt.plot(star_radius_DF_xs[time][code], np.add.accumulate(star_radius_DF_profiles[time][code]), color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogy()
+                plt.xlim(0, 14)
+                plt.ylim(1e7, 2e10)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Newly\ Formed\ Stellar\ Mass\ (M_{\odot})}$", fontsize='large')
+                plt.legend(codes, loc=4, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+                plt.savefig("star_radius_DF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+
+                plt.clf()
+#		plt.subplot(111, aspect=1)
+                fig = plt.figure(figsize=(8, 8))
+                gridspec.GridSpec(4, 1)
+                plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                for code in range(len(codes)):
+                        temp = []
+                        dr = 0.5*(star_radius_DF_xs[time][code][1] - star_radius_DF_xs[time][code][0])
+                        for radius in range(len(star_radius_DF_profiles[time][code])):
+                                surface_area = np.pi*(((star_radius_DF_xs[time][code][radius]+dr)*1e3)**2 - ((star_radius_DF_xs[time][code][radius]-dr)*1e3)**2)
+                                temp.append(star_radius_DF_profiles[time][code][radius] / surface_area)
+                        star_surface_density[time].append(temp)
+                        lines = plt.plot(star_radius_DF_xs[time][code], star_surface_density[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogy()
+                plt.xlim(0, 14)
+                plt.ylim(1e-1, 2e3)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Newly\ Formed\ Stellar\ Surface\ Density\ (M_{\odot}/pc^2)}$", fontsize='large')
+                plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+
+                plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                ave_profiles = np.mean(np.array(star_surface_density[time]), axis=0)
+                for code in range(len(codes)):
+                        lines = plt.plot(star_radius_DF_xs[time][code], (star_surface_density[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.xlim(0, 14)
+                plt.ylim(-1.0, 3.0)
+                plt.grid(True)
+                plt.locator_params(axis='y',nbins=4)
+                plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{\Sigma} - \mathrm{\bar \Sigma})/\mathrm{\bar \Sigma}$", fontsize='large')
+                if add_mean_fractional_dispersion == 1:
+                        mean_fractional_dispersion = (np.std(np.array(star_surface_density[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < star_radius_DF_xs[time][0]) & (star_radius_DF_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                        mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                        plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.02, 0.84), size=10, xycoords='axes fraction')
+                plt.savefig("star_surface_density_radial_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+
+                if draw_star_radius_DF == 2 and time != 0:
+#			plt.subplot(111, aspect=1)
+                        fig = plt.figure(figsize=(8, 8))
+                        gridspec.GridSpec(4, 1)
+                        plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                        for code in range(len(codes)):
+                                temp = []
+                                dr = 0.5*(sfr_radius_DF_xs[time][code][1] - sfr_radius_DF_xs[time][code][0])
+                                for radius in range(len(sfr_radius_DF_profiles[time][code])):
+                                        surface_area = np.pi*((sfr_radius_DF_xs[time][code][radius]+dr)**2 - (sfr_radius_DF_xs[time][code][radius]-dr)**2)
+                                        temp.append(sfr_radius_DF_profiles[time][code][radius] / surface_area)
+                                sfr_surface_density[time].append(temp)
+                                lines = plt.plot(sfr_radius_DF_xs[time][code], sfr_surface_density[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.semilogy()
+                        plt.xlim(0, 14)
+                        plt.ylim(1e-4, 1e1)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel("$\mathrm{Star\ Formation\ Rate\ Surface\ Density\ (M_{\odot}/yr/kpc^2)}$", fontsize='large')
+                        plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+
+                        plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                        ave_profiles = np.mean(np.array(sfr_surface_density[time]), axis=0)
+                        for code in range(len(codes)):
+                                lines = plt.plot(sfr_radius_DF_xs[time][code], (sfr_surface_density[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                        plt.xlim(0, 14)
+                        plt.ylim(-1.0, 3.0)
+                        plt.grid(True)
+                        plt.locator_params(axis='y',nbins=4)
+                        plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                        plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{\Sigma} - \mathrm{\bar \Sigma})/\mathrm{\bar \Sigma}$", fontsize='large')
+                        # if add_mean_fractional_dispersion == 1:
+                        #       mean_fractional_dispersion = (np.std(np.array(sfr_surface_density[time]), axis=0)/ave_profiles)[(mean_dispersion_radius_range[0] < sfr_radius_DF_xs[time][0]) & (sfr_radius_DF_xs[time][0] < mean_dispersion_radius_range[1])].mean()
+                        #       mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                        #       plt.annotate("Mean fractional dispersion for %d < r < %d kpc = %.3f (%.3f dex) " % (mean_dispersion_radius_range[0], mean_dispersion_radius_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.02, 0.84), size=10, xycoords='axes fraction')
+                        plt.savefig("sfr_surface_density_radial_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+
+                        # Draw K-S plot; below assumes that surface_density (or radius_DF_profiles) and sfr_surface_density (or sfr_radius_DF_profiles) have the identical size (n_bins in ProfilePlot)
+#			plt.subplot(111)
+                        fig = plt.figure(figsize=(8, 7))
+                        KS_fit_t1 = []
+                        KS_fit_t2 = []
+                        Bigiel_surface_density = []
+                        Bigiel_sfr_surface_density = []
+                        for code in range(len(codes)):
+                                # Remove bins where SFR surface density is zero
+                                KS_x = np.array(surface_density[time][code])
+                                KS_x[np.where(np.array(sfr_surface_density[time][code]) < 1e-10)] = 0
+                                KS_x = np.log10(KS_x)
+                                KS_y = np.log10(np.array(sfr_surface_density[time][code]))
+                                KS_x = KS_x[~np.isinf(KS_x)]
+                                KS_y = KS_y[~np.isinf(KS_y)]
+                                t1, t2 = np.polyfit(KS_x, KS_y, 1)
+                                KS_fit_t1.append(t1)
+                                KS_fit_t2.append(t2)
+                                plt.scatter(KS_x, KS_y, color=color_names[code], edgecolor=color_names[code], s=30, linewidth=0.7, marker=marker_names[code], alpha=0.8)
+                        plt.xlim(0, 3)
+                        plt.ylim(-4, 1)
+                        plt.grid(True)
+                        plt.xlabel("$\mathrm{log[Gas\ Surface\ Density\ (M_{\odot}/pc^2)]}$")
+                        plt.ylabel("$\mathrm{log[Star\ Formation\ Rate\ Surface\ Density\ (M_{\odot}/yr/kpc^2)]}$")
+                        plt.legend(codes, loc=2, frameon=True, ncol=2, fancybox=True)
+                        leg = plt.gca().get_legend()
+                        ltext = leg.get_texts()
+                        plt.setp(ltext, fontsize='small')
+                        t = np.arange(-2, 5, 0.01)
+                        for line in import_text("./Bigieletal2008_Fig8_Contour.txt", " "):
+                                Bigiel_surface_density.append(float(line[0]) + np.log10(1.36)) # for the factor 1.36, see Section 2.3.1 of Bigiel et al. 2008
+                                Bigiel_sfr_surface_density.append(float(line[1]))
+                        plt.fill(Bigiel_surface_density, Bigiel_sfr_surface_density, fill=True, color='b', alpha = 0.1, hatch='\\') # contour by Bigiel et al. 2008 (Fig 8), or Feldmann et al. 2012 (Fig 1)
+#			plt.plot(Bigiel_surface_density, Bigiel_sfr_surface_density, 'b-', linewidth = 0.7, alpha = 0.4)
+                        plt.plot(t, 1.37*t - 3.78, 'k--', linewidth = 2, alpha = 0.7) # observational fit by Kennicutt et al. 2007
+                        plt.savefig("K-S_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        for code in range(len(codes)):
+                                plt.plot(t, np.polyval([KS_fit_t1[code], KS_fit_t2[code]], t), color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], alpha = 0.7) # linear fits
+                        plt.savefig("K-S_with_fits_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                        plt.clf()
+        if draw_height_DF == 1:
+                plt.clf()
+#		plt.subplot(111, aspect=0.5)
+                fig = plt.figure(figsize=(8, 6))
+                for code in range(len(codes)):
+                        lines = plt.plot(height_DF_xs[time][code], np.add.accumulate(height_DF_profiles[time][code]), color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogy()
+                plt.xlim(0, 1.4)
+                plt.ylim(1e9, 2e10)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Vertical\ Height\ (kpc)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Mass\ (M_{\odot})}$", fontsize='large')
+                plt.legend(codes, loc=4, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+                plt.savefig("height_DF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+
+                plt.clf()
+#		plt.subplot(111, aspect=0.8)
+                fig = plt.figure(figsize=(8, 8))
+                gridspec.GridSpec(4, 1)
+                plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                for code in range(len(codes)):
+                        temp = []
+                        dh = height_DF_xs[time][code][1] - height_DF_xs[time][code][0]
+                        for height in range(len(height_DF_profiles[time][code])):
+                                surface_area = 2 * dh*1e3 * figure_width*1e3 # surface_area = 2*d(height)*figure_width in pc^2
+                                temp.append(height_DF_profiles[time][code][height] / surface_area)
+                        height_surface_density[time].append(temp)
+                        lines = plt.plot(height_DF_xs[time][code], height_surface_density[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogy()
+                plt.xlim(0, 1.4)
+                plt.ylim(1e-1, 2e3)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Vertical\ Height\ (kpc)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Surface\ Density\ (M_{\odot}/pc^2)}$", fontsize='large')
+                plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+
+                plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                ave_profiles = np.mean(np.array(height_surface_density[time]), axis=0)
+                for code in range(len(codes)):
+                        lines = plt.plot(height_DF_xs[time][code], (height_surface_density[time][code] - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.xlim(0, 1.4)
+                plt.ylim(-1.0, 3.0)
+                plt.grid(True)
+                plt.locator_params(axis='y',nbins=4)
+                plt.xlabel("$\mathrm{Vertical\ Height\ (kpc)}$", fontsize='large')
+                plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{\Sigma} - \mathrm{\bar \Sigma})/\mathrm{\bar \Sigma}$", fontsize='large')
+                if add_mean_fractional_dispersion == 1:
+                        mean_fractional_dispersion = (np.std(np.array(height_surface_density[time]), axis=0)/ave_profiles)[(mean_dispersion_height_range[0] < height_DF_xs[time][0]) & (height_DF_xs[time][0] < mean_dispersion_height_range[1])].mean()
+                        mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                        plt.annotate("Mean fractional dispersion for %.1f < z < %.1f kpc = %.3f (%.3f dex) " % (mean_dispersion_height_range[0], mean_dispersion_height_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.02, 0.84), size=10, xycoords='axes fraction')
+                plt.savefig("gas_surface_density_vertical_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+        if draw_SFR >= 1 and time != 0:
+                plt.clf()
+#		plt.subplot(111)#, aspect=1e-7)
+                fig = plt.figure(figsize=(8, 6))
+                for code in range(len(codes)):
+                        lines = plt.plot(sfr_ts[time][code], sfr_cum_masses[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.xlim(0, times[time])
+                plt.ylim(0, 2.5e9)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Time\ (Myr)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Stellar\ Mass\ (M_{\odot})}$", fontsize='large')
+                plt.legend(codes, loc=2, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+                plt.savefig("Stellar_mass_evolution_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+#		plt.subplot(111, aspect=55)
+                fig = plt.figure(figsize=(8, 8))
+                gridspec.GridSpec(4, 1)
+                plt.subplot2grid((4, 1), (0, 0), rowspan=3)
+                plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
+                for code in range(len(codes)):
+                        lines = plt.plot(sfr_ts[time][code], sfr_sfrs[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.xlim(0, times[time])
+                plt.ylim(0, 8)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Time\ (Myr)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Star\ Formation\ Rate\ (M_{\odot}/yr)}$", fontsize='large')
+                if draw_SFR == 2:
+                        pf_sfr_ref = load_dataset(2, 1, 0, ['GIZMO'],
+                                                  [['dummy', '/lustre/ki/pfs/mornkr/080112_CHaRGe/pfs-hyades/AGORA-DISK-repository/Grackle+SF+ThermalFbck/GIZMO/AGORA_disk_second_ps1.5/snapshot_temp_100_last']])
+                        pf_sfr_ref.unit_registry.add('pccm', pf_sfr_ref.unit_registry.lut['pc'][0], length, "\\rm{pc}/(1+z)")
+                        pf_sfr_ref.hubble_constant = 0.71; pf_sfr_ref.omega_lambda = 0.73; pf_sfr_ref.omega_matter = 0.27; pf_sfr_ref.omega_curvature = 0.0
+                        sp_sfr_ref = pf_sfr_ref.sphere(center, (0.5*figure_width, "kpc"))
+                        draw_SFR_mass_ref = sp_sfr_ref[(PartType_Star_to_use, "particle_mass")].in_units('Msun')
+                        draw_SFR_ct_ref   = sp_sfr_ref[(PartType_Star_to_use, FormationTimeType_to_use)].in_units('Myr')
+                        sfr_ref = StarFormationRate(pf_sfr_ref, star_mass = draw_SFR_mass_ref, star_creation_time = draw_SFR_ct_ref,
+                                                    volume = sp_sfr_ref.volume(), bins = 25)
+                        lines = plt.plot(sfr_ref.time.in_units('Myr'), sfr_ref.Msol_yr, color='k', linestyle='--', marker='*', markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.legend(codes+['GIZMO-ps2'], loc=2, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+                plt.subplot2grid((4, 1), (3, 0), rowspan=1)
+                ave_profiles = np.mean(np.array(sfr_sfrs[time]), axis=0)
+                for code in range(len(codes)):
+                        lines = plt.plot(sfr_ts[time][code], (sfr_sfrs[time][code].d - ave_profiles)/ave_profiles, color=color_names[code], linestyle='none', marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.xlim(0, times[time])
+                plt.ylim(-1.0, 1.0)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Time\ (Myr)}$", fontsize='large')
+                plt.ylabel(r"$\mathrm{Residual,}\/(\mathrm{\dot{\rho}_\star} - \mathrm{\bar{\dot{\rho}_\star}})/\mathrm{\bar{\dot{\rho}_\star}}$", fontsize='large')
+                if add_mean_fractional_dispersion == 1:
+                        mean_fractional_dispersion = (np.std(np.array(sfr_sfrs[time]), axis=0)/ave_profiles)[(mean_dispersion_time_range[0] < sfr_ts[time][0]) & (sfr_ts[time][0] < mean_dispersion_time_range[1])].mean()
+                        mean_fractional_dispersion_in_dex = np.log10(1.+mean_fractional_dispersion)
+                        plt.annotate("Mean fractional dispersion for %d < t < %d Myr = %.3f (%.3f dex) " % (mean_dispersion_time_range[0], mean_dispersion_time_range[1], mean_fractional_dispersion, mean_fractional_dispersion_in_dex), xy=(0.35, 0.08), size=10, xycoords='axes fraction')
+                plt.savefig("SFR_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+        if draw_cut_through == 1:
+                plt.clf()
+#		plt.subplot(111, aspect=0.5)
+                fig = plt.figure(figsize=(8, 6))
+                for code in range(len(codes)):
+                        lines = plt.plot(cut_through_zs[time][code], cut_through_zvalues[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogy()
+                plt.xlim(-1.4, 1.4)
+                plt.ylim(1e-26, 1e-22)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Vertical\ Height\ (kpc)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Density\ (g/cm^3)}$", fontsize='large')
+                plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+                z = np.arange(-1.4, 1.4, 0.05)
+                plt.plot(z, rho_agora_disk(0, np.abs(z)), linestyle="--", linewidth=2, color='k', alpha=0.7)
+                plt.savefig("cut_through_z_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
+#		plt.subplot(111, aspect=0.5)
+                fig = plt.figure(figsize=(8, 6))
+                for code in range(len(codes)):
+                        lines = plt.plot(cut_through_xs[time][code], cut_through_xvalues[time][code], color=color_names[code], linestyle=linestyle_names[np.mod(code, len(linestyle_names))], marker=marker_names[code], markeredgecolor='none', linewidth=1.2, alpha=0.8)
+                plt.semilogy()
+                plt.xlim(-14, 14)
+                plt.ylim(1e-26, 1e-22)
+                plt.grid(True)
+                plt.xlabel("$\mathrm{Cylindrical\ Radius\ (kpc)}$", fontsize='large')
+                plt.ylabel("$\mathrm{Density\ (g/cm^3)}$", fontsize='large')
+                plt.legend(codes, loc=1, frameon=True, ncol=2, fancybox=True)
+                leg = plt.gca().get_legend()
+                ltext = leg.get_texts()
+                plt.setp(ltext, fontsize='small')
+                x = np.arange(-14, 14, 0.05)
+                plt.plot(x, rho_agora_disk(np.abs(x), 0), linestyle="--", linewidth=2, color='k', alpha=0.7)
+                plt.savefig("cut_through_x_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
+                plt.clf()
diff --git a/examples/AgoraDisk/run.sh b/examples/AgoraDisk/run.sh
new file mode 100644
index 0000000000000000000000000000000000000000..d7e284db52c2e6750fd713b3607a7f423bac7769
--- /dev/null
+++ b/examples/AgoraDisk/run.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+# This example is based on the AGORA disk article (DOI: 10.3847/1538-4357/833/2/202)
+
+# currently only the low resolution is available
+sim=low
+
+# enable cooling or not
+cooling=0
+
+# make run.sh fail if a subcommand fails
+set -e
+
+# define flags
+flag=
+if [ $cooling -eq 1 ]
+then
+    flag=-C
+fi
+
+# Generate the initial conditions if they are not present.
+if [ ! -e $sim.hdf5 ]
+then
+    echo "Fetching initial glass file for the Sedov blast example..."
+    ./getIC.sh $sim.hdf5
+fi
+
+# Get the Grackle cooling table
+if [ ! -e CloudyData_UVB=HM2012.h5 ]
+then
+    echo "Fetching the Cloudy tables required by Grackle..."
+    ../getCoolingTable.sh
+fi
+
+# copy the initial conditions
+cp $sim.hdf5 agora_disk.hdf5
+# Update the particle types
+python3 changeType.py agora_disk.hdf5
+
+# Run SWIFT
+#../swift $flag -s -G -t 4 agora_disk.yml 2>&1 | tee output.log
+
+
+echo "Changing smoothing length to be Gadget compatible"
+python3 cleanupSwift.py agora_disk_0000.hdf5 agora_disk_IC.hdf5
+python3 cleanupSwift.py agora_disk_0050.hdf5 agora_disk_500Myr.hdf5
+
+echo "Fetching GEAR solution..."
+./getSolution.sh $flag
+
+echo "Plotting..."
+python3 plotSolution.py $flag
diff --git a/examples/BigCosmoVolume/makeIC.py b/examples/BigCosmoVolume/makeIC.py
deleted file mode 100644
index 8f83b564a3f747d164fd03b7dddf3cd9c609d9eb..0000000000000000000000000000000000000000
--- a/examples/BigCosmoVolume/makeIC.py
+++ /dev/null
@@ -1,152 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-import h5py
-import urllib
-import os.path
-import numpy as np
-import sys
-
-outputName = "bigCosmoVolume.hdf5" 
-
-
-#--------------------------------------------------
-if len(sys.argv) != 3:
-    print "Invalid number of arguments. Need to provide down-sampling factor [0.,1.] and number of copies (integer)"
-    print "Example: python makeIC.py 0.8 4"
-    exit()
-
-downsample = float(sys.argv[1])
-n_copy = int(sys.argv[2])
-
-if n_copy < 1:
-    print "Number of copy must be >1"
-    exit()
-
-if downsample > 1. or downsample <= 0.:
-    print "Down-sampling factor must be in [0,1]"
-    exit()
-
-#--------------------------------------------------
-
-# Download the tile
-if (not os.path.isfile("tile.hdf5")):
-    print "Downloading initial tile..."
-    urllib.urlretrieve ("http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/tile.hdf5", "tile.hdf5")
-    print "Done."
-else:
-    print "Tile already exists. No need to download..."
-
-# Read in the tile
-inputFile = h5py.File("tile.hdf5", 'r+')
-
-grp = inputFile["/Header"]
-boxSize = grp.attrs["BoxSize"]
-numPart = grp.attrs["NumPart_Total"][0]
-
-coords = inputFile["/PartType0/Coordinates"][:,:]
-v = inputFile["/PartType0/Velocities"][:,:]
-m = inputFile["/PartType0/Masses"][:]
-h = inputFile["/PartType0/SmoothingLength"][:]
-u = inputFile["/PartType0/InternalEnergy"][:]
-ids = np.array(range(np.size(u)), dtype='L') + 1
-
-# Downsample
-print "Downsampling..."
-indices = np.array(range(np.size(ids)))
-np.random.shuffle(indices)
-
-numPart *= downsample
-indices = indices < numPart
-
-coords = coords[indices,:]
-v = v[indices,:]
-m = m[indices]
-h = h[indices] / 1.825742 # Correct from Gadget defintion of h to physical definition
-u = u[indices]
-ids = ids[indices]
-
-numPart = np.size(ids)
-
-# Now replicate the tile
-if n_copy > 1:
-
-    print "Tiling..."
-
-    coords_tile = np.copy(coords)
-    v_tile = np.copy(v)
-    m_tile = np.copy(m)
-    h_tile = np.copy(h)
-    u_tile = np.copy(u)
-    ids_tile = np.copy(ids)
-
-    coords = np.zeros((0,3))
-    v = np.zeros((0,3))
-    m = np.zeros(0)
-    h = np.zeros(0)
-    u = np.zeros(0)
-    ids = np.zeros(0, dtype='L')
-
-    count = 0
-
-    for i in range(n_copy):
-        for j in range(n_copy):
-            for k in range(n_copy):
-                coords = np.append(coords, coords_tile + np.array([ i * boxSize[0], j * boxSize[1], k * boxSize[2] ]), axis=0)
-                v = np.append(v, v_tile, axis=0)
-                m = np.append(m, m_tile)
-                h = np.append(h, h_tile)
-                u = np.append(u, u_tile)
-                
-                ids = np.append(ids, ids_tile + count*numPart)
-                count+=1
-
-
-    numPart *= n_copy**3
-    boxSize *= n_copy
-
-# Copy the tile out
-file = h5py.File(outputName, 'w')
-
-# Header
-grp = file.create_group("/Header")
-grp.attrs["BoxSize"] = boxSize
-grp.attrs["NumPart_Total"] =  [numPart, 0, 0, 0, 0, 0]
-grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
-grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0]
-grp.attrs["Time"] = 0.0
-grp.attrs["NumFilesPerSnapshot"] = 1
-grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
-grp.attrs["Flag_Entropy_ICs"] = 0
-grp.attrs["Dimension"] = 3
-
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 1
-
-#Particle group
-grp = file.create_group("/PartType0")
-grp.create_dataset('Coordinates', data=coords, dtype='d')
-grp.create_dataset('Velocities', data=v, dtype='f')
-grp.create_dataset('Masses', data=m, dtype='f')
-grp.create_dataset('SmoothingLength', data=h, dtype='f')
-grp.create_dataset('InternalEnergy', data=u, dtype='f')
-grp.create_dataset('ParticleIDs', data=ids, dtype='L')
-
-
-
diff --git a/examples/BigPerturbedBox/makeIC_fcc.py b/examples/BigPerturbedBox/makeIC_fcc.py
deleted file mode 100644
index 13809b41b65890c51abfbd6db48640fb07b21623..0000000000000000000000000000000000000000
--- a/examples/BigPerturbedBox/makeIC_fcc.py
+++ /dev/null
@@ -1,116 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import h5py
-import random
-from numpy import *
-
-# Generates a swift IC file for the Sedov blast test in a periodic cubic box
-
-# Parameters
-periodic= 1      # 1 For periodic box
-boxSize = 10.
-L = 128           # Number of particles boxes along one axis
-rho = 1.          # Density
-P = 1.e-5         # Pressure
-E0= 1.e2          # Energy of the explosion
-pert = 0.025
-gamma = 5./3.     # Gas adiabatic index
-fileName = "perturbedBox.hdf5" 
-
-
-#---------------------------------------------------
-numPart = 4*(L**3) 
-mass = boxSize**3 * rho / numPart
-internalEnergy = P / ((gamma - 1.)*rho)
-off = array( [ [ 0.0 , 0.0 , 0.0 ] , [ 0.0 , 0.5 , 0.5 ] , [ 0.5 , 0.0 , 0.5 ] , [ 0.5 , 0.5 , 0.0 ] ] );
-hbox = boxSize / L
-
-# if L%2 == 0:
-#     print "Number of particles along each dimension must be odd."
-#     exit()
-
-#Generate particles
-coords = zeros((numPart, 3))
-v      = zeros((numPart, 3))
-m      = zeros((numPart, 1))
-h      = zeros((numPart, 1))
-u      = zeros((numPart, 1))
-ids    = zeros((numPart, 1), dtype='L')
-
-for i in range(L):
-    for j in range(L):
-        for k in range(L):
-            x = (i + 0.25) * hbox
-            y = (j + 0.25) * hbox
-            z = (k + 0.25) * hbox
-            for ell in range(4): 
-                index = 4*(i*L*L + j*L + k) + ell
-                coords[index,0] = x + off[ell,0] * hbox
-                coords[index,1] = y + off[ell,1] * hbox
-                coords[index,2] = z + off[ell,2] * hbox
-                v[index,0] = 0.
-                v[index,1] = 0.
-                v[index,2] = 0.
-                m[index] = mass
-                h[index] = 2.251 / 4 * hbox
-                u[index] = internalEnergy
-                ids[index] = index
-                coords[index,0] += random.random() * pert * hbox
-                coords[index,1] += random.random() * pert * hbox
-                coords[index,2] += random.random() * pert * hbox
-
-
-#--------------------------------------------------
-
-#File
-file = h5py.File(fileName, 'w')
-
-# Header
-grp = file.create_group("/Header")
-grp.attrs["BoxSize"] = boxSize
-grp.attrs["NumPart_Total"] =  [numPart, 0, 0, 0, 0, 0]
-grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
-grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0]
-grp.attrs["Time"] = 0.0
-grp.attrs["NumFilesPerSnapshot"] = 1
-grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
-grp.attrs["Flag_Entropy_ICs"] = 0
-
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
-#Particle group
-grp = file.create_group("/PartType0")
-ds = grp.create_dataset('Coordinates', (numPart, 3), 'd')
-ds[()] = coords
-ds = grp.create_dataset('Velocities', (numPart, 3), 'f')
-ds[()] = v
-ds = grp.create_dataset('Masses', (numPart,1), 'f')
-ds[()] = m
-ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f')
-ds[()] = h
-ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f')
-ds[()] = u
-ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L')
-ds[()] = ids + 1
-
-file.close()
diff --git a/examples/CoolingBox/run.sh b/examples/CoolingBox/run.sh
index 61106d1f4819145ad4fb3a017918ca0e074a87c2..30b2177a6e8bb95a20146397f8b6a5021161b27f 100755
--- a/examples/CoolingBox/run.sh
+++ b/examples/CoolingBox/run.sh
@@ -17,7 +17,7 @@ fi
 if [ ! -e CloudyData_UVB=HM2012.h5 ]
 then
     echo "Fetching the Cloudy tables required by Grackle..."
-    ./getCoolingTable.sh
+    ../getCoolingTable.sh
 fi
 
 # Run SWIFT
diff --git a/examples/CoolingHalo/cooling_halo.yml b/examples/CoolingHalo/cooling_halo.yml
index e4e8750f46bf7f46c692c4ccd3afe8453e0f606a..68c3478b717261698ac175835fc246e134e3a6a7 100644
--- a/examples/CoolingHalo/cooling_halo.yml
+++ b/examples/CoolingHalo/cooling_halo.yml
@@ -31,15 +31,9 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  CoolingHalo.hdf5       # The file to read
-  shift_x:    0.                  # A shift to apply to all particles read from the ICs (in internal units).
-  shift_y:    0.
-  shift_z:    0.
  
 # External potential parameters
 IsothermalPotential:
-  position_x:      0.     # location of centre of isothermal potential in internal units
-  position_y:      0.
-  position_z:      0.	
   vrot:            200.     # rotation speed of isothermal potential in internal units
   timestep_mult:   0.03     # controls time step
   epsilon:         0.1    #softening for the isothermal potential
diff --git a/examples/CoolingHaloWithSpin/cooling_halo.yml b/examples/CoolingHaloWithSpin/cooling_halo.yml
index 1d548b2fb7e531b712548eb5834b2c4de575f941..f6e9fe3b124631fc2d5336db8a7ffb18f7b34a95 100644
--- a/examples/CoolingHaloWithSpin/cooling_halo.yml
+++ b/examples/CoolingHaloWithSpin/cooling_halo.yml
@@ -34,9 +34,6 @@ InitialConditions:
  
 # External potential parameters
 IsothermalPotential:
-  position_x:      0.     # Location of centre of isothermal potential in internal units
-  position_y:      0.
-  position_z:      0.	
   vrot:            200.   # Rotation speed of isothermal potential in internal units
   timestep_mult:   0.03   # Controls time step
   epsilon:         1.0    # Softening for the isothermal potential
diff --git a/examples/CosmoVolume/cosmoVolume.yml b/examples/CosmoVolume/cosmoVolume.yml
deleted file mode 100644
index c114c4b95cc4f11218a7d410f9caeaf96abdf696..0000000000000000000000000000000000000000
--- a/examples/CosmoVolume/cosmoVolume.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-# Define the system of units to use internally. 
-InternalUnitSystem:
-  UnitMass_in_cgs:     1   # Grams
-  UnitLength_in_cgs:   1   # Centimeters
-  UnitVelocity_in_cgs: 1   # Centimeters per second
-  UnitCurrent_in_cgs:  1   # Amperes
-  UnitTemp_in_cgs:     1   # Kelvin
-
-# Parameters governing the time integration
-TimeIntegration:
-  time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   1.    # The end time of the simulation (in internal units).
-  dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
-
-# Parameters governing the snapshots
-Snapshots:
-  basename:            cosmo # Common part of the name of output files
-  time_first:          0.    # Time of the first output (in internal units)
-  delta_time:          0.05  # Time difference between consecutive outputs (in internal units)
-
-# Parameters governing the conserved quantities statistics
-Statistics:
-  delta_time:          1e-2 # Time between statistics output
-
-# Parameters for the hydrodynamics scheme
-SPH:
-  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
-  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
-
-# Parameters related to the initial conditions
-InitialConditions:
-  file_name:  ./cosmoVolume.hdf5     # The file to read
-
diff --git a/examples/CosmoVolume/getIC.sh b/examples/CosmoVolume/getIC.sh
deleted file mode 100755
index fdaddd1d1f09a941244f520fb368aaed8f2316b4..0000000000000000000000000000000000000000
--- a/examples/CosmoVolume/getIC.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/cosmoVolume.hdf5
diff --git a/examples/CosmoVolume/run.sh b/examples/CosmoVolume/run.sh
deleted file mode 100755
index e128aa8f88cc30e13c420bda2088c9958c5bfc31..0000000000000000000000000000000000000000
--- a/examples/CosmoVolume/run.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
- # Generate the initial conditions if they are not present.
-if [ ! -e cosmoVolume.hdf5 ]
-then
-    echo "Fetching initial conditions for the cosmo volume example..."
-    ./getIC.sh
-fi
-
-../swift -s -t 16 cosmoVolume.yml 2>&1 | tee output.log
diff --git a/examples/CosmologicalBox/cosmo.yml b/examples/CosmologicalBox/cosmo.yml
deleted file mode 100644
index a82cc5610b3317076a908902efdbd5b87d94262a..0000000000000000000000000000000000000000
--- a/examples/CosmologicalBox/cosmo.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-# Define the system of units to use internally. 
-InternalUnitSystem:
-  UnitMass_in_cgs:     1.989e43      # 10^10 M_sun in grams
-  UnitLength_in_cgs:   3.085678e24   # Mpc in centimeters
-  UnitVelocity_in_cgs: 1e5           # km/s in centimeters per second
-  UnitCurrent_in_cgs:  1             # Amperes
-  UnitTemp_in_cgs:     1             # Kelvin
-
-#  UnitMass_in_cgs:     1   # Grams
-#  UnitLength_in_cgs:   1   # Centimeters
-#  UnitVelocity_in_cgs: 1   # Centimeters per second
-#  UnitCurrent_in_cgs:  1   # Amperes
-#  UnitTemp_in_cgs:     1   # Kelvin
-
-# Parameters governing the time integration
-TimeIntegration:
-  time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   1.    # The end time of the simulation (in internal units).
-  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
-
-# Parameters governing the snapshots
-Snapshots:
-  basename:            uniformBox # Common part of the name of output files
-  time_first:          0.         # Time of the first output (in internal units)
-  delta_time:          0.01       # Time difference between consecutive outputs (in internal units)
-
-# Parameters governing the conserved quantities statistics
-Statistics:
-  delta_time:          1e-2 # Time between statistics output
-
-# Parameters for the hydrodynamics scheme
-SPH:
-  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
-  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
-  
-# Parameters related to the initial conditions
-InitialConditions:
-  file_name:  ./uniformBox.hdf5     # The file to read
-
-Cosmology:
-  Omega_m:        0.307
-  Omega_lambda:   0.693
-  Omega_b:        0.0455
-  h:              0.6777
-  a_begin:        0.0078125
-  a_end:          1.0
-  
-
diff --git a/examples/CosmologicalBox/makeIC.py b/examples/CosmologicalBox/makeIC.py
deleted file mode 100644
index 01e37c67b6e2eec2984d62f4ffd503b23b5bd9ec..0000000000000000000000000000000000000000
--- a/examples/CosmologicalBox/makeIC.py
+++ /dev/null
@@ -1,109 +0,0 @@
-###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
- #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- # 
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- # 
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- # GNU General Public License for more details.
- # 
- # You should have received a copy of the GNU Lesser General Public License
- # along with this program.  If not, see <http://www.gnu.org/licenses/>.
- # 
- ##############################################################################
-
-import h5py
-import sys
-from numpy import *
-
-# Generates a swift IC file containing a cartesian distribution of particles
-# at a constant density and pressure in a cubic box
-
-# Parameters
-periodic= 1           # 1 For periodic box
-boxSize = 1.
-L = int(sys.argv[1])  # Number of particles along one axis
-rho = 2.              # Density
-P = 1.                # Pressure
-gamma = 5./3.         # Gas adiabatic index
-eta = 1.2349          # 48 ngbs with cubic spline kernel
-fileName = "uniformBox.hdf5" 
-
-#---------------------------------------------------
-numPart = L**3
-mass = boxSize**3 * rho / numPart
-internalEnergy = P / ((gamma - 1.)*rho)
-
-#--------------------------------------------------
-
-#File
-file = h5py.File(fileName, 'w')
-
-# Header
-grp = file.create_group("/Header")
-grp.attrs["BoxSize"] = boxSize
-grp.attrs["NumPart_Total"] =  [numPart, 0, 0, 0, 0, 0]
-grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
-grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0]
-grp.attrs["Time"] = 0.0
-grp.attrs["NumFilesPerSnapshot"] = 1
-grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
-grp.attrs["Flag_Entropy_ICs"] = 0
-grp.attrs["Dimension"] = 3
-
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = periodic
-
-#Units
-grp = file.create_group("/Units")
-grp.attrs["Unit length in cgs (U_L)"] = 1.
-grp.attrs["Unit mass in cgs (U_M)"] = 1.
-grp.attrs["Unit time in cgs (U_t)"] = 1.
-grp.attrs["Unit current in cgs (U_I)"] = 1.
-grp.attrs["Unit temperature in cgs (U_T)"] = 1.
-
-#Particle group
-grp = file.create_group("/PartType0")
-
-v  = zeros((numPart, 3))
-ds = grp.create_dataset('Velocities', (numPart, 3), 'f')
-ds[()] = v
-v = zeros(1)
-
-m = full((numPart, 1), mass)
-ds = grp.create_dataset('Masses', (numPart,1), 'f')
-ds[()] = m
-m = zeros(1)
-
-h = full((numPart, 1), eta * boxSize / L)
-ds = grp.create_dataset('SmoothingLength', (numPart,1), 'f')
-ds[()] = h
-h = zeros(1)
-
-u = full((numPart, 1), internalEnergy)
-ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f')
-ds[()] = u
-u = zeros(1)
-
-
-ids = linspace(0, numPart, numPart, endpoint=False).reshape((numPart,1))
-ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L')
-ds[()] = ids + 1
-x      = ids % L;
-y      = ((ids - x) / L) % L;
-z      = (ids - x - L * y) / L**2;
-coords = zeros((numPart, 3))
-coords[:,0] = z[:,0] * boxSize / L + boxSize / (2*L)
-coords[:,1] = y[:,0] * boxSize / L + boxSize / (2*L)
-coords[:,2] = x[:,0] * boxSize / L + boxSize / (2*L)
-ds = grp.create_dataset('Coordinates', (numPart, 3), 'd')
-ds[()] = coords
-
-file.close()
diff --git a/examples/EAGLE_100/eagle_100.yml b/examples/EAGLE_100/eagle_100.yml
index a570d81f403b303a41f286cc2407ce39e10735b9..3183f40fb1a727167dad32b2c01f995fea0ae61b 100644
--- a/examples/EAGLE_100/eagle_100.yml
+++ b/examples/EAGLE_100/eagle_100.yml
@@ -20,7 +20,7 @@ TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   1e-2  # The end time of the simulation (in internal units).
   dt_min:     1e-12 # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
+  dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
 
 Scheduler:
   max_top_level_cells: 80
@@ -31,7 +31,7 @@ Snapshots:
   scale_factor_first:  0.92  # Scale-factor of the first snaphot (cosmological run)
   time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
   delta_time:          1.10  # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -54,5 +54,7 @@ SPH:
 
 # Parameters related to the initial conditions
 InitialConditions:
-  file_name:  ./EAGLE_ICs_100.hdf5     # The file to read
-  cleanup_h_factors: 1                 # Remove the h-factors inherited from Gadget
+  file_name:  ./EAGLE_ICs_100.hdf5   # The file to read
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
diff --git a/examples/EAGLE_12/eagle_12.yml b/examples/EAGLE_12/eagle_12.yml
index e400610d967e6a821cc46495c827a52bc42ea1aa..0c6fa94570af635866158301aa964bc6c58762a1 100644
--- a/examples/EAGLE_12/eagle_12.yml
+++ b/examples/EAGLE_12/eagle_12.yml
@@ -20,18 +20,19 @@ TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   1e-2  # The end time of the simulation (in internal units).
   dt_min:     1e-10 # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
+  dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
   
 Scheduler:
-  max_top_level_cells:    15
-  
+  max_top_level_cells:    8
+  cell_split_size:        50
+
 # Parameters governing the snapshots
 Snapshots:
   basename:            eagle # Common part of the name of output files
   scale_factor_first:  0.92  # Scale-factor of the first snaphot (cosmological run)
   time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
   delta_time:          1.10  # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -42,9 +43,10 @@ Statistics:
 # Parameters for the self-gravity scheme
 Gravity:
   eta:                    0.025     # Constant dimensionless multiplier for time integration.
-  theta:                  0.85      # Opening angle (Multipole acceptance criterion)
+  theta:                  0.7       # Opening angle (Multipole acceptance criterion)
   comoving_softening:     0.0026994 # Comoving softening length (in internal units).
   max_physical_softening: 0.0007    # Physical softening length (in internal units).
+  mesh_side_length:       32
   
 # Parameters for the hydrodynamics scheme
 SPH:
@@ -54,5 +56,7 @@ SPH:
 
 # Parameters related to the initial conditions
 InitialConditions:
-  file_name:  ./EAGLE_ICs_12.hdf5     # The file to read
+  file_name:  ./EAGLE_ICs_12.hdf5    # The file to read
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
diff --git a/examples/EAGLE_25/eagle_25.yml b/examples/EAGLE_25/eagle_25.yml
index cc4a41b90805b2731554a4946db90a0b7e9ab311..e5550d3b141b1c91d1819dae06b2966688682405 100644
--- a/examples/EAGLE_25/eagle_25.yml
+++ b/examples/EAGLE_25/eagle_25.yml
@@ -36,10 +36,11 @@ TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   1e-2  # The end time of the simulation (in internal units).
   dt_min:     1e-10 # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
+  dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
 
 Scheduler:
-  max_top_level_cells:    20
+  max_top_level_cells:    16
+  cell_split_size:        50
 
 # Parameters governing the snapshots
 Snapshots:
@@ -47,7 +48,6 @@ Snapshots:
   scale_factor_first:  0.92  # Scale-factor of the first snaphot (cosmological run)
   time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
   delta_time:          1.10  # Time difference between consecutive outputs (in internal units)
-  compression:         4
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -58,9 +58,10 @@ Statistics:
 # Parameters for the self-gravity scheme
 Gravity:
   eta:                    0.025    # Constant dimensionless multiplier for time integration. 
-  theta:                  0.85     # Opening angle (Multipole acceptance criterion)
+  theta:                  0.7     # Opening angle (Multipole acceptance criterion)
   comoving_softening:     0.0026994 # Comoving softening length (in internal units).
   max_physical_softening: 0.0007    # Physical softening length (in internal units).
+  mesh_side_length:       32
 
 # Parameters for the hydrodynamics scheme
 SPH:
@@ -70,6 +71,8 @@ SPH:
 
 # Parameters related to the initial conditions
 InitialConditions:
-  file_name:  ./EAGLE_ICs_25.hdf5     # The file to read
+  file_name:  ./EAGLE_ICs_25.hdf5    # The file to read
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
 
diff --git a/examples/EAGLE_50/eagle_50.yml b/examples/EAGLE_50/eagle_50.yml
index 5e4193e5c1c6295ded19e12bf50f6e156d797656..ec8eb3785a7fb706e92dd3d8b53ab94b0a98ccf7 100644
--- a/examples/EAGLE_50/eagle_50.yml
+++ b/examples/EAGLE_50/eagle_50.yml
@@ -20,10 +20,11 @@ TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   1e-2  # The end time of the simulation (in internal units).
   dt_min:     1e-10 # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
+  dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
 
 Scheduler:
-  max_top_level_cells: 40
+  max_top_level_cells: 20
+  cell_split_size:     50
 
 # Parameters governing the snapshots
 Snapshots:
@@ -31,7 +32,6 @@ Snapshots:
   scale_factor_first:  0.92  # Scale-factor of the first snaphot (cosmological run)
   time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
   delta_time:          1.10  # Time difference between consecutive outputs (in internal units)
-  compression:         4
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -42,9 +42,10 @@ Statistics:
 # Parameters for the self-gravity scheme
 Gravity:
   eta:                    0.025    # Constant dimensionless multiplier for time integration.
-  theta:                  0.85     # Opening angle (Multipole acceptance criterion)
+  theta:                  0.7      # Opening angle (Multipole acceptance criterion)
   comoving_softening:     0.0026994 # Comoving softening length (in internal units).
   max_physical_softening: 0.0007    # Physical softening length (in internal units).
+  mesh_side_length:       64
 
 # Parameters for the hydrodynamics scheme
 SPH:
@@ -56,4 +57,6 @@ SPH:
 InitialConditions:
   file_name:  ./EAGLE_ICs_50.hdf5     # The file to read
   cleanup_h_factors: 1                # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1         # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
 
diff --git a/examples/EAGLE_6/eagle_6.yml b/examples/EAGLE_6/eagle_6.yml
index 27f8b42ebda3e45b1b3d07873518236be6d23cb7..56e894dc16f2199b5f1a717860916d52a8506999 100644
--- a/examples/EAGLE_6/eagle_6.yml
+++ b/examples/EAGLE_6/eagle_6.yml
@@ -12,7 +12,7 @@ StructureFinding:
   basename:             ./halo/stf       # Common part of the name of output files.
   output_time_format:   0                # Specifies format of delta_time. 0 for simulation steps and 1 for simulation time intervals.
   time_first:           0.               # Time of the first structure finding output (in internal units).
-  delta_time:           5                # Time difference between consecutive structure finding outputs (in internal units). Can either be given in simulation steps or simulation time intervals.
+  delta_time:           250               # Time difference between consecutive structure finding outputs (in internal units). Can either be given in simulation steps or simulation time intervals.
 
 # Define the system of units to use int VELOCIraptor. 
 VelociraptorUnitSystem:
@@ -32,23 +32,23 @@ Cosmology:
   Omega_b:        0.0455        # Baryon density parameter
   
 Scheduler:
-  max_top_level_cells: 8
+  max_top_level_cells:    8
+  cell_split_size:        50
   
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   1e-2  # The end time of the simulation (in internal units).
   dt_min:     1e-10 # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
-  max_dt_RMS_factor: 0.25
-
+  dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
+  
 # Parameters governing the snapshots
 Snapshots:
   basename:            eagle # Common part of the name of output files
   scale_factor_first:  0.92  # Scale-factor of the first snaphot (cosmological run)
   time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
   delta_time:          1.10  # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -59,10 +59,11 @@ Statistics:
 # Parameters for the self-gravity scheme
 Gravity:
   eta:                    0.025    # Constant dimensionless multiplier for time integration.
-  theta:                  0.85     # Opening angle (Multipole acceptance criterion)
+  theta:                  0.7      # Opening angle (Multipole acceptance criterion)
   comoving_softening:     0.0026994 # Comoving softening length (in internal units).
   max_physical_softening: 0.0007    # Physical softening length (in internal units).
-  
+  mesh_side_length:       16
+
 # Parameters for the hydrodynamics scheme
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
@@ -73,4 +74,6 @@ SPH:
 InitialConditions:
   file_name:  ./EAGLE_ICs_6.hdf5     # The file to read
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
 
diff --git a/examples/EAGLE_DMO_100/eagle_100.yml b/examples/EAGLE_DMO_100/eagle_100.yml
index 0548980cc6b2a785eb55781f213da4d3980cd9d5..c1bdbc2c3837ffc974c04b46ddcee9fb7bdc18c9 100644
--- a/examples/EAGLE_DMO_100/eagle_100.yml
+++ b/examples/EAGLE_DMO_100/eagle_100.yml
@@ -31,7 +31,7 @@ Snapshots:
   scale_factor_first:  0.92  # Scale-factor of the first snaphot (cosmological run)
   time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
   delta_time:          1.10  # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -49,4 +49,6 @@ Gravity:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  EAGLE_DMO_ICs_100.hdf5
-  cleanup_h_factors: 1                 # Remove the h-factors inherited from Gadget
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
diff --git a/examples/EAGLE_DMO_12/eagle_12.yml b/examples/EAGLE_DMO_12/eagle_12.yml
index f76ca2f833cfe2722ef6b68bf4105c0344a569bd..a541cb44efd12827957ea060edfc0c38f3408119 100644
--- a/examples/EAGLE_DMO_12/eagle_12.yml
+++ b/examples/EAGLE_DMO_12/eagle_12.yml
@@ -23,7 +23,8 @@ TimeIntegration:
   dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
   
 Scheduler:
-  max_top_level_cells:    15
+  max_top_level_cells:    8
+  cell_split_size:        50
   
 # Parameters governing the snapshots
 Snapshots:
@@ -31,7 +32,7 @@ Snapshots:
   scale_factor_first:  0.92  # Scale-factor of the first snaphot (cosmological run)
   time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
   delta_time:          1.10  # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -41,12 +42,16 @@ Statistics:
 
 # Parameters for the self-gravity scheme
 Gravity:
+  mesh_side_length:       32
   eta:                    0.025     # Constant dimensionless multiplier for time integration.
-  theta:                  0.85      # Opening angle (Multipole acceptance criterion)
+  theta:                  0.7       # Opening angle (Multipole acceptance criterion)
   comoving_softening:     0.0026994 # Comoving softening length (in internal units).
   max_physical_softening: 0.0007    # Physical softening length (in internal units).
+  mesh_side_length:       32
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  EAGLE_DMO_ICs_12.hdf5
-  cleanup_h_factors: 1                 # Remove the h-factors inherited from Gadget
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
diff --git a/examples/EAGLE_DMO_25/eagle_25.yml b/examples/EAGLE_DMO_25/eagle_25.yml
index 4b8dcf7bf30b62ff65bbe44f4a80b31b320d167e..5a4d83af808ad08274808b4b6a794dcb6f68deb2 100644
--- a/examples/EAGLE_DMO_25/eagle_25.yml
+++ b/examples/EAGLE_DMO_25/eagle_25.yml
@@ -23,7 +23,8 @@ TimeIntegration:
   dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
   
 Scheduler:
-  max_top_level_cells:    20
+  max_top_level_cells:    16
+  cell_split_size:        50
   
 # Parameters governing the snapshots
 Snapshots:
@@ -31,7 +32,7 @@ Snapshots:
   scale_factor_first:  0.92  # Scale-factor of the first snaphot (cosmological run)
   time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
   delta_time:          1.10  # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -42,11 +43,14 @@ Statistics:
 # Parameters for the self-gravity scheme
 Gravity:
   eta:                    0.025     # Constant dimensionless multiplier for time integration.
-  theta:                  0.85      # Opening angle (Multipole acceptance criterion)
+  theta:                  0.7       # Opening angle (Multipole acceptance criterion)
   comoving_softening:     0.0026994 # Comoving softening length (in internal units).
   max_physical_softening: 0.0007    # Physical softening length (in internal units).
+  mesh_side_length:       32
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  EAGLE_DMO_ICs_25.hdf5
-  cleanup_h_factors: 1                 # Remove the h-factors inherited from Gadget
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
diff --git a/examples/EAGLE_DMO_50/eagle_50.yml b/examples/EAGLE_DMO_50/eagle_50.yml
index 181b633c498fd4b4b4b17a7bffbd32aabc1d4726..e128a74a916abc007b0d0542d1b6891edad71450 100644
--- a/examples/EAGLE_DMO_50/eagle_50.yml
+++ b/examples/EAGLE_DMO_50/eagle_50.yml
@@ -23,7 +23,8 @@ TimeIntegration:
   dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
   
 Scheduler:
-  max_top_level_cells:    40
+  max_top_level_cells: 20
+  cell_split_size:     50
   
 # Parameters governing the snapshots
 Snapshots:
@@ -31,7 +32,6 @@ Snapshots:
   scale_factor_first:  0.92  # Scale-factor of the first snaphot (cosmological run)
   time_first:          0.01  # Time of the first output (non-cosmological run) (in internal units)
   delta_time:          1.10  # Time difference between consecutive outputs (in internal units)
-  compression:         4
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -42,11 +42,14 @@ Statistics:
 # Parameters for the self-gravity scheme
 Gravity:
   eta:                    0.025     # Constant dimensionless multiplier for time integration.
-  theta:                  0.85      # Opening angle (Multipole acceptance criterion)
+  theta:                  0.7       # Opening angle (Multipole acceptance criterion)
   comoving_softening:     0.0026994 # Comoving softening length (in internal units).
   max_physical_softening: 0.0007    # Physical softening length (in internal units).
+  mesh_side_length:       64
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  EAGLE_DMO_ICs_50.hdf5
-  cleanup_h_factors: 1                 # Remove the h-factors inherited from Gadget
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
+  cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
+
diff --git a/examples/EvrardCollapse_3D/evrard.yml b/examples/EvrardCollapse_3D/evrard.yml
index a2b39fb1e7fa36098abe720945ece4b611eb4fe8..f9a4e69f72e6bb19b818cb985ef92122b1a10b2a 100644
--- a/examples/EvrardCollapse_3D/evrard.yml
+++ b/examples/EvrardCollapse_3D/evrard.yml
@@ -18,7 +18,7 @@ Snapshots:
   basename:            evrard # Common part of the name of output files
   time_first:          0.       # Time of the first output (in internal units)
   delta_time:          0.1     # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/ExternalPointMass/externalPointMass.yml b/examples/ExternalPointMass/externalPointMass.yml
index 6e2315cc4dd5c2c354375d39e03f13a1553ed08b..79dcd6458489958622ed7d6167d7f5f214ebd8b9 100644
--- a/examples/ExternalPointMass/externalPointMass.yml
+++ b/examples/ExternalPointMass/externalPointMass.yml
@@ -31,15 +31,11 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  PointMass.hdf5        # The file to read
-  shift_x:    50.                   # A shift to apply to all particles read from the ICs (in internal units).
-  shift_y:    50.
-  shift_z:    50.
+  shift:      [50.,50.,50.]         # A shift to apply to all particles read from the ICs (in internal units).
 
 # External potential parameters
 PointMassPotential:
-  position_x:      50.     # location of external point mass in internal units
-  position_y:      50.
-  position_z:      50.	
+  position:        [50.,50.,50.]    # location of external point mass in internal units
   mass:            1e10     # mass of external point mass in internal units
   timestep_mult:   0.03     # controls time step
 
diff --git a/examples/GreshoVortex_3D/gresho.yml b/examples/GreshoVortex_3D/gresho.yml
index f42fcbfd00941e7b9c5c09c0d2e3118f5cc1f57d..113c03b9bd0e411bf04f29c70937ac7fab3708f3 100644
--- a/examples/GreshoVortex_3D/gresho.yml
+++ b/examples/GreshoVortex_3D/gresho.yml
@@ -21,7 +21,7 @@ Snapshots:
   basename:            gresho # Common part of the name of output files
   time_first:          0.     # Time of the first output (in internal units)
   delta_time:          1e-1   # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/HydrostaticHalo/hydrostatic.yml b/examples/HydrostaticHalo/hydrostatic.yml
index 38a5df2866d39523e6e4b9f6c1c97a7537bff5a4..c37a5314976c3116eb51040b1feeaa9b23ac1326 100644
--- a/examples/HydrostaticHalo/hydrostatic.yml
+++ b/examples/HydrostaticHalo/hydrostatic.yml
@@ -34,9 +34,6 @@ InitialConditions:
  
 # External potential parameters
 IsothermalPotential:
-  position_x:      0.     # location of centre of isothermal potential in internal units
-  position_y:      0.
-  position_z:      0.	
   vrot:            200.     # rotation speed of isothermal potential in internal units
   epsilon:         1.0
   timestep_mult:   0.03     # controls time step
diff --git a/examples/IsothermalPotential/isothermal.yml b/examples/IsothermalPotential/isothermal.yml
index bd59ddc5117f71c478ce5eeda0a08817eefe8624..5dd0b831c839b3307a1c118d9bd64bda29da487c 100644
--- a/examples/IsothermalPotential/isothermal.yml
+++ b/examples/IsothermalPotential/isothermal.yml
@@ -26,15 +26,10 @@ Snapshots:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  Isothermal.hdf5       # The file to read
-  shift_x:    200.                  # Shift all particles to be in the potential
-  shift_y:    200.
-  shift_z:    200.
+  shift:      [200.,200.,200.]      # Shift all particles to be in the potential
  
 # External potential parameters
 IsothermalPotential:
-  position_x:      0.       # location of centre of isothermal potential in internal units
-  position_y:      0.
-  position_z:      0.
-  vrot:            200.     # rotation speed of isothermal potential in internal units
-  timestep_mult:   0.01     # controls time step
-  epsilon:         0.       # No softening at the centre of the halo
+  vrot:            200.       # rotation speed of isothermal potential in internal units
+  timestep_mult:   0.01       # controls time step
+  epsilon:         0.         # No softening at the centre of the halo
diff --git a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml
index 3133e2769e81b80c18760e8258665fc1a6eee6ca..e39c01645b766ae585558452683dc8e1bdf425a8 100644
--- a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml
+++ b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml
@@ -18,7 +18,7 @@ Snapshots:
   basename:            kelvinHelmholtzGrowthRate  # Common part of the name of output files
   time_first:          0.               # Time of the first output (in internal units)
   delta_time:          0.04      # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
index 19f036c364c4e9f40580e906e2840cad6df378a0..ccc7526b391374a4da0883f6615a65c7b93a0948 100644
--- a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
+++ b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
@@ -8,8 +8,8 @@ InternalUnitSystem:
   
 # Parameters governing the time integration
 TimeIntegration:
-  time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   1.5   # The end time of the simulation (in internal units).
+  time_begin: 0.0    # The starting time of the simulation (in internal units).
+  time_end:   4.5   # The end time of the simulation (in internal units).
   dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
   dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
 
@@ -17,7 +17,7 @@ TimeIntegration:
 Snapshots:
   basename:            kelvinHelmholtz  # Common part of the name of output files
   time_first:          0.               # Time of the first output (in internal units)
-  delta_time:          0.25      # Time difference between consecutive outputs (in internal units)
+  delta_time:          0.01      # Time difference between consecutive outputs (in internal units)
 
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/KelvinHelmholtz_2D/makeIC.py b/examples/KelvinHelmholtz_2D/makeIC.py
index bd0f39ed90faf0d67ff4a508bff83067bf748d43..744b39de8260720521ae8e77ed5d0a12161f2b6a 100644
--- a/examples/KelvinHelmholtz_2D/makeIC.py
+++ b/examples/KelvinHelmholtz_2D/makeIC.py
@@ -24,7 +24,7 @@ import sys
 # Generates a swift IC file for the Kelvin-Helmholtz vortex in a periodic box
 
 # Parameters
-L2    = 128       # Particles along one edge in the low-density region
+L2    = 256       # Particles along one edge in the low-density region
 gamma = 5./3.     # Gas adiabatic index
 P1    = 2.5       # Central region pressure
 P2    = 2.5       # Outskirts pressure
diff --git a/examples/KelvinHelmholtz_2D/makeMovie.py b/examples/KelvinHelmholtz_2D/makeMovie.py
new file mode 100644
index 0000000000000000000000000000000000000000..84fe99106bf607830e89b6aa663135b48b6c0744
--- /dev/null
+++ b/examples/KelvinHelmholtz_2D/makeMovie.py
@@ -0,0 +1,112 @@
+"""
+Makes a movie of the KH 2D data.
+
+You will need to run your movie with far higher time-resolution than usual to
+get a nice movie; around 450 snapshots over 6s is required.
+
+Edit this file near the bottom with the number of snaps you have.
+
+Written by Josh Borrow (joshua.borrow@durham.ac.uk)
+"""
+
+import os
+import h5py as h5
+import numpy as np
+import scipy.interpolate as si
+
+
+def load_and_extract(filename):
+    """
+    Load the data and extract relevant info.
+    """
+
+    with h5.File(filename, "r") as f:
+        x, y, _ = f["PartType0/Coordinates"][...].T
+        density = f["PartType0/Density"][...]
+
+    return x, y, density
+
+
+def make_plot(filename, array, nx, ny, dx, dy):
+    """
+    Load the data and plop it on the grid using nearest
+    neighbour searching for finding the 'correct' value of
+    the density.
+    """
+
+    data_x, data_y, density = load_and_extract(filename)
+
+    # Make the grid
+    x = np.linspace(*dx, nx)
+    y = np.linspace(*dy, ny)
+
+    xv, yv = np.meshgrid(x, y)
+
+    mesh = si.griddata((data_x, data_y), density, (xv, yv), method="nearest")
+
+    array.set_array(mesh)
+
+    return array,
+
+
+def frame(n, *args):
+    """
+    Make a single frame. Requires the global variables plot and dpi.
+    """
+
+    global plot, dpi
+
+    fn = "{}_{:04d}.hdf5".format(filename, n)
+
+    return make_plot(fn, plot, dpi, dpi, (0, 1), (0, 1))
+
+
+if __name__ == "__main__":
+    import matplotlib
+    matplotlib.use("Agg")
+
+    from tqdm import tqdm
+    from matplotlib.animation import FuncAnimation
+    from scipy.stats import gaussian_kde
+
+    import matplotlib.pyplot as plt
+
+    filename = "kelvinhelmholtz"
+    dpi = 512
+
+
+    # Look for the number of files in the directory.
+    i = 0
+    while True:
+        if os.path.isfile("{}_{:04d}.hdf5".format(filename, i)):
+            i += 1
+        else:
+            break
+
+        if i > 10000:
+            raise FileNotFoundError(
+                "Could not find the snapshots in the directory")
+
+    frames = tqdm(np.arange(0, i))
+
+    # Creation of first frame
+    fig, ax = plt.subplots(1, 1, figsize=(1, 1), frameon=False)
+
+    data_x, data_y, density = load_and_extract("kelvinhelmholtz_0000.hdf5")
+
+    x = np.linspace(0, 1, dpi)
+    y = np.linspace(0, 1, dpi)
+    xv, yv = np.meshgrid(x, y)
+
+    mesh = si.griddata((data_x, data_y), density, (xv, yv), method="nearest")
+    
+    # Global variable for set_array
+    plot = ax.imshow(mesh, extent=[0, 1, 0, 1], animated=True, interpolation="none")
+
+    anim = FuncAnimation(fig, frame, frames, interval=40, blit=False)
+
+    # Remove all whitespace
+    fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=None, hspace=None)
+
+    # Actually make the movie
+    anim.save("khmovie.mp4", dpi=dpi, bitrate=4096)
diff --git a/examples/KelvinHelmholtz_2D/run.sh b/examples/KelvinHelmholtz_2D/run.sh
index 3d83bcc6dd7c226885ed83c3188f3177c4807154..dbb39caf383279dbc71c2baa125499d115538654 100755
--- a/examples/KelvinHelmholtz_2D/run.sh
+++ b/examples/KelvinHelmholtz_2D/run.sh
@@ -8,7 +8,8 @@ then
 fi
 
 # Run SWIFT
-../swift -s -t 1 kelvinHelmholtz.yml 2>&1 | tee output.log
+../swift -s -t 4 kelvinHelmholtz.yml 2>&1 | tee output.log
 
 # Plot the solution
 python plotSolution.py 6
+python makeMovie.py
diff --git a/examples/KeplerianRing/README.md b/examples/KeplerianRing/README.md
index d486cbfe412c7ff9ca121c87bbc548d0ece078dd..1c361f275d60ef1ca46d696e2e9507bb749e531c 100644
--- a/examples/KeplerianRing/README.md
+++ b/examples/KeplerianRing/README.md
@@ -2,7 +2,8 @@ Keplerian Ring Test
 ===================
 
 This test uses the '2D Keplerian Ring' to assess the accuracy of SPH
-calculations. For more information on the test, please see Cullen & Dehnen 2009
+calculations. It is known to fail with all schemes in SWIFT. For more
+information on the test, please see Cullen & Dehnen 2009
 (http://arxiv.org/abs/1006.1524) Section 4.3.
 
 It is key that for this test in particular that the initial conditions are
diff --git a/examples/KeplerianRing/keplerian_ring.yml b/examples/KeplerianRing/keplerian_ring.yml
index 421d1e89255195cd05a66975d838c59a4ad77c72..cc5db2a06adbe9678207454c6504a6fa315675cf 100644
--- a/examples/KeplerianRing/keplerian_ring.yml
+++ b/examples/KeplerianRing/keplerian_ring.yml
@@ -32,15 +32,10 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  initial_conditions.hdf5        # The file to read
-  shift_x:    0.                   # A shift to apply to all particles read from the ICs (in internal units).
-  shift_y:    0.
-  shift_z:    0.
 
 # External potential parameters
 PointMassPotential:
-  position_x:      5.     # location of external point mass in internal units
-  position_y:      5.     # here just take this to be the centre of the ring
-  position_z:      5.	
-  mass:            1.498351e7     # mass of external point mass in internal units
-  timestep_mult:   0.03     # controls time step
+  position:        [5.,5.,5.]  # location of external point mass in internal units
+  mass:            1.498351e7  # mass of external point mass in internal units
+  timestep_mult:   0.03        # controls time step
   softening:       0.05
diff --git a/examples/KeplerianRing/run.sh b/examples/KeplerianRing/run.sh
index ee8f7b247f38b74a204f9caf2bd543b72c4f94fa..0195846a8839a27083594c20569b1fd4d49f4c16 100755
--- a/examples/KeplerianRing/run.sh
+++ b/examples/KeplerianRing/run.sh
@@ -10,3 +10,9 @@ fi
 
 rm -rf keplerian_ring_*.hdf5
 ../swift -g -s -t 1 -v 1 keplerian_ring.yml 2>&1 | tee output.log
+
+echo
+echo
+echo "This test is expected to fail, please read the README.md file"
+echo
+echo
diff --git a/examples/KeplerianRing/write_gadget.py b/examples/KeplerianRing/write_gadget.py
index d2519cdaf4d78d9c7327419283072b8001e70fcb..0f745782ef81c7500e8b965938eb5eea33ede288 100644
--- a/examples/KeplerianRing/write_gadget.py
+++ b/examples/KeplerianRing/write_gadget.py
@@ -300,7 +300,7 @@ def write_block(f, part_type, pos, vel, ids, mass, int_energy, smoothing, other=
     else:
         data = default_data
 
-    particles = f.create_group(f"PartType{part_type}")
+    particles = f.create_group("PartType" + str(part_type))
 
     for name, value in data.items():
         particles.create_dataset(name, data=value)
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 95057c9bee7d3b4cc001d6c19ca39cab5d8544c4..9b9f76294f6727ac174a34dc45d993c43708fd92 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -24,7 +24,7 @@ AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS)
 AM_LDFLAGS = $(HDF5_LDFLAGS)
 
 # Extra libraries.
-EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS)
+EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS)
 
 # MPI libraries.
 MPI_LIBS = $(METIS_LIBS) $(MPI_THREAD_LIBS)
@@ -56,19 +56,23 @@ swift_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_
 swift_mpi_LDADD =  ../src/.libs/libswiftsim_mpi.a $(MPI_LIBS) $(EXTRA_LIBS)
 
 # Scripts to generate ICs
-EXTRA_DIST = BigCosmoVolume/makeIC.py \
-	     BigPerturbedBox/makeIC_fcc.py \
-	     CosmoVolume/cosmoVolume.yml CosmoVolume/getIC.sh CosmoVolume/run.sh \
-	     CoolingBox/coolingBox.yml CoolingBox/energy_plot.py CoolingBox/makeIC.py CoolingBox/run.sh \
+EXTRA_DIST = CoolingBox/coolingBox.yml CoolingBox/energy_plot.py CoolingBox/makeIC.py CoolingBox/run.sh \
 	     EAGLE_6/eagle_6.yml EAGLE_6/getIC.sh EAGLE_6/README EAGLE_6/run.sh \
 	     EAGLE_12/eagle_12.yml EAGLE_12/getIC.sh EAGLE_12/README EAGLE_12/run.sh \
 	     EAGLE_25/eagle_25.yml EAGLE_25/getIC.sh EAGLE_25/README EAGLE_25/run.sh \
 	     EAGLE_50/eagle_50.yml EAGLE_50/getIC.sh EAGLE_50/README EAGLE_50/run.sh \
 	     EAGLE_100/eagle_100.yml EAGLE_100/getIC.sh EAGLE_100/README EAGLE_100/run.sh \
+	     EAGLE_DMO_12/eagle_12.yml EAGLE_DMO_12/getIC.sh EAGLE_DMO_12/README EAGLE_DMO_12/run.sh \
+	     EAGLE_DMO_25/eagle_25.yml EAGLE_DMO_25/getIC.sh EAGLE_DMO_25/README EAGLE_DMO_25/run.sh \
+	     EAGLE_DMO_50/eagle_50.yml EAGLE_DMO_50/getIC.sh EAGLE_DMO_50/README EAGLE_DMO_50/run.sh \
+	     EAGLE_DMO_100/eagle_100.yml EAGLE_DMO_100/getIC.sh EAGLE_DMO_100/README EAGLE_DMO_100/run.sh \
+	     EvrardCollapse_3D/evrard.yml EvrardCollapse_3D/makeIC.py EvrardCollapse_3D/plotSolution.py EvrardCollapse_3D/run.sh EvrardCollapse_3D/getReference.sh \
 	     ExternalPointMass/externalPointMass.yml ExternalPointMass/makeIC.py ExternalPointMass/run.sh ExternalPointMass/energy_plot.py \
 	     GreshoVortex_2D/getGlass.sh GreshoVortex_2D/gresho.yml GreshoVortex_2D/makeIC.py GreshoVortex_2D/plotSolution.py GreshoVortex_2D/run.sh \
+	     GreshoVortex_3D/getGlass.sh GreshoVortex_3D/gresho.yml GreshoVortex_3D/makeIC.py GreshoVortex_3D/plotSolution.py GreshoVortex_3D/run.sh \
 	     HydrostaticHalo/README HydrostaticHalo/hydrostatic.yml HydrostaticHalo/makeIC.py HydrostaticHalo/run.sh \
 	     HydrostaticHalo/density_profile.py HydrostaticHalo/velocity_profile.py HydrostaticHalo/internal_energy_profile.py HydrostaticHalo/test_energy_conservation.py \
+	     InteractingBlastWaves_1D/run.sh InteractingBlastWaves_1D/makeIC.py InteractingBlastWaves_1D/plotSolution.py InteractingBlastWaves_1D/interactingBlastWaves.yml InteractingBlastWaves_1D/getReference.sh \
 	     IsothermalPotential/README IsothermalPotential/run.sh IsothermalPotential/energy_plot.py IsothermalPotential/isothermal.yml IsothermalPotential/makeIC.py \
 	     KelvinHelmholtz_2D/kelvinHelmholtz.yml KelvinHelmholtz_2D/makeIC.py KelvinHelmholtz_2D/plotSolution.py KelvinHelmholtz_2D/run.sh \
 	     MultiTypes/makeIC.py  MultiTypes/multiTypes.yml MultiTypes/run.sh \
@@ -83,13 +87,15 @@ EXTRA_DIST = BigCosmoVolume/makeIC.py \
              SineWavePotential_1D/makeIC.py SineWavePotential_1D/plotSolution.py SineWavePotential_1D/run.sh SineWavePotential_1D/sineWavePotential.yml \
              SineWavePotential_2D/makeIC.py SineWavePotential_2D/plotSolution.py SineWavePotential_2D/run.sh SineWavePotential_2D/sineWavePotential.yml \
              SineWavePotential_3D/makeIC.py SineWavePotential_3D/plotSolution.py SineWavePotential_3D/run.sh SineWavePotential_3D/sineWavePotential.yml \
+             SmallCosmoVolume/README SmallCosmoVolume/getIC.sh SmallCosmoVolume/run.sh SmallCosmoVolume/small_cosmo_volume.yml \
 	     SodShock_1D/makeIC.py SodShock_1D/plotSolution.py SodShock_1D/run.sh SodShock_1D/sodShock.yml \
 	     SodShock_2D/getGlass.sh SodShock_2D/makeIC.py SodShock_2D/plotSolution.py SodShock_2D/run.sh SodShock_2D/sodShock.yml \
 	     SodShock_3D/getGlass.sh SodShock_3D/makeIC.py SodShock_3D/plotSolution.py SodShock_3D/run.sh SodShock_3D/sodShock.yml \
 	     SquareTest_2D/makeIC.py SquareTest_2D/plotSolution.py SquareTest_2D/run.sh SquareTest_2D/square.yml \
 	     UniformBox_2D/makeIC.py UniformBox_2D/run.sh UniformBox_2D/uniformPlane.yml \
 	     UniformBox_3D/makeICbig.py UniformBox_3D/makeIC.py UniformBox_3D/run.sh UniformBox_3D/uniformBox.yml \
-	     UniformDMBox/makeIC.py
+	     UniformDMBox/makeIC.py \
+             ZeldovichPancake_3D/makeIC.py ZeldovichPancake_3D/zeldovichPancake.yml ZeldovichPancake_3D/run.sh ZeldovichPancake_3D/plotSolution.py
 
 # Default parameter file
 EXTRA_DIST += parameter_example.yml
@@ -106,3 +112,5 @@ EXTRA_DIST += analyse_threadpool_tasks.py \
 EXTRA_DIST += plot_scaling_results.py \
               plot_scaling_results_breakdown.py
 
+# Script for gravity accuracy
+EXTRA_DIST += plot_gravity_checks.py
diff --git a/examples/MoonFormingImpact/README.md b/examples/MoonFormingImpact/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..97a84f67c6aeeff4176a1385381f1cfe9e340c91
--- /dev/null
+++ b/examples/MoonFormingImpact/README.md
@@ -0,0 +1,34 @@
+Canonical Moon-Forming Giant Impact
+===================================
+
+NOTE: This doesn't really work because the EOS are different to Canup (2004) so
+the impactor just glances then flies away!
+
+A version of the canonical moon-forming giant impact of Theia onto the early
+Earth (Canup 2004; Barr 2016). Both bodies are here made of a (Tillotson) iron
+core and granite mantle. Only ~10,000 particles are used for a quick and crude
+simulation.
+
+Setup
+-----
+
+In `swiftsim/`:
+
+`$ ./configure --with-hydro=minimal-multi-mat --with-equation-of-state=planetary`
+`$ make`
+
+In `swiftsim/examples/MoonFormingImpact/`:
+
+`$ ./get_init_cond.sh`
+
+Run
+---
+
+`$ ./run.sh`
+
+Output
+------
+
+`$ python plot.py`
+`$ mplayer anim.mpg`
+
diff --git a/examples/MoonFormingImpact/get_init_cond.sh b/examples/MoonFormingImpact/get_init_cond.sh
new file mode 100755
index 0000000000000000000000000000000000000000..7d63943c2c5dc3bd4ab88e63a2abba62cc3f04a5
--- /dev/null
+++ b/examples/MoonFormingImpact/get_init_cond.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/moon_forming_impact.hdf5
diff --git a/examples/MoonFormingImpact/moon_forming_impact.yml b/examples/MoonFormingImpact/moon_forming_impact.yml
new file mode 100644
index 0000000000000000000000000000000000000000..323adf7f3ac73f41b45b50eaa76a95033dca35d7
--- /dev/null
+++ b/examples/MoonFormingImpact/moon_forming_impact.yml
@@ -0,0 +1,48 @@
+# Define the system of units to use internally.
+InternalUnitSystem:
+    UnitMass_in_cgs:        5.9724e27   # Grams
+    UnitLength_in_cgs:      6.371e8     # Centimeters
+    UnitVelocity_in_cgs:    6.371e8     # 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:       100000              # The end time of the simulation (in internal units).
+    dt_min:         0.001               # The minimal time-step size of the simulation (in internal units).
+    dt_max:         100                 # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+                                        # Common part of the name of output files
+    basename:       snapshots/moon_forming_impact
+    time_first:     0                   # Time of the first output (in internal units)
+    delta_time:     100                 # Time difference between consecutive outputs (in internal units)
+    label_delta:    100                 # Integer increment between snapshot output labels
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+    delta_time:     500                 # 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.
+    CFL_condition:      0.2             # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters for the self-gravity scheme
+Gravity:
+    eta:                    0.025       # Constant dimensionless multiplier for time integration.
+    theta:                  0.7         # Opening angle (Multipole acceptance criterion)
+    comoving_softening:     0.005       # Comoving softening length (in internal units).
+    max_physical_softening: 0.005       # Physical softening length (in internal units).
+
+# Parameters related to the initial conditions
+InitialConditions:
+                                        # The initial conditions file to read
+    file_name:  moon_forming_impact.hdf5
+
+# Parameters related to the equation of state
+EoS:
+    planetary_use_Til:    1                       # Whether to prepare the Tillotson EOS
diff --git a/examples/MoonFormingImpact/plot.py b/examples/MoonFormingImpact/plot.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa0d64a5d0d06709d51b1db231c507e22861f36c
--- /dev/null
+++ b/examples/MoonFormingImpact/plot.py
@@ -0,0 +1,285 @@
+"""
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+###############################################################################
+
+Plotting script for the Canonical Moon-Forming Giant Impact example.
+
+Save a figure for each snapshot in `./plots/` then make them into a simple
+animation with ffmpeg in `./`.
+
+Usage:
+    `$ python  plot.py  time_end  delta_time`
+
+    Sys args:
+        + `time_end` | (opt) int | The time of the last snapshot to plot.
+            Default = 100000
+        + `delta_time` | (opt) int | The time between successive snapshots.
+            Default = 100
+"""
+
+from __future__ import division
+import numpy as np
+import matplotlib
+import matplotlib.pyplot as plt
+import h5py
+import sys
+import subprocess
+
+# Particle array fields
+dtype_picle = [
+    ('m', float), ('x', float), ('y', float), ('z', float), ('v_x', float),
+    ('v_y', float), ('v_z', float), ('ID', int), ('rho', float), ('u', float),
+    ('phi', float), ('P', float), ('h', float), ('mat_ID', int), ('r', float)
+    ]
+
+s_to_hour   = 1 / 60**2
+
+# Snapshot info
+file_snap   = "./snapshots/moon_forming_impact_"
+file_plot   = "./plots/moon_forming_impact_"
+# Number of particles in the target body
+num_target  = 9496
+
+# Material types (copied from src/equation_of_state/planetary/equation_of_state.h)
+type_factor = 100
+Di_type = {
+    'Til'       : 1,
+    'HM80'      : 2,
+    'ANEOS'     : 3,
+    'SESAME'    : 4,
+}
+Di_material = {
+    # Tillotson
+    'Til_iron'      : Di_type['Til']*type_factor,
+    'Til_granite'   : Di_type['Til']*type_factor + 1,
+    'Til_water'     : Di_type['Til']*type_factor + 2,
+    # Hubbard & MacFarlane (1980) Uranus/Neptune
+    'HM80_HHe'      : Di_type['HM80']*type_factor,      # Hydrogen-helium atmosphere
+    'HM80_ice'      : Di_type['HM80']*type_factor + 1,  # H20-CH4-NH3 ice mix
+    'HM80_rock'     : Di_type['HM80']*type_factor + 2,  # SiO2-MgO-FeS-FeO rock mix
+    # ANEOS
+    'ANEOS_iron'        : Di_type['ANEOS']*type_factor,
+    'MANEOS_forsterite' : Di_type['ANEOS']*type_factor + 1,
+    # SESAME
+    'SESAME_iron'   : Di_type['SESAME']*type_factor,
+}
+
+# Material offset for impactor particles
+ID_imp  = 10000
+# Material colours
+Di_mat_colour = {
+    # Target
+    Di_material['Til_iron']             : 'orange',
+    Di_material['Til_granite']          : '#FFF0E0',
+    # Impactor
+    Di_material['Til_iron'] + ID_imp    : 'dodgerblue',
+    Di_material['Til_granite'] + ID_imp : '#A080D0',
+    }
+
+
+def load_snapshot(filename):
+    """ Load the hdf5 snapshot file and return the structured particle array.
+    """
+    # Add extension if needed
+    if (filename[-5:] != ".hdf5"):
+        filename += ".hdf5"
+
+    # Load the hdf5 file
+    with h5py.File(filename, 'r') as f:
+        header      = f['Header'].attrs
+        A2_pos      = f['PartType0/Coordinates'].value
+        A2_vel      = f['PartType0/Velocities'].value
+
+        # Structured array of all particle data
+        A2_picle    = np.empty(header['NumPart_Total'][0],
+                               dtype=dtype_picle)
+
+        A2_picle['x']       = A2_pos[:, 0]
+        A2_picle['y']       = A2_pos[:, 1]
+        A2_picle['z']       = A2_pos[:, 2]
+        A2_picle['v_x']     = A2_vel[:, 0]
+        A2_picle['v_y']     = A2_vel[:, 1]
+        A2_picle['v_z']     = A2_vel[:, 2]
+        A2_picle['m']       = f['PartType0/Masses'].value
+        A2_picle['ID']      = f['PartType0/ParticleIDs'].value
+        A2_picle['rho']     = f['PartType0/Density'].value
+        A2_picle['u']       = f['PartType0/InternalEnergy'].value
+        A2_picle['phi']     = f['PartType0/Potential'].value
+        A2_picle['P']       = f['PartType0/Pressure'].value
+        A2_picle['h']       = f['PartType0/SmoothingLength'].value
+        A2_picle['mat_ID']  = f['PartType0/MaterialID'].value
+
+    return A2_picle
+
+
+def process_particles(A2_picle, num_target):
+    """ Modify things like particle units, material IDs, and coordinate origins.
+    """
+    # Offset material IDs for impactor particles
+    A2_picle['mat_ID'][A2_picle['ID'] >= num_target] += ID_imp
+
+    # Shift coordinates to the centre of the target's core's mass and momentum
+    sel_tar  = np.where(A2_picle['mat_ID'] == Di_material['Til_iron'])[0]
+
+    # Centre of mass
+    m_tot   = np.sum(A2_picle[sel_tar]['m'])
+    x_com   = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['x']) / m_tot
+    y_com   = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['y']) / m_tot
+    z_com   = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['z']) / m_tot
+
+    # Change origin to the centre-of-mass
+    A2_picle['x']   -= x_com
+    A2_picle['y']   -= y_com
+    A2_picle['z']   -= z_com
+    A2_picle['r']   = np.sqrt(
+        A2_picle['x']**2 + A2_picle['y']**2 + A2_picle['z']**2
+        )
+
+    # Centre of momentum
+    v_x_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_x']) / m_tot
+    v_y_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_y']) / m_tot
+    v_z_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_z']) / m_tot
+
+    # Change to the centre-of-momentum frame of reference
+    A2_picle['v_x'] -= v_x_com
+    A2_picle['v_y'] -= v_y_com
+    A2_picle['v_z'] -= v_z_com
+
+    return A2_picle
+
+
+def plot_snapshot(A2_picle, filename, time, ax_lim=100, dz=0.1):
+    """ Plot the snapshot particles and save the figure.
+    """
+    # Add extension if needed
+    if (filename[-5:] != ".png"):
+        filename += ".png"
+
+    fig = plt.figure(figsize=(9, 9))
+    ax  = fig.add_subplot(111, aspect='equal')
+
+    # Plot slices in z below zero
+    for z in np.arange(-ax_lim, 0, dz):
+        sel_z       = np.where((z < A2_picle['z']) & (A2_picle['z'] < z+dz))[0]
+        A2_picle_z  = A2_picle[sel_z]
+
+        # Plot each material
+        for mat_ID, colour in Di_mat_colour.iteritems():
+            sel_col = np.where(A2_picle_z['mat_ID'] == mat_ID)[0]
+
+            ax.scatter(
+                A2_picle_z[sel_col]['x'], A2_picle_z[sel_col]['y'],
+                c=colour, edgecolors='none', marker='.', s=50, alpha=0.7
+                )
+
+    # Axes etc.
+    ax.set_axis_bgcolor('k')
+
+    ax.set_xlabel("x Position ($R_\oplus$)")
+    ax.set_ylabel("y Position ($R_\oplus$)")
+
+    ax.set_xlim(-ax_lim, ax_lim)
+    ax.set_ylim(-ax_lim, ax_lim)
+
+    plt.text(
+        -0.92*ax_lim, 0.85*ax_lim, "%.1f h" % (time*s_to_hour), fontsize=20,
+        color='w'
+        )
+
+    # Font sizes
+    for item in (
+        [ax.title, ax.xaxis.label, ax.yaxis.label] + ax.get_xticklabels() +
+        ax.get_yticklabels()
+        ):
+        item.set_fontsize(20)
+
+    plt.tight_layout()
+
+    plt.savefig(filename)
+    plt.close()
+
+
+if __name__ == '__main__':
+    # Sys args
+    try:
+        time_end    = int(sys.argv[1])
+
+        try:
+            delta_time  = int(sys.argv[2])
+        except IndexError:
+            delta_time  = 100
+    except IndexError:
+        time_end    = 100000
+        delta_time  = 100
+
+    # Load and plot each snapshot
+    for i_snap in range(int(time_end/delta_time) + 1):
+        snap_time   = i_snap * delta_time
+        print "\rPlotting snapshot %06d (%d of %d)" % (
+            snap_time, i_snap+1, int(time_end/delta_time)
+            ),
+        sys.stdout.flush()
+
+        # Load particle data
+        filename    = "%s%06d" % (file_snap, snap_time)
+        A2_picle    = load_snapshot(filename)
+
+        # Process particle data
+        A2_picle    = process_particles(A2_picle, num_target)
+
+        # Plot particles
+        filename    = "%s%06d" % (file_plot, snap_time)
+        plot_snapshot(A2_picle, filename, snap_time)
+
+    # Animation
+    command = (
+        "ffmpeg -framerate 12 -i plots/moon_forming_impact_%*.png -r 25 "
+        "anim.mpg -y"
+        )
+    print "\n%s\n" % command
+    subprocess.check_output(command, shell=True)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/MoonFormingImpact/run.sh b/examples/MoonFormingImpact/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..165dae3a24a9c30960959fbb37aa6e1da2eb851f
--- /dev/null
+++ b/examples/MoonFormingImpact/run.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+../swift -G -s -t 8 moon_forming_impact.yml
diff --git a/examples/MultiTypes/multiTypes.yml b/examples/MultiTypes/multiTypes.yml
index 5872634cd620e0b7e2b7edb3a38e363c34229b9a..04647f0f00e69f5baf2560aca0feeb14a26cc50a 100644
--- a/examples/MultiTypes/multiTypes.yml
+++ b/examples/MultiTypes/multiTypes.yml
@@ -35,8 +35,6 @@ InitialConditions:
 
 # External potential parameters
 PointMassPotential:
-  position_x:      50.     # location of external point mass in internal units
-  position_y:      50.
-  position_z:      50.	
+  position:        [50.,50.,50.] # location of external point mass in internal units
   mass:            1e10     # mass of external point mass in internal units
   timestep_mult:   1e-2
diff --git a/examples/Noh_3D/noh.yml b/examples/Noh_3D/noh.yml
index 88119827501e17cc26742e8cad92a3611e83faa7..cc15af7ec190cd2c10cdff3a3ccb3f0beaf7e177 100644
--- a/examples/Noh_3D/noh.yml
+++ b/examples/Noh_3D/noh.yml
@@ -18,7 +18,7 @@ Snapshots:
   basename:            noh # Common part of the name of output files
   time_first:          0.    # Time of the first output (in internal units)
   delta_time:          5e-2  # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/SedovBlast_3D/sedov.yml b/examples/SedovBlast_3D/sedov.yml
index 2df2c432cef8afec49c687b643a872b06f9abb60..75849e33c0c644a18cd7357f901699d0d682c160 100644
--- a/examples/SedovBlast_3D/sedov.yml
+++ b/examples/SedovBlast_3D/sedov.yml
@@ -18,8 +18,8 @@ Snapshots:
   basename:            sedov # Common part of the name of output files
   time_first:          0.    # Time of the first output (in internal units)
   delta_time:          1e-2  # Time difference between consecutive outputs (in internal units)
-  compression:         4
-  
+  compression:         1
+ 
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-3 # Time between statistics output
@@ -33,4 +33,4 @@ SPH:
 InitialConditions:
   file_name:                    ./sedov.hdf5          
   smoothing_length_scaling:     3.33
-
+ 
diff --git a/examples/SmallCosmoVolume/README b/examples/SmallCosmoVolume/README
new file mode 100644
index 0000000000000000000000000000000000000000..68c137aee30c08bb476b760c75dceaa5e1ede87e
--- /dev/null
+++ b/examples/SmallCosmoVolume/README
@@ -0,0 +1,9 @@
+Small LCDM cosmological simulation generated by C. Power. Cosmology
+is WMAP9 and the box is 100Mpc/h in size with 64^3 particles.
+We use a softening length of 1/25th of the mean inter-particle separation.
+
+The ICs have been generated to run with Gadget-2 so we need to switch
+on the options to cancel the h-factors and a-factors at reading time.
+
+MD5 checksum of the ICs:
+2a9c603ffb1f6d29f3d98a3ecb9d3238  small_cosmo_volume.hdf5
diff --git a/examples/SmallCosmoVolume/getIC.sh b/examples/SmallCosmoVolume/getIC.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3b8136cc5aca00a25792655c6c505cfeeb0f2bc9
--- /dev/null
+++ b/examples/SmallCosmoVolume/getIC.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/small_cosmo_volume.hdf5
+
diff --git a/examples/SmallCosmoVolume/run.sh b/examples/SmallCosmoVolume/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..fe67706d7512d6f4ff1537ce008ce3a52a6ce6a6
--- /dev/null
+++ b/examples/SmallCosmoVolume/run.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+ # Generate the initial conditions if they are not present.
+if [ ! -e small_cosmo_volume.hdf5 ]
+then
+    echo "Fetching initial conditions for the small cosmological volume example..."
+    ./getIC.sh
+fi
+
+../swift -c -G -t 8 small_cosmo_volume.yml 2>&1 | tee output.log
+
diff --git a/examples/SmallCosmoVolume/small_cosmo_volume.yml b/examples/SmallCosmoVolume/small_cosmo_volume.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3d32e0f062450f52161950a80dbf001c73b3c11d
--- /dev/null
+++ b/examples/SmallCosmoVolume/small_cosmo_volume.yml
@@ -0,0 +1,50 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.989e43      # 10^10 M_sun in grams
+  UnitLength_in_cgs:   3.085678e24   # Mpc in centimeters
+  UnitVelocity_in_cgs: 1e5           # km/s in centimeters per second
+  UnitCurrent_in_cgs:  1             # Amperes
+  UnitTemp_in_cgs:     1             # Kelvin
+
+# WMAP9 cosmology
+Cosmology:
+  Omega_m:        0.276
+  Omega_lambda:   0.724
+  Omega_b:        0.0455
+  h:              0.703
+  a_begin:        0.0196078
+  a_end:          1.0
+
+# Parameters governing the time integration
+TimeIntegration:
+  dt_min:     1e-6 
+  dt_max:     1e-2 
+
+# Parameters for the self-gravity scheme
+Gravity:
+  eta:          0.025         
+  theta:        0.3           
+  comoving_softening:     0.08
+  max_physical_softening: 0.08
+  mesh_side_length:         32
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            snap
+  delta_time:          1.02
+  scale_factor_first:  0.02
+  
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1.02
+  scale_factor_first:  0.02
+  
+Scheduler:
+  max_top_level_cells: 8
+  cell_split_size:     50
+  
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  small_cosmo_volume.hdf5
+  cleanup_h_factors:           1    
+  cleanup_velocity_factors:    1  
diff --git a/examples/SodShockSpherical_3D/sodShock.yml b/examples/SodShockSpherical_3D/sodShock.yml
index 52c06dd65ec0b37a0ec0707315ccd15356a7b2d6..3fc4a1fb2b8cc5f6a603abf4c87ac99c7647b9bd 100644
--- a/examples/SodShockSpherical_3D/sodShock.yml
+++ b/examples/SodShockSpherical_3D/sodShock.yml
@@ -18,7 +18,7 @@ Snapshots:
   basename:            sodShock # Common part of the name of output files
   time_first:          0.       # Time of the first output (in internal units)
   delta_time:          0.1      # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/SodShock_3D/sodShock.yml b/examples/SodShock_3D/sodShock.yml
index 26fbfe4faacf2bbbfd9b077bf9f9c075ce93ef6d..6042c8090d00fef5467a7fed3d6f5a104c626f43 100644
--- a/examples/SodShock_3D/sodShock.yml
+++ b/examples/SodShock_3D/sodShock.yml
@@ -18,7 +18,7 @@ Snapshots:
   basename:            sodShock # Common part of the name of output files
   time_first:          0.       # Time of the first output (in internal units)
   delta_time:          0.2      # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/UranusImpact/README.md b/examples/UranusImpact/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..178a3937ecbe527df8e8e82a0d8fd8bcbf9dbef7
--- /dev/null
+++ b/examples/UranusImpact/README.md
@@ -0,0 +1,40 @@
+Uranus Giant Impact
+===================
+
+A simple version of the low angular momentum impact onto the early Uranus shown
+in Kegerreis et al. (2018), Fig. 2; with only ~10,000 particles for a quick and
+crude simulation.
+
+The collision of a 2 Earth mass impactor onto a proto-Uranus that can explain
+the spin of the present-day planet, with an angular momentum of 2e36 kg m^2 s^-1
+and velocity at infinity of 5 km s^-1 for a relatively head-on impact.
+
+Both bodies have a rocky core and icy mantle, with a hydrogen-helium atmosphere
+on the target as well. Although with this low number of particles it cannot be
+modelled in any detail.
+
+Setup
+-----
+
+In `swiftsim/`:
+
+`$ ./configure --with-hydro=minimal-multi-mat --with-equation-of-state=planetary`
+
+`$ make`
+
+In `swiftsim/examples/UranusImpact/`:
+
+`$ ./get_init_cond.sh`
+
+Run
+---
+
+`$ ./run.sh`
+
+Analysis
+--------
+
+`$ python plot.py`
+
+`$ mplayer anim.mpg`
+
diff --git a/examples/UranusImpact/get_init_cond.sh b/examples/UranusImpact/get_init_cond.sh
new file mode 100755
index 0000000000000000000000000000000000000000..e12e009adfbd727cb2452ac21c477b3ecd77b9c9
--- /dev/null
+++ b/examples/UranusImpact/get_init_cond.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/uranus_impact.hdf5
diff --git a/examples/UranusImpact/plot.py b/examples/UranusImpact/plot.py
new file mode 100644
index 0000000000000000000000000000000000000000..3db3bf21bb15862ec524a069c38e47564b48df1d
--- /dev/null
+++ b/examples/UranusImpact/plot.py
@@ -0,0 +1,291 @@
+"""
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+###############################################################################
+
+Plotting script for the Uranus Giant Impact example.
+
+Save a figure for each snapshot in `./plots/` then make them into a simple
+animation with ffmpeg in `./`.
+
+The snapshot plots show all particles with z < 0, coloured by their material.
+
+Usage:
+    `$ python  plot.py  time_end  delta_time`
+
+    Sys args:
+        + `time_end` | (opt) int | The time of the last snapshot to plot.
+            Default = 100000
+        + `delta_time` | (opt) int | The time between successive snapshots.
+            Default = 500
+"""
+
+from __future__ import division
+import numpy as np
+import matplotlib
+import matplotlib.pyplot as plt
+import h5py
+import sys
+import subprocess
+
+# Particle array fields
+dtype_picle = [
+    ('m', float), ('x', float), ('y', float), ('z', float), ('v_x', float),
+    ('v_y', float), ('v_z', float), ('ID', int), ('rho', float), ('u', float),
+    ('phi', float), ('P', float), ('h', float), ('mat_ID', int), ('r', float)
+    ]
+
+s_to_hour   = 1 / 60**2
+R_Ea        = 6.371e6
+
+# Default sys args
+time_end_default    = 100000
+delta_time_default  = 500
+
+# Snapshot info
+file_snap   = "./snapshots/uranus_impact_"
+file_plot   = "./plots/uranus_impact_"
+
+# Number of particles in the target body
+num_target  = 8992
+
+# Material types (copied from src/equation_of_state/planetary/equation_of_state.h)
+type_factor = 100
+Di_type = {
+    'Til'       : 1,
+    'HM80'      : 2,
+    'ANEOS'     : 3,
+    'SESAME'    : 4,
+}
+Di_material = {
+    # Tillotson
+    'Til_iron'      : Di_type['Til']*type_factor,
+    'Til_granite'   : Di_type['Til']*type_factor + 1,
+    'Til_water'     : Di_type['Til']*type_factor + 2,
+    # Hubbard & MacFarlane (1980) Uranus/Neptune
+    'HM80_HHe'      : Di_type['HM80']*type_factor,      # Hydrogen-helium atmosphere
+    'HM80_ice'      : Di_type['HM80']*type_factor + 1,  # H20-CH4-NH3 ice mix
+    'HM80_rock'     : Di_type['HM80']*type_factor + 2,  # SiO2-MgO-FeS-FeO rock mix
+    # ANEOS
+    'ANEOS_iron'        : Di_type['ANEOS']*type_factor,
+    'MANEOS_forsterite' : Di_type['ANEOS']*type_factor + 1,
+    # SESAME
+    'SESAME_iron'   : Di_type['SESAME']*type_factor,
+}
+
+# Material offset for impactor particles
+ID_imp  = 10000
+# Material colours
+Di_mat_colour = {
+    # Target
+    Di_material['HM80_HHe']     : '#33DDFF',
+    Di_material['HM80_ice']     : 'lightsteelblue',
+    Di_material['HM80_rock']    : 'slategrey',
+    # Impactor
+    Di_material['HM80_ice'] + ID_imp    : '#A080D0',
+    Di_material['HM80_rock'] + ID_imp   : '#706050',
+    }
+
+
+def load_snapshot(filename):
+    """ Load the hdf5 snapshot file and return the structured particle array.
+    """
+    # Add extension if needed
+    if (filename[-5:] != ".hdf5"):
+        filename += ".hdf5"
+
+    # Load the hdf5 file
+    with h5py.File(filename, 'r') as f:
+        header      = f['Header'].attrs
+        A2_pos      = f['PartType0/Coordinates'].value
+        A2_vel      = f['PartType0/Velocities'].value
+
+        # Structured array of all particle data
+        A2_picle    = np.empty(header['NumPart_Total'][0],
+                               dtype=dtype_picle)
+
+        A2_picle['x']       = A2_pos[:, 0]
+        A2_picle['y']       = A2_pos[:, 1]
+        A2_picle['z']       = A2_pos[:, 2]
+        A2_picle['v_x']     = A2_vel[:, 0]
+        A2_picle['v_y']     = A2_vel[:, 1]
+        A2_picle['v_z']     = A2_vel[:, 2]
+        A2_picle['m']       = f['PartType0/Masses'].value
+        A2_picle['ID']      = f['PartType0/ParticleIDs'].value
+        A2_picle['rho']     = f['PartType0/Density'].value
+        A2_picle['u']       = f['PartType0/InternalEnergy'].value
+        A2_picle['phi']     = f['PartType0/Potential'].value
+        A2_picle['P']       = f['PartType0/Pressure'].value
+        A2_picle['h']       = f['PartType0/SmoothingLength'].value
+        A2_picle['mat_ID']  = f['PartType0/MaterialID'].value
+
+    return A2_picle
+
+
+def process_particles(A2_picle, num_target):
+    """ Modify things like particle units, material IDs, and coordinate origins.
+    """
+    # Offset material IDs for impactor particles
+    A2_picle['mat_ID'][A2_picle['ID'] >= num_target] += ID_imp
+
+    # Shift coordinates to the centre of the target's core's mass and momentum
+    sel_tar  = np.where(A2_picle['mat_ID'] == Di_material['HM80_rock'])[0]
+
+    # Centre of mass
+    m_tot   = np.sum(A2_picle[sel_tar]['m'])
+    x_com   = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['x']) / m_tot
+    y_com   = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['y']) / m_tot
+    z_com   = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['z']) / m_tot
+
+    # Change origin to the centre-of-mass
+    A2_picle['x']   -= x_com
+    A2_picle['y']   -= y_com
+    A2_picle['z']   -= z_com
+    A2_picle['r']   = np.sqrt(
+        A2_picle['x']**2 + A2_picle['y']**2 + A2_picle['z']**2
+        )
+
+    # Centre of momentum
+    v_x_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_x']) / m_tot
+    v_y_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_y']) / m_tot
+    v_z_com = np.sum(A2_picle[sel_tar]['m'] * A2_picle[sel_tar]['v_z']) / m_tot
+
+    # Change to the centre-of-momentum frame of reference
+    A2_picle['v_x'] -= v_x_com
+    A2_picle['v_y'] -= v_y_com
+    A2_picle['v_z'] -= v_z_com
+
+    return A2_picle
+
+
+def plot_snapshot(A2_picle, filename, time, ax_lim=13, dz=0.1):
+    """ Plot the snapshot particles and save the figure.
+    """
+    # Add extension if needed
+    if (filename[-5:] != ".png"):
+        filename += ".png"
+
+    fig = plt.figure(figsize=(9, 9))
+    ax  = fig.add_subplot(111, aspect='equal')
+
+    # Plot slices in z below zero
+    for z in np.arange(-ax_lim, 0, dz):
+        sel_z       = np.where((z < A2_picle['z']) & (A2_picle['z'] < z+dz))[0]
+        A2_picle_z  = A2_picle[sel_z]
+
+        # Plot each material
+        for mat_ID, colour in Di_mat_colour.iteritems():
+            sel_col = np.where(A2_picle_z['mat_ID'] == mat_ID)[0]
+
+            ax.scatter(
+                A2_picle_z[sel_col]['x'], A2_picle_z[sel_col]['y'],
+                c=colour, edgecolors='none', marker='.', s=50, alpha=0.7
+                )
+
+    # Axes etc.
+    ax.set_axis_bgcolor('k')
+
+    ax.set_xlabel("x Position ($R_\oplus$)")
+    ax.set_ylabel("y Position ($R_\oplus$)")
+
+    ax.set_xlim(-ax_lim, ax_lim)
+    ax.set_ylim(-ax_lim, ax_lim)
+
+    plt.text(
+        -0.92*ax_lim, 0.85*ax_lim, "%.1f h" % (time*s_to_hour), fontsize=20,
+        color='w'
+        )
+
+    # Font sizes
+    for item in (
+        [ax.title, ax.xaxis.label, ax.yaxis.label] + ax.get_xticklabels() +
+        ax.get_yticklabels()
+        ):
+        item.set_fontsize(20)
+
+    plt.tight_layout()
+
+    plt.savefig(filename)
+    plt.close()
+
+
+if __name__ == '__main__':
+    # Sys args
+    try:
+        time_end    = int(sys.argv[1])
+        try:
+            delta_time  = int(sys.argv[2])
+        except IndexError:
+            delta_time  = delta_time_default
+    except IndexError:
+        time_end    = time_end_default
+        delta_time  = delta_time_default
+
+    # Load and plot each snapshot
+    for i_snap in range(int(time_end/delta_time) + 1):
+        snap_time   = i_snap * delta_time
+        print "\rPlotting snapshot %06d (%d of %d)" % (
+            snap_time, i_snap+1, int(time_end/delta_time)
+            ),
+        sys.stdout.flush()
+
+        # Load particle data
+        filename    = "%s%06d" % (file_snap, snap_time)
+        A2_picle    = load_snapshot(filename)
+
+        # Process particle data
+        A2_picle    = process_particles(A2_picle, num_target)
+
+        # Plot particles
+        filename    = "%s%06d" % (file_plot, snap_time)
+        plot_snapshot(A2_picle, filename, snap_time)
+
+    # Animation
+    command = (
+        "ffmpeg -framerate 10 -i plots/uranus_impact_%*.png -r 25 anim.mpg -y"
+        )
+    print "\n$ %s\n" % command
+    subprocess.call(command, shell=True)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/UranusImpact/run.sh b/examples/UranusImpact/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c6773b7e40fff3fa312dfcb5ba4ada9d9e4b1b8d
--- /dev/null
+++ b/examples/UranusImpact/run.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+../swift -G -s -t 8 uranus_impact.yml
diff --git a/examples/UranusImpact/uranus_impact.yml b/examples/UranusImpact/uranus_impact.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fabddca00f80fcdd79ff6114ff0544cd251046f4
--- /dev/null
+++ b/examples/UranusImpact/uranus_impact.yml
@@ -0,0 +1,51 @@
+# Define the system of units to use internally.
+InternalUnitSystem:
+    UnitMass_in_cgs:        5.9724e27   # Grams
+    UnitLength_in_cgs:      6.371e8     # Centimeters
+    UnitVelocity_in_cgs:    6.371e8     # 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:       100000              # The end time of the simulation (in internal units).
+    dt_min:         0.001               # The minimal time-step size of the simulation (in internal units).
+    dt_max:         100                 # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+                                        # Common part of the name of output files
+    basename:       snapshots/uranus_impact
+    time_first:     0                   # Time of the first output (in internal units)
+    delta_time:     500                 # Time difference between consecutive outputs (in internal units)
+    label_delta:    500                 # Integer increment between snapshot output labels
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+    delta_time:     1000                # 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.
+    CFL_condition:      0.2             # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters for the self-gravity scheme
+Gravity:
+    eta:                    0.025       # Constant dimensionless multiplier for time integration.
+    theta:                  0.7         # Opening angle (Multipole acceptance criterion)
+    comoving_softening:     0.01        # Comoving softening length (in internal units).
+    max_physical_softening: 0.01        # Physical softening length (in internal units).
+
+# Parameters related to the initial conditions
+InitialConditions:
+    file_name:      uranus_impact.hdf5  # The initial conditions file to read
+
+# Parameters related to the equation of state
+EoS:
+    planetary_use_HM80:   1                       # Whether to prepare the Hubbard & MacFarlane (1980) EOS
+                                        # Table file paths
+    planetary_HM80_HHe_table_file:    /gpfs/data/dc-kege1/gihr_data/P_rho_u_HHe.txt
+    planetary_HM80_ice_table_file:    /gpfs/data/dc-kege1/gihr_data/P_rho_u_ice.txt
+    planetary_HM80_rock_table_file:   /gpfs/data/dc-kege1/gihr_data/P_rho_u_roc.txt
diff --git a/examples/VacuumSpherical_3D/vacuum.yml b/examples/VacuumSpherical_3D/vacuum.yml
index 92164a18a46404ad7730f3411dc14953139501bb..8792f029d97f413882ae0ea6c8603d64efaddbfa 100644
--- a/examples/VacuumSpherical_3D/vacuum.yml
+++ b/examples/VacuumSpherical_3D/vacuum.yml
@@ -18,7 +18,7 @@ Snapshots:
   basename:            vacuum # Common part of the name of output files
   time_first:          0.       # Time of the first output (in internal units)
   delta_time:          0.05     # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/Vacuum_3D/vacuum.yml b/examples/Vacuum_3D/vacuum.yml
index 45a6b73d54de69d71e194ec074ebdd00cd6a57c0..cf44d2441f5009d2fc75084a2c872e3618e40912 100644
--- a/examples/Vacuum_3D/vacuum.yml
+++ b/examples/Vacuum_3D/vacuum.yml
@@ -18,7 +18,7 @@ Snapshots:
   basename:            vacuum # Common part of the name of output files
   time_first:          0.       # Time of the first output (in internal units)
   delta_time:          0.1     # Time difference between consecutive outputs (in internal units)
-  compression:         4
+  compression:         1
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/ZeldovichPancake_3D/makeIC.py b/examples/ZeldovichPancake_3D/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..15fb8bdef95f6e830e78f4d1b2c419051a6f00af
--- /dev/null
+++ b/examples/ZeldovichPancake_3D/makeIC.py
@@ -0,0 +1,147 @@
+################################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+
+import h5py
+from numpy import *
+
+# Generates a swift IC file for the 1D Zeldovich pancake
+
+# Parameters
+T_i = 100.           # Initial temperature of the gas (in K)
+z_c = 1.             # Redshift of caustic formation (non-linear collapse)
+z_i = 100.           # Initial redshift
+gamma = 5./3.        # Gas adiabatic index
+numPart_1D = 32      # Number of particles along each dimension
+
+
+# Some units
+Mpc_in_m = 3.085678e22
+Msol_in_kg = 1.989e30
+Gyr_in_s = 3.085678e19
+mH_in_kg = 1.6737236e-27
+k_in_J_K = 1.38064852e-23
+
+# Parameters
+rho_0 = 1.8788e-26 # h^2 kg m^-3
+H_0 = 1. / Mpc_in_m * 10**5 # s^-1
+lambda_i = 64. / H_0 * 10**5 # h^-1 m (= 64 h^-1 Mpc)
+x_min = -0.5 * lambda_i
+x_max = 0.5 * lambda_i
+fileName = "zeldovichPancake.hdf5"
+
+unit_l_in_si = Mpc_in_m
+unit_m_in_si = Msol_in_kg * 1.e10
+unit_t_in_si = Gyr_in_s
+unit_v_in_si = unit_l_in_si / unit_t_in_si
+unit_u_in_si = unit_v_in_si**2
+
+numPart = numPart_1D**3
+
+#---------------------------------------------------
+
+# Get the frequency of the initial perturbation
+k_i = 2. * pi / lambda_i
+
+# Get the redshift prefactor for the initial positions
+zfac = (1. + z_c) / (1. + z_i)
+
+# Set box size and interparticle distance
+boxSize = x_max - x_min
+delta_x = boxSize / numPart_1D
+
+# Get the particle mass
+a_i = 1. / (1. + z_i)
+m_i = boxSize**3 * rho_0 / numPart
+
+# Build the arrays
+coords = zeros((numPart, 3))
+v = zeros((numPart, 3))
+ids = linspace(1, numPart, numPart)
+m = zeros(numPart)
+h = zeros(numPart)
+u = zeros(numPart)
+
+# Set the particles on the left
+for i in range(numPart_1D):
+  for j in range(numPart_1D):
+    for k in range(numPart_1D):
+      index = i * numPart_1D**2 + j * numPart_1D + k
+      q = x_min + (i + 0.5) * delta_x
+      coords[index,0] = q - zfac * sin(k_i * q) / k_i - x_min
+      coords[index,1] = (j + 0.5) * delta_x
+      coords[index,2] = (k + 0.5) * delta_x
+      T = T_i * (1. / (1. - zfac * cos(k_i * q)))**(2. / 3.)
+      u[index] = k_in_J_K * T / (gamma - 1.) / mH_in_kg
+      h[index] = 1.2348 * delta_x
+      m[index] = m_i
+      v[index,0] = -H_0 * (1. + z_c) / sqrt(1. + z_i) * sin(k_i * q) / k_i
+      v[index,1] = 0.
+      v[index,2] = 0.
+
+# Unit conversion
+coords /= unit_l_in_si
+v /= unit_v_in_si
+m /= unit_m_in_si
+h /= unit_l_in_si
+u /= unit_u_in_si
+
+boxSize /= unit_l_in_si
+
+#File
+file = h5py.File(fileName, 'w')
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = [boxSize, boxSize, boxSize]
+grp.attrs["NumPart_Total"] =  [numPart, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = 0
+grp.attrs["Dimension"] = 3
+
+#Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 100. * unit_l_in_si
+grp.attrs["Unit mass in cgs (U_M)"] = 1000. * unit_m_in_si
+grp.attrs["Unit time in cgs (U_t)"] = 1. * unit_t_in_si
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+#Particle group
+grp = file.create_group("/PartType0")
+grp.create_dataset('Coordinates', data=coords, dtype='d')
+grp.create_dataset('Velocities', data=v, dtype='f')
+grp.create_dataset('Masses', data=m, dtype='f')
+grp.create_dataset('SmoothingLength', data=h, dtype='f')
+grp.create_dataset('InternalEnergy', data=u, dtype='f')
+grp.create_dataset('ParticleIDs', data=ids, dtype='L')
+
+file.close()
+
+import pylab as pl
+
+pl.plot(coords[:,0], v[:,0], "k.")
+pl.show()
diff --git a/examples/ZeldovichPancake_3D/plotSolution.py b/examples/ZeldovichPancake_3D/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..163e61eb9d3738c39912c8c42c0b6c6bb9990cac
--- /dev/null
+++ b/examples/ZeldovichPancake_3D/plotSolution.py
@@ -0,0 +1,193 @@
+################################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+
+# Computes the analytical solution of the Zeldovich pancake and compares with
+# the simulation result
+
+# Parameters
+T_i = 100.           # Initial temperature of the gas (in K)
+z_c = 1.             # Redshift of caustic formation (non-linear collapse)
+z_i = 100.           # Initial redshift
+
+# Physical constants needed for internal energy to temperature conversion
+k_in_J_K = 1.38064852e-23
+mH_in_kg = 1.6737236e-27
+
+import matplotlib
+matplotlib.use("Agg")
+from pylab import *
+import h5py
+import os.path
+
+# Plot parameters
+params = {'axes.labelsize': 10,
+'axes.titlesize': 10,
+'font.size': 12,
+'legend.fontsize': 12,
+'xtick.labelsize': 10,
+'ytick.labelsize': 10,
+'text.usetex': True,
+ 'figure.figsize' : (9.90,6.45),
+'figure.subplot.left'    : 0.045,
+'figure.subplot.right'   : 0.99,
+'figure.subplot.bottom'  : 0.05,
+'figure.subplot.top'     : 0.99,
+'figure.subplot.wspace'  : 0.15,
+'figure.subplot.hspace'  : 0.12,
+'lines.markersize' : 6,
+'lines.linewidth' : 3.,
+'text.latex.unicode': True
+}
+rcParams.update(params)
+rc('font',**{'family':'sans-serif','sans-serif':['Times']})
+
+snap = int(sys.argv[1])
+
+# Read the simulation data
+sim = h5py.File("zeldovichPancake_%04d.hdf5"%snap, "r")
+boxSize = sim["/Header"].attrs["BoxSize"][0]
+time = sim["/Header"].attrs["Time"][0]
+redshift = sim["/Header"].attrs["Redshift"][0]
+a = sim["/Header"].attrs["Scale-factor"][0]
+scheme = sim["/HydroScheme"].attrs["Scheme"]
+kernel = sim["/HydroScheme"].attrs["Kernel function"]
+neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"]
+eta = sim["/HydroScheme"].attrs["Kernel eta"]
+git = sim["Code"].attrs["Git Revision"]
+
+# Cosmological parameters
+H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
+gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0]
+
+x = sim["/PartType0/Coordinates"][:,0]
+v = sim["/PartType0/Velocities"][:,0]
+u = sim["/PartType0/InternalEnergy"][:]
+S = sim["/PartType0/Entropy"][:]
+P = sim["/PartType0/Pressure"][:]
+rho = sim["/PartType0/Density"][:]
+m = sim["/PartType0/Masses"][:]
+phi = sim["/PartType0/Potential"][:]
+
+x -= 0.5 * boxSize
+
+# Check for Gadget solution
+filename_g = "snapshot_%03d.hdf5"%(snap)
+if os.path.exists(filename_g):
+    sim_g = h5py.File(filename_g, "r")
+    x_g = sim_g["/PartType0/Coordinates"][:,0]
+    v_g = sim_g["/PartType0/Velocities"][:,0]
+    u_g = sim_g["/PartType0/InternalEnergy"][:]
+    rho_g = sim_g["/PartType0/Density"][:]
+    phi_g = sim_g["/PartType0/Potential"][:]
+    a_g = sim_g["/Header"].attrs["Time"]
+    print "Gadget Scale-factor:", a_g, "redshift:", 1/a_g - 1.
+    
+    x_g -= 0.5 * boxSize
+else:
+    x_g = np.zeros(1)
+    v_g = np.zeros(1)
+    u_g = np.zeros(1)
+    rho_g = np.zeros(1)
+    phi_g = np.zeros(1)
+
+# Derived parameters
+rho_0 = m.sum() / (boxSize**3) # critical density of the box
+lambda_i = boxSize             # wavelength of the perturbation
+
+
+# Solution taken from Springel 2010. Eqs. 127 - 130
+q = linspace(-0.5 * lambda_i, 0.5 * lambda_i, 256)
+k_i = 2. * pi / lambda_i
+zfac = (1. + z_c) / (1. + redshift)
+x_s = q - zfac * sin(k_i * q) / k_i
+rho_s = rho_0 / (1. - zfac * cos(k_i * q))
+v_s = -H_0 * (1. + z_c) / sqrt(1. + redshift) * sin(k_i * q) / k_i
+T_s = T_i * (((1. + redshift) / ( 1. + z_i))**3. * rho_s / rho_0)**(2. / 3.)
+
+
+unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"]
+unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"]
+unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"]
+
+unit_length_in_si = 0.01 * unit_length_in_cgs
+unit_mass_in_si = 0.001 * unit_mass_in_cgs
+unit_time_in_si = unit_time_in_cgs
+
+# Plot the interesting quantities
+figure()
+
+# Velocity profile --------------------------------
+subplot(231)
+if np.size(x_g) > 1:
+    plot(x_g, v_g, 's', color='g', alpha=0.8, lw=1.2, ms=4)
+plot(x, v, '.', color='r', ms=4.0)
+plot(x_s, v_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Comoving Position}}~x$", labelpad=0)
+ylabel("${\\rm{Peculiar Velocity}}~v_x$", labelpad=0)
+
+
+# Density profile --------------------------------
+subplot(232)#, yscale="log")
+if np.size(x_g) > 1:
+    plot(x_g, rho_g/rho_0, 's', color='g', alpha=0.8, lw=1.2, ms=4)
+plot(x, rho/rho_0, '.', color='r', ms=4.0)
+plot(x_s, rho_s/rho_0, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Comoving Position}}~x$", labelpad=0)
+ylabel("${\\rm{Density}}~\\rho / \\rho_0$", labelpad=0)
+
+# Potential profile --------------------------------
+subplot(233)
+if np.size(x_g) > 1:
+    plot(x_g, phi_g, 's', color='g', alpha=0.8, lw=1.2, ms=4)
+plot(x, phi, '.', color='r', ms=4.0)
+xlabel("${\\rm{Comoving Position}}~x$", labelpad=0)
+ylabel("${\\rm{Potential}}~\\phi$", labelpad=0)
+
+# Temperature profile -------------------------
+subplot(234)#, yscale="log")
+u *= (unit_length_in_si**2 / unit_time_in_si**2)
+u_g *= (unit_length_in_si**2 / unit_time_in_si**2)
+u /= a**(3 * (gas_gamma - 1.))
+u_g /= a**(3 * (gas_gamma - 1.))
+T = (gas_gamma - 1.) * u * mH_in_kg / k_in_J_K
+T_g = (gas_gamma - 1.) * u_g * mH_in_kg / k_in_J_K
+print "z = {0:.2f}, T_avg = {1:.2f}".format(redshift, T.mean())
+if np.size(x_g) > 1:
+    plot(x_g, T_g, 's', color='g', alpha=0.8, lw=1.2, ms=4)
+plot(x, T, '.', color='r', ms=4.0)
+plot(x_s, T_s, '--', color='k', alpha=0.8, lw=1.2)
+xlabel("${\\rm{Comoving Position}}~x$", labelpad=0)
+ylabel("${\\rm{Temperature}}~T$", labelpad=0)
+
+# Information -------------------------------------
+subplot(236, frameon=False)
+
+text(-0.49, 0.9, "Zeldovich pancake with  $\\gamma=%.3f$ in 1D at $t=%.2f$"%(gas_gamma,time), fontsize=10)
+text(-0.49, 0.8, "$z={0:.2f}$".format(redshift))
+plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+text(-0.49, 0.5, "$\\textsc{Swift}$ %s"%git, fontsize=10)
+text(-0.49, 0.4, scheme, fontsize=10)
+text(-0.49, 0.3, kernel, fontsize=10)
+text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)"%(neighbours, eta), fontsize=10)
+xlim(-0.5, 0.5)
+ylim(0, 1)
+xticks([])
+yticks([])
+
+savefig("ZeldovichPancake_%.4d.png"%snap, dpi=200)
diff --git a/examples/ZeldovichPancake_3D/run.sh b/examples/ZeldovichPancake_3D/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9b6b8166ac0d084898b96e2de6b0fc1ef378aecd
--- /dev/null
+++ b/examples/ZeldovichPancake_3D/run.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+if [ ! -e zeldovichPancake.hdf5 ]
+then
+    echo "Generating initial conditions for the 3D Zeldovich pancake example..."
+    python makeIC.py
+fi
+
+# Run SWIFT
+../swift -a -s -c -G -t 8 zeldovichPancake.yml 2>&1 | tee output.log
+
+# Plot the result
+for i in {0..119}
+do 
+    python plotSolution.py $i
+done
diff --git a/examples/ZeldovichPancake_3D/zeldovichPancake.yml b/examples/ZeldovichPancake_3D/zeldovichPancake.yml
new file mode 100644
index 0000000000000000000000000000000000000000..03445aa666c4a13f400b2458f6a2fff471248ace
--- /dev/null
+++ b/examples/ZeldovichPancake_3D/zeldovichPancake.yml
@@ -0,0 +1,53 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.989e43  # 10^10 M_su in grams
+  UnitLength_in_cgs:   3.085678e24  # Mpc in centimeters
+  UnitVelocity_in_cgs: 1e5   # km/s in centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            zeldovichPancake # Common part of the name of output files
+  time_first:          0.       # Time of the first output (in internal units)
+  delta_time:          1.04     # Time difference between consecutive outputs (in internal units)
+  scale_factor_first: 0.00991
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          10. # Time between statistics output
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  ./zeldovichPancake.hdf5       # The file to read
+
+Scheduler:
+  max_top_level_cells: 8
+  cell_split_size:     50
+  tasks_per_cell:      125
+  
+Cosmology:
+  Omega_m: 1.
+  Omega_lambda: 0.
+  Omega_b: 1.
+  h: 1.
+  a_begin: 0.00990099
+  a_end: 1.0
+
+Gravity:
+  mesh_side_length:   16
+  eta: 0.025
+  theta: 0.3
+  r_cut_max: 5.
+  comoving_softening: 0.001
+  max_physical_softening: 0.001
diff --git a/examples/CoolingBox/getCoolingTable.sh b/examples/getCoolingTable.sh
old mode 100755
new mode 100644
similarity index 100%
rename from examples/CoolingBox/getCoolingTable.sh
rename to examples/getCoolingTable.sh
diff --git a/examples/main.c b/examples/main.c
index 2150e36abb449e9296ad0c1d6069b292414f14e3..10ae2e904c6033dd327a815e3adbebf79c8ef355 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -55,7 +55,7 @@ struct profiler prof;
 /**
  * @brief Help messages for the command line parameters.
  */
-void print_help_message() {
+void print_help_message(void) {
 
   printf("\nUsage: swift [OPTION]... PARAMFILE\n");
   printf("       swift_mpi [OPTION]... PARAMFILE\n\n");
@@ -91,6 +91,8 @@ void print_help_message() {
   printf("  %2s %14s %s\n", "-n", "{int}",
          "Execute a fixed number of time steps. When unset use the time_end "
          "parameter to stop.");
+  printf("  %2s %14s %s\n", "-o", "{str}",
+         "Generate a default output parameter file.");
   printf("  %2s %14s %s\n", "-P", "{sec:par:val}",
          "Set parameter value and overwrites values read from the parameters "
          "file. Can be used more than once.");
@@ -130,6 +132,7 @@ int main(int argc, char *argv[]) {
   struct cooling_function_data cooling_func;
   struct cosmology cosmo;
   struct external_potential potential;
+  struct pm_mesh mesh;
   struct gpart *gparts = NULL;
   struct gravity_props gravity_properties;
   struct hydro_props hydro_properties;
@@ -198,6 +201,7 @@ int main(int argc, char *argv[]) {
   int nr_threads = 1;
   int with_verbose_timers = 0;
   int nparams = 0;
+  char output_parameters_filename[200] = "";
   char *cmdparams[PARSER_MAX_NO_OF_PARAMS];
   char paramFileName[200] = "";
   char restart_file[200] = "";
@@ -205,7 +209,7 @@ int main(int argc, char *argv[]) {
 
   /* Parse the parameters */
   int c;
-  while ((c = getopt(argc, argv, "acCdDef:FgGhMn:P:rsSt:Tv:xy:Y:")) != -1)
+  while ((c = getopt(argc, argv, "acCdDef:FgGhMn:o:P:rsSt:Tv:xy:Y:")) != -1)
     switch (c) {
       case 'a':
 #if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA)
@@ -262,6 +266,15 @@ int main(int argc, char *argv[]) {
           return 1;
         }
         break;
+      case 'o':
+        if (sscanf(optarg, "%s", output_parameters_filename) != 1) {
+          if (myrank == 0) {
+            printf("Error parsing output fields filename");
+            print_help_message();
+          }
+          return 1;
+        }
+        break;
       case 'P':
         cmdparams[nparams] = optarg;
         nparams++;
@@ -330,6 +343,15 @@ int main(int argc, char *argv[]) {
         return 1;
         break;
     }
+
+  /* Write output parameter file */
+  if (myrank == 0 && strcmp(output_parameters_filename, "") != 0) {
+    io_write_output_field_parameter(output_parameters_filename);
+    printf("End of run.\n");
+    return 0;
+  }
+
+  /* check inputs */
   if (optind == argc - 1) {
     if (!strcpy(paramFileName, argv[optind++]))
       error("Error reading parameter file name.");
@@ -453,10 +475,6 @@ int main(int argc, char *argv[]) {
           "values.");
       for (int k = 0; k < nparams; k++) parser_set_param(params, cmdparams[k]);
     }
-
-    /* And dump the parameters as used. */
-    // parser_print_params(&params);
-    parser_write_params_to_file(params, "used_parameters.yml");
   }
 #ifdef WITH_MPI
   /* Broadcast the parameter file */
@@ -607,7 +625,7 @@ int main(int argc, char *argv[]) {
 
     /* Not restarting so look for the ICs. */
     /* Initialize unit system and constants */
-    units_init(&us, params, "InternalUnitSystem");
+    units_init_from_params(&us, params, "InternalUnitSystem");
     phys_const_init(&us, params, &prog_const);
     if (myrank == 0 && verbose > 0) {
       message("Internal unit system: U_M = %e g.", us.UnitMass_in_cgs);
@@ -632,7 +650,7 @@ int main(int argc, char *argv[]) {
 
     /* Initialise the gravity properties */
     if (with_self_gravity)
-      gravity_props_init(&gravity_properties, params, &cosmo);
+      gravity_props_init(&gravity_properties, params, &cosmo, with_cosmology);
 
     /* Read particles and space information from (GADGET) ICs */
     char ICfileName[200] = "";
@@ -643,6 +661,8 @@ int main(int argc, char *argv[]) {
         params, "InitialConditions:cleanup_smoothing_lengths", 0);
     const int cleanup_h = parser_get_opt_param_int(
         params, "InitialConditions:cleanup_h_factors", 0);
+    const int cleanup_sqrt_a = parser_get_opt_param_int(
+        params, "InitialConditions:cleanup_velocity_factors", 0);
     const int generate_gas_in_ics = parser_get_opt_param_int(
         params, "InitialConditions:generate_gas_in_ics", 0);
     if (generate_gas_in_ics && flag_entropy_ICs)
@@ -652,6 +672,8 @@ int main(int argc, char *argv[]) {
     if (myrank == 0) message("Reading ICs from file '%s'", ICfileName);
     if (myrank == 0 && cleanup_h)
       message("Cleaning up h-factors (h=%f)", cosmo.h);
+    if (myrank == 0 && cleanup_sqrt_a)
+      message("Cleaning up a-factors from velocity (a=%f)", cosmo.a);
     fflush(stdout);
 
     /* Get ready to read particles of all kinds */
@@ -665,20 +687,23 @@ int main(int argc, char *argv[]) {
     read_ic_parallel(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas,
                      &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro,
                      (with_external_gravity || with_self_gravity), with_stars,
-                     cleanup_h, cosmo.h, myrank, nr_nodes, MPI_COMM_WORLD,
-                     MPI_INFO_NULL, nr_threads, dry_run);
+                     cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank,
+                     nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads,
+                     dry_run);
 #else
     read_ic_serial(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas,
                    &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro,
                    (with_external_gravity || with_self_gravity), with_stars,
-                   cleanup_h, cosmo.h, myrank, nr_nodes, MPI_COMM_WORLD,
-                   MPI_INFO_NULL, nr_threads, dry_run);
+                   cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, myrank,
+                   nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads,
+                   dry_run);
 #endif
 #else
     read_ic_single(ICfileName, &us, dim, &parts, &gparts, &sparts, &Ngas,
                    &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, with_hydro,
                    (with_external_gravity || with_self_gravity), with_stars,
-                   cleanup_h, cosmo.h, nr_threads, dry_run);
+                   cleanup_h, cleanup_sqrt_a, cosmo.h, cosmo.a, nr_threads,
+                   dry_run);
 #endif
 #endif
     if (myrank == 0) {
@@ -688,6 +713,11 @@ int main(int argc, char *argv[]) {
       fflush(stdout);
     }
 
+#ifdef WITH_MPI
+    if (periodic && with_self_gravity && nr_nodes > 1)
+      error("Periodic self-gravity over MPI temporarily disabled.");
+#endif
+
 #ifdef SWIFT_DEBUG_CHECKS
     /* Check once and for all that we don't have unwanted links */
     if (!with_stars && !dry_run) {
@@ -723,6 +753,22 @@ int main(int argc, char *argv[]) {
           "ICs.",
           N_total[0], N_total[2], N_total[1]);
 
+    /* Verify that the fields to dump actually exist */
+    if (myrank == 0) io_check_output_fields(params, N_total);
+
+    /* Initialise the long-range gravity mesh */
+    if (with_self_gravity && periodic) {
+#ifdef HAVE_FFTW
+      pm_mesh_init(&mesh, &gravity_properties, dim);
+#else
+      /* Need the FFTW library if periodic and self gravity. */
+      error(
+          "No FFTW library found. Cannot compute periodic long-range forces.");
+#endif
+    } else {
+      pm_mesh_init_no_mesh(&mesh, dim);
+    }
+
     /* Initialize the space with these data. */
     if (myrank == 0) clocks_gettime(&tic);
     space_init(&s, params, &cosmo, dim, parts, gparts, sparts, Ngas, Ngpart,
@@ -816,12 +862,13 @@ int main(int argc, char *argv[]) {
 
     /* Initialize the engine with the space and policies. */
     if (myrank == 0) clocks_gettime(&tic);
-    engine_init(&e, &s, params, N_total[0], N_total[1], engine_policies,
-                talking, &reparttype, &us, &prog_const, &cosmo,
-                &hydro_properties, &gravity_properties, &potential,
+    engine_init(&e, &s, params, N_total[0], N_total[1], N_total[2],
+                engine_policies, talking, &reparttype, &us, &prog_const, &cosmo,
+                &hydro_properties, &gravity_properties, &mesh, &potential,
                 &cooling_func, &chemistry, &sourceterms);
     engine_config(0, &e, params, nr_nodes, myrank, nr_threads, with_aff,
                   talking, restart_file);
+
     if (myrank == 0) {
       clocks_gettime(&toc);
       message("engine_init took %.3f %s.", clocks_diff(&tic, &toc),
@@ -904,6 +951,13 @@ int main(int argc, char *argv[]) {
       0)
     error("Failed to generate restart filename");
 
+  /* dump the parameters as used. */
+
+  /* used parameters */
+  parser_write_params_to_file(params, "used_parameters.yml", 1);
+  /* unused parameters */
+  parser_write_params_to_file(params, "unused_parameters.yml", 0);
+
   /* Main simulation loop */
   /* ==================== */
   int force_stop = 0;
@@ -958,9 +1012,10 @@ int main(int argc, char *argv[]) {
           /* Open file and position at end. */
           file_thread = fopen(dumpfile, "a");
 
-          fprintf(file_thread, " %03i 0 0 0 0 %lli %lli %zi %zi %zi 0 0 %lli\n",
-                  myrank, e.tic_step, e.toc_step, e.updates, e.g_updates,
-                  e.s_updates, cpufreq);
+          fprintf(file_thread,
+                  " %03d 0 0 0 0 %lld %lld %lld %lld %lld 0 0 %lld\n", myrank,
+                  e.tic_step, e.toc_step, e.updates, e.g_updates, e.s_updates,
+                  cpufreq);
           int count = 0;
           for (int l = 0; l < e.sched.nr_tasks; l++) {
             if (!e.sched.tasks[l].implicit && e.sched.tasks[l].toc != 0) {
@@ -996,8 +1051,8 @@ int main(int argc, char *argv[]) {
       FILE *file_thread;
       file_thread = fopen(dumpfile, "w");
       /* Add some information to help with the plots */
-      fprintf(file_thread, " %i %i %i %i %lli %lli %zi %zi %zi %i %lli\n", -2,
-              -1, -1, 1, e.tic_step, e.toc_step, e.updates, e.g_updates,
+      fprintf(file_thread, " %d %d %d %d %lld %lld %lld %lld %lld %d %lld\n",
+              -2, -1, -1, 1, e.tic_step, e.toc_step, e.updates, e.g_updates,
               e.s_updates, 0, cpufreq);
       for (int l = 0; l < e.sched.nr_tasks; l++) {
         if (!e.sched.tasks[l].implicit && e.sched.tasks[l].toc != 0) {
@@ -1050,14 +1105,15 @@ int main(int argc, char *argv[]) {
   if (myrank == 0) {
 
     /* Print some information to the screen */
-    printf("  %6d %14e %14e %10.5f %14e %4d %4d %12zu %12zu %12zu %21.3f %6d\n",
-           e.step, e.time, e.cosmology->a, e.cosmology->z, e.time_step,
-           e.min_active_bin, e.max_active_bin, e.updates, e.g_updates,
-           e.s_updates, e.wallclock_time, e.step_props);
+    printf(
+        "  %6d %14e %14e %10.5f %14e %4d %4d %12lld %12lld %12lld %21.3f %6d\n",
+        e.step, e.time, e.cosmology->a, e.cosmology->z, e.time_step,
+        e.min_active_bin, e.max_active_bin, e.updates, e.g_updates, e.s_updates,
+        e.wallclock_time, e.step_props);
     fflush(stdout);
 
     fprintf(e.file_timesteps,
-            "  %6d %14e %14e %14e %4d %4d %12zu %12zu %12zu %21.3f %6d\n",
+            "  %6d %14e %14e %14e %4d %4d %12lld %12lld %12lld %21.3f %6d\n",
             e.step, e.time, e.cosmology->a, e.time_step, e.min_active_bin,
             e.max_active_bin, e.updates, e.g_updates, e.s_updates,
             e.wallclock_time, e.step_props);
@@ -1081,6 +1137,7 @@ int main(int argc, char *argv[]) {
   /* Clean everything */
   if (with_verbose_timers) timers_close_file();
   if (with_cosmology) cosmology_clean(&cosmo);
+  if (with_self_gravity) pm_mesh_clean(&mesh);
   engine_clean(&e);
   free(params);
 
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index c7dd4846add74f13f53f293a1cc5ed1b871340c8..b6d9228dd42e231da79df47334bfa7b04b4d563b 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -33,13 +33,15 @@ SPH:
   initial_temperature:   0        # (Optional) Initial temperature (in internal units) to set the gas particles at start-up. Value is ignored if set to 0.
   minimal_temperature:   0        # (Optional) Minimal temperature (in internal units) allowed for the gas particles. Value is ignored if set to 0.
   H_mass_fraction:       0.76     # (Optional) Hydrogen mass fraction used for initial conversion from temp to internal energy.
-  
+
 # Parameters for the self-gravity scheme
 Gravity:
+  mesh_side_length:       32        # Number of cells along each axis for the periodic gravity mesh.
   eta:          0.025               # Constant dimensionless multiplier for time integration.
-  theta:        0.7                 # Opening angle (Multipole acceptance criterion)
+  theta:        0.7                 # Opening angle (Multipole acceptance criterion).
   comoving_softening:     0.0026994 # Comoving softening length (in internal units).
   max_physical_softening: 0.0007    # Physical softening length (in internal units).
+  rebuild_frequency:      0.01      # (Optional) Frequency of the gravity-tree rebuild in units of the number of g-particles (this is the default value).
   a_smooth:     1.25                # (Optional) Smoothing scale in top-level cell sizes to smooth the long-range forces over (this is the default value).
   r_cut_max:    4.5                 # (Optional) Cut-off in number of top-level cells beyond which no FMM forces are computed (this is the default value).
   r_cut_min:    0.1                 # (Optional) Cut-off in number of top-level cells below which no truncation of FMM forces are performed (this is the default value).
@@ -59,10 +61,11 @@ Scheduler:
 
 # Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.)
 TimeIntegration:
-  time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   1.    # The end time of the simulation (in internal units).
-  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+  time_begin:        0.    # The starting time of the simulation (in internal units).
+  time_end:          1.    # The end time of the simulation (in internal units).
+  dt_min:            1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:            1e-2  # The maximal time-step size of the simulation (in internal units).
+  max_dt_RMS_factor: 0.25  # (Optional) Dimensionless factor for the maximal displacement allowed based on the RMS velocities.
 
 # Parameters governing the snapshots
 Snapshots:
@@ -71,6 +74,7 @@ Snapshots:
   time_first: 0.          # (Optional) Time of the first output if non-cosmological time-integration (in internal units)
   delta_time: 0.01        # Time difference between consecutive outputs (in internal units)
   compression: 0          # (Optional) Set the level of compression of the HDF5 datasets [0-9]. 0 does no compression.
+  label_delta: 1          # (Optional) Set the integer increment between snapshot output labels
   UnitMass_in_cgs:     1  # (Optional) Unit system for the outputs (Grams)
   UnitLength_in_cgs:   1  # (Optional) Unit system for the outputs (Centimeters)
   UnitVelocity_in_cgs: 1  # (Optional) Unit system for the outputs (Centimeters per second)
@@ -90,11 +94,10 @@ InitialConditions:
   file_name:  SedovBlast/sedov.hdf5 # The file to read
   generate_gas_in_ics:         0    # (Optional) Generate gas particles from the DM-only ICs (e.g. from panphasia).
   cleanup_h_factors:           0    # (Optional) Clean up the h-factors used in the ICs (e.g. in Gadget files).
+  cleanup_velocity_factors:    0    # (Optional) Clean up the scale-factors used in the definition of the velocity variable in the ICs (e.g. in Gadget files).
   cleanup_smoothing_lengths:   0    # (Optional) Clean the values of the smoothing lengths that are read in to remove stupid values. Set to 1 to activate.
   smoothing_length_scaling:    1.   # (Optional) A scaling factor to apply to all smoothing lengths in the ICs.
-  shift_x:    0.                    # (Optional) A shift to apply to all particles read from the ICs (in internal units).
-  shift_y:    0.
-  shift_z:    0.
+  shift:      [0.0,0.0,0.0]         # (Optional) A shift to apply to all particles read from the ICs (in internal units).
   replicate:  2                     # (Optional) Replicate all particles along each axis a given integer number of times. Default 1.
 
 # Parameters controlling restarts
@@ -111,9 +114,7 @@ Restarts:
 DomainDecomposition:
   initial_type:     simple_metis # (Optional) The initial decomposition strategy: "grid",
                                  #            "simple_metis", "weighted_metis", or "vectorized".
-  initial_grid_x:   10      # (Optional) Grid size if the "grid" strategy is chosen.
-  initial_grid_y:   10      # ""
-  initial_grid_z:   10      # ""
+  initial_grid: [10,10,10] # (Optional) Grid sizes if the "grid" strategy is chosen.
 
   repartition_type: costs/costs # (Optional) The re-decomposition strategy, one of:
                             # "none/none", "costs/costs", "counts/none", "none/costs", "counts/costs",
@@ -129,23 +130,29 @@ DomainDecomposition:
 # Parameters related to the equation of state ------------------------------------------
 
 EoS:
-  isothermal_internal_energy: 20.26784 # Thermal energy per unit mass for the case of isothermal equation of state (in internal units).
+  isothermal_internal_energy: 20.26784  # Thermal energy per unit mass for the case of isothermal equation of state (in internal units).
+
+  planetary_use_Til:    1   # (Optional) Whether to prepare the Tillotson EOS
+  planetary_use_HM80:   0   # (Optional) Whether to prepare the Hubbard & MacFarlane (1980) EOS
+  planetary_use_ANEOS:  0   # (Optional) Whether to prepare the ANEOS EOS
+  planetary_use_SESAME: 0   # (Optional) Whether to prepare the SESAME EOS
+                            # (Optional) Table file paths
+  planetary_HM80_HHe_table_file:    HM80_HHe.txt
+  planetary_HM80_ice_table_file:    HM80_ice.txt
+  planetary_HM80_rock_table_file:   HM80_rock.txt
 
 # Parameters related to external potentials --------------------------------------------
 
 # Point mass external potentials
 PointMassPotential:
-  position_x:      50.      # location of external point mass (internal units)
-  position_y:      50.
-  position_z:      50.
-  mass:            1e10     # mass of external point mass (internal units)
-  timestep_mult:   0.03     # Dimensionless pre-factor for the time-step condition
+  position:        [50.,50.0,50.]      # location of external point mass (internal units)
+  mass:            1e10                # mass of external point mass (internal units)
+  timestep_mult:   0.03                # Dimensionless pre-factor for the time-step condition
+  softening:       0.05                # For point-mass-softened option
 
 # Isothermal potential parameters
 IsothermalPotential:
-  position_x:      100.     # Location of centre of isothermal potential with respect to centre of the box (internal units)
-  position_y:      100.
-  position_z:      100.
+  position:        [100.,100.,100.]    # Location of centre of isothermal potential with respect to centre of the box (internal units)
   vrot:            200.     # Rotation speed of isothermal potential (internal units)
   timestep_mult:   0.03     # Dimensionless pre-factor for the time-step condition
   epsilon:         0.1      # Softening size (internal units)
diff --git a/examples/plot_gravity_checks.py b/examples/plot_gravity_checks.py
old mode 100644
new mode 100755
index de4f37af32cf0d051afb4c5090075654e6fcd65c..23866ac2a6952ff918dbc80533269c0d2e9bcbc5
--- a/examples/plot_gravity_checks.py
+++ b/examples/plot_gravity_checks.py
@@ -43,20 +43,24 @@ cols = ['#332288', '#88CCEE', '#117733', '#DDCC77', '#CC6677']
 
 # Time-step to plot
 step = int(sys.argv[1])
+periodic = int(sys.argv[2])
 
 # Find the files for the different expansion orders
-order_list = glob.glob("gravity_checks_swift_step%d_order*.dat"%step)
+order_list = glob.glob("gravity_checks_swift_step%.4d_order*.dat"%step)
 num_order = len(order_list)
 
 # Get the multipole orders
 order = np.zeros(num_order)
 for i in range(num_order):
-    order[i] = int(order_list[i][32])
+    order[i] = int(order_list[i][35])
 order = sorted(order)
 order_list = sorted(order_list)
 
 # Read the exact accelerations first
-data = np.loadtxt('gravity_checks_exact_step%d.dat'%step)
+if periodic:
+    data = np.loadtxt('gravity_checks_exact_periodic_step%.4d.dat'%step)
+else:
+    data = np.loadtxt('gravity_checks_exact_step%.4d.dat'%step)
 exact_ids = data[:,0]
 exact_pos = data[:,1:4]
 exact_a = data[:,4:7]
@@ -68,6 +72,8 @@ exact_pos = exact_pos[sort_index, :]
 exact_a = exact_a[sort_index, :]        
 exact_pot = exact_pot[sort_index]
 exact_a_norm = np.sqrt(exact_a[:,0]**2 + exact_a[:,1]**2 + exact_a[:,2]**2)
+
+print "Number of particles tested:", np.size(exact_ids)
     
 # Start the plot
 plt.figure()
@@ -75,7 +81,10 @@ plt.figure()
 count = 0
 
 # Get the Gadget-2 data if existing
-gadget2_file_list = glob.glob("forcetest_gadget2.txt")
+if periodic:
+    gadget2_file_list = glob.glob("forcetest_gadget2_periodic.txt")
+else:
+    gadget2_file_list = glob.glob("forcetest_gadget2.txt")
 if len(gadget2_file_list) != 0:
 
     gadget2_data = np.loadtxt(gadget2_file_list[0])
@@ -89,6 +98,7 @@ if len(gadget2_file_list) != 0:
     gadget2_ids = gadget2_ids[sort_index]
     gadget2_pos = gadget2_pos[sort_index, :]
     gadget2_a_exact = gadget2_a_exact[sort_index, :]
+    gadget2_exact_a_norm = np.sqrt(gadget2_a_exact[:,0]**2 + gadget2_a_exact[:,1]**2 + gadget2_a_exact[:,2]**2)
     gadget2_a_grav = gadget2_a_grav[sort_index, :]
 
     # Cross-checks
@@ -100,11 +110,16 @@ if len(gadget2_file_list) != 0:
         index = np.argmax(exact_pos[:,0]**2 + exact_pos[:,1]**2 + exact_pos[:,2]**2 - gadget2_pos[:,0]**2 - gadget2_pos[:,1]**2 - gadget2_pos[:,2]**2)
         print "Gadget2 (id=%d):"%gadget2_ids[index], gadget2_pos[index,:], "exact (id=%d):"%exact_ids[index], exact_pos[index,:], "\n"
 
-    if np.max(np.abs(exact_a - gadget2_a_exact) / np.abs(gadget2_a_exact)) > 2e-6:
-        print "Comparing different exact accelerations ! max difference:"
-        index = np.argmax(exact_a[:,0]**2 + exact_a[:,1]**2 + exact_a[:,2]**2 - gadget2_a_exact[:,0]**2 - gadget2_a_exact[:,1]**2 - gadget2_a_exact[:,2]**2)
+    diff = np.abs(exact_a_norm - gadget2_exact_a_norm) / np.abs(gadget2_exact_a_norm)
+    max_diff = np.max(diff)
+    if max_diff > 2e-6:
+        print "Comparing different exact accelerations !"
+        print "Median=", np.median(diff), "Mean=", np.mean(diff), "99%=", np.percentile(diff, 99)
+        print "max difference ( relative diff =", max_diff, "):"
+        #index = np.argmax(exact_a[:,0]**2 + exact_a[:,1]**2 + exact_a[:,2]**2 - gadget2_a_exact[:,0]**2 - gadget2_a_exact[:,1]**2 - gadget2_a_exact[:,2]**2)
+        index = np.argmax(diff)
         print "a_exact --- Gadget2:", gadget2_a_exact[index,:], "exact:", exact_a[index,:]
-        print "pos ---     Gadget2: (id=%d):"%gadget2_ids[index], gadget2_pos[index,:], "exact (id=%d):"%ids[index], pos[index,:],"\n"
+        print "pos ---     Gadget2: (id=%d):"%gadget2_ids[index], gadget2_pos[index,:], "exact (id=%d):"%gadget2_ids[index], gadget2_pos[index,:],"\n"
 
     
     # Compute the error norm
@@ -146,16 +161,16 @@ if len(gadget2_file_list) != 0:
     print "Z   : median= %f 99%%= %f max= %f"%(median_z, per99_z, max_z)
     print ""
 
-    plt.subplot(221)    
+    plt.subplot(231)    
     plt.text(min_error * 1.5, 1.55, "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(norm_median, norm_per99), ha="left", va="top", alpha=0.8)
     plt.semilogx(bins, norm_error_hist, 'k--', label="Gadget-2", alpha=0.8)
-    plt.subplot(222)
+    plt.subplot(232)
     plt.semilogx(bins, error_x_hist, 'k--', label="Gadget-2", alpha=0.8)
     plt.text(min_error * 1.5, 1.55, "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_x, per99_x), ha="left", va="top", alpha=0.8)
-    plt.subplot(223)    
+    plt.subplot(233)    
     plt.semilogx(bins, error_y_hist, 'k--', label="Gadget-2", alpha=0.8)
     plt.text(min_error * 1.5, 1.55, "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_y, per99_y), ha="left", va="top", alpha=0.8)
-    plt.subplot(224)    
+    plt.subplot(234)    
     plt.semilogx(bins, error_z_hist, 'k--', label="Gadget-2", alpha=0.8)
     plt.text(min_error * 1.5, 1.55, "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_z, per99_z), ha="left", va="top", alpha=0.8)
     
@@ -185,12 +200,18 @@ for i in range(num_order):
         print "Comparing different positions ! max difference:"
         index = np.argmax(exact_pos[:,0]**2 + exact_pos[:,1]**2 + exact_pos[:,2]**2 - pos[:,0]**2 - pos[:,1]**2 - pos[:,2]**2)
         print "SWIFT (id=%d):"%ids[index], pos[index,:], "exact (id=%d):"%exact_ids[index], exact_pos[index,:], "\n"
-
     
     # Compute the error norm
     diff = exact_a - a_grav
     diff_pot = exact_pot - pot
 
+    # Correct for different normalization of potential
+    print "Difference in normalization of potential:", np.mean(diff_pot),
+    print "std_dev=", np.std(diff_pot), "99-percentile:", np.percentile(diff_pot, 99)-np.median(diff_pot), "1-percentile:", np.median(diff_pot) - np.percentile(diff_pot, 1)
+
+    exact_pot -= np.mean(diff_pot)
+    diff_pot = exact_pot - pot
+
     norm_diff = np.sqrt(diff[:,0]**2 + diff[:,1]**2 + diff[:,2]**2)
 
     norm_error = norm_diff / exact_a_norm
@@ -282,5 +303,5 @@ plt.ylim(0,1.75)
 
 
 
-plt.savefig("gravity_checks_step%d.png"%step, dpi=200)
-plt.savefig("gravity_checks_step%d.pdf"%step, dpi=200)
+plt.savefig("gravity_checks_step%.4d.png"%step, dpi=200)
+plt.savefig("gravity_checks_step%.4d.pdf"%step, dpi=200)
diff --git a/src/Makefile.am b/src/Makefile.am
index 5444da017c9fa91527f2b9b34666f52466f75e35..4ef050a096665c9f352f69636158aa5f284d50fe 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -25,7 +25,7 @@ AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0
 GIT_CMD = @GIT_CMD@
 
 # Additional dependencies for shared libraries.
-EXTRA_LIBS = $(HDF5_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(GRACKLE_LIB) $(GSL_LIBS)
+EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIB) $(GSL_LIBS)
 
 # MPI libraries.
 MPI_LIBS = $(METIS_LIBS) $(MPI_THREAD_LIBS)
@@ -47,7 +47,8 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \
     sourceterms.h sourceterms_struct.h statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h \
     dump.h logger.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h \
     gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h \
-    chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h velociraptor_interface.h
+    chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h space_getsid.h utilities.h \
+    mesh_gravity.h cbrt.h velociraptor_interface.h
 
 # Common source files
 AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \
@@ -55,15 +56,15 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \
     units.c common_io.c single_io.c multipole.c version.c map.c \
     kernel_hydro.c tools.c part.c partition.c clocks.c parser.c \
     physical_constants.c potential.c hydro_properties.c \
-    runner_doiact_fft.c threadpool.c cooling.c sourceterms.c \
+    threadpool.c cooling.c sourceterms.c \
     statistics.c runner_doiact_vec.c profiler.c dump.c logger.c \
     part_type.c xmf.c gravity_properties.c gravity.c \
     collectgroup.c hydro_space.c equation_of_state.c \
-    chemistry.c cosmology.c restart.c velociraptor_interface.c
+    chemistry.c cosmology.c restart.c mesh_gravity.c velociraptor_interface.c
 
 # Include files for distribution, not installation.
 nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \
-		 kernel_long_gravity.h vector.h cache.h runner_doiact.h runner_doiact_vec.h runner_doiact_grav.h runner_doiact_fft.h \
+		 kernel_long_gravity.h vector.h cache.h runner_doiact.h runner_doiact_vec.h runner_doiact_grav.h  \
                  runner_doiact_nosort.h units.h intrinsics.h minmax.h kick.h timestep.h drift.h adiabatic_index.h io_properties.h \
 		 dimension.h part_type.h periodic.h memswap.h dump.h logger.h sign.h \
 		 gravity.h gravity_io.h gravity_cache.h \
@@ -81,17 +82,28 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h
                  hydro/Gadget2/hydro_debug.h hydro/Gadget2/hydro_part.h \
 		 hydro/PressureEntropy/hydro.h hydro/PressureEntropy/hydro_iact.h hydro/PressureEntropy/hydro_io.h \
                  hydro/PressureEntropy/hydro_debug.h hydro/PressureEntropy/hydro_part.h \
-		 hydro/Gizmo/hydro.h hydro/Gizmo/hydro_iact.h \
-                 hydro/Gizmo/hydro_io.h hydro/Gizmo/hydro_debug.h \
-                 hydro/Gizmo/hydro_part.h \
-                 hydro/Gizmo/hydro_gradients_gizmo.h \
-                 hydro/Gizmo/hydro_gradients.h \
-                 hydro/Gizmo/hydro_gradients_sph.h \
-                 hydro/Gizmo/hydro_slope_limiters_cell.h \
-                 hydro/Gizmo/hydro_slope_limiters_face.h \
-                 hydro/Gizmo/hydro_slope_limiters.h \
-                 hydro/Gizmo/hydro_unphysical.h \
-                 hydro/Gizmo/hydro_velocities.h \
+		 hydro/GizmoMFV/hydro.h hydro/GizmoMFV/hydro_iact.h \
+                 hydro/GizmoMFV/hydro_io.h hydro/GizmoMFV/hydro_debug.h \
+                 hydro/GizmoMFV/hydro_part.h \
+                 hydro/GizmoMFV/hydro_gradients_gizmo.h \
+                 hydro/GizmoMFV/hydro_gradients.h \
+                 hydro/GizmoMFV/hydro_gradients_sph.h \
+                 hydro/GizmoMFV/hydro_slope_limiters_cell.h \
+                 hydro/GizmoMFV/hydro_slope_limiters_face.h \
+                 hydro/GizmoMFV/hydro_slope_limiters.h \
+                 hydro/GizmoMFV/hydro_unphysical.h \
+                 hydro/GizmoMFV/hydro_velocities.h \
+		 hydro/GizmoMFM/hydro.h hydro/GizmoMFM/hydro_iact.h \
+                 hydro/GizmoMFM/hydro_io.h hydro/GizmoMFM/hydro_debug.h \
+                 hydro/GizmoMFM/hydro_part.h \
+                 hydro/GizmoMFM/hydro_gradients_gizmo.h \
+                 hydro/GizmoMFM/hydro_gradients.h \
+                 hydro/GizmoMFM/hydro_gradients_sph.h \
+                 hydro/GizmoMFM/hydro_slope_limiters_cell.h \
+                 hydro/GizmoMFM/hydro_slope_limiters_face.h \
+                 hydro/GizmoMFM/hydro_slope_limiters.h \
+                 hydro/GizmoMFM/hydro_unphysical.h \
+                 hydro/GizmoMFM/hydro_velocities.h \
                  hydro/Shadowswift/hydro_debug.h \
                  hydro/Shadowswift/hydro_gradients.h hydro/Shadowswift/hydro.h \
                  hydro/Shadowswift/hydro_iact.h \
diff --git a/src/adiabatic_index.h b/src/adiabatic_index.h
index f65f2dac13b9cf1a470ded155590cf5e443d0c76..de7c3871cfb6e42739edf45a7d5b4882547d3cc2 100644
--- a/src/adiabatic_index.h
+++ b/src/adiabatic_index.h
@@ -33,6 +33,7 @@
 #include <math.h>
 
 /* Local headers. */
+#include "cbrt.h"
 #include "error.h"
 #include "inline.h"
 
@@ -108,12 +109,17 @@
  *
  * Computes \f$x^\gamma\f$.
  */
-__attribute__((always_inline)) INLINE static float pow_gamma(float x) {
+__attribute__((always_inline, const)) INLINE static float pow_gamma(float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
 
-  const float cbrt = cbrtf(x); /* x^(1/3) */
-  return cbrt * cbrt * x;      /* x^(5/3) */
+#ifdef WITH_ICBRTF
+  const float icbrt = icbrtf(x); /* x^(-1/3) */
+  return icbrt * x * x;          /* x^(5/3) */
+#else
+  const float cbrt = cbrtf(x);                 /* x^(1/3) */
+  return cbrt * cbrt * x;                      /* x^(5/3) */
+#endif  // WITH_ICBRTF
 
 #elif defined(HYDRO_GAMMA_7_5)
 
@@ -121,7 +127,12 @@ __attribute__((always_inline)) INLINE static float pow_gamma(float x) {
 
 #elif defined(HYDRO_GAMMA_4_3)
 
-  return cbrtf(x) * x; /* x^(4/3) */
+#ifdef WITH_ICBRTF
+  const float icbrt = icbrtf(x);               /* x^(-1/3) */
+  return icbrt * icbrt * x * x;                /* x^(4/3) */
+#else
+  return cbrtf(x) * x;                   /* x^(4/3) */
+#endif  // WITH_ICBRTF
 
 #elif defined(HYDRO_GAMMA_2_1)
 
@@ -141,13 +152,18 @@ __attribute__((always_inline)) INLINE static float pow_gamma(float x) {
  *
  * Computes \f$x^{(\gamma-1)}\f$.
  */
-__attribute__((always_inline)) INLINE static float pow_gamma_minus_one(
+__attribute__((always_inline, const)) INLINE static float pow_gamma_minus_one(
     float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
 
-  const float cbrt = cbrtf(x); /* x^(1/3) */
-  return cbrt * cbrt;          /* x^(2/3) */
+#ifdef WITH_ICBRTF
+  const float icbrt = icbrtf(x); /* x^(-1/3) */
+  return x * icbrt;              /* x^(2/3) */
+#else
+  const float cbrt = cbrtf(x);                 /* x^(1/3) */
+  return cbrt * cbrt;                          /* x^(2/3) */
+#endif  // WITH_ICBRTF
 
 #elif defined(HYDRO_GAMMA_7_5)
 
@@ -155,7 +171,12 @@ __attribute__((always_inline)) INLINE static float pow_gamma_minus_one(
 
 #elif defined(HYDRO_GAMMA_4_3)
 
-  return cbrtf(x); /* x^(1/3) */
+#ifdef WITH_ICBRTF
+  const float icbrt = icbrtf(x);               /* x^(-1/3) */
+  return x * icbrt * icbrt;                    /* x^(1/3) */
+#else
+  return cbrtf(x);                       /* x^(1/3) */
+#endif  // WITH_ICBRTF
 
 #elif defined(HYDRO_GAMMA_2_1)
 
@@ -175,13 +196,18 @@ __attribute__((always_inline)) INLINE static float pow_gamma_minus_one(
  *
  * Computes \f$x^{-(\gamma-1)}\f$.
  */
-__attribute__((always_inline)) INLINE static float pow_minus_gamma_minus_one(
-    float x) {
+__attribute__((always_inline, const)) INLINE static float
+pow_minus_gamma_minus_one(float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
 
-  const float cbrt_inv = 1.f / cbrtf(x); /* x^(-1/3) */
-  return cbrt_inv * cbrt_inv;            /* x^(-2/3) */
+#ifdef WITH_ICBRTF
+  const float icbrt = icbrtf(x); /* x^(-1/3) */
+  return icbrt * icbrt;          /* x^(-2/3) */
+#else
+  const float cbrt_inv = 1.f / cbrtf(x);       /* x^(-1/3) */
+  return cbrt_inv * cbrt_inv;                  /* x^(-2/3) */
+#endif  // WITH_ICBRTF
 
 #elif defined(HYDRO_GAMMA_7_5)
 
@@ -189,7 +215,11 @@ __attribute__((always_inline)) INLINE static float pow_minus_gamma_minus_one(
 
 #elif defined(HYDRO_GAMMA_4_3)
 
-  return 1.f / cbrtf(x); /* x^(-1/3) */
+#ifdef WITH_ICBRTF
+  return icbrtf(x);                            /* x^(-1/3) */
+#else
+  return 1.f / cbrtf(x);                 /* x^(-1/3) */
+#endif  // WITH_ICBRTF
 
 #elif defined(HYDRO_GAMMA_2_1)
 
@@ -212,13 +242,20 @@ __attribute__((always_inline)) INLINE static float pow_minus_gamma_minus_one(
  * @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) {
+__attribute__((always_inline, const)) INLINE static float pow_minus_gamma(
+    float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
 
+#ifdef WITH_ICBRTF
+  const float icbrt = icbrtf(x);      /* x^(-1/3) */
+  const float icbrt2 = icbrt * icbrt; /* x^(-2/3) */
+  return icbrt * icbrt2 * icbrt2;     /* x^(-5/3) */
+#else
   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) */
+#endif  // WITH_ICBRTF
 
 #elif defined(HYDRO_GAMMA_7_5)
 
@@ -226,7 +263,11 @@ __attribute__((always_inline)) INLINE static float pow_minus_gamma(float x) {
 
 #elif defined(HYDRO_GAMMA_4_3)
 
-  const float cbrt_inv = 1.f / cbrtf(x);       /* x^(-1/3) */
+#ifdef WITH_ICBRTF
+  const float cbrt_inv = icbrtf(x);            /* x^(-1/3) */
+#else
+  const float cbrt_inv = 1.f / cbrtf(x); /* x^(-1/3) */
+#endif  // WITH_ICBRTF
   const float cbrt_inv2 = cbrt_inv * cbrt_inv; /* x^(-2/3) */
   return cbrt_inv2 * cbrt_inv2;                /* x^(-4/3) */
 
@@ -252,8 +293,8 @@ __attribute__((always_inline)) INLINE static float pow_minus_gamma(float x) {
  * @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) {
+__attribute__((always_inline, const)) INLINE static float
+pow_two_over_gamma_minus_one(float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
 
@@ -292,7 +333,7 @@ __attribute__((always_inline)) INLINE static float pow_two_over_gamma_minus_one(
  * @return Argument to the power two times the adiabatic index divided by the
  * adiabatic index minus one
  */
-__attribute__((always_inline)) INLINE static float
+__attribute__((always_inline, const)) INLINE static float
 pow_two_gamma_over_gamma_minus_one(float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
@@ -336,7 +377,7 @@ pow_two_gamma_over_gamma_minus_one(float x) {
  * @return Argument to the power the adiabatic index minus one divided by two
  * times the adiabatic index
  */
-__attribute__((always_inline)) INLINE static float
+__attribute__((always_inline, const)) INLINE static float
 pow_gamma_minus_one_over_two_gamma(float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
@@ -373,7 +414,7 @@ pow_gamma_minus_one_over_two_gamma(float x) {
  * @return Inverse argument to the power the adiabatic index plus one divided by
  * two times the adiabatic index
  */
-__attribute__((always_inline)) INLINE static float
+__attribute__((always_inline, const)) INLINE static float
 pow_minus_gamma_plus_one_over_two_gamma(float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
@@ -408,7 +449,8 @@ pow_minus_gamma_plus_one_over_two_gamma(float x) {
  * @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) {
+__attribute__((always_inline, const)) INLINE static float pow_one_over_gamma(
+    float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
 
@@ -441,8 +483,8 @@ __attribute__((always_inline)) INLINE static float pow_one_over_gamma(float x) {
  *
  * @param x Argument
  */
-__attribute__((always_inline)) INLINE static float pow_three_gamma_minus_two(
-    float x) {
+__attribute__((always_inline, const)) INLINE static float
+pow_three_gamma_minus_two(float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
 
@@ -476,7 +518,7 @@ __attribute__((always_inline)) INLINE static float pow_three_gamma_minus_two(
  *
  * @param x Argument
  */
-__attribute__((always_inline)) INLINE static float
+__attribute__((always_inline, const)) INLINE static float
 pow_three_gamma_minus_five_over_two(float x) {
 
 #if defined(HYDRO_GAMMA_5_3)
diff --git a/src/align.h b/src/align.h
index 243557ee0b4c6c0ae6d7ee75d92c50ec5e3b2f4a..6d329ae7983d68aee096f6f9e65990d5fed6a0f2 100644
--- a/src/align.h
+++ b/src/align.h
@@ -45,6 +45,7 @@
  *
  * Note that this turns into a no-op but gives information to the compiler.
  *
+ * @param type The type of the array.
  * @param array The array.
  * @param alignment The alignment in bytes of the array.
  */
diff --git a/src/approx_math.h b/src/approx_math.h
index aa9ed2b4efa6e0542e2eb2432132f4b0232f7403..1445a84ab09666d08987519d90aba130684e7d2c 100644
--- a/src/approx_math.h
+++ b/src/approx_math.h
@@ -32,7 +32,7 @@
  *
  * @param x The number to take the exponential of.
  */
-__attribute__((always_inline)) INLINE static float approx_expf(float x) {
+__attribute__((always_inline, const)) INLINE static float approx_expf(float x) {
   return 1.f + x * (1.f + x * (0.5f + x * (1.f / 6.f + 1.f / 24.f * x)));
 }
 
@@ -40,7 +40,8 @@ __attribute__((always_inline)) INLINE static float approx_expf(float x) {
  * @brief Approximate version of expf(x) using a 6th order Taylor expansion
  *
  */
-__attribute__((always_inline)) INLINE static float good_approx_expf(float x) {
+__attribute__((always_inline, const)) INLINE static float good_approx_expf(
+    float x) {
   return 1.f +
          x * (1.f +
               x * (0.5f +
@@ -52,7 +53,8 @@ __attribute__((always_inline)) INLINE static float good_approx_expf(float x) {
 /**
  * @brief Approximate version of exp(x) using a 6th order Taylor expansion
  */
-__attribute__((always_inline)) INLINE static double good_approx_exp(double x) {
+__attribute__((always_inline, const)) INLINE static double good_approx_exp(
+    double x) {
   return 1. +
          x * (1. +
               x * (0.5 +
diff --git a/src/atomic.h b/src/atomic.h
index b09ed3dd22001586cfde8545e636de67a819c003..69df59e9fba965422eaf9a3b3de9d28ab9f09dad 100644
--- a/src/atomic.h
+++ b/src/atomic.h
@@ -24,6 +24,7 @@
 
 /* Includes. */
 #include "inline.h"
+#include "minmax.h"
 
 #define atomic_add(v, i) __sync_fetch_and_add(v, i)
 #define atomic_sub(v, i) __sync_fetch_and_sub(v, i)
@@ -33,4 +34,97 @@
 #define atomic_cas(v, o, n) __sync_val_compare_and_swap(v, o, n)
 #define atomic_swap(v, n) __sync_lock_test_and_set(v, n)
 
+/**
+ * @brief Atomic min operation on floats.
+ *
+ * This is a text-book implementation based on an atomic CAS.
+ *
+ * We create a temporary union to cope with the int-only atomic CAS
+ * and the floating-point min that we want.
+ *
+ * @param address The address to update.
+ * @param y The value to update the address with.
+ */
+__attribute__((always_inline)) INLINE static void atomic_min_f(
+    volatile float *const address, const float y) {
+
+  int *const int_ptr = (int *)address;
+
+  typedef union {
+    float as_float;
+    int as_int;
+  } cast_type;
+
+  cast_type test_val, old_val, new_val;
+  old_val.as_float = *address;
+
+  do {
+    test_val.as_int = old_val.as_int;
+    new_val.as_float = min(old_val.as_float, y);
+    old_val.as_int = atomic_cas(int_ptr, test_val.as_int, new_val.as_int);
+  } while (test_val.as_int != old_val.as_int);
+}
+
+/**
+ * @brief Atomic max operation on floats.
+ *
+ * This is a text-book implementation based on an atomic CAS.
+ *
+ * We create a temporary union to cope with the int-only atomic CAS
+ * and the floating-point max that we want.
+ *
+ * @param address The address to update.
+ * @param y The value to update the address with.
+ */
+__attribute__((always_inline)) INLINE static void atomic_max_f(
+    volatile float *const address, const float y) {
+
+  int *const int_ptr = (int *)address;
+
+  typedef union {
+    float as_float;
+    int as_int;
+  } cast_type;
+
+  cast_type test_val, old_val, new_val;
+  old_val.as_float = *address;
+
+  do {
+    test_val.as_int = old_val.as_int;
+    new_val.as_float = max(old_val.as_float, y);
+    old_val.as_int = atomic_cas(int_ptr, test_val.as_int, new_val.as_int);
+  } while (test_val.as_int != old_val.as_int);
+}
+
+/**
+ * @brief Atomic add operation on floats.
+ *
+ * This is a text-book implementation based on an atomic CAS.
+ *
+ * We create a temporary union to cope with the int-only atomic CAS
+ * and the floating-point add that we want.
+ *
+ * @param address The address to update.
+ * @param y The value to update the address with.
+ */
+__attribute__((always_inline)) INLINE static void atomic_add_f(
+    volatile float *const address, const float y) {
+
+  int *const int_ptr = (int *)address;
+
+  typedef union {
+    float as_float;
+    int as_int;
+  } cast_type;
+
+  cast_type test_val, old_val, new_val;
+  old_val.as_float = *address;
+
+  do {
+    test_val.as_int = old_val.as_int;
+    new_val.as_float = old_val.as_float + y;
+    old_val.as_int = atomic_cas(int_ptr, test_val.as_int, new_val.as_int);
+  } while (test_val.as_int != old_val.as_int);
+}
+
 #endif /* SWIFT_ATOMIC_H */
diff --git a/src/cbrt.h b/src/cbrt.h
new file mode 100644
index 0000000000000000000000000000000000000000..54560e41d5e6ef143b839f014ce8f0c4a7624ff6
--- /dev/null
+++ b/src/cbrt.h
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
+ *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_CBRT_H
+#define SWIFT_CBRT_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local headers. */
+#include "inline.h"
+
+/**
+ * @brief Compute the inverse cube root of a single-precision floating-point
+ * number.
+ *
+ * This function does not care about non-finite inputs.
+ *
+ * @warning This function is faster than both gcc and Intel's `cbrtf()`
+ * functions on x86 systems. However, Other compilers or other architectures
+ * may have faster implementations of the standard function `cbrtf()` that
+ * will potentionally outperform this function.
+ *
+ * @param x_in The input value.
+ *
+ * @return The inverse cubic root of @c x_in (i.e. \f$x_{in}^{-1/3} \f$) .
+ */
+__attribute__((always_inline)) INLINE static float icbrtf(float x_in) {
+
+  union {
+    float as_float;
+    unsigned int as_uint;
+    int as_int;
+  } cast;
+
+  /* Extract the exponent. */
+  cast.as_float = x_in;
+  const int exponent = ((cast.as_int & 0x7f800000) >> 23) - 127;
+
+  /* Clear the exponent and sign to get the mantissa. */
+  cast.as_uint = (cast.as_uint & ~0xff800000) | 0x3f800000;
+  const float x_norm = cast.as_float;
+
+  /* Multiply by sqrt(1/2) and subtract one, should then be in the
+     range [sqrt(1/2) - 1, sqrt(2) - 1). */
+  const float x = x_norm * (float)M_SQRT1_2 - 1.0f;
+
+  /* Compute the polynomial interpolant. */
+  float res =
+      9.99976591940035e-01f +
+      x * (-3.32901212909283e-01f +
+           x * (2.24361110929912e-01f +
+                x * (-1.88913279594895e-01f + x * 1.28384036492344e-01f)));
+
+  /* Compute the new exponent and the correction factor. */
+  int exponent_new = exponent;
+  if (exponent_new < 0) exponent_new -= 2;
+  exponent_new = -exponent_new / 3;
+  const int exponent_rem = exponent + 3 * exponent_new;
+  cast.as_uint = (exponent_new + 127) << 23;
+  static const float scale[3] = {8.90898718140339e-01f, 7.07106781186548e-01f,
+                                 5.61231024154687e-01f};
+  const float exponent_scale = cast.as_float * scale[exponent_rem];
+
+  /* Scale the result and set the correct sign. */
+  res = copysignf(res * exponent_scale, x_in);
+
+  /* One step of Newton iteration to refine the result. */
+  res *= (1.0f / 3.0f) * (4.0f - x_in * res * res * res);
+
+  /* We're done. */
+  return res;
+}
+
+#endif /* SWIFT_CBRT_H */
diff --git a/src/cell.c b/src/cell.c
index 4d0884274cb593ae19ecb8f5664019f73c5bf553..94f8c4a10520936cc862807e390bd6a08cea7dba 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -60,6 +60,7 @@
 #include "minmax.h"
 #include "scheduler.h"
 #include "space.h"
+#include "space_getsid.h"
 #include "timers.h"
 
 /* Global variables. */
@@ -2294,13 +2295,12 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) {
   if (c->nodeID == nodeID && cell_is_active_gravity(c, e)) {
 
     if (c->init_grav != NULL) scheduler_activate(s, c->init_grav);
-    if (c->grav_ghost_in != NULL) scheduler_activate(s, c->grav_ghost_in);
-    if (c->grav_ghost_out != NULL) scheduler_activate(s, c->grav_ghost_out);
     if (c->kick1 != NULL) scheduler_activate(s, c->kick1);
     if (c->kick2 != NULL) scheduler_activate(s, c->kick2);
     if (c->timestep != NULL) scheduler_activate(s, c->timestep);
     if (c->end_force != NULL) scheduler_activate(s, c->end_force);
     if (c->grav_down != NULL) scheduler_activate(s, c->grav_down);
+    if (c->grav_mesh != NULL) scheduler_activate(s, c->grav_mesh);
     if (c->grav_long_range != NULL) scheduler_activate(s, c->grav_long_range);
   }
 
diff --git a/src/cell.h b/src/cell.h
index 87af742baf935668b0f065e70f065b978b6c9b9f..e53bf7e305c78df4af1a093a2fff4c9689d94ef2 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -255,12 +255,12 @@ struct cell {
   /*! The task to compute time-steps */
   struct task *timestep;
 
-  /*! Task linking the FFT mesh to the rest of gravity tasks */
-  struct task *grav_ghost_in, *grav_ghost_out;
-
   /*! Task computing long range non-periodic gravity interactions */
   struct task *grav_long_range;
 
+  /*! Task propagating the mesh forces to the particles */
+  struct task *grav_mesh;
+
   /*! Task propagating the multipole to the particles */
   struct task *grav_down;
 
diff --git a/src/chemistry.c b/src/chemistry.c
index 44cbea1361d96c4cf1d4d3d21c3c91e5225640a5..4afa199258f56d4fc01d67c9335e87a86ead09bc 100644
--- a/src/chemistry.c
+++ b/src/chemistry.c
@@ -33,7 +33,7 @@
  * @param phys_const The physical constants in internal units.
  * @param data The properties to initialise.
  */
-void chemistry_init(const struct swift_params* parameter_file,
+void chemistry_init(struct swift_params* parameter_file,
                     const struct unit_system* us,
                     const struct phys_const* phys_const,
                     struct chemistry_global_data* data) {
diff --git a/src/chemistry.h b/src/chemistry.h
index bacc15c483c168dbf86bd34dc2af92a3eefb9e02..f9daa41db22a69a09f06be3fb560a68edac2f078 100644
--- a/src/chemistry.h
+++ b/src/chemistry.h
@@ -43,7 +43,7 @@
 #endif
 
 /* Common functions */
-void chemistry_init(const struct swift_params* parameter_file,
+void chemistry_init(struct swift_params* parameter_file,
                     const struct unit_system* us,
                     const struct phys_const* phys_const,
                     struct chemistry_global_data* data);
diff --git a/src/chemistry/EAGLE/chemistry.h b/src/chemistry/EAGLE/chemistry.h
index 459de24ef3c5e9140fd136155ba55d2364795fb8..7f8a672669e1c5b1f8997ecf5971c63efee7522f 100644
--- a/src/chemistry/EAGLE/chemistry.h
+++ b/src/chemistry/EAGLE/chemistry.h
@@ -78,13 +78,32 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density(
     struct part* restrict p, const struct chemistry_global_data* cd,
     const struct cosmology* cosmo) {}
 
+/**
+ * @brief Sets all particle fields to sensible values when the #part has 0 ngbs.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cd #chemistry_global_data containing chemistry informations.
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void
+chemistry_part_has_no_neighbours(struct part* restrict p,
+                                 struct xpart* restrict xp,
+                                 const struct chemistry_global_data* cd,
+                                 const struct cosmology* cosmo) {
+  error("Needs implementing!");
+}
+
 /**
  * @brief Sets the chemistry properties of the (x-)particles to a valid start
  * state.
  *
+ * @param phys_const The physical constants in internal units.
+ * @param us The internal system of units.
+ * @param cosmo The current cosmological model.
+ * @param data The global chemistry information.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
- * @param data The global chemistry information.
  */
 __attribute__((always_inline)) INLINE static void chemistry_first_init_part(
     const struct phys_const* restrict phys_const,
@@ -108,9 +127,10 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part(
  * @param phys_const The physical constants in internal units.
  * @param data The properties to initialise.
  */
-static INLINE void chemistry_init_backend(
-    const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const, struct chemistry_global_data* data) {
+static INLINE void chemistry_init_backend(struct swift_params* parameter_file,
+                                          const struct unit_system* us,
+                                          const struct phys_const* phys_const,
+                                          struct chemistry_global_data* data) {
 
   /* Read the total metallicity */
   data->initial_metal_mass_fraction_total =
@@ -120,7 +140,7 @@ static INLINE void chemistry_init_backend(
   for (int elem = 0; elem < chemistry_element_count; ++elem) {
     char buffer[50];
     sprintf(buffer, "EAGLEChemistry:InitAbundance_%s",
-            chemistry_get_element_name(elem));
+            chemistry_get_element_name((enum chemistry_element)elem));
 
     data->initial_metal_mass_fraction[elem] =
         parser_get_param_float(parameter_file, buffer);
diff --git a/src/chemistry/EAGLE/chemistry_io.h b/src/chemistry/EAGLE/chemistry_io.h
index aab8ec240207a47289e35a711af8b245bf2b40fa..a717318c1e649c3cede7ad833bce5d40dbff5d77 100644
--- a/src/chemistry/EAGLE/chemistry_io.h
+++ b/src/chemistry/EAGLE/chemistry_io.h
@@ -30,7 +30,8 @@
  *
  * @return Returns the number of fields to read.
  */
-int chemistry_read_particles(struct part* parts, struct io_props* list) {
+INLINE static int chemistry_read_particles(struct part* parts,
+                                           struct io_props* list) {
 
   /* Nothing to read */
   return 0;
@@ -44,7 +45,8 @@ int chemistry_read_particles(struct part* parts, struct io_props* list) {
  *
  * @return Returns the number of fields to write.
  */
-int chemistry_write_particles(const struct part* parts, struct io_props* list) {
+INLINE static int chemistry_write_particles(const struct part* parts,
+                                            struct io_props* list) {
 
   /* List what we want to write */
   list[0] = io_make_output_field("ElementAbundance", FLOAT,
@@ -99,15 +101,16 @@ int chemistry_write_particles(const struct part* parts, struct io_props* list) {
 
 /**
  * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
+ * @param h_grp The HDF5 group in which to write
  */
-void chemistry_write_flavour(hid_t h_grp) {
+INLINE static void chemistry_write_flavour(hid_t h_grp) {
 
   io_write_attribute_s(h_grp, "Chemistry Model", "EAGLE");
   for (int elem = 0; elem < chemistry_element_count; ++elem) {
     char buffer[20];
     sprintf(buffer, "Element %d", elem);
-    io_write_attribute_s(h_grp, buffer, chemistry_get_element_name(elem));
+    io_write_attribute_s(h_grp, buffer, chemistry_get_element_name(
+                                            (enum chemistry_element)elem));
   }
 }
 #endif
diff --git a/src/chemistry/GEAR/chemistry.h b/src/chemistry/GEAR/chemistry.h
index a51051ca3ae45476986d39c868a9fc71bf7f9ae5..6212ed1efb423717b800d431a83f0e8bec7c6c6f 100644
--- a/src/chemistry/GEAR/chemistry.h
+++ b/src/chemistry/GEAR/chemistry.h
@@ -72,9 +72,10 @@ static INLINE void chemistry_print_backend(
  * @param phys_const The physical constants in internal units.
  * @param data The properties to initialise.
  */
-static INLINE void chemistry_init_backend(
-    const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const, struct chemistry_global_data* data) {
+static INLINE void chemistry_init_backend(struct swift_params* parameter_file,
+                                          const struct unit_system* us,
+                                          const struct phys_const* phys_const,
+                                          struct chemistry_global_data* data) {
 
   /* read parameters */
   data->initial_metallicity = parser_get_opt_param_float(
@@ -134,6 +135,22 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density(
   }
 }
 
+/**
+ * @brief Sets all particle fields to sensible values when the #part has 0 ngbs.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cd #chemistry_global_data containing chemistry informations.
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void
+chemistry_part_has_no_neighbours(struct part* restrict p,
+                                 struct xpart* restrict xp,
+                                 const struct chemistry_global_data* cd,
+                                 const struct cosmology* cosmo) {
+  error("Needs implementing!");
+}
+
 /**
  * @brief Sets the chemistry properties of the (x-)particles to a valid start
  * state.
diff --git a/src/chemistry/GEAR/chemistry_io.h b/src/chemistry/GEAR/chemistry_io.h
index 0557d5c520dfc7ad5eaff2b92e6588751c072df5..2a0847bebfb8c1734f21bda2f6ad55b354a7aec9 100644
--- a/src/chemistry/GEAR/chemistry_io.h
+++ b/src/chemistry/GEAR/chemistry_io.h
@@ -48,8 +48,8 @@ chemistry_get_element_name(enum chemistry_element elem) {
  *
  * @return Returns the number of fields to read.
  */
-__attribute__((always_inline)) INLINE static int chemistry_read_particles(
-    struct part* parts, struct io_props* list) {
+INLINE static int chemistry_read_particles(struct part* parts,
+                                           struct io_props* list) {
 
   /* List what we want to read */
   list[0] = io_make_input_field(
@@ -69,13 +69,14 @@ __attribute__((always_inline)) INLINE static int chemistry_read_particles(
  *
  * @return Returns the number of fields to write.
  */
-__attribute__((always_inline)) INLINE static int chemistry_write_particles(
-    const struct part* parts, struct io_props* list) {
+INLINE static int chemistry_write_particles(const struct part* parts,
+                                            struct io_props* list) {
 
   /* List what we want to write */
   list[0] = io_make_output_field(
       "SmoothedElementAbundance", FLOAT, chemistry_element_count,
       UNIT_CONV_NO_UNITS, parts, chemistry_data.smoothed_metal_mass_fraction);
+
   list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, parts,
                                  chemistry_data.Z);
 
@@ -92,8 +93,7 @@ __attribute__((always_inline)) INLINE static int chemistry_write_particles(
  * @brief Writes the current model of SPH to the file
  * @param h_grp The HDF5 group in which to write
  */
-__attribute__((always_inline)) INLINE static void chemistry_write_flavour(
-    hid_t h_grp) {
+INLINE static void chemistry_write_flavour(hid_t h_grp) {
 
   io_write_attribute_s(h_grp, "Chemistry Model", "GEAR");
   for (enum chemistry_element i = chemistry_element_O;
diff --git a/src/chemistry/none/chemistry.h b/src/chemistry/none/chemistry.h
index 3ca51660ddfeead2b7ad0010979b719e59c4934e..dce06ffda339e8a6c4925c7b7c430485a208adb7 100644
--- a/src/chemistry/none/chemistry.h
+++ b/src/chemistry/none/chemistry.h
@@ -59,9 +59,10 @@ chemistry_get_element_name(enum chemistry_element elem) {
  * @param phys_const The physical constants in internal units.
  * @param data The global chemistry information (to be filled).
  */
-static INLINE void chemistry_init_backend(
-    const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const, struct chemistry_global_data* data) {}
+static INLINE void chemistry_init_backend(struct swift_params* parameter_file,
+                                          const struct unit_system* us,
+                                          const struct phys_const* phys_const,
+                                          struct chemistry_global_data* data) {}
 
 /**
  * @brief Prints the properties of the chemistry model to stdout.
@@ -86,6 +87,20 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density(
     struct part* restrict p, const struct chemistry_global_data* cd,
     const struct cosmology* cosmo) {}
 
+/**
+ * @brief Sets all particle fields to sensible values when the #part has 0 ngbs.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cd #chemistry_global_data containing chemistry informations.
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void
+chemistry_part_has_no_neighbours(struct part* restrict p,
+                                 struct xpart* restrict xp,
+                                 const struct chemistry_global_data* cd,
+                                 const struct cosmology* cosmo) {}
+
 /**
  * @brief Sets the chemistry properties of the (x-)particles to a valid start
  * state.
diff --git a/src/chemistry/none/chemistry_io.h b/src/chemistry/none/chemistry_io.h
index 142d2f75ce1487393e8689edbb9a6fdb1b1e85cd..ef7e0d8d87dfeab5978f0e86bbf6279f7901d10a 100644
--- a/src/chemistry/none/chemistry_io.h
+++ b/src/chemistry/none/chemistry_io.h
@@ -29,7 +29,8 @@
  *
  * @return Returns the number of fields to write.
  */
-int chemistry_read_particles(struct part* parts, struct io_props* list) {
+INLINE static int chemistry_read_particles(struct part* parts,
+                                           struct io_props* list) {
 
   /* update list according to hydro_io */
 
@@ -45,7 +46,8 @@ int chemistry_read_particles(struct part* parts, struct io_props* list) {
  *
  * @return Returns the number of fields to write.
  */
-int chemistry_write_particles(const struct part* parts, struct io_props* list) {
+INLINE static int chemistry_write_particles(const struct part* parts,
+                                            struct io_props* list) {
 
   /* update list according to hydro_io */
 
@@ -59,7 +61,7 @@ int chemistry_write_particles(const struct part* parts, struct io_props* list) {
  * @brief Writes the current model of SPH to the file
  * @param h_grp The HDF5 group in which to write
  */
-void chemistry_write_flavour(hid_t h_grp) {
+INLINE static void chemistry_write_flavour(hid_t h_grp) {
 
   io_write_attribute_s(h_grp, "Chemistry Model", "None");
 }
diff --git a/src/clocks.c b/src/clocks.c
index fbaa83f15fafda23751d5d6c34d40750132287b5..cac0131acade08e41ee7ed4a22fabde49e197060 100644
--- a/src/clocks.c
+++ b/src/clocks.c
@@ -50,7 +50,7 @@ static int clocks_units_index = 0;
 static double clocks_units_scale = 1000.0;
 
 /* Local prototypes. */
-static void clocks_estimate_cpufreq();
+static void clocks_estimate_cpufreq(void);
 
 /**
  * @brief Get the current time.
@@ -113,7 +113,7 @@ void clocks_set_cpufreq(unsigned long long freq) {
  *
  * @result the CPU frequency.
  */
-unsigned long long clocks_get_cpufreq() {
+unsigned long long clocks_get_cpufreq(void) {
 
   if (clocks_cpufreq > 0) return clocks_cpufreq;
 
@@ -132,7 +132,7 @@ unsigned long long clocks_get_cpufreq() {
  * file (probably a overestimate) or finally just use a value of 1 with
  * time units of ticks.
  */
-static void clocks_estimate_cpufreq() {
+static void clocks_estimate_cpufreq(void) {
 
 #ifdef HAVE_CLOCK_GETTIME
   /* Try to time a nanosleep() in ticks. */
@@ -241,7 +241,7 @@ ticks clocks_to_ticks(double ms) {
  *
  * @result the current time units.
  */
-const char *clocks_getunit() { return clocks_units[clocks_units_index]; }
+const char *clocks_getunit(void) { return clocks_units[clocks_units_index]; }
 
 /**
  * @brief returns the time since the start of the execution in seconds
@@ -252,7 +252,7 @@ const char *clocks_getunit() { return clocks_units[clocks_units_index]; }
  *
  * @result the time since the start of the execution
  */
-const char *clocks_get_timesincestart() {
+const char *clocks_get_timesincestart(void) {
 
   static char buffer[40];
 
@@ -274,7 +274,7 @@ const char *clocks_get_timesincestart() {
  * @result cpu time used in sysconf(_SC_CLK_TCK) ticks, usually 100/s not our
  *         usual ticks.
  */
-double clocks_get_cputime_used() {
+double clocks_get_cputime_used(void) {
 
   struct tms tmstic;
   times(&tmstic);
diff --git a/src/clocks.h b/src/clocks.h
index bdb3a6651e52f5b165e644015b91f96aa5812d57..f3901584774c7586d6a68b4415d6b443cb53c466 100644
--- a/src/clocks.h
+++ b/src/clocks.h
@@ -34,15 +34,15 @@ struct clocks_time {
 
 void clocks_gettime(struct clocks_time *time);
 double clocks_diff(struct clocks_time *start, struct clocks_time *end);
-const char *clocks_getunit();
+const char *clocks_getunit(void);
 
 void clocks_set_cpufreq(unsigned long long freq);
-unsigned long long clocks_get_cpufreq();
+unsigned long long clocks_get_cpufreq(void);
 double clocks_from_ticks(ticks tics);
 ticks clocks_to_ticks(double interval);
 double clocks_diff_ticks(ticks tic, ticks toc);
-const char *clocks_get_timesincestart();
+const char *clocks_get_timesincestart(void);
 
-double clocks_get_cputime_used();
+double clocks_get_cputime_used(void);
 
 #endif /* SWIFT_CLOCKS_H */
diff --git a/src/collectgroup.c b/src/collectgroup.c
index b704a0a5ea33cc1c5332fc0575061ad8e38f4d21..0a7780aba1d5d41cef756d2132c75f9357796c73 100644
--- a/src/collectgroup.c
+++ b/src/collectgroup.c
@@ -36,14 +36,14 @@
 
 /* Local collections for MPI reduces. */
 struct mpicollectgroup1 {
-  size_t updates, g_updates, s_updates;
+  long long updates, g_updates, s_updates;
   integertime_t ti_hydro_end_min;
   integertime_t ti_gravity_end_min;
   int forcerebuild;
 };
 
 /* Forward declarations. */
-static void mpicollect_create_MPI_type();
+static void mpicollect_create_MPI_type(void);
 
 /**
  * @brief MPI datatype for the #mpicollectgroup1 structure.
@@ -60,7 +60,7 @@ static MPI_Op mpicollectgroup1_reduce_op;
 /**
  * @brief Perform any once only initialisations. Must be called once.
  */
-void collectgroup_init() {
+void collectgroup_init(void) {
 
 #ifdef WITH_MPI
   /* Initialise the MPI types. */
@@ -88,7 +88,6 @@ void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e) {
   e->updates = grp1->updates;
   e->g_updates = grp1->g_updates;
   e->s_updates = grp1->s_updates;
-  e->forcerebuild = grp1->forcerebuild;
 }
 
 /**
@@ -211,7 +210,7 @@ static void mpicollectgroup1_reduce(void *in, void *inout, int *len,
 /**
  * @brief Registers any MPI collection types and reduction functions.
  */
-static void mpicollect_create_MPI_type() {
+static void mpicollect_create_MPI_type(void) {
 
   if (MPI_Type_contiguous(sizeof(struct mpicollectgroup1), MPI_BYTE,
                           &mpicollectgroup1_type) != MPI_SUCCESS ||
diff --git a/src/collectgroup.h b/src/collectgroup.h
index f2014ed254fdde7ea293224751061d824782b4a7..8bf8a9d1b75f9a5ddb3f19fa9cdb4103e044ea59 100644
--- a/src/collectgroup.h
+++ b/src/collectgroup.h
@@ -35,7 +35,7 @@ struct engine;
 struct collectgroup1 {
 
   /* Number of particles updated */
-  size_t updates, g_updates, s_updates;
+  long long updates, g_updates, s_updates;
 
   /* Times for the time-step */
   integertime_t ti_hydro_end_min, ti_hydro_end_max, ti_hydro_beg_max;
@@ -45,7 +45,7 @@ struct collectgroup1 {
   int forcerebuild;
 };
 
-void collectgroup_init();
+void collectgroup_init(void);
 void collectgroup1_apply(struct collectgroup1 *grp1, struct engine *e);
 void collectgroup1_init(struct collectgroup1 *grp1, size_t updates,
                         size_t g_updates, size_t s_updates,
diff --git a/src/common_io.c b/src/common_io.c
index 8b173adb7b5e5a014b0967b4fd04aef5ee6606e9..68311107575a89ce8a2990a8e0f7a8eeb5d2d644 100644
--- a/src/common_io.c
+++ b/src/common_io.c
@@ -25,12 +25,17 @@
 #include "common_io.h"
 
 /* Local includes. */
+#include "chemistry_io.h"
 #include "engine.h"
 #include "error.h"
+#include "gravity_io.h"
 #include "hydro.h"
+#include "hydro_io.h"
 #include "io_properties.h"
 #include "kernel_hydro.h"
 #include "part.h"
+#include "part_type.h"
+#include "stars_io.h"
 #include "threadpool.h"
 #include "units.h"
 #include "version.h"
@@ -245,13 +250,18 @@ void io_write_attribute_s(hid_t grp, const char* name, const char* str) {
 
 /**
  * @brief Reads the Unit System from an IC file.
+ *
+ * If the 'Units' group does not exist in the ICs, we will use the internal
+ * system of units.
+ *
  * @param h_file The (opened) HDF5 file from which to read.
- * @param us The unit_system to fill.
+ * @param ic_units The unit_system to fill.
+ * @param internal_units The internal system of units to copy if needed.
  * @param mpi_rank The MPI rank we are on.
- *
- * If the 'Units' group does not exist in the ICs, cgs units will be assumed
  */
-void io_read_unit_system(hid_t h_file, struct unit_system* us, int mpi_rank) {
+void io_read_unit_system(hid_t h_file, struct unit_system* ic_units,
+                         const struct unit_system* internal_units,
+                         int mpi_rank) {
 
   /* First check if it exists as this is *not* required. */
   const htri_t exists = H5Lexists(h_file, "/Units", H5P_DEFAULT);
@@ -259,16 +269,12 @@ void io_read_unit_system(hid_t h_file, struct unit_system* us, int mpi_rank) {
   if (exists == 0) {
 
     if (mpi_rank == 0)
-      message("'Units' group not found in ICs. Assuming CGS unit system.");
+      message("'Units' group not found in ICs. Assuming internal unit system.");
 
-    /* Default to CGS */
-    us->UnitMass_in_cgs = 1.;
-    us->UnitLength_in_cgs = 1.;
-    us->UnitTime_in_cgs = 1.;
-    us->UnitCurrent_in_cgs = 1.;
-    us->UnitTemperature_in_cgs = 1.;
+    units_copy(ic_units, internal_units);
 
     return;
+
   } else if (exists < 0) {
     error("Serious problem with 'Units' group in ICs. H5Lexists gives %d",
           exists);
@@ -279,15 +285,15 @@ void io_read_unit_system(hid_t h_file, struct unit_system* us, int mpi_rank) {
 
   /* Ok, Read the damn thing */
   io_read_attribute(h_grp, "Unit length in cgs (U_L)", DOUBLE,
-                    &us->UnitLength_in_cgs);
+                    &ic_units->UnitLength_in_cgs);
   io_read_attribute(h_grp, "Unit mass in cgs (U_M)", DOUBLE,
-                    &us->UnitMass_in_cgs);
+                    &ic_units->UnitMass_in_cgs);
   io_read_attribute(h_grp, "Unit time in cgs (U_t)", DOUBLE,
-                    &us->UnitTime_in_cgs);
+                    &ic_units->UnitTime_in_cgs);
   io_read_attribute(h_grp, "Unit current in cgs (U_I)", DOUBLE,
-                    &us->UnitCurrent_in_cgs);
+                    &ic_units->UnitCurrent_in_cgs);
   io_read_attribute(h_grp, "Unit temperature in cgs (U_T)", DOUBLE,
-                    &us->UnitTemperature_in_cgs);
+                    &ic_units->UnitTemperature_in_cgs);
 
   /* Clean up */
   H5Gclose(h_grp);
@@ -340,6 +346,7 @@ void io_write_code_description(hid_t h_file) {
   io_write_attribute_s(h_grpcode, "CFLAGS", compilation_cflags());
   io_write_attribute_s(h_grpcode, "HDF5 library version", hdf5_version());
   io_write_attribute_s(h_grpcode, "Thread barriers", thread_barrier_version());
+  io_write_attribute_s(h_grpcode, "Allocators", allocator_version());
 #ifdef HAVE_FFTW
   io_write_attribute_s(h_grpcode, "FFTW library version", fftw3_version());
 #endif
@@ -805,3 +812,157 @@ void io_collect_dm_gparts(const struct gpart* const gparts, size_t Ntot,
     error("Collected the wrong number of dm particles (%zu vs. %zu expected)",
           count, Ndm);
 }
+
+/**
+ * @brief Verify the io parameter file
+ *
+ * @param params The #swift_params
+ * @param N_total The total number of each particle type.
+ */
+void io_check_output_fields(const struct swift_params* params,
+                            const long long N_total[3]) {
+
+  /* Create some fake particles as arguments for the writing routines */
+  struct part p;
+  struct xpart xp;
+  struct spart sp;
+  struct gpart gp;
+
+  /* Copy N_total to array with length == 6 */
+  const long long nr_total[swift_type_count] = {N_total[0], N_total[1], 0,
+                                                0,          N_total[2], 0};
+
+  /* Loop over all particle types to check the fields */
+  for (int ptype = 0; ptype < swift_type_count; ptype++) {
+
+    int num_fields = 0;
+    struct io_props list[100];
+
+    /* Don't do anything if no particle of this kind */
+    if (nr_total[ptype] == 0) continue;
+
+    /* Gather particle fields from the particle structures */
+    switch (ptype) {
+
+      case swift_type_gas:
+        hydro_write_particles(&p, &xp, list, &num_fields);
+        num_fields += chemistry_write_particles(&p, list + num_fields);
+        break;
+
+      case swift_type_dark_matter:
+        darkmatter_write_particles(&gp, list, &num_fields);
+        break;
+
+      case swift_type_star:
+        star_write_particles(&sp, list, &num_fields);
+        break;
+
+      default:
+        error("Particle Type %d not yet supported. Aborting", ptype);
+    }
+
+    /* loop over each parameter */
+    for (int param_id = 0; param_id < params->paramCount; param_id++) {
+      const char* param_name = params->data[param_id].name;
+
+      char section_name[PARSER_MAX_LINE_SIZE];
+
+      /* Skip if wrong section */
+      sprintf(section_name, "SelectOutput:");
+      if (strstr(param_name, section_name) == NULL) continue;
+
+      /* Skip if wrong particle type */
+      sprintf(section_name, "_%s", part_type_names[ptype]);
+      if (strstr(param_name, section_name) == NULL) continue;
+
+      int found = 0;
+
+      /* loop over each possible output field */
+      for (int field_id = 0; field_id < num_fields; field_id++) {
+        char field_name[PARSER_MAX_LINE_SIZE];
+        sprintf(field_name, "SelectOutput:%s_%s", list[field_id].name,
+                part_type_names[ptype]);
+
+        if (strcmp(param_name, field_name) == 0) {
+          found = 1;
+          /* check if correct input */
+          int retParam = 0;
+          char str[PARSER_MAX_LINE_SIZE];
+          sscanf(params->data[param_id].value, "%d%s", &retParam, str);
+
+          /* Check that we have a 0 or 1 */
+          if (retParam != 0 && retParam != 1)
+            message(
+                "WARNING: Unexpected input for %s. Received %i but expect 0 or "
+                "1. ",
+                field_name, retParam);
+
+          /* Found it, so move to the next one. */
+          break;
+        }
+      }
+      if (!found)
+        message(
+            "WARNING: Trying to dump particle field '%s' (read from '%s') that "
+            "does not exist.",
+            param_name, params->fileName);
+    }
+  }
+}
+
+/**
+ * @brief Write the output field parameters file
+ *
+ * @param filename The file to write
+ */
+void io_write_output_field_parameter(const char* filename) {
+
+  FILE* file = fopen(filename, "w");
+  if (file == NULL) error("Error opening file '%s'", filename);
+
+  /* Loop over all particle types */
+  fprintf(file, "SelectOutput:\n");
+  for (int ptype = 0; ptype < swift_type_count; ptype++) {
+
+    int num_fields = 0;
+    struct io_props list[100];
+
+    /* Write particle fields from the particle structure */
+    switch (ptype) {
+
+      case swift_type_gas:
+        hydro_write_particles(NULL, NULL, list, &num_fields);
+        num_fields += chemistry_write_particles(NULL, list + num_fields);
+        break;
+
+      case swift_type_dark_matter:
+        darkmatter_write_particles(NULL, list, &num_fields);
+        break;
+
+      case swift_type_star:
+        star_write_particles(NULL, list, &num_fields);
+        break;
+
+      default:
+        break;
+    }
+
+    if (num_fields == 0) continue;
+
+    /* Output a header for that particle type */
+    fprintf(file, "  # Particle Type %s\n", part_type_names[ptype]);
+
+    /* Write all the fields of this particle type */
+    for (int i = 0; i < num_fields; ++i)
+      fprintf(file, "  %s_%s: 1\n", list[i].name, part_type_names[ptype]);
+
+    fprintf(file, "\n");
+  }
+
+  fclose(file);
+
+  printf(
+      "List of valid ouput fields for the particle in snapshots dumped in "
+      "'%s'.\n",
+      filename);
+}
diff --git a/src/common_io.h b/src/common_io.h
index 4068176dc1e66926e8f5586c6ce5587059831361..4a9779974c75ad7b1d60d38cdb7c2f5292429ea8 100644
--- a/src/common_io.h
+++ b/src/common_io.h
@@ -77,7 +77,9 @@ void io_write_attribute_s(hid_t grp, const char* name, const char* str);
 void io_write_code_description(hid_t h_file);
 void io_write_engine_policy(hid_t h_file, const struct engine* e);
 
-void io_read_unit_system(hid_t h_file, struct unit_system* us, int mpi_rank);
+void io_read_unit_system(hid_t h_file, struct unit_system* ic_units,
+                         const struct unit_system* internal_units,
+                         int mpi_rank);
 void io_write_unit_system(hid_t h_grp, const struct unit_system* us,
                           const char* groupName);
 
@@ -102,4 +104,9 @@ void io_duplicate_star_gparts(struct threadpool* tp, struct spart* const sparts,
                               struct gpart* const gparts, size_t Nstars,
                               size_t Ndm);
 
+void io_check_output_fields(const struct swift_params* params,
+                            const long long N_total[3]);
+
+void io_write_output_field_parameter(const char* filename);
+
 #endif /* SWIFT_COMMON_IO_H */
diff --git a/src/const.h b/src/const.h
index 928835c5ae0ac70d641243b0d183c2c0652cdc8a..6c5b5299c08efb7935b046ecfd0b3d67b7dc4c7a 100644
--- a/src/const.h
+++ b/src/const.h
@@ -53,7 +53,8 @@
 /* This option disables particle movement */
 //#define GIZMO_FIX_PARTICLES
 /* Try to keep cells regular by adding a correction velocity. */
-#define GIZMO_STEER_MOTION
+//#define GIZMO_STEER_MOTION
+/* Use the total energy instead of the thermal energy as conserved variable. */
 //#define GIZMO_TOTAL_ENERGY
 
 /* Options to control handling of unphysical values (GIZMO_SPH only). */
diff --git a/src/cooling.c b/src/cooling.c
index 57d1928a5d59ac2ff46c6cd20a45d69dec25ec60..154b859f74402d9e9a8adf1fb6c796b5195b8cd1 100644
--- a/src/cooling.c
+++ b/src/cooling.c
@@ -34,7 +34,7 @@
  * @param phys_const The physical constants in internal units.
  * @param cooling The cooling properties to initialize
  */
-void cooling_init(const struct swift_params* parameter_file,
+void cooling_init(struct swift_params* parameter_file,
                   const struct unit_system* us,
                   const struct phys_const* phys_const,
                   struct cooling_function_data* cooling) {
diff --git a/src/cooling.h b/src/cooling.h
index 9d1001d360a1816837381e9aa52b17ba47f50fce..0fb04b9e484d989e746a254fc1934dc20033fb09 100644
--- a/src/cooling.h
+++ b/src/cooling.h
@@ -43,7 +43,7 @@
 #endif
 
 /* Common functions */
-void cooling_init(const struct swift_params* parameter_file,
+void cooling_init(struct swift_params* parameter_file,
                   const struct unit_system* us,
                   const struct phys_const* phys_const,
                   struct cooling_function_data* cooling);
diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h
index bdf3801887256cb97ae1d5b6a3095250764aa822..1c56572856a88d763d5ef7ca77e14d378891a264 100644
--- a/src/cooling/EAGLE/cooling.h
+++ b/src/cooling/EAGLE/cooling.h
@@ -79,9 +79,12 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  * @brief Sets the cooling properties of the (x-)particles to a valid start
  * state.
  *
+ * @param phys_const The physical constants in internal units.
+ * @param us The internal system of units.
+ * @param cosmo The current cosmological model.
+ * @param cooling The properties of the cooling function.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
- * @param cooling The properties of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
     const struct phys_const* restrict phys_const,
@@ -109,10 +112,11 @@ __attribute__((always_inline)) INLINE static float cooling_get_radiated_energy(
  * @param phys_const The physical constants in internal units.
  * @param cooling The cooling properties to initialize
  */
-static INLINE void cooling_init_backend(
-    const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const,
-    struct cooling_function_data* cooling) {}
+static INLINE void cooling_init_backend(struct swift_params* parameter_file,
+                                        const struct unit_system* us,
+                                        const struct phys_const* phys_const,
+                                        struct cooling_function_data* cooling) {
+}
 
 /**
  * @brief Prints the properties of the cooling model to stdout.
diff --git a/src/cooling/EAGLE/cooling_io.h b/src/cooling/EAGLE/cooling_io.h
index d6ed4f122863be7b591b095b11803a3d2729f694..f98539605de5c231a821758e9bd8fdb89bd19a59 100644
--- a/src/cooling/EAGLE/cooling_io.h
+++ b/src/cooling/EAGLE/cooling_io.h
@@ -41,7 +41,7 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
 /**
  * @brief Specifies which particle fields to write to a dataset
  *
- * @param parts The particle array.
+ * @param xparts The extended data particle array.
  * @param list The list of i/o properties to write.
  * @param cooling The #cooling_function_data
  *
diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h
index ba8211174919419c37856dc1fcbdaa73b23e319e..b6fea7eea7b0fb208c4bffece425ec836d5df0c0 100644
--- a/src/cooling/const_du/cooling.h
+++ b/src/cooling/const_du/cooling.h
@@ -163,10 +163,10 @@ __attribute__((always_inline)) INLINE static float cooling_get_radiated_energy(
  * @param phys_const The physical constants in internal units.
  * @param cooling The cooling properties to initialize
  */
-static INLINE void cooling_init_backend(
-    const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const,
-    struct cooling_function_data* cooling) {
+static INLINE void cooling_init_backend(struct swift_params* parameter_file,
+                                        const struct unit_system* us,
+                                        const struct phys_const* phys_const,
+                                        struct cooling_function_data* cooling) {
 
   cooling->cooling_rate =
       parser_get_param_double(parameter_file, "ConstCooling:cooling_rate");
diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h
index 43ca7ab75b0bce370d7405e52cea9b54335ae73c..f1a7abdbe14a39d98bbd01eb36ba870c8af0ee1a 100644
--- a/src/cooling/const_lambda/cooling.h
+++ b/src/cooling/const_lambda/cooling.h
@@ -171,10 +171,10 @@ __attribute__((always_inline)) INLINE static float cooling_get_radiated_energy(
  * @param phys_const The physical constants in internal units.
  * @param cooling The cooling properties to initialize
  */
-static INLINE void cooling_init_backend(
-    const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const,
-    struct cooling_function_data* cooling) {
+static INLINE void cooling_init_backend(struct swift_params* parameter_file,
+                                        const struct unit_system* us,
+                                        const struct phys_const* phys_const,
+                                        struct cooling_function_data* cooling) {
 
   const double lambda_cgs =
       parser_get_param_double(parameter_file, "LambdaCooling:lambda_cgs");
diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h
index dd59e9af1431681a8c5bdc1e5cb0c22053063651..cb77b63294aacee425b917c1900eefd7ebfa5f34 100644
--- a/src/cooling/grackle/cooling.h
+++ b/src/cooling/grackle/cooling.h
@@ -771,7 +771,7 @@ __attribute__((always_inline)) INLINE static void cooling_init_grackle(
  * @param cooling The cooling properties to initialize
  */
 __attribute__((always_inline)) INLINE static void cooling_init_backend(
-    const struct swift_params* parameter_file, const struct unit_system* us,
+    struct swift_params* parameter_file, const struct unit_system* us,
     const struct phys_const* phys_const,
     struct cooling_function_data* cooling) {
 
diff --git a/src/cooling/grackle/cooling_io.h b/src/cooling/grackle/cooling_io.h
index 5a6edb8f1c559a7b495351e256559f251b97c1cf..faf84cf97d8449d54f2727ec26b16a9d81d117c6 100644
--- a/src/cooling/grackle/cooling_io.h
+++ b/src/cooling/grackle/cooling_io.h
@@ -133,7 +133,7 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles(
  * @param cooling The cooling properties to initialize
  */
 __attribute__((always_inline)) INLINE static void cooling_read_parameters(
-    const struct swift_params* parameter_file,
+    struct swift_params* parameter_file,
     struct cooling_function_data* cooling) {
 
   parser_get_param_string(parameter_file, "GrackleCooling:CloudyTable",
diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h
index 5081c7cbe6c4b5168da082ead80687226f9d0c16..0cc465adcdad8fe19afe4a9867e5d68a22ed9119 100644
--- a/src/cooling/none/cooling.h
+++ b/src/cooling/none/cooling.h
@@ -119,10 +119,11 @@ __attribute__((always_inline)) INLINE static float cooling_get_radiated_energy(
  * @param phys_const The physical constants in internal units.
  * @param cooling The cooling properties to initialize
  */
-static INLINE void cooling_init_backend(
-    const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const,
-    struct cooling_function_data* cooling) {}
+static INLINE void cooling_init_backend(struct swift_params* parameter_file,
+                                        const struct unit_system* us,
+                                        const struct phys_const* phys_const,
+                                        struct cooling_function_data* cooling) {
+}
 
 /**
  * @brief Prints the properties of the cooling model to stdout.
diff --git a/src/cosmology.c b/src/cosmology.c
index 4da9528784b1fb7fdb04761b77a3c0056a32f41a..e0ea1e9cd30610504821926720a73cb839e37312 100644
--- a/src/cosmology.c
+++ b/src/cosmology.c
@@ -119,6 +119,10 @@ static INLINE double E(double Or, double Om, double Ok, double Ol, double w0,
  */
 double cosmology_get_time_since_big_bang(const struct cosmology *c, double a) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (a < c->a_begin) error("Error a can't be smaller than a_begin");
+#endif
+
   /* Time between a_begin and a */
   const double delta_t =
       interp_table(c->time_interp_table, log(a), c->log_a_begin, c->log_a_end);
@@ -387,8 +391,7 @@ void cosmology_init_tables(struct cosmology *c) {
  * @param phys_const The physical constants in the current system of units.
  * @param c The #cosmology to initialise.
  */
-void cosmology_init(const struct swift_params *params,
-                    const struct unit_system *us,
+void cosmology_init(struct swift_params *params, const struct unit_system *us,
                     const struct phys_const *phys_const, struct cosmology *c) {
 
   /* Read in the cosmological parameters */
@@ -507,6 +510,10 @@ double cosmology_get_drift_factor(const struct cosmology *c,
                                   integertime_t ti_start,
                                   integertime_t ti_end) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (ti_end < ti_start) error("ti_end must be >= ti_start");
+#endif
+
   const double a_start = c->log_a_begin + ti_start * c->time_base;
   const double a_end = c->log_a_begin + ti_end * c->time_base;
 
@@ -531,6 +538,10 @@ double cosmology_get_grav_kick_factor(const struct cosmology *c,
                                       integertime_t ti_start,
                                       integertime_t ti_end) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (ti_end < ti_start) error("ti_end must be >= ti_start");
+#endif
+
   const double a_start = c->log_a_begin + ti_start * c->time_base;
   const double a_end = c->log_a_begin + ti_end * c->time_base;
 
@@ -555,6 +566,10 @@ double cosmology_get_hydro_kick_factor(const struct cosmology *c,
                                        integertime_t ti_start,
                                        integertime_t ti_end) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (ti_end < ti_start) error("ti_end must be >= ti_start");
+#endif
+
   const double a_start = c->log_a_begin + ti_start * c->time_base;
   const double a_end = c->log_a_begin + ti_end * c->time_base;
 
@@ -580,6 +595,10 @@ double cosmology_get_therm_kick_factor(const struct cosmology *c,
                                        integertime_t ti_start,
                                        integertime_t ti_end) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (ti_end < ti_start) error("ti_end must be >= ti_start");
+#endif
+
   const double a_start = c->log_a_begin + ti_start * c->time_base;
   const double a_end = c->log_a_begin + ti_end * c->time_base;
 
@@ -592,22 +611,30 @@ double cosmology_get_therm_kick_factor(const struct cosmology *c,
 }
 
 /**
- * @brief Compute the cosmic time (in internal units) between two scale-factors.
+ * @brief Compute the cosmic time (in internal units) between two points
+ * on the integer time line.
  *
- * @brief c The current #cosmology.
- * @brief a1 The first scale-factor.
- * @brief a2 The second scale-factor.
+ * @param c The current #cosmology.
+ * @param ti_start the (integer) time of the start.
+ * @param ti_end the (integer) time of the end.
  */
-double cosmology_get_delta_time(const struct cosmology *c, double a1,
-                                double a2) {
+double cosmology_get_delta_time(const struct cosmology *c,
+                                integertime_t ti_start, integertime_t ti_end) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (ti_end < ti_start) error("ti_end must be >= ti_start");
+#endif
+
+  const double log_a_start = c->log_a_begin + ti_start * c->time_base;
+  const double log_a_end = c->log_a_begin + ti_end * c->time_base;
 
-  /* Time between a_begin and a1 */
-  const double t1 =
-      interp_table(c->time_interp_table, log(a1), c->log_a_begin, c->log_a_end);
+  /* Time between a_begin and a_start */
+  const double t1 = interp_table(c->time_interp_table, log_a_start,
+                                 c->log_a_begin, c->log_a_end);
 
-  /* Time between a_begin and a1 */
-  const double t2 =
-      interp_table(c->time_interp_table, log(a2), c->log_a_begin, c->log_a_end);
+  /* Time between a_begin and a_end */
+  const double t2 = interp_table(c->time_interp_table, log_a_end,
+                                 c->log_a_begin, c->log_a_end);
 
   return t2 - t1;
 }
diff --git a/src/cosmology.h b/src/cosmology.h
index 109b80a57d8dbc4eb942dd4ecbbc0db84198100b..97d62ec98156caf4cf70275798dd8e9da806b4b2 100644
--- a/src/cosmology.h
+++ b/src/cosmology.h
@@ -177,12 +177,10 @@ double cosmology_get_hydro_kick_factor(const struct cosmology *cosmo,
 double cosmology_get_therm_kick_factor(const struct cosmology *cosmo,
                                        integertime_t ti_start,
                                        integertime_t ti_end);
+double cosmology_get_delta_time(const struct cosmology *c,
+                                integertime_t ti_start, integertime_t ti_end);
 
-double cosmology_get_delta_time(const struct cosmology *c, double a1,
-                                double a2);
-
-void cosmology_init(const struct swift_params *params,
-                    const struct unit_system *us,
+void cosmology_init(struct swift_params *params, const struct unit_system *us,
                     const struct phys_const *phys_const, struct cosmology *c);
 
 void cosmology_init_no_cosmo(struct cosmology *c);
diff --git a/src/cycle.h b/src/cycle.h
index f220ecd120b14db0a8cdaf5d1105be4bd0e70831..842510e066e2f6f94e736851bf636c9a73e4f25f 100644
--- a/src/cycle.h
+++ b/src/cycle.h
@@ -519,8 +519,19 @@ INLINE_ELAPSED(inline)
 #define HAVE_TICK_COUNTER
 #endif
 
+#if defined(HAVE_ARMV7A_PMCCNTR)
+typedef uint64_t ticks;
+static inline ticks getticks(void) {
+  uint32_t r;
+  asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(r));
+  return r;
+}
+INLINE_ELAPSED(inline)
+#define HAVE_TICK_COUNTER
+#endif
+
 #if defined(__aarch64__) && defined(HAVE_ARMV8_CNTVCT_EL0) && \
-    !defined(HAVE_ARMV8CC)
+    !defined(HAVE_ARMV8_PMCCNTR_EL0)
 typedef uint64_t ticks;
 static inline ticks getticks(void) {
   uint64_t Rt;
@@ -531,7 +542,7 @@ INLINE_ELAPSED(inline)
 #define HAVE_TICK_COUNTER
 #endif
 
-#if defined(__aarch64__) && defined(HAVE_ARMV8CC)
+#if defined(__aarch64__) && defined(HAVE_ARMV8_PMCCNTR_EL0)
 typedef uint64_t ticks;
 static inline ticks getticks(void) {
   uint64_t cc = 0;
diff --git a/src/debug.c b/src/debug.c
index 4000625a5e7d1bc83600a3f20a0db160057f07a6..05c21de0a73bba3a5e867a4265de0a5c14736a14 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -46,12 +46,18 @@
 #include "./hydro/Gadget2/hydro_debug.h"
 #elif defined(HOPKINS_PE_SPH)
 #include "./hydro/PressureEntropy/hydro_debug.h"
+#elif defined(HOPKINS_PU_SPH)
+#include "./hydro/PressureEnergy/hydro_debug.h"
 #elif defined(DEFAULT_SPH)
 #include "./hydro/Default/hydro_debug.h"
-#elif defined(GIZMO_SPH)
-#include "./hydro/Gizmo/hydro_debug.h"
+#elif defined(GIZMO_MFV_SPH)
+#include "./hydro/GizmoMFV/hydro_debug.h"
+#elif defined(GIZMO_MFM_SPH)
+#include "./hydro/GizmoMFM/hydro_debug.h"
 #elif defined(SHADOWFAX_SPH)
 #include "./hydro/Shadowswift/hydro_debug.h"
+#elif defined(MINIMAL_MULTI_MAT_SPH)
+#include "./hydro/MinimalMultiMat/hydro_debug.h"
 #else
 #error "Invalid choice of SPH variant"
 #endif
@@ -636,7 +642,7 @@ void getProcMemUse(long *size, long *resident, long *share, long *trs,
 /**
  * @brief Print the current memory use of the process. A la "top".
  */
-void printProcMemUse() {
+void printProcMemUse(void) {
   long size;
   long resident;
   long share;
diff --git a/src/debug.h b/src/debug.h
index 1e482c05c5af2dfebff1a254018fb1802df6cc5d..c9d65ad06cf5307a5fd8596c9c5b6c8b83cb6d9e 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -51,5 +51,5 @@ void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells);
 
 void getProcMemUse(long *size, long *resident, long *share, long *trs,
                    long *lrs, long *drs, long *dt);
-void printProcMemUse();
+void printProcMemUse(void);
 #endif /* SWIFT_DEBUG_H */
diff --git a/src/engine.c b/src/engine.c
index 62b90bf8e8d94ebd8f057f2c08ca02b3e9911f28..a6ac49be0942a03387fce25bb9a12b8a6f33349a 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -44,6 +44,11 @@
 #include <numa.h>
 #endif
 
+/* Load the profiler header, if needed. */
+#ifdef WITH_PROFILER
+#include <gperftools/profiler.h>
+#endif
+
 /* This object's header. */
 #include "engine.h"
 
@@ -59,8 +64,10 @@
 #include "debug.h"
 #include "error.h"
 #include "gravity.h"
+#include "gravity_cache.h"
 #include "hydro.h"
 #include "map.h"
+#include "memswap.h"
 #include "minmax.h"
 #include "parallel_io.h"
 #include "part.h"
@@ -127,7 +134,7 @@ struct end_of_step_data {
 void engine_addlink(struct engine *e, struct link **l, struct task *t) {
 
   /* Get the next free link. */
-  const int ind = atomic_inc(&e->nr_links);
+  const size_t ind = atomic_inc(&e->nr_links);
   if (ind >= e->size_links) {
     error("Link table overflow.");
   }
@@ -328,8 +335,13 @@ void engine_make_hierarchical_tasks_gravity(struct engine *e, struct cell *c) {
         c->grav_down = scheduler_addtask(s, task_type_grav_down,
                                          task_subtype_none, 0, 0, c, NULL);
 
-        if (periodic) scheduler_addunlock(s, c->init_grav, c->grav_ghost_in);
-        if (periodic) scheduler_addunlock(s, c->grav_ghost_out, c->grav_down);
+        /* Gravity mesh force propagation */
+        if (periodic)
+          c->grav_mesh = scheduler_addtask(s, task_type_grav_mesh,
+                                           task_subtype_none, 0, 0, c, NULL);
+
+        if (periodic) scheduler_addunlock(s, c->drift_gpart, c->grav_mesh);
+        if (periodic) scheduler_addunlock(s, c->grav_mesh, c->super->end_force);
         scheduler_addunlock(s, c->init_grav, c->grav_long_range);
         scheduler_addunlock(s, c->grav_long_range, c->grav_down);
         scheduler_addunlock(s, c->grav_down, c->super->end_force);
@@ -2322,28 +2334,21 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements,
                                            void *extra_data) {
 
   struct engine *e = ((struct engine **)extra_data)[0];
-  struct task **ghosts = ((struct task ***)extra_data)[1];
-
   struct space *s = e->s;
   struct scheduler *sched = &e->sched;
   const int nodeID = e->nodeID;
   const int periodic = s->periodic;
   const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
   const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]};
-  const int cdim_ghost[3] = {s->cdim[0] / 4 + 1, s->cdim[1] / 4 + 1,
-                             s->cdim[2] / 4 + 1};
   const double theta_crit2 = e->gravity_properties->theta_crit2;
   struct cell *cells = s->cells_top;
-  const int n_ghosts = cdim_ghost[0] * cdim_ghost[1] * cdim_ghost[2] * 2;
+  const double max_distance = e->mesh->r_cut_max;
 
   /* Loop through the elements, which are just byte offsets from NULL. */
   for (int ind = 0; ind < num_elements; ind++) {
 
     /* Get the cell index. */
     const int cid = (size_t)(map_data) + ind;
-    const int i = cid / (cdim[1] * cdim[2]);
-    const int j = (cid / cdim[2]) % cdim[1];
-    const int k = cid % cdim[2];
 
     /* Get the cell */
     struct cell *ci = &cells[cid];
@@ -2357,17 +2362,17 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements,
     /* If the cells is local build a self-interaction */
     scheduler_addtask(sched, task_type_self, task_subtype_grav, 0, 0, ci, NULL);
 
-    /* Deal with periodicity FFT task dependencies */
-    const int ghost_id = cell_getid(cdim_ghost, i / 4, j / 4, k / 4);
-    if (ghost_id > n_ghosts) error("Invalid ghost_id");
-    if (periodic) {
-      ci->grav_ghost_in = ghosts[2 * ghost_id + 0];
-      ci->grav_ghost_out = ghosts[2 * ghost_id + 1];
-    }
-
     /* Recover the multipole information */
-    struct gravity_tensors *const multi_i = ci->multipole;
+    const struct gravity_tensors *const multi_i = ci->multipole;
     const double CoM_i[3] = {multi_i->CoM[0], multi_i->CoM[1], multi_i->CoM[2]};
+    const double r_max_i = multi_i->r_max;
+
+#ifdef SWIFT_DEBUG_CHECKS
+    if (multi_i->r_max != multi_i->r_max_rebuild)
+      error(
+          "Multipole size not equal ot it's size after rebuild. But we just "
+          "rebuilt...");
+#endif
 
     /* Loop over every other cell */
     for (int ii = 0; ii < cdim[0]; ii++) {
@@ -2400,9 +2405,15 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements,
           }
           const double r2 = dx * dx + dy * dy + dz * dz;
 
+          /* Minimal distance between any pair of particles */
+          const double min_radius =
+              sqrt(r2) - (multi_i->r_max + multi_j->r_max);
+
+          /* Are we beyond the distance where the truncated forces are 0 ?*/
+          if (periodic && min_radius > max_distance) continue;
+
           /* Are the cells too close for a MM interaction ? */
-          if (!gravity_M2L_accept(multi_i->r_max_rebuild,
-                                  multi_j->r_max_rebuild, theta_crit2, r2)) {
+          if (!gravity_M2L_accept(r_max_i, multi_j->r_max, theta_crit2, r2)) {
 
             /* Ok, we need to add a direct pair calculation */
             scheduler_addtask(sched, task_type_pair, task_subtype_grav, 0, 0,
@@ -2426,60 +2437,17 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements,
 void engine_make_self_gravity_tasks(struct engine *e) {
 
   struct space *s = e->s;
-  struct scheduler *sched = &e->sched;
-  const int periodic = s->periodic;
-  const int cdim_ghost[3] = {s->cdim[0] / 4 + 1, s->cdim[1] / 4 + 1,
-                             s->cdim[2] / 4 + 1};
+  /* struct scheduler *sched = &e->sched; */
+  /* const int periodic = s->periodic; */
+  /* const int cdim_ghost[3] = {s->cdim[0] / 4 + 1, s->cdim[1] / 4 + 1, */
+  /*                            s->cdim[2] / 4 + 1}; */
   struct task **ghosts = NULL;
-  const int n_ghosts = cdim_ghost[0] * cdim_ghost[1] * cdim_ghost[2] * 2;
-
-  /* Create the top-level task if periodic */
-  if (periodic) {
-
-    /* Create the FFT task for this MPI rank */
-    s->grav_top_level = scheduler_addtask(sched, task_type_grav_top_level,
-                                          task_subtype_none, 0, 0, NULL, NULL);
-
-    /* Create a grid of ghosts to deal with the dependencies */
-    if ((ghosts = (struct task **)malloc(n_ghosts * sizeof(struct task *))) ==
-        0)
-      error("Error allocating memory for gravity fft ghosts");
-
-    /* Make the ghosts implicit and add the dependencies */
-    for (int n = 0; n < n_ghosts / 2; ++n) {
-      ghosts[2 * n + 0] = scheduler_addtask(
-          sched, task_type_grav_ghost_in, task_subtype_none, 0, 1, NULL, NULL);
-      ghosts[2 * n + 1] = scheduler_addtask(
-          sched, task_type_grav_ghost_out, task_subtype_none, 0, 1, NULL, NULL);
-      scheduler_addunlock(sched, ghosts[2 * n + 0], s->grav_top_level);
-      scheduler_addunlock(sched, s->grav_top_level, ghosts[2 * n + 1]);
-    }
-  }
+  // const int n_ghosts = cdim_ghost[0] * cdim_ghost[1] * cdim_ghost[2] * 2;
 
   /* Cretae the multipole self and pair tasks. */
   void *extra_data[2] = {e, ghosts};
   threadpool_map(&e->threadpool, engine_make_self_gravity_tasks_mapper, NULL,
                  s->nr_cells, 1, 0, extra_data);
-
-#ifdef SWIFT_DEBUG_CHECKS
-  if (periodic)
-    for (int i = 0; i < s->nr_cells; ++i) {
-      const struct cell *c = &s->cells_top[i];
-      /* Skip empty cells */
-      if (c->gcount == 0) continue;
-
-      /* Did we correctly attach the FFT task ghosts? */
-      if (c->nodeID == engine_rank &&
-          (c->grav_ghost_in == NULL || c->grav_ghost_out == NULL))
-        error("Invalid gravity_ghost for local cell");
-      if (c->nodeID != engine_rank &&
-          (c->grav_ghost_in != NULL || c->grav_ghost_out != NULL))
-        error("Invalid gravity_ghost for foreign cell");
-    }
-#endif
-
-  /* Clean up. */
-  if (periodic) free(ghosts);
 }
 
 /**
@@ -3138,12 +3106,12 @@ void engine_maketasks(struct engine *e) {
  * and 2 (density, force) self.
  */
 #ifdef EXTRA_HYDRO_LOOP
-  const int hydro_tasks_per_cell = 27 * 3;
+  const size_t hydro_tasks_per_cell = 27 * 3;
 #else
-  const int hydro_tasks_per_cell = 27 * 2;
+  const size_t hydro_tasks_per_cell = 27 * 2;
 #endif
-  const int self_grav_tasks_per_cell = 27 * 2;
-  const int ext_grav_tasks_per_cell = 1;
+  const size_t self_grav_tasks_per_cell = 125;
+  const size_t ext_grav_tasks_per_cell = 1;
 
   if (e->policy & engine_policy_hydro)
     e->size_links += s->tot_cells * hydro_tasks_per_cell;
@@ -3580,19 +3548,12 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
     }
 
     /* Gravity stuff ? */
-    else if (t->type == task_type_grav_down ||
+    else if (t->type == task_type_grav_down || t->type == task_type_grav_mesh ||
              t->type == task_type_grav_long_range ||
              t->type == task_type_init_grav) {
       if (cell_is_active_gravity(t->ci, e)) scheduler_activate(s, t);
     }
 
-    /* Periodic gravity stuff (Note this is not linked to a cell) ? */
-    else if (t->type == task_type_grav_top_level ||
-             t->type == task_type_grav_ghost_in ||
-             t->type == task_type_grav_ghost_out) {
-      scheduler_activate(s, t);
-    }
-
     /* Time-step? */
     else if (t->type == task_type_timestep) {
       t->ci->updated = 0;
@@ -3717,7 +3678,7 @@ int engine_estimate_nr_tasks(struct engine *e) {
 #endif
   }
   if (e->policy & engine_policy_self_gravity) {
-    n1 += 32;
+    n1 += 125;
     n2 += 1;
 #ifdef WITH_MPI
     n2 += 2;
@@ -3805,6 +3766,14 @@ void engine_rebuild(struct engine *e, int clean_smoothing_length_values) {
   /* Re-build the space. */
   space_rebuild(e->s, e->verbose);
 
+  /* Re-compute the mesh forces */
+  if ((e->policy & engine_policy_self_gravity) && e->s->periodic)
+    pm_mesh_compute_potential(e->mesh, e);
+
+  /* Re-compute the maximal RMS displacement constraint */
+  if (e->policy & engine_policy_cosmology)
+    engine_recompute_displacement_constraint(e);
+
 #ifdef SWIFT_DEBUG_CHECKS
   part_verify_links(e->s->parts, e->s->gparts, e->s->sparts, e->s->nr_parts,
                     e->s->nr_gparts, e->s->nr_sparts, e->verbose);
@@ -3845,6 +3814,11 @@ void engine_rebuild(struct engine *e, int clean_smoothing_length_values) {
   /* Print the status of the system */
   if (e->verbose) engine_print_task_counts(e);
 
+  /* Clear the counters of updates since the last rebuild */
+  e->updates_since_rebuild = 0;
+  e->g_updates_since_rebuild = 0;
+  e->s_updates_since_rebuild = 0;
+
   /* Flag that a rebuild has taken place */
   e->step_props |= engine_step_prop_rebuild;
 
@@ -4099,13 +4073,13 @@ void engine_collect_end_of_step(struct engine *e, int apply) {
                       MPI_COMM_WORLD) != MPI_SUCCESS)
       error("Failed to aggregate particle counts.");
     if (in_ll[0] != (long long)e->collect_group1.updates)
-      error("Failed to get same updates, is %lld, should be %ld", in_ll[0],
+      error("Failed to get same updates, is %lld, should be %lld", in_ll[0],
             e->collect_group1.updates);
     if (in_ll[1] != (long long)e->collect_group1.g_updates)
-      error("Failed to get same g_updates, is %lld, should be %ld", in_ll[1],
+      error("Failed to get same g_updates, is %lld, should be %lld", in_ll[1],
             e->collect_group1.g_updates);
     if (in_ll[2] != (long long)e->collect_group1.s_updates)
-      error("Failed to get same s_updates, is %lld, should be %ld", in_ll[2],
+      error("Failed to get same s_updates, is %lld, should be %lld", in_ll[2],
             e->collect_group1.s_updates);
 
     int buff = 0;
@@ -4220,10 +4194,8 @@ void engine_skip_force_and_kick(struct engine *e) {
         t->type == task_type_timestep || t->subtype == task_subtype_force ||
         t->subtype == task_subtype_grav || t->type == task_type_end_force ||
         t->type == task_type_grav_long_range ||
-        t->type == task_type_grav_ghost_in ||
-        t->type == task_type_grav_ghost_out ||
-        t->type == task_type_grav_top_level || t->type == task_type_grav_down ||
-        t->type == task_type_cooling || t->type == task_type_sourceterms)
+        t->type == task_type_grav_down || t->type == task_type_cooling ||
+        t->type == task_type_sourceterms)
       t->skip = 1;
   }
 
@@ -4520,14 +4492,15 @@ void engine_step(struct engine *e) {
   if (e->nodeID == 0) {
 
     /* Print some information to the screen */
-    printf("  %6d %14e %14e %10.5f %14e %4d %4d %12zu %12zu %12zu %21.3f %6d\n",
-           e->step, e->time, e->cosmology->a, e->cosmology->z, e->time_step,
-           e->min_active_bin, e->max_active_bin, e->updates, e->g_updates,
-           e->s_updates, e->wallclock_time, e->step_props);
+    printf(
+        "  %6d %14e %14e %10.5f %14e %4d %4d %12lld %12lld %12lld %21.3f %6d\n",
+        e->step, e->time, e->cosmology->a, e->cosmology->z, e->time_step,
+        e->min_active_bin, e->max_active_bin, e->updates, e->g_updates,
+        e->s_updates, e->wallclock_time, e->step_props);
     fflush(stdout);
 
     fprintf(e->file_timesteps,
-            "  %6d %14e %14e %14e %4d %4d %12zu %12zu %12zu %21.3f %6d\n",
+            "  %6d %14e %14e %14e %4d %4d %12lld %12lld %12lld %21.3f %6d\n",
             e->step, e->time, e->cosmology->a, e->time_step, e->min_active_bin,
             e->max_active_bin, e->updates, e->g_updates, e->s_updates,
             e->wallclock_time, e->step_props);
@@ -4614,11 +4587,6 @@ void engine_step(struct engine *e) {
     gravity_exact_force_check(e->s, e, 1e-1);
 #endif
 
-  /* Let's trigger a non-SPH rebuild every-so-often for good measure */
-  if (!(e->policy & engine_policy_hydro) &&  // MATTHIEU improve this
-      (e->policy & engine_policy_self_gravity) && e->step % 20 == 0)
-    e->forcerebuild = 1;
-
   /* Collect the values of rebuild from all nodes and recover the (integer)
    * end of the next time-step. Do these together to reduce the collective MPI
    * calls per step, but some of the gathered information is not applied just
@@ -4626,6 +4594,17 @@ void engine_step(struct engine *e) {
   engine_collect_end_of_step(e, 0);
   e->forcerebuild = e->collect_group1.forcerebuild;
 
+  /* Update the counters */
+  e->updates_since_rebuild += e->collect_group1.updates;
+  e->g_updates_since_rebuild += e->collect_group1.g_updates;
+  e->s_updates_since_rebuild += e->collect_group1.s_updates;
+
+  /* Trigger a tree-rebuild if we passed the frequency threshold */
+  if ((e->policy & engine_policy_self_gravity) &&
+      ((double)e->g_updates_since_rebuild >
+       ((double)e->total_nr_gparts) * e->gravity_properties->rebuild_frequency))
+    e->forcerebuild = 1;
+
   /* Save some statistics ? */
   if (e->ti_end_min >= e->ti_next_stats && e->ti_next_stats > 0)
     e->save_stats = 1;
@@ -4752,18 +4731,37 @@ int engine_is_done(struct engine *e) {
 void engine_unskip(struct engine *e) {
 
   const ticks tic = getticks();
+  struct space *s = e->s;
 
-  /* Activate all the regular tasks */
-  threadpool_map(&e->threadpool, runner_do_unskip_mapper, e->s->local_cells_top,
-                 e->s->nr_local_cells, sizeof(int), 1, e);
+#ifdef WITH_PROFILER
+  static int count = 0;
+  char filename[100];
+  sprintf(filename, "/tmp/swift_runner_do_usnkip_mapper_%06i.prof", count++);
+  ProfilerStart(filename);
+#endif  // WITH_PROFILER
+
+  /* Move the active local cells to the top of the list. */
+  int *local_cells = e->s->local_cells_top;
+  int num_active_cells = 0;
+  for (int k = 0; k < s->nr_local_cells; k++) {
+    struct cell *c = &s->cells_top[local_cells[k]];
+    if ((e->policy & engine_policy_hydro && cell_is_active_hydro(c, e)) ||
+        (e->policy &
+             (engine_policy_self_gravity | engine_policy_external_gravity) &&
+         cell_is_active_gravity(c, e))) {
+      if (num_active_cells != k)
+        memswap(&local_cells[k], &local_cells[num_active_cells], sizeof(int));
+      num_active_cells += 1;
+    }
+  }
 
-  /* And the top level gravity FFT one when periodicity is on.*/
-  if (e->s->periodic && (e->policy & engine_policy_self_gravity)) {
+  /* Activate all the regular tasks */
+  threadpool_map(&e->threadpool, runner_do_unskip_mapper, local_cells,
+                 num_active_cells, sizeof(int), 1, e);
 
-    /* Only if there are other tasks (i.e. something happens on this node) */
-    if (e->sched.active_count > 0)
-      scheduler_activate(&e->sched, e->s->grav_top_level);
-  }
+#ifdef WITH_PROFILER
+  ProfilerStop();
+#endif  // WITH_PROFILER
 
   if (e->verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
@@ -4970,6 +4968,8 @@ void engine_makeproxies(struct engine *e) {
   if (with_gravity) {
     const double distance = 2.5 * cells[0].width[0] / props->theta_crit;
     delta = (int)(distance / cells[0].width[0]) + 1;
+
+    // MATTHIEU: Check this calculation
   }
 
   /* Let's be verbose about this choice */
@@ -5314,7 +5314,7 @@ void engine_dump_snapshot(struct engine *e) {
 /**
  * @brief Returns the initial affinity the main thread is using.
  */
-cpu_set_t *engine_entry_affinity() {
+cpu_set_t *engine_entry_affinity(void) {
 
   static int use_entry_affinity = 0;
   static cpu_set_t entry_affinity;
@@ -5333,7 +5333,7 @@ cpu_set_t *engine_entry_affinity() {
  * @brief  Ensure the NUMA node on which we initialise (first touch) everything
  * doesn't change before engine_init allocates NUMA-local workers.
  */
-void engine_pin() {
+void engine_pin(void) {
 
 #ifdef HAVE_SETAFFINITY
   cpu_set_t *entry_affinity = engine_entry_affinity();
@@ -5355,7 +5355,7 @@ void engine_pin() {
 /**
  * @brief Unpins the main thread.
  */
-void engine_unpin() {
+void engine_unpin(void) {
 #ifdef HAVE_SETAFFINITY
   pthread_t main_thread = pthread_self();
   cpu_set_t *entry_affinity = engine_entry_affinity();
@@ -5376,7 +5376,8 @@ void engine_unpin() {
  * @param s The #space in which this #runner will run.
  * @param params The parsed parameter file.
  * @param Ngas total number of gas particles in the simulation.
- * @param Ndm total number of gravity particles in the simulation.
+ * @param Ngparts total number of gravity particles in the simulation.
+ * @param Nstars total number of star particles in the simulation.
  * @param policy The queuing policy to use.
  * @param verbose Is this #engine talkative ?
  * @param reparttype What type of repartition algorithm are we using ?
@@ -5385,19 +5386,19 @@ void engine_unpin() {
  * @param cosmo The #cosmology used for this run.
  * @param hydro The #hydro_props used for this run.
  * @param gravity The #gravity_props used for this run.
+ * @param mesh The #pm_mesh used for the long-range periodic forces.
  * @param potential The properties of the external potential.
  * @param cooling_func The properties of the cooling function.
  * @param chemistry The chemistry information.
  * @param sourceterms The properties of the source terms function.
  */
-void engine_init(struct engine *e, struct space *s,
-                 const struct swift_params *params, long long Ngas,
-                 long long Ndm, int policy, int verbose,
-                 struct repartition *reparttype,
+void engine_init(struct engine *e, struct space *s, struct swift_params *params,
+                 long long Ngas, long long Ngparts, long long Nstars,
+                 int policy, int verbose, struct repartition *reparttype,
                  const struct unit_system *internal_units,
                  const struct phys_const *physical_constants,
                  struct cosmology *cosmo, const struct hydro_props *hydro,
-                 struct gravity_props *gravity,
+                 struct gravity_props *gravity, struct pm_mesh *mesh,
                  const struct external_potential *potential,
                  const struct cooling_function_data *cooling_func,
                  const struct chemistry_global_data *chemistry,
@@ -5411,7 +5412,8 @@ void engine_init(struct engine *e, struct space *s,
   e->policy = policy;
   e->step = 0;
   e->total_nr_parts = Ngas;
-  e->total_nr_gparts = Ndm;
+  e->total_nr_gparts = Ngparts;
+  e->total_nr_sparts = Nstars;
   e->proxy_ind = NULL;
   e->nr_proxies = 0;
   e->reparttype = reparttype;
@@ -5435,14 +5437,16 @@ void engine_init(struct engine *e, struct space *s,
   parser_get_param_string(params, "Snapshots:basename", e->snapshot_base_name);
   e->snapshot_compression =
       parser_get_opt_param_int(params, "Snapshots:compression", 0);
+  e->snapshot_label_delta =
+      parser_get_opt_param_int(params, "Snapshots:label_delta", 1);
   e->snapshot_units = (struct unit_system *)malloc(sizeof(struct unit_system));
   units_init_default(e->snapshot_units, params, "Snapshots", internal_units);
   e->snapshot_output_count = 0;
   e->dt_min = parser_get_param_double(params, "TimeIntegration:dt_min");
   e->dt_max = parser_get_param_double(params, "TimeIntegration:dt_max");
   e->dt_max_RMS_displacement = FLT_MAX;
-  e->max_RMS_displacement_factor =
-      parser_get_param_double(params, "TimeIntegration:max_dt_RMS_factor");
+  e->max_RMS_displacement_factor = parser_get_opt_param_double(
+      params, "TimeIntegration:max_dt_RMS_factor", 0.25);
   e->a_first_statistics =
       parser_get_opt_param_double(params, "Statistics:scale_factor_first", 0.1);
   e->time_first_statistics =
@@ -5451,12 +5455,12 @@ void engine_init(struct engine *e, struct space *s,
       parser_get_param_double(params, "Statistics:delta_time");
   e->ti_next_stats = 0;
   e->verbose = verbose;
-  e->count_step = 0;
   e->wallclock_time = 0.f;
   e->physical_constants = physical_constants;
   e->cosmology = cosmo;
   e->hydro_properties = hydro;
   e->gravity_properties = gravity;
+  e->mesh = mesh;
   e->external_potential = potential;
   e->cooling_func = cooling_func;
   e->chemistry = chemistry;
@@ -5518,10 +5522,9 @@ void engine_init(struct engine *e, struct space *s,
  * @param verbose Is this #engine talkative ?
  * @param restart_file The name of our restart file.
  */
-void engine_config(int restart, struct engine *e,
-                   const struct swift_params *params, int nr_nodes, int nodeID,
-                   int nr_threads, int with_aff, int verbose,
-                   const char *restart_file) {
+void engine_config(int restart, struct engine *e, struct swift_params *params,
+                   int nr_nodes, int nodeID, int nr_threads, int with_aff,
+                   int verbose, const char *restart_file) {
 
   /* Store the values and initialise global fields. */
   e->nodeID = nodeID;
@@ -6253,6 +6256,124 @@ void engine_compute_next_stf_time(struct engine *e) {
   }
 }
 
+/**
+ * @brief Computes the maximal time-step allowed by the max RMS displacement
+ * condition.
+ *
+ * @param e The #engine.
+ */
+void engine_recompute_displacement_constraint(struct engine *e) {
+
+  /* Get the cosmological information */
+  const struct cosmology *cosmo = e->cosmology;
+  const float Om = cosmo->Omega_m;
+  const float Ob = cosmo->Omega_b;
+  const float rho_crit = cosmo->critical_density;
+  const float a = cosmo->a;
+
+  /* Start by reducing the minimal mass of each particle type */
+  float min_mass[swift_type_count] = {e->s->min_part_mass,
+                                      e->s->min_gpart_mass,
+                                      FLT_MAX,
+                                      FLT_MAX,
+                                      e->s->min_spart_mass,
+                                      FLT_MAX};
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Check that the minimal mass collection worked */
+  float min_part_mass_check = FLT_MAX;
+  for (size_t i = 0; i < e->s->nr_parts; ++i)
+    min_part_mass_check =
+        min(min_part_mass_check, hydro_get_mass(&e->s->parts[i]));
+  if (min_part_mass_check != min_mass[swift_type_gas])
+    error("Error collecting minimal mass of gas particles.");
+#endif
+
+#ifdef WITH_MPI
+  MPI_Allreduce(MPI_IN_PLACE, min_mass, swift_type_count, MPI_FLOAT, MPI_MIN,
+                MPI_COMM_WORLD);
+#endif
+
+  /* Do the same for the velocity norm sum */
+  float vel_norm[swift_type_count] = {e->s->sum_part_vel_norm,
+                                      e->s->sum_gpart_vel_norm,
+                                      0.f,
+                                      0.f,
+                                      e->s->sum_spart_vel_norm,
+                                      0.f};
+#ifdef WITH_MPI
+  MPI_Allreduce(MPI_IN_PLACE, vel_norm, swift_type_count, MPI_FLOAT, MPI_SUM,
+                MPI_COMM_WORLD);
+#endif
+
+  /* Get the counts of each particle types */
+  const long long total_nr_dm_gparts =
+      e->total_nr_gparts - e->total_nr_parts - e->total_nr_sparts;
+  float count_parts[swift_type_count] = {(float)e->total_nr_parts,
+                                         (float)total_nr_dm_gparts,
+                                         0.f,
+                                         0.f,
+                                         (float)e->total_nr_sparts,
+                                         0.f};
+
+  /* Count of particles for the two species */
+  const float N_dm = count_parts[1];
+  const float N_b = count_parts[0] + count_parts[4];
+
+  /* Peculiar motion norm for the two species */
+  const float vel_norm_dm = vel_norm[1];
+  const float vel_norm_b = vel_norm[0] + vel_norm[4];
+
+  /* Mesh forces smoothing scale */
+  float r_s;
+  if ((e->policy & engine_policy_self_gravity) && e->s->periodic == 1)
+    r_s = e->mesh->r_s;
+  else
+    r_s = FLT_MAX;
+
+  float dt_dm = FLT_MAX, dt_b = FLT_MAX;
+
+  /* DM case */
+  if (N_dm > 0.f) {
+
+    /* Minimal mass for the DM */
+    const float min_mass_dm = min_mass[1];
+
+    /* Inter-particle sepration for the DM */
+    const float d_dm = cbrtf(min_mass_dm / ((Om - Ob) * rho_crit));
+
+    /* RMS peculiar motion for the DM */
+    const float rms_vel_dm = vel_norm_dm / N_dm;
+
+    /* Time-step based on maximum displacement */
+    dt_dm = a * a * min(r_s, d_dm) / sqrtf(rms_vel_dm);
+  }
+
+  /* Baryon case */
+  if (N_b > 0.f) {
+
+    /* Minimal mass for the baryons */
+    const float min_mass_b = min(min_mass[0], min_mass[4]);
+
+    /* Inter-particle sepration for the baryons */
+    const float d_b = cbrtf(min_mass_b / (Ob * rho_crit));
+
+    /* RMS peculiar motion for the baryons */
+    const float rms_vel_b = vel_norm_b / N_b;
+
+    /* Time-step based on maximum displacement */
+    dt_b = a * a * min(r_s, d_b) / sqrtf(rms_vel_b);
+  }
+
+  /* Use the minimum */
+  const float dt = min(dt_dm, dt_b);
+
+  /* Apply the dimensionless factor */
+  e->dt_max_RMS_displacement = dt * e->max_RMS_displacement_factor;
+
+  if (e->verbose)
+    message("max_dt_RMS_displacement = %e", e->dt_max_RMS_displacement);
+}
+
 /**
  * @brief Frees up the memory allocated for this #engine
  */
@@ -6303,6 +6424,7 @@ void engine_struct_dump(struct engine *e, FILE *stream) {
   phys_const_struct_dump(e->physical_constants, stream);
   hydro_props_struct_dump(e->hydro_properties, stream);
   gravity_props_struct_dump(e->gravity_properties, stream);
+  pm_mesh_struct_dump(e->mesh, stream);
   potential_struct_dump(e->external_potential, stream);
   cooling_struct_dump(e->cooling_func, stream);
   chemistry_struct_dump(e->chemistry, stream);
@@ -6372,6 +6494,10 @@ void engine_struct_restore(struct engine *e, FILE *stream) {
   gravity_props_struct_restore(gravity_properties, stream);
   e->gravity_properties = gravity_properties;
 
+  struct pm_mesh *mesh = (struct pm_mesh *)malloc(sizeof(struct pm_mesh));
+  pm_mesh_struct_restore(mesh, stream);
+  e->mesh = mesh;
+
   struct external_potential *external_potential =
       (struct external_potential *)malloc(sizeof(struct external_potential));
   potential_struct_restore(external_potential, stream);
diff --git a/src/engine.h b/src/engine.h
index 5a6025c24b48fdf34fc3ee4ae13b87d7032c87cc..746a24b591d05d3511624349bd8e8c0670d0ab6c 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -39,6 +39,7 @@
 #include "collectgroup.h"
 #include "cooling_struct.h"
 #include "gravity_properties.h"
+#include "mesh_gravity.h"
 #include "parser.h"
 #include "partition.h"
 #include "potential.h"
@@ -187,13 +188,18 @@ struct engine {
   integertime_t ti_beg_max;
 
   /* Number of particles updated in the previous step */
-  size_t updates, g_updates, s_updates;
+  long long updates, g_updates, s_updates;
+
+  /* Number of updates since the last rebuild */
+  long long updates_since_rebuild;
+  long long g_updates_since_rebuild;
+  long long s_updates_since_rebuild;
 
   /* Properties of the previous step */
   int step_props;
 
   /* Total numbers of particles in the system. */
-  long long total_nr_parts, total_nr_gparts;
+  long long total_nr_parts, total_nr_gparts, total_nr_sparts;
 
   /* The internal system of units */
   const struct unit_system *internal_units;
@@ -212,6 +218,7 @@ struct engine {
 
   char snapshot_base_name[PARSER_MAX_LINE_SIZE];
   int snapshot_compression;
+  int snapshot_label_delta;
   struct unit_system *snapshot_units;
   int snapshot_output_count;
 
@@ -244,9 +251,6 @@ struct engine {
   /* The current step number. */
   int step;
 
-  /* The number of particles updated in the previous step. */
-  int count_step;
-
   /* Data for the threads' barrier. */
   swift_barrier_t wait_barrier;
   swift_barrier_t run_barrier;
@@ -292,11 +296,11 @@ struct engine {
 
   /* Linked list for cell-task association. */
   struct link *links;
-  int nr_links, size_links;
+  size_t nr_links, size_links;
 
   /* Average number of tasks per cell. Used to estimate the sizes
    * of the various task arrays. */
-  int tasks_per_cell;
+  size_t tasks_per_cell;
 
   /* Are we talkative ? */
   int verbose;
@@ -313,6 +317,9 @@ struct engine {
   /* Properties of the self-gravity scheme */
   struct gravity_props *gravity_properties;
 
+  /* The mesh used for long-range gravity forces */
+  struct pm_mesh *mesh;
+
   /* Properties of external gravitational potential */
   const struct external_potential *external_potential;
 
@@ -326,7 +333,7 @@ struct engine {
   struct sourceterms *sourceterms;
 
   /* The (parsed) parameter file */
-  const struct swift_params *parameter_file;
+  struct swift_params *parameter_file;
 
   /* Temporary struct to hold a group of deferable properties (in MPI mode
    * these are reduced together, but may not be required just yet). */
@@ -365,22 +372,20 @@ void engine_drift_top_multipoles(struct engine *e);
 void engine_reconstruct_multipoles(struct engine *e);
 void engine_print_stats(struct engine *e);
 void engine_dump_snapshot(struct engine *e);
-void engine_init(struct engine *e, struct space *s,
-                 const struct swift_params *params, long long Ngas,
-                 long long Ndm, int policy, int verbose,
-                 struct repartition *reparttype,
+void engine_init(struct engine *e, struct space *s, struct swift_params *params,
+                 long long Ngas, long long Ngparts, long long Nstars,
+                 int policy, int verbose, struct repartition *reparttype,
                  const struct unit_system *internal_units,
                  const struct phys_const *physical_constants,
                  struct cosmology *cosmo, const struct hydro_props *hydro,
-                 struct gravity_props *gravity,
+                 struct gravity_props *gravity, struct pm_mesh *mesh,
                  const struct external_potential *potential,
                  const struct cooling_function_data *cooling_func,
                  const struct chemistry_global_data *chemistry,
                  struct sourceterms *sourceterms);
-void engine_config(int restart, struct engine *e,
-                   const struct swift_params *params, int nr_nodes, int nodeID,
-                   int nr_threads, int with_aff, int verbose,
-                   const char *restart_file);
+void engine_config(int restart, struct engine *e, struct swift_params *params,
+                   int nr_nodes, int nodeID, int nr_threads, int with_aff,
+                   int verbose, const char *restart_file);
 void engine_launch(struct engine *e);
 void engine_prepare(struct engine *e);
 void engine_init_particles(struct engine *e, int flag_entropy_ICs,
@@ -400,8 +405,8 @@ void engine_makeproxies(struct engine *e);
 void engine_redistribute(struct engine *e);
 void engine_print_policy(struct engine *e);
 int engine_is_done(struct engine *e);
-void engine_pin();
-void engine_unpin();
+void engine_pin(void);
+void engine_unpin(void);
 void engine_clean(struct engine *e);
 int engine_estimate_nr_tasks(struct engine *e);
 
diff --git a/src/equation_of_state.h b/src/equation_of_state.h
index 195b52514f2acc0c40959e09c088a06f0a411869..d170ce1a7c5c64fe95e415c997c037caeb576258 100644
--- a/src/equation_of_state.h
+++ b/src/equation_of_state.h
@@ -20,7 +20,7 @@
 #define SWIFT_EQUATION_OF_STATE_H
 
 /**
- * @file equation_of_state.h
+ * @file src/equation_of_state.h
  * @brief Defines the equation of state of the gas we simulate in the form of
  * relations between thermodynamic quantities. These are later used internally
  * by all hydro schemes
@@ -34,6 +34,8 @@
 #include "./equation_of_state/ideal_gas/equation_of_state.h"
 #elif defined(EOS_ISOTHERMAL_GAS)
 #include "./equation_of_state/isothermal/equation_of_state.h"
+#elif defined(EOS_PLANETARY)
+#include "./equation_of_state/planetary/equation_of_state.h"
 #else
 #error "Invalid choice of equation of state"
 #endif
diff --git a/src/equation_of_state/ideal_gas/equation_of_state.h b/src/equation_of_state/ideal_gas/equation_of_state.h
index 4d873fbfbb7c672365eda0f668e05679441742cf..4da5f69f29a2ef3443443fa25fe0388fea29e495 100644
--- a/src/equation_of_state/ideal_gas/equation_of_state.h
+++ b/src/equation_of_state/ideal_gas/equation_of_state.h
@@ -45,7 +45,7 @@ struct eos_parameters {};
  * @param density The density \f$\rho\f$.
  * @param entropy The entropy \f$S\f$.
  */
-__attribute__((always_inline)) INLINE static float
+__attribute__((always_inline, const)) INLINE static float
 gas_internal_energy_from_entropy(float density, float entropy) {
 
   return entropy * pow_gamma_minus_one(density) *
@@ -60,8 +60,8 @@ gas_internal_energy_from_entropy(float density, float entropy) {
  * @param density The density \f$\rho\f$.
  * @param entropy The entropy \f$S\f$.
  */
-__attribute__((always_inline)) INLINE static float gas_pressure_from_entropy(
-    float density, float entropy) {
+__attribute__((always_inline, const)) INLINE static float
+gas_pressure_from_entropy(float density, float entropy) {
 
   return entropy * pow_gamma(density);
 }
@@ -75,8 +75,8 @@ __attribute__((always_inline)) INLINE static float gas_pressure_from_entropy(
  * @param pressure The pressure \f$P\f$.
  * @return The entropy \f$A\f$.
  */
-__attribute__((always_inline)) INLINE static float gas_entropy_from_pressure(
-    float density, float pressure) {
+__attribute__((always_inline, const)) INLINE static float
+gas_entropy_from_pressure(float density, float pressure) {
 
   return pressure * pow_minus_gamma(density);
 }
@@ -89,8 +89,8 @@ __attribute__((always_inline)) INLINE static float gas_entropy_from_pressure(
  * @param density The density \f$\rho\f$.
  * @param entropy The entropy \f$S\f$.
  */
-__attribute__((always_inline)) INLINE static float gas_soundspeed_from_entropy(
-    float density, float entropy) {
+__attribute__((always_inline, const)) INLINE static float
+gas_soundspeed_from_entropy(float density, float entropy) {
 
   return sqrtf(hydro_gamma * pow_gamma_minus_one(density) * entropy);
 }
@@ -103,7 +103,7 @@ __attribute__((always_inline)) INLINE static float gas_soundspeed_from_entropy(
  * @param density The density \f$\rho\f$
  * @param u The internal energy \f$u\f$
  */
-__attribute__((always_inline)) INLINE static float
+__attribute__((always_inline, const)) INLINE static float
 gas_entropy_from_internal_energy(float density, float u) {
 
   return hydro_gamma_minus_one * u * pow_minus_gamma_minus_one(density);
@@ -117,7 +117,7 @@ gas_entropy_from_internal_energy(float density, float u) {
  * @param density The density \f$\rho\f$
  * @param u The internal energy \f$u\f$
  */
-__attribute__((always_inline)) INLINE static float
+__attribute__((always_inline, const)) INLINE static float
 gas_pressure_from_internal_energy(float density, float u) {
 
   return hydro_gamma_minus_one * u * density;
@@ -132,7 +132,7 @@ gas_pressure_from_internal_energy(float density, float u) {
  * @param pressure The pressure \f$P\f$.
  * @return The internal energy \f$u\f$.
  */
-__attribute__((always_inline)) INLINE static float
+__attribute__((always_inline, const)) INLINE static float
 gas_internal_energy_from_pressure(float density, float pressure) {
   return hydro_one_over_gamma_minus_one * pressure / density;
 }
@@ -145,7 +145,7 @@ gas_internal_energy_from_pressure(float density, float pressure) {
  * @param density The density \f$\rho\f$
  * @param u The internal energy \f$u\f$
  */
-__attribute__((always_inline)) INLINE static float
+__attribute__((always_inline, const)) INLINE static float
 gas_soundspeed_from_internal_energy(float density, float u) {
 
   return sqrtf(u * hydro_gamma * hydro_gamma_minus_one);
@@ -159,8 +159,8 @@ gas_soundspeed_from_internal_energy(float density, float u) {
  * @param density The density \f$\rho\f$
  * @param P The pressure \f$P\f$
  */
-__attribute__((always_inline)) INLINE static float gas_soundspeed_from_pressure(
-    float density, float P) {
+__attribute__((always_inline, const)) INLINE static float
+gas_soundspeed_from_pressure(float density, float P) {
 
   const float density_inv = 1.f / density;
   return sqrtf(hydro_gamma * P * density_inv);
@@ -171,21 +171,21 @@ __attribute__((always_inline)) INLINE static float gas_soundspeed_from_pressure(
  *
  * Nothing to do here since this EoS is parameter-free.
  *
- * @param e The #eos_paramters.
+ * @param e The #eos_parameters.
  * @param phys_const The physical constants in the internal unit system.
  * @param us The internal unit system.
  * @param params The parsed parameters.
  */
-__attribute__((always_inline)) INLINE static void eos_init(
-    struct eos_parameters *e, const struct phys_const *phys_const,
-    const struct unit_system *us, const struct swift_params *params) {}
+INLINE static void eos_init(struct eos_parameters *e,
+                            const struct phys_const *phys_const,
+                            const struct unit_system *us,
+                            struct swift_params *params) {}
 /**
  * @brief Print the equation of state
  *
  * @param e The #eos_parameters
  */
-__attribute__((always_inline)) INLINE static void eos_print(
-    const struct eos_parameters *e) {
+INLINE static void eos_print(const struct eos_parameters *e) {
 
   message("Equation of state: Ideal gas.");
 
@@ -199,8 +199,8 @@ __attribute__((always_inline)) INLINE static void eos_print(
  * @param h_grpsph The HDF5 group in which to write
  * @param e The #eos_parameters
  */
-__attribute__((always_inline)) INLINE static void eos_print_snapshot(
-    hid_t h_grpsph, const struct eos_parameters *e) {
+INLINE static void eos_print_snapshot(hid_t h_grpsph,
+                                      const struct eos_parameters *e) {
 
   io_write_attribute_f(h_grpsph, "Adiabatic index", hydro_gamma);
 
diff --git a/src/equation_of_state/isothermal/equation_of_state.h b/src/equation_of_state/isothermal/equation_of_state.h
index c7afac6caaacf354f8067218fbe2013b9287309a..540bf073cee106b91e2a9f4ecedb1b20238856fd 100644
--- a/src/equation_of_state/isothermal/equation_of_state.h
+++ b/src/equation_of_state/isothermal/equation_of_state.h
@@ -195,7 +195,7 @@ __attribute__((always_inline)) INLINE static float gas_soundspeed_from_pressure(
  */
 __attribute__((always_inline)) INLINE static void eos_init(
     struct eos_parameters *e, const struct phys_const *phys_const,
-    const struct unit_system *us, const struct swift_params *params) {
+    const struct unit_system *us, struct swift_params *params) {
 
   e->isothermal_internal_energy =
       parser_get_param_float(params, "EoS:isothermal_internal_energy");
diff --git a/src/equation_of_state/planetary/aneos.h b/src/equation_of_state/planetary/aneos.h
new file mode 100644
index 0000000000000000000000000000000000000000..904288b2fdf3ba825cdc7d114ebb61cd42de198d
--- /dev/null
+++ b/src/equation_of_state/planetary/aneos.h
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016   Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ *               2018   Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_ANEOS_EQUATION_OF_STATE_H
+#define SWIFT_ANEOS_EQUATION_OF_STATE_H
+
+/**
+ * @file equation_of_state/planetary/aneos.h
+ *
+ * Contains the (M)ANEOS EOS functions for
+ * equation_of_state/planetary/equation_of_state.h
+ *
+ * Adapted from the implementation in Gadget 2 of Cuk & Stewart (2012)
+ *
+ */
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local headers. */
+#include "adiabatic_index.h"
+#include "common_io.h"
+#include "equation_of_state.h"
+#include "inline.h"
+#include "physical_constants.h"
+#include "units.h"
+
+// ANEOS parameters
+struct ANEOS_params {
+  enum eos_planetary_material_id mat_id;
+};
+
+// Parameter values for each material (cgs units)
+INLINE static void set_ANEOS_iron(struct ANEOS_params *mat,
+                                  enum eos_planetary_material_id mat_id) {
+  mat->mat_id = mat_id;
+}
+INLINE static void set_MANEOS_forsterite(
+    struct ANEOS_params *mat, enum eos_planetary_material_id mat_id) {
+  mat->mat_id = mat_id;
+}
+
+// Convert from cgs to internal units
+INLINE static void convert_units_ANEOS(struct ANEOS_params *mat,
+                                       const struct unit_system *us) {}
+
+// gas_internal_energy_from_entropy
+INLINE static float ANEOS_internal_energy_from_entropy(
+    float density, float entropy, const struct ANEOS_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_pressure_from_entropy
+INLINE static float ANEOS_pressure_from_entropy(
+    float density, float entropy, const struct ANEOS_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_entropy_from_pressure
+INLINE static float ANEOS_entropy_from_pressure(
+    float density, float pressure, const struct ANEOS_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_entropy
+INLINE static float ANEOS_soundspeed_from_entropy(
+    float density, float entropy, const struct ANEOS_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_entropy_from_internal_energy
+INLINE static float ANEOS_entropy_from_internal_energy(
+    float density, float u, const struct ANEOS_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_pressure_from_internal_energy
+INLINE static float ANEOS_pressure_from_internal_energy(
+    float density, float u, const struct ANEOS_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_internal_energy_from_pressure
+INLINE static float ANEOS_internal_energy_from_pressure(
+    float density, float P, const struct ANEOS_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_internal_energy
+INLINE static float ANEOS_soundspeed_from_internal_energy(
+    float density, float u, const struct ANEOS_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_pressure
+INLINE static float ANEOS_soundspeed_from_pressure(
+    float density, float P, const struct ANEOS_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+#endif /* SWIFT_ANEOS_EQUATION_OF_STATE_H */
diff --git a/src/equation_of_state/planetary/equation_of_state.h b/src/equation_of_state/planetary/equation_of_state.h
new file mode 100644
index 0000000000000000000000000000000000000000..61e23dc0b4eb82e9ae5c0869f7a10dfff97fc45e
--- /dev/null
+++ b/src/equation_of_state/planetary/equation_of_state.h
@@ -0,0 +1,1171 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016   Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ *               2018   Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_PLANETARY_EQUATION_OF_STATE_H
+#define SWIFT_PLANETARY_EQUATION_OF_STATE_H
+
+/**
+ * @file equation_of_state/planetary/equation_of_state.h
+ *
+ * For any/all of the planetary EOS. Each EOS type's functions are set in its
+ * own header file: `equation_of_state/planetary/<eos_type>.h`.
+ * See `eos_planetary_material_id` for the available choices.
+ *
+ * Not all functions are implemented for all EOS types, so not all can be used
+ * with all hydro formulations yet.
+ */
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local headers. */
+#include "adiabatic_index.h"
+#include "common_io.h"
+#include "inline.h"
+#include "physical_constants.h"
+#include "units.h"
+
+extern struct eos_parameters eos;
+
+/*! Material identifier flags (material_ID = type_ID * type_factor + unit_ID) */
+#define eos_planetary_type_factor 100
+
+/**
+ * @brief Master type for the planetary equation of state.
+ */
+enum eos_planetary_type_id {
+  eos_planetary_type_Til = 1,
+  eos_planetary_type_HM80 = 2,
+  eos_planetary_type_ANEOS = 3,
+  eos_planetary_type_SESAME = 4,
+};
+
+/**
+ * @brief Minor type for the planetary equation of state.
+ */
+enum eos_planetary_material_id {
+
+  /* Tillotson */
+
+  /*! Tillotson iron */
+  eos_planetary_id_Til_iron =
+      eos_planetary_type_Til * eos_planetary_type_factor,
+
+  /*! Tillotson granite */
+  eos_planetary_id_Til_granite =
+      eos_planetary_type_Til * eos_planetary_type_factor + 1,
+
+  /*! Tillotson water */
+  eos_planetary_id_Til_water =
+      eos_planetary_type_Til * eos_planetary_type_factor + 2,
+
+  /* Hubbard & MacFarlane (1980) Uranus/Neptune */
+
+  /*! Hydrogen-helium atmosphere */
+  eos_planetary_id_HM80_HHe =
+      eos_planetary_type_HM80 * eos_planetary_type_factor,
+
+  /*! H20-CH4-NH3 ice mix */
+  eos_planetary_id_HM80_ice =
+      eos_planetary_type_HM80 * eos_planetary_type_factor + 1,
+
+  /*! SiO2-MgO-FeS-FeO rock mix */
+  eos_planetary_id_HM80_rock =
+      eos_planetary_type_HM80 * eos_planetary_type_factor + 2,
+
+  /* ANEOS */
+
+  /*! ANEOS iron */
+  eos_planetary_id_ANEOS_iron =
+      eos_planetary_type_ANEOS * eos_planetary_type_factor,
+
+  /*! MANEOS forsterite */
+  eos_planetary_id_MANEOS_forsterite =
+      eos_planetary_type_ANEOS * eos_planetary_type_factor + 1,
+
+  /* SESAME */
+
+  /*! SESAME iron */
+  eos_planetary_id_SESAME_iron =
+      eos_planetary_type_SESAME * eos_planetary_type_factor,
+};
+
+/* Individual EOS function headers. */
+#include "aneos.h"
+#include "hm80.h"
+#include "sesame.h"
+#include "tillotson.h"
+
+/**
+ * @brief The parameters of the equation of state.
+ */
+struct eos_parameters {
+  struct Til_params Til_iron, Til_granite, Til_water;
+  struct HM80_params HM80_HHe, HM80_ice, HM80_rock;
+  struct ANEOS_params ANEOS_iron, MANEOS_forsterite;
+  struct SESAME_params SESAME_iron;
+};
+
+/**
+ * @brief Returns the internal energy given density and entropy
+ *
+ * @param density The density \f$\rho\f$.
+ * @param entropy The entropy \f$S\f$.
+ */
+__attribute__((always_inline)) INLINE static float
+gas_internal_energy_from_entropy(float density, float entropy,
+                                 enum eos_planetary_material_id mat_id) {
+
+  const enum eos_planetary_type_id type =
+      (enum eos_planetary_type_id)(mat_id / eos_planetary_type_factor);
+
+  /* Select the material base type */
+  switch (type) {
+
+    /* Tillotson EoS */
+    case eos_planetary_type_Til:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          return Til_internal_energy_from_entropy(density, entropy,
+                                                  &eos.Til_iron);
+          break;
+
+        case eos_planetary_id_Til_granite:
+          return Til_internal_energy_from_entropy(density, entropy,
+                                                  &eos.Til_granite);
+          break;
+
+        case eos_planetary_id_Til_water:
+          return Til_internal_energy_from_entropy(density, entropy,
+                                                  &eos.Til_water);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* Hubbard & MacFarlane (1980) EoS */
+    case eos_planetary_type_HM80:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          return HM80_internal_energy_from_entropy(density, entropy,
+                                                   &eos.HM80_HHe);
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          return HM80_internal_energy_from_entropy(density, entropy,
+                                                   &eos.HM80_ice);
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          return HM80_internal_energy_from_entropy(density, entropy,
+                                                   &eos.HM80_rock);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* ANEOS EoS */
+    case eos_planetary_type_ANEOS:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          return ANEOS_internal_energy_from_entropy(density, entropy,
+                                                    &eos.ANEOS_iron);
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          return ANEOS_internal_energy_from_entropy(density, entropy,
+                                                    &eos.MANEOS_forsterite);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* SESAME EoS */
+    case eos_planetary_type_SESAME:;
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          return SESAME_internal_energy_from_entropy(density, entropy,
+                                                     &eos.SESAME_iron);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d", mat_id);
+      return 0.f;
+  }
+}
+
+/**
+ * @brief Returns the pressure given density and entropy
+ *
+ * @param density The density \f$\rho\f$.
+ * @param entropy The entropy \f$S\f$.
+ */
+__attribute__((always_inline)) INLINE static float gas_pressure_from_entropy(
+    float density, float entropy, enum eos_planetary_material_id mat_id) {
+
+  const enum eos_planetary_type_id type =
+      (enum eos_planetary_type_id)(mat_id / eos_planetary_type_factor);
+
+  /* Select the material base type */
+  switch (type) {
+
+    /* Tillotson EoS */
+    case eos_planetary_type_Til:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          return Til_pressure_from_entropy(density, entropy, &eos.Til_iron);
+          break;
+
+        case eos_planetary_id_Til_granite:
+          return Til_pressure_from_entropy(density, entropy, &eos.Til_granite);
+          break;
+
+        case eos_planetary_id_Til_water:
+          return Til_pressure_from_entropy(density, entropy, &eos.Til_water);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* Hubbard & MacFarlane (1980) EoS */
+    case eos_planetary_type_HM80:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          return HM80_pressure_from_entropy(density, entropy, &eos.HM80_HHe);
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          return HM80_pressure_from_entropy(density, entropy, &eos.HM80_ice);
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          return HM80_pressure_from_entropy(density, entropy, &eos.HM80_rock);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* ANEOS EoS */
+    case eos_planetary_type_ANEOS:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          return ANEOS_pressure_from_entropy(density, entropy, &eos.ANEOS_iron);
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          return ANEOS_pressure_from_entropy(density, entropy,
+                                             &eos.MANEOS_forsterite);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* SESAME EoS */
+    case eos_planetary_type_SESAME:;
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          return SESAME_pressure_from_entropy(density, entropy,
+                                              &eos.SESAME_iron);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d", mat_id);
+      return 0.f;
+  }
+}
+
+/**
+ * @brief Returns the entropy given density and pressure.
+ *
+ * @param density The density \f$\rho\f$.
+ * @param pressure The pressure \f$P\f$.
+ * @return The entropy \f$A\f$.
+ */
+__attribute__((always_inline)) INLINE static float gas_entropy_from_pressure(
+    float density, float P, enum eos_planetary_material_id mat_id) {
+
+  const enum eos_planetary_type_id type =
+      (enum eos_planetary_type_id)(mat_id / eos_planetary_type_factor);
+
+  /* Select the material base type */
+  switch (type) {
+
+    /* Tillotson EoS */
+    case eos_planetary_type_Til:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          return Til_entropy_from_pressure(density, P, &eos.Til_iron);
+          break;
+
+        case eos_planetary_id_Til_granite:
+          return Til_entropy_from_pressure(density, P, &eos.Til_granite);
+          break;
+
+        case eos_planetary_id_Til_water:
+          return Til_entropy_from_pressure(density, P, &eos.Til_water);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* Hubbard & MacFarlane (1980) EoS */
+    case eos_planetary_type_HM80:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          return HM80_entropy_from_pressure(density, P, &eos.HM80_HHe);
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          return HM80_entropy_from_pressure(density, P, &eos.HM80_ice);
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          return HM80_entropy_from_pressure(density, P, &eos.HM80_rock);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* ANEOS EoS */
+    case eos_planetary_type_ANEOS:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          return ANEOS_entropy_from_pressure(density, P, &eos.ANEOS_iron);
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          return ANEOS_entropy_from_pressure(density, P,
+                                             &eos.MANEOS_forsterite);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* SESAME EoS */
+    case eos_planetary_type_SESAME:;
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          return SESAME_entropy_from_pressure(density, P, &eos.SESAME_iron);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d", mat_id);
+      return 0.f;
+  }
+}
+
+/**
+ * @brief Returns the sound speed given density and entropy
+ *
+ * @param density The density \f$\rho\f$.
+ * @param entropy The entropy \f$S\f$.
+ */
+__attribute__((always_inline)) INLINE static float gas_soundspeed_from_entropy(
+    float density, float entropy, enum eos_planetary_material_id mat_id) {
+
+  const enum eos_planetary_type_id type =
+      (enum eos_planetary_type_id)(mat_id / eos_planetary_type_factor);
+
+  /* Select the material base type */
+  switch (type) {
+
+    /* Tillotson EoS */
+    case eos_planetary_type_Til:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          return Til_soundspeed_from_entropy(density, entropy, &eos.Til_iron);
+          break;
+
+        case eos_planetary_id_Til_granite:
+          return Til_soundspeed_from_entropy(density, entropy,
+                                             &eos.Til_granite);
+          break;
+
+        case eos_planetary_id_Til_water:
+          return Til_soundspeed_from_entropy(density, entropy, &eos.Til_water);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* Hubbard & MacFarlane (1980) EoS */
+    case eos_planetary_type_HM80:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          return HM80_soundspeed_from_entropy(density, entropy, &eos.HM80_HHe);
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          return HM80_soundspeed_from_entropy(density, entropy, &eos.HM80_ice);
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          return HM80_soundspeed_from_entropy(density, entropy, &eos.HM80_rock);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* ANEOS EoS */
+    case eos_planetary_type_ANEOS:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          return ANEOS_soundspeed_from_entropy(density, entropy,
+                                               &eos.ANEOS_iron);
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          return ANEOS_soundspeed_from_entropy(density, entropy,
+                                               &eos.MANEOS_forsterite);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* SESAME EoS */
+    case eos_planetary_type_SESAME:;
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          return SESAME_soundspeed_from_entropy(density, entropy,
+                                                &eos.SESAME_iron);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d", mat_id);
+      return 0.f;
+  }
+}
+
+/**
+ * @brief Returns the entropy given density and internal energy
+ *
+ * @param density The density \f$\rho\f$
+ * @param u The internal energy \f$u\f$
+ */
+__attribute__((always_inline)) INLINE static float
+gas_entropy_from_internal_energy(float density, float u,
+                                 enum eos_planetary_material_id mat_id) {
+  const enum eos_planetary_type_id type =
+      (enum eos_planetary_type_id)(mat_id / eos_planetary_type_factor);
+
+  /* Select the material base type */
+  switch (type) {
+
+    /* Tillotson EoS */
+    case eos_planetary_type_Til:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          return Til_entropy_from_internal_energy(density, u, &eos.Til_iron);
+          break;
+
+        case eos_planetary_id_Til_granite:
+          return Til_entropy_from_internal_energy(density, u, &eos.Til_granite);
+          break;
+
+        case eos_planetary_id_Til_water:
+          return Til_entropy_from_internal_energy(density, u, &eos.Til_water);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* Hubbard & MacFarlane (1980) EoS */
+    case eos_planetary_type_HM80:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          return HM80_entropy_from_internal_energy(density, u, &eos.HM80_HHe);
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          return HM80_entropy_from_internal_energy(density, u, &eos.HM80_ice);
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          return HM80_entropy_from_internal_energy(density, u, &eos.HM80_rock);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* ANEOS EoS */
+    case eos_planetary_type_ANEOS:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          return ANEOS_entropy_from_internal_energy(density, u,
+                                                    &eos.ANEOS_iron);
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          return ANEOS_entropy_from_internal_energy(density, u,
+                                                    &eos.MANEOS_forsterite);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* SESAME EoS */
+    case eos_planetary_type_SESAME:;
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          return SESAME_entropy_from_internal_energy(density, u,
+                                                     &eos.SESAME_iron);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d", mat_id);
+      return 0.f;
+  }
+}
+
+/**
+ * @brief Returns the pressure given density and internal energy
+ *
+ * @param density The density \f$\rho\f$
+ * @param u The internal energy \f$u\f$
+ */
+__attribute__((always_inline)) INLINE static float
+gas_pressure_from_internal_energy(float density, float u,
+                                  enum eos_planetary_material_id mat_id) {
+
+  const enum eos_planetary_type_id type =
+      (enum eos_planetary_type_id)(mat_id / eos_planetary_type_factor);
+
+  /* Select the material base type */
+  switch (type) {
+
+    /* Tillotson EoS */
+    case eos_planetary_type_Til:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          return Til_pressure_from_internal_energy(density, u, &eos.Til_iron);
+          break;
+
+        case eos_planetary_id_Til_granite:
+          return Til_pressure_from_internal_energy(density, u,
+                                                   &eos.Til_granite);
+          break;
+
+        case eos_planetary_id_Til_water:
+          return Til_pressure_from_internal_energy(density, u, &eos.Til_water);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* Hubbard & MacFarlane (1980) EoS */
+    case eos_planetary_type_HM80:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          return HM80_pressure_from_internal_energy(density, u, &eos.HM80_HHe);
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          return HM80_pressure_from_internal_energy(density, u, &eos.HM80_ice);
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          return HM80_pressure_from_internal_energy(density, u, &eos.HM80_rock);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* ANEOS EoS */
+    case eos_planetary_type_ANEOS:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          return ANEOS_pressure_from_internal_energy(density, u,
+                                                     &eos.ANEOS_iron);
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          return ANEOS_pressure_from_internal_energy(density, u,
+                                                     &eos.MANEOS_forsterite);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* SESAME EoS */
+    case eos_planetary_type_SESAME:;
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          return SESAME_pressure_from_internal_energy(density, u,
+                                                      &eos.SESAME_iron);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d", mat_id);
+      return 0.f;
+  }
+}
+
+/**
+ * @brief Returns the internal energy given density and pressure.
+ *
+ * NOT IMPLEMENTED!
+ *
+ * @param density The density \f$\rho\f$.
+ * @param pressure The pressure \f$P\f$.
+ * @return The internal energy \f$u\f$.
+ */
+__attribute__((always_inline)) INLINE static float
+gas_internal_energy_from_pressure(float density, float P,
+                                  enum eos_planetary_material_id mat_id) {
+
+  const enum eos_planetary_type_id type =
+      (enum eos_planetary_type_id)(mat_id / eos_planetary_type_factor);
+
+  /* Select the material base type */
+  switch (type) {
+
+    /* Tillotson EoS */
+    case eos_planetary_type_Til:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          return Til_internal_energy_from_pressure(density, P, &eos.Til_iron);
+          break;
+
+        case eos_planetary_id_Til_granite:
+          return Til_internal_energy_from_pressure(density, P,
+                                                   &eos.Til_granite);
+          break;
+
+        case eos_planetary_id_Til_water:
+          return Til_internal_energy_from_pressure(density, P, &eos.Til_water);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* Hubbard & MacFarlane (1980) EoS */
+    case eos_planetary_type_HM80:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          return HM80_internal_energy_from_pressure(density, P, &eos.HM80_HHe);
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          return HM80_internal_energy_from_pressure(density, P, &eos.HM80_ice);
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          return HM80_internal_energy_from_pressure(density, P, &eos.HM80_rock);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* ANEOS EoS */
+    case eos_planetary_type_ANEOS:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          return ANEOS_internal_energy_from_pressure(density, P,
+                                                     &eos.ANEOS_iron);
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          return ANEOS_internal_energy_from_pressure(density, P,
+                                                     &eos.MANEOS_forsterite);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* SESAME EoS */
+    case eos_planetary_type_SESAME:;
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          return SESAME_internal_energy_from_pressure(density, P,
+                                                      &eos.SESAME_iron);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d", mat_id);
+      return 0.f;
+  }
+}
+
+/**
+ * @brief Returns the sound speed given density and internal energy
+ *
+ * @param density The density \f$\rho\f$
+ * @param u The internal energy \f$u\f$
+ */
+__attribute__((always_inline)) INLINE static float
+gas_soundspeed_from_internal_energy(float density, float u,
+                                    enum eos_planetary_material_id mat_id) {
+
+  const enum eos_planetary_type_id type =
+      (enum eos_planetary_type_id)(mat_id / eos_planetary_type_factor);
+
+  /* Select the material base type */
+  switch (type) {
+
+    /* Tillotson EoS */
+    case eos_planetary_type_Til:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          return Til_soundspeed_from_internal_energy(density, u, &eos.Til_iron);
+          break;
+
+        case eos_planetary_id_Til_granite:
+          return Til_soundspeed_from_internal_energy(density, u,
+                                                     &eos.Til_granite);
+          break;
+
+        case eos_planetary_id_Til_water:
+          return Til_soundspeed_from_internal_energy(density, u,
+                                                     &eos.Til_water);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* Hubbard & MacFarlane (1980) EoS */
+    case eos_planetary_type_HM80:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          return HM80_soundspeed_from_internal_energy(density, u,
+                                                      &eos.HM80_HHe);
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          return HM80_soundspeed_from_internal_energy(density, u,
+                                                      &eos.HM80_ice);
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          return HM80_soundspeed_from_internal_energy(density, u,
+                                                      &eos.HM80_rock);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* ANEOS EoS */
+    case eos_planetary_type_ANEOS:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          return ANEOS_soundspeed_from_internal_energy(density, u,
+                                                       &eos.ANEOS_iron);
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          return ANEOS_soundspeed_from_internal_energy(density, u,
+                                                       &eos.MANEOS_forsterite);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* SESAME EoS */
+    case eos_planetary_type_SESAME:;
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          return SESAME_soundspeed_from_internal_energy(density, u,
+                                                        &eos.SESAME_iron);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d", mat_id);
+      return 0.f;
+  }
+}
+
+/**
+ * @brief Returns the sound speed given density and pressure
+ *
+ * @param density The density \f$\rho\f$
+ * @param P The pressure \f$P\f$
+ */
+__attribute__((always_inline)) INLINE static float gas_soundspeed_from_pressure(
+    float density, float P, enum eos_planetary_material_id mat_id) {
+
+  const enum eos_planetary_type_id type =
+      (enum eos_planetary_type_id)(mat_id / eos_planetary_type_factor);
+
+  /* Select the material base type */
+  switch (type) {
+
+    /* Tillotson EoS */
+    case eos_planetary_type_Til:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          return Til_soundspeed_from_pressure(density, P, &eos.Til_iron);
+          break;
+
+        case eos_planetary_id_Til_granite:
+          return Til_soundspeed_from_pressure(density, P, &eos.Til_granite);
+          break;
+
+        case eos_planetary_id_Til_water:
+          return Til_soundspeed_from_pressure(density, P, &eos.Til_water);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* Hubbard & MacFarlane (1980) EoS */
+    case eos_planetary_type_HM80:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          return HM80_soundspeed_from_pressure(density, P, &eos.HM80_HHe);
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          return HM80_soundspeed_from_pressure(density, P, &eos.HM80_ice);
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          return HM80_soundspeed_from_pressure(density, P, &eos.HM80_rock);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* ANEOS EoS */
+    case eos_planetary_type_ANEOS:
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          return ANEOS_soundspeed_from_pressure(density, P, &eos.ANEOS_iron);
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          return ANEOS_soundspeed_from_pressure(density, P,
+                                                &eos.MANEOS_forsterite);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    /* SESAME EoS */
+    case eos_planetary_type_SESAME:;
+
+      /* Select the material */
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          return SESAME_soundspeed_from_pressure(density, P, &eos.SESAME_iron);
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d", mat_id);
+          return 0.f;
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d", mat_id);
+      return 0.f;
+  }
+}
+
+/**
+ * @brief Initialize the eos parameters
+ *
+ * @param e The #eos_parameters
+ * @param params The parsed parameters
+ */
+__attribute__((always_inline)) INLINE static void eos_init(
+    struct eos_parameters *e, const struct phys_const *phys_const,
+    const struct unit_system *us, struct swift_params *params) {
+
+  // Table file names
+  char HM80_HHe_table_file[PARSER_MAX_LINE_SIZE];
+  char HM80_ice_table_file[PARSER_MAX_LINE_SIZE];
+  char HM80_rock_table_file[PARSER_MAX_LINE_SIZE];
+
+  // Set the parameters and material IDs, load tables, etc. for each material
+  // and convert to internal units
+  // Tillotson
+  if (parser_get_opt_param_int(params, "EoS:planetary_use_Til", 0)) {
+    set_Til_iron(&e->Til_iron, eos_planetary_id_Til_iron);
+    set_Til_granite(&e->Til_granite, eos_planetary_id_Til_granite);
+    set_Til_water(&e->Til_water, eos_planetary_id_Til_water);
+
+    convert_units_Til(&e->Til_iron, us);
+    convert_units_Til(&e->Til_granite, us);
+    convert_units_Til(&e->Til_water, us);
+  }
+
+  // Hubbard & MacFarlane (1980)
+  if (parser_get_opt_param_int(params, "EoS:planetary_use_HM80", 0)) {
+    set_HM80_HHe(&e->HM80_HHe, eos_planetary_id_HM80_HHe);
+    set_HM80_ice(&e->HM80_ice, eos_planetary_id_HM80_ice);
+    set_HM80_rock(&e->HM80_rock, eos_planetary_id_HM80_rock);
+
+    parser_get_param_string(params, "EoS:planetary_HM80_HHe_table_file",
+                            HM80_HHe_table_file);
+    parser_get_param_string(params, "EoS:planetary_HM80_ice_table_file",
+                            HM80_ice_table_file);
+    parser_get_param_string(params, "EoS:planetary_HM80_rock_table_file",
+                            HM80_rock_table_file);
+
+    load_HM80_table(&e->HM80_HHe, HM80_HHe_table_file);
+    load_HM80_table(&e->HM80_ice, HM80_ice_table_file);
+    load_HM80_table(&e->HM80_rock, HM80_rock_table_file);
+
+    convert_units_HM80(&e->HM80_HHe, us);
+    convert_units_HM80(&e->HM80_ice, us);
+    convert_units_HM80(&e->HM80_rock, us);
+  }
+
+  // ANEOS
+  if (parser_get_opt_param_int(params, "EoS:planetary_use_ANEOS", 0)) {
+    set_ANEOS_iron(&e->ANEOS_iron, eos_planetary_id_ANEOS_iron);
+    set_MANEOS_forsterite(&e->MANEOS_forsterite,
+                          eos_planetary_id_MANEOS_forsterite);
+
+    convert_units_ANEOS(&e->ANEOS_iron, us);
+    convert_units_ANEOS(&e->MANEOS_forsterite, us);
+  }
+
+  // SESAME
+  if (parser_get_opt_param_int(params, "EoS:planetary_use_SESAME", 0)) {
+    set_SESAME_iron(&e->SESAME_iron, eos_planetary_id_SESAME_iron);
+
+    convert_units_SESAME(&e->SESAME_iron, us);
+  }
+}
+
+/**
+ * @brief Print the equation of state
+ *
+ * @param e The #eos_parameters
+ */
+__attribute__((always_inline)) INLINE static void eos_print(
+    const struct eos_parameters *e) {
+
+  message("Equation of state: Planetary.");
+}
+
+#if defined(HAVE_HDF5)
+/**
+ * @brief Write equation of state information to the snapshot
+ *
+ * @param h_grpsph The HDF5 group in which to write
+ * @param e The #eos_parameters
+ */
+__attribute__((always_inline)) INLINE static void eos_print_snapshot(
+    hid_t h_grpsph, const struct eos_parameters *e) {
+
+  io_write_attribute_s(h_grpsph, "Equation of state", "Planetary");
+}
+#endif
+
+#endif /* SWIFT_PLANETARY_EQUATION_OF_STATE_H */
diff --git a/src/equation_of_state/planetary/hm80.h b/src/equation_of_state/planetary/hm80.h
new file mode 100644
index 0000000000000000000000000000000000000000..0131bab6c447e5a8898e29e13dc3f8f6e1c897c6
--- /dev/null
+++ b/src/equation_of_state/planetary/hm80.h
@@ -0,0 +1,300 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016   Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ *               2018   Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_HUBBARD_MACFARLANE_EQUATION_OF_STATE_H
+#define SWIFT_HUBBARD_MACFARLANE_EQUATION_OF_STATE_H
+
+/**
+ * @file equation_of_state/planetary/hm80.h
+ *
+ * Contains the Hubbard & MacFarlane (1980) Uranus/Neptune EOS functions for
+ * equation_of_state/planetary/equation_of_state.h
+ *
+ */
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local headers. */
+#include "adiabatic_index.h"
+#include "common_io.h"
+#include "equation_of_state.h"
+#include "inline.h"
+#include "physical_constants.h"
+#include "units.h"
+
+// Hubbard & MacFarlane (1980) parameters
+struct HM80_params {
+  float *table_P_rho_u;
+  int num_rho, num_u;
+  float log_rho_min, log_rho_max, log_rho_step, inv_log_rho_step, log_u_min,
+      log_u_max, log_u_step, inv_log_u_step, bulk_mod;
+  enum eos_planetary_material_id mat_id;
+};
+
+// Parameter values for each material (cgs units)
+INLINE static void set_HM80_HHe(struct HM80_params *mat,
+                                enum eos_planetary_material_id mat_id) {
+  mat->mat_id = mat_id;
+  mat->num_rho = 100;
+  mat->num_u = 100;
+  mat->log_rho_min = -9.2103404f;
+  mat->log_rho_max = 1.6094379f;
+  mat->log_rho_step = 0.1092907f;
+  mat->log_u_min = 9.2103404f;
+  mat->log_u_max = 22.3327037f;
+  mat->log_u_step = 0.1325491f;
+  mat->bulk_mod = 0;
+
+  mat->inv_log_rho_step = 1.f / mat->log_rho_step;
+  mat->inv_log_u_step = 1.f / mat->log_u_step;
+}
+INLINE static void set_HM80_ice(struct HM80_params *mat,
+                                enum eos_planetary_material_id mat_id) {
+  mat->mat_id = mat_id;
+  mat->num_rho = 200;
+  mat->num_u = 200;
+  mat->log_rho_min = -6.9077553f;
+  mat->log_rho_max = 2.7080502f;
+  mat->log_rho_step = 0.0483206f;
+  mat->log_u_min = 6.9077553f;
+  mat->log_u_max = 22.3327037f;
+  mat->log_u_step = 0.0775123f;
+  mat->bulk_mod = 2.0e10f;
+
+  mat->inv_log_rho_step = 1.f / mat->log_rho_step;
+  mat->inv_log_u_step = 1.f / mat->log_u_step;
+}
+INLINE static void set_HM80_rock(struct HM80_params *mat,
+                                 enum eos_planetary_material_id mat_id) {
+  mat->mat_id = mat_id;
+  mat->num_rho = 100;
+  mat->num_u = 100;
+  mat->log_rho_min = -6.9077553f;
+  mat->log_rho_max = 2.9957323f;
+  mat->log_rho_step = 0.1000352f;
+  mat->log_u_min = 9.2103404f;
+  mat->log_u_max = 20.7232658f;
+  mat->log_u_step = 0.1162922f;
+  mat->bulk_mod = 3.49e11f;
+
+  mat->inv_log_rho_step = 1.f / mat->log_rho_step;
+  mat->inv_log_u_step = 1.f / mat->log_u_step;
+}
+
+// Read the table from file
+INLINE static void load_HM80_table(struct HM80_params *mat, char *table_file) {
+  // Allocate table memory
+  mat->table_P_rho_u =
+      (float *)malloc(mat->num_rho * mat->num_u * sizeof(float *));
+
+  // Load table contents from file
+  FILE *f = fopen(table_file, "r");
+  int c;
+  for (int i = 0; i < mat->num_rho; i++) {
+    for (int j = 0; j < mat->num_u; j++) {
+      c = fscanf(f, "%f", &mat->table_P_rho_u[i * mat->num_rho + j]);
+      if (c != 1) {
+        error("Failed to read EOS table");
+      }
+    }
+  }
+  fclose(f);
+}
+
+// Convert from cgs to internal units
+INLINE static void convert_units_HM80(struct HM80_params *mat,
+                                      const struct unit_system *us) {
+  const float Mbar_to_Ba = 1e12f;    // Convert Megabar to Barye
+  const float J_kg_to_erg_g = 1e4f;  // Convert J/kg to erg/g
+
+  // Table densities in cgs
+  mat->log_rho_min -= logf(units_cgs_conversion_factor(us, UNIT_CONV_DENSITY));
+  mat->log_rho_max -= logf(units_cgs_conversion_factor(us, UNIT_CONV_DENSITY));
+
+  // Table energies in SI
+  mat->log_u_min +=
+      logf(J_kg_to_erg_g /
+           units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS));
+  mat->log_u_max +=
+      logf(J_kg_to_erg_g /
+           units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS));
+
+  // Table Pressures in Mbar
+  for (int i = 0; i < mat->num_rho; i++) {
+    for (int j = 0; j < mat->num_u; j++) {
+      mat->table_P_rho_u[i * mat->num_rho + j] *=
+          Mbar_to_Ba / units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE);
+    }
+  }
+
+  mat->bulk_mod /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE);
+}
+
+// gas_internal_energy_from_entropy
+INLINE static float HM80_internal_energy_from_entropy(
+    float density, float entropy, const struct HM80_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_pressure_from_entropy
+INLINE static float HM80_pressure_from_entropy(float density, float entropy,
+                                               const struct HM80_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_entropy_from_pressure
+INLINE static float HM80_entropy_from_pressure(float density, float pressure,
+                                               const struct HM80_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_entropy
+INLINE static float HM80_soundspeed_from_entropy(
+    float density, float entropy, const struct HM80_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_entropy_from_internal_energy
+INLINE static float HM80_entropy_from_internal_energy(
+    float density, float u, const struct HM80_params *mat) {
+
+  return 0;
+}
+
+// gas_pressure_from_internal_energy
+INLINE static float HM80_pressure_from_internal_energy(
+    float density, float u, const struct HM80_params *mat) {
+
+  float P;
+
+  if (u <= 0.f) {
+    return 0.f;
+  }
+
+  int rho_idx, u_idx;
+  float intp_rho, intp_u;
+  const float log_rho = logf(density);
+  const float log_u = logf(u);
+
+  // 2D interpolation (linear in log(rho), log(u)) to find P(rho, u)
+  rho_idx = floorf((log_rho - mat->log_rho_min) * mat->inv_log_rho_step);
+  u_idx = floorf((log_u - mat->log_u_min) * mat->inv_log_u_step);
+
+  intp_rho = (log_rho - mat->log_rho_min - rho_idx * mat->log_rho_step) *
+             mat->inv_log_rho_step;
+  intp_u =
+      (log_u - mat->log_u_min - u_idx * mat->log_u_step) * mat->inv_log_u_step;
+
+  // Return zero pressure if below the table minimum/a
+  // Extrapolate the pressure for low densities
+  if (rho_idx < 0) {  // Too-low rho
+    P = expf(logf((1 - intp_u) * mat->table_P_rho_u[u_idx] +
+                  intp_u * mat->table_P_rho_u[u_idx + 1]) +
+             log_rho - mat->log_rho_min);
+    if (u_idx < 0) {  // and too-low u
+      P = 0.f;
+    }
+  } else if (u_idx < 0) {  // Too-low u
+    P = 0.f;
+  }
+  // Return an edge value if above the table maximum/a
+  else if (rho_idx >= mat->num_rho - 1) {  // Too-high rho
+    if (u_idx >= mat->num_u - 1) {         // and too-high u
+      P = mat->table_P_rho_u[(mat->num_rho - 1) * mat->num_u + mat->num_u - 1];
+    } else {
+      P = mat->table_P_rho_u[(mat->num_rho - 1) * mat->num_u + u_idx];
+    }
+  } else if (u_idx >= mat->num_u - 1) {  // Too-high u
+    P = mat->table_P_rho_u[rho_idx * mat->num_u + mat->num_u - 1];
+  }
+  // Normal interpolation within the table
+  else {
+    P = (1.f - intp_rho) *
+            ((1.f - intp_u) * mat->table_P_rho_u[rho_idx * mat->num_u + u_idx] +
+             intp_u * mat->table_P_rho_u[rho_idx * mat->num_u + u_idx + 1]) +
+        intp_rho *
+            ((1 - intp_u) *
+                 mat->table_P_rho_u[(rho_idx + 1) * mat->num_u + u_idx] +
+             intp_u *
+                 mat->table_P_rho_u[(rho_idx + 1) * mat->num_u + u_idx + 1]);
+  }
+
+  return P;
+}
+
+// gas_internal_energy_from_pressure
+INLINE static float HM80_internal_energy_from_pressure(
+    float density, float P, const struct HM80_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_internal_energy
+INLINE static float HM80_soundspeed_from_internal_energy(
+    float density, float u, const struct HM80_params *mat) {
+
+  float c, P;
+
+  // Bulk modulus
+  if (mat->bulk_mod != 0) {
+    c = sqrtf(mat->bulk_mod / density);
+  }
+  // Ideal gas
+  else {
+    P = HM80_pressure_from_internal_energy(density, u, mat);
+    c = sqrtf(hydro_gamma * P / density);
+  }
+
+  return c;
+}
+
+// gas_soundspeed_from_pressure
+INLINE static float HM80_soundspeed_from_pressure(
+    float density, float P, const struct HM80_params *mat) {
+
+  float c;
+
+  // Bulk modulus
+  if (mat->bulk_mod != 0) {
+    c = sqrtf(mat->bulk_mod / density);
+  }
+  // Ideal gas
+  else {
+    c = sqrtf(hydro_gamma * P / density);
+  }
+
+  return c;
+}
+
+#endif /* SWIFT_HUBBARD_MACFARLANE_EQUATION_OF_STATE_H */
diff --git a/src/equation_of_state/planetary/sesame.h b/src/equation_of_state/planetary/sesame.h
new file mode 100644
index 0000000000000000000000000000000000000000..76574c2ad00282a82649705cd8a2b5a1b428d867
--- /dev/null
+++ b/src/equation_of_state/planetary/sesame.h
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016   Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ *               2018   Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_SESAME_EQUATION_OF_STATE_H
+#define SWIFT_SESAME_EQUATION_OF_STATE_H
+
+/**
+ * @file equation_of_state/planetary/sesame.h
+ *
+ * Contains the SESAME EOS functions for
+ * equation_of_state/planetary/equation_of_state.h
+ *
+ *              WORK IN PROGRESS!
+ *
+ */
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local headers. */
+#include "adiabatic_index.h"
+#include "common_io.h"
+#include "equation_of_state.h"
+#include "inline.h"
+#include "physical_constants.h"
+#include "units.h"
+
+// SESAME parameters
+struct SESAME_params {
+  enum eos_planetary_material_id mat_id;
+};
+
+// Parameter values for each material (cgs units)
+INLINE static void set_SESAME_iron(struct SESAME_params *mat,
+                                   enum eos_planetary_material_id mat_id) {
+  mat->mat_id = mat_id;
+}
+
+// Convert from cgs to internal units
+INLINE static void convert_units_SESAME(struct SESAME_params *mat,
+                                        const struct unit_system *us) {}
+
+// gas_internal_energy_from_entropy
+INLINE static float SESAME_internal_energy_from_entropy(
+    float density, float entropy, const struct SESAME_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_pressure_from_entropy
+INLINE static float SESAME_pressure_from_entropy(
+    float density, float entropy, const struct SESAME_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_entropy_from_pressure
+INLINE static float SESAME_entropy_from_pressure(
+    float density, float pressure, const struct SESAME_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_entropy
+INLINE static float SESAME_soundspeed_from_entropy(
+    float density, float entropy, const struct SESAME_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_entropy_from_internal_energy
+INLINE static float SESAME_entropy_from_internal_energy(
+    float density, float u, const struct SESAME_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_pressure_from_internal_energy
+INLINE static float SESAME_pressure_from_internal_energy(
+    float density, float u, const struct SESAME_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_internal_energy_from_pressure
+INLINE static float SESAME_internal_energy_from_pressure(
+    float density, float P, const struct SESAME_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_internal_energy
+INLINE static float SESAME_soundspeed_from_internal_energy(
+    float density, float u, const struct SESAME_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_pressure
+INLINE static float SESAME_soundspeed_from_pressure(
+    float density, float P, const struct SESAME_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+#endif /* SWIFT_SESAME_EQUATION_OF_STATE_H */
diff --git a/src/equation_of_state/planetary/tillotson.h b/src/equation_of_state/planetary/tillotson.h
new file mode 100644
index 0000000000000000000000000000000000000000..d5b6d5c35d5edf9e114fe7f010c4f5b1e2327a83
--- /dev/null
+++ b/src/equation_of_state/planetary/tillotson.h
@@ -0,0 +1,281 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016   Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ *               2018   Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_TILLOTSON_EQUATION_OF_STATE_H
+#define SWIFT_TILLOTSON_EQUATION_OF_STATE_H
+
+/**
+ * @file equation_of_state/planetary/tillotson.h
+ *
+ * Contains the Tillotson EOS functions for
+ * equation_of_state/planetary/equation_of_state.h
+ *
+ */
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local headers. */
+#include "adiabatic_index.h"
+#include "common_io.h"
+#include "equation_of_state.h"
+#include "inline.h"
+#include "physical_constants.h"
+#include "units.h"
+
+// Tillotson parameters
+struct Til_params {
+  float rho_0, a, b, A, B, E_0, E_iv, E_cv, alpha, beta, eta_min, P_min;
+  enum eos_planetary_material_id mat_id;
+};
+
+// Parameter values for each material (cgs units)
+INLINE static void set_Til_iron(struct Til_params *mat,
+                                enum eos_planetary_material_id mat_id) {
+  mat->mat_id = mat_id;
+  mat->rho_0 = 7.800f;
+  mat->a = 0.5f;
+  mat->b = 1.5f;
+  mat->A = 1.28e12f;
+  mat->B = 1.05e12f;
+  mat->E_0 = 9.5e10f;
+  mat->E_iv = 2.4e10f;
+  mat->E_cv = 8.67e10f;
+  mat->alpha = 5.0f;
+  mat->beta = 5.0f;
+  mat->eta_min = 0.0f;
+  mat->P_min = 0.0f;
+}
+INLINE static void set_Til_granite(struct Til_params *mat,
+                                   enum eos_planetary_material_id mat_id) {
+  mat->mat_id = mat_id;
+  mat->rho_0 = 2.680f;
+  mat->a = 0.5f;
+  mat->b = 1.3f;
+  mat->A = 1.8e11f;
+  mat->B = 1.8e11f;
+  mat->E_0 = 1.6e11f;
+  mat->E_iv = 3.5e10f;
+  mat->E_cv = 1.8e11f;
+  mat->alpha = 5.0f;
+  mat->beta = 5.0f;
+  mat->eta_min = 0.0f;
+  mat->P_min = 0.0f;
+}
+INLINE static void set_Til_water(struct Til_params *mat,
+                                 enum eos_planetary_material_id mat_id) {
+  mat->mat_id = mat_id;
+  mat->rho_0 = 0.998f;
+  mat->a = 0.7f;
+  mat->b = 0.15f;
+  mat->A = 2.18e10f;
+  mat->B = 1.325e11f;
+  mat->E_0 = 7.0e10f;
+  mat->E_iv = 4.19e9f;
+  mat->E_cv = 2.69e10f;
+  mat->alpha = 10.0f;
+  mat->beta = 5.0f;
+  mat->eta_min = 0.915f;
+  mat->P_min = 0.0f;
+}
+
+// Convert from cgs to internal units
+INLINE static void convert_units_Til(struct Til_params *mat,
+                                     const struct unit_system *us) {
+
+  mat->rho_0 /= units_cgs_conversion_factor(us, UNIT_CONV_DENSITY);
+  mat->A /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE);
+  mat->B /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE);
+  mat->E_0 /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS);
+  mat->E_iv /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS);
+  mat->E_cv /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS);
+  mat->P_min /= units_cgs_conversion_factor(us, UNIT_CONV_PRESSURE);
+}
+
+// gas_internal_energy_from_entropy
+INLINE static float Til_internal_energy_from_entropy(
+    float density, float entropy, const struct Til_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_pressure_from_entropy
+INLINE static float Til_pressure_from_entropy(float density, float entropy,
+                                              const struct Til_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_entropy_from_pressure
+INLINE static float Til_entropy_from_pressure(float density, float pressure,
+                                              const struct Til_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_entropy
+INLINE static float Til_soundspeed_from_entropy(float density, float entropy,
+                                                const struct Til_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_entropy_from_internal_energy
+INLINE static float Til_entropy_from_internal_energy(
+    float density, float u, const struct Til_params *mat) {
+
+  return 0;
+}
+
+// gas_pressure_from_internal_energy
+INLINE static float Til_pressure_from_internal_energy(
+    float density, float u, const struct Til_params *mat) {
+
+  const float eta = density / mat->rho_0;
+  const float mu = eta - 1.f;
+  const float nu = 1.f / eta - 1.f;
+  float P_c, P_e, P;
+
+  // Condensed or cold
+  if (eta < mat->eta_min) {
+    P_c = 0.f;
+  } else {
+    P_c = (mat->a + mat->b / (u / (mat->E_0 * eta * eta) + 1.f)) * density * u +
+          mat->A * mu + mat->B * mu * mu;
+  }
+  // Expanded and hot
+  P_e = mat->a * density * u +
+        (mat->b * density * u / (u / (mat->E_0 * eta * eta) + 1.f) +
+         mat->A * mu * expf(-mat->beta * nu)) *
+            expf(-mat->alpha * nu * nu);
+
+  // Condensed or cold state
+  if ((1.f < eta) || (u < mat->E_iv)) {
+    P = P_c;
+  }
+  // Expanded and hot state
+  else if ((eta < 1.f) && (mat->E_cv < u)) {
+    P = P_e;
+  }
+  // Hybrid state
+  else {
+    P = ((u - mat->E_iv) * P_e + (mat->E_cv - u) * P_c) /
+        (mat->E_cv - mat->E_iv);
+  }
+
+  // Minimum pressure
+  if (P < mat->P_min) {
+    P = mat->P_min;
+  }
+
+  return P;
+}
+
+// gas_internal_energy_from_pressure
+INLINE static float Til_internal_energy_from_pressure(
+    float density, float P, const struct Til_params *mat) {
+
+  error("This EOS function is not yet implemented!");
+
+  return 0;
+}
+
+// gas_soundspeed_from_internal_energy
+INLINE static float Til_soundspeed_from_internal_energy(
+    float density, float u, const struct Til_params *mat) {
+
+  //    const float eta = density / mat->rho_0;
+  //    const float mu = eta - 1.f;
+  //    const float nu = 1.f/eta - 1.f;
+  //    float P_c, P_e, P, c_c, c_e, c;
+  //
+  //    // Condensed or cold
+  //    if (eta < mat->eta_min) {
+  //        P_c = 0.f;
+  //    }
+  //    else {
+  //        P_c = (mat->a + mat->b / (u / (mat->E_0 * eta*eta) + 1.f)) * density
+  //        * u
+  //            + mat->A * mu + mat->B * mu*mu;
+  //    }
+  //    c_c = mat->a*u + mat->b*u / ((u / (mat->E_0*eta*eta)+1.f) *
+  //        (u / (mat->E_0*eta*eta)+1.f)) *
+  //        (3.f*(u / (mat->E_0*eta*eta)+1.f) - 2.f) +
+  //        (mat->A + 2.f*mat->B*mu) / mat->rho_0  +  P_c / (rho*rho) *
+  //        (mat->a*rho + mat->b*rho / ((u / (mat->E_0*eta*eta)+1.f) *
+  //        (u / (mat->E_0*eta*eta)+1.f)));
+  //
+  //    c_c = max(c_c, mat->A / mat->rho_0);
+  //
+  //    // Expanded and hot
+  //    P_e = mat->a*density*u + (
+  //        mat->b * density * u / (u / (mat->E_0 * eta*eta) + 1.f)
+  //        + mat->A*mu * expf(-mat->beta * nu)
+  //        ) * expf(-mat->alpha * nu*nu);
+  //
+  //    c_e = (mat->a + mat->b / (u / (mat->E_0*eta*eta)+1.f) *
+  //        expf(-mat->beta*((1.f - eta)/eta)*((1.f - eta)/eta))
+  //        + 1.f)*P_e/rho + mat->A/mat->rho_0
+  //        *expf(-(mat->alpha*((1.f - eta)/eta)+mat->beta *
+  //        ((1.f - eta)/eta)*((1.f - eta)/eta)))*(1.f+mu/(eta*eta)
+  //        *(mat->alpha+2.f*mat->beta*((1.f - eta)/eta)-eta)) +
+  //        mat->b*rho*u/((u / (mat->E_0*eta*eta)+1.f)*
+  //        (u / (mat->E_0*eta*eta)+1.f)*eta*eta)*
+  //        expf(-mat->beta*((1.f - eta)/eta)*((1.f - eta)/eta))*
+  //        (2.f*mat->beta*((1.f - eta)/eta)*(u / (mat->E_0*eta*eta)+1.f) /
+  //         mat->rho_0 + 1.f/(mat->E_0*rho)*(2.f*u-P_e/rho));
+  //
+  //    // Condensed or cold state
+  //    if ((1.f < eta) || (u < mat->E_iv)) {
+  //        c = c_c;
+  //    }
+  //    // Expanded and hot state
+  //    else if ((eta < 1.f) && (mat->E_cv < u)) {
+  //        c = c_e;
+  //    }
+  //    // Hybrid state
+  //    else {
+  //		c = ((u - mat->E_iv)*c_e + (mat->E_cv - u)*c_c) /
+  //            (mat->E_cv - mat->E_iv);
+  //
+  //        c = max(c_c, mat->A / mat->rho0);
+  //    }
+  float c = sqrtf(mat->A / mat->rho_0);
+
+  return c;
+}
+
+// gas_soundspeed_from_pressure
+INLINE static float Til_soundspeed_from_pressure(float density, float P,
+                                                 const struct Til_params *mat) {
+
+  float c = sqrtf(mat->A / mat->rho_0);
+
+  return c;
+}
+
+#endif /* SWIFT_TILLOTSON_EQUATION_OF_STATE_H */
diff --git a/src/gravity.c b/src/gravity.c
index 06a1e941670f1342db9780d9212b34b9f339f36c..d0eb589d15ba89aecc5a581de0c143dc7504d3ee 100644
--- a/src/gravity.c
+++ b/src/gravity.c
@@ -73,155 +73,229 @@ float ewald_fac;
 void gravity_exact_force_ewald_init(double boxSize) {
 
 #ifdef SWIFT_GRAVITY_FORCE_CHECKS
-  const ticks tic = getticks();
-  message("Computing Ewald correction table...");
-
-  /* Level of correction  (Hernquist et al. 1991)*/
-  const float alpha = 2.f;
-
-  /* some useful constants */
-  const float alpha2 = alpha * alpha;
-  const float factor_exp1 = 2.f * alpha / sqrt(M_PI);
-  const float factor_exp2 = -M_PI * M_PI / alpha2;
-  const float factor_sin = 2.f * M_PI;
-  const float factor_cos = 2.f * M_PI;
-  const float factor_pot = M_PI / alpha2;
+
+  const float boxSize_inv = 1.f / boxSize;
   const float boxSize_inv2 = 1.f / (boxSize * boxSize);
 
-  /* Ewald factor to access the table */
-  ewald_fac = (double)(2 * Newald) / boxSize;
+  int use_file = 0;
 
-  /* Zero everything */
-  bzero(fewald_x, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float));
-  bzero(fewald_y, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float));
-  bzero(fewald_z, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float));
-  bzero(potewald, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float));
+#ifdef HAVE_HDF5
+  if (access("Ewald.hdf5", R_OK) != -1) use_file = 1;
+#endif
 
-  potewald[0][0][0] = 2.8372975f;
+  /* Can we use the stored HDF5 file? */
+  if (use_file) {
 
-  /* Compute the values in one of the octants */
-  for (int i = 0; i <= Newald; ++i) {
-    for (int j = 0; j <= Newald; ++j) {
-      for (int k = 0; k <= Newald; ++k) {
+    const ticks tic = getticks();
+    message("Reading Ewald correction table from file...");
+
+#ifdef HAVE_HDF5
+    const hid_t h_file = H5Fopen("Ewald.hdf5", H5F_ACC_RDONLY, H5P_DEFAULT);
+    if (h_file < 0) error("Error opening the old 'Ewald.hdf5' file.");
+
+    /* Check the table size */
+    const hid_t h_grp = H5Gopen1(h_file, "Info");
+    const hid_t h_attr = H5Aopen(h_grp, "Ewald_size", H5P_DEFAULT);
+    int size;
+    H5Aread(h_attr, H5T_NATIVE_INT, &size);
+    if (size != Newald)
+      error("File 'Ewald.hdf5' contains arrays of the wrong size");
+    H5Aclose(h_attr);
+    H5Gclose(h_grp);
+
+    /* Now read the tables themselves */
+    hid_t h_data;
+    h_data = H5Dopen(h_file, "Ewald_x", H5P_DEFAULT);
+    H5Dread(h_data, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+            &(fewald_x[0][0][0]));
+    H5Dclose(h_data);
+    h_data = H5Dopen(h_file, "Ewald_y", H5P_DEFAULT);
+    H5Dread(h_data, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+            &(fewald_y[0][0][0]));
+    H5Dclose(h_data);
+    h_data = H5Dopen(h_file, "Ewald_z", H5P_DEFAULT);
+    H5Dread(h_data, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+            &(fewald_z[0][0][0]));
+    H5Dclose(h_data);
+    h_data = H5Dopen(h_file, "Ewald_pot", H5P_DEFAULT);
+    H5Dread(h_data, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+            &(potewald[0][0][0]));
+    H5Dclose(h_data);
+
+    /* Done */
+    H5Fclose(h_file);
+#endif
 
-        if (i == 0 && j == 0 && k == 0) continue;
-
-        /* Distance vector */
-        const float r_x = 0.5f * ((float)i) / Newald;
-        const float r_y = 0.5f * ((float)j) / Newald;
-        const float r_z = 0.5f * ((float)k) / Newald;
-
-        /* Norm of distance vector */
-        const float r2 = r_x * r_x + r_y * r_y + r_z * r_z;
-        const float r_inv = 1.f / sqrtf(r2);
-        const float r_inv3 = r_inv * r_inv * r_inv;
-
-        /* Normal gravity potential term */
-        float f_x = r_x * r_inv3;
-        float f_y = r_y * r_inv3;
-        float f_z = r_z * r_inv3;
-        float pot = r_inv + factor_pot;
-
-        for (int n_i = -4; n_i <= 4; ++n_i) {
-          for (int n_j = -4; n_j <= 4; ++n_j) {
-            for (int n_k = -4; n_k <= 4; ++n_k) {
-
-              const float d_x = r_x - n_i;
-              const float d_y = r_y - n_j;
-              const float d_z = r_z - n_k;
-
-              /* Discretised distance */
-              const float r_tilde2 = d_x * d_x + d_y * d_y + d_z * d_z;
-              const float r_tilde_inv = 1.f / sqrtf(r_tilde2);
-              const float r_tilde = r_tilde_inv * r_tilde2;
-              const float r_tilde_inv3 =
-                  r_tilde_inv * r_tilde_inv * r_tilde_inv;
-
-              const float val_pot = erfcf(alpha * r_tilde);
-
-              const float val_f =
-                  val_pot + factor_exp1 * r_tilde * expf(-alpha2 * r_tilde2);
-
-              /* First correction term */
-              const float f = val_f * r_tilde_inv3;
-              f_x -= f * d_x;
-              f_y -= f * d_y;
-              f_z -= f * d_z;
-              pot -= val_pot * r_tilde_inv;
+    /* Report time this took */
+    message("Ewald correction table read in (took %.3f %s). ",
+            clocks_from_ticks(getticks() - tic), clocks_getunit());
+  } else {
+
+    /* Ok.. let's recompute everything */
+    const ticks tic = getticks();
+    message("Computing Ewald correction table...");
+
+    /* Level of correction  (Hernquist et al. 1991)*/
+    const float alpha = 2.f;
+
+    /* some useful constants */
+    const float alpha2 = alpha * alpha;
+    const float factor_exp1 = 2.f * alpha / sqrt(M_PI);
+    const float factor_exp2 = -M_PI * M_PI / alpha2;
+    const float factor_sin = 2.f * M_PI;
+    const float factor_cos = 2.f * M_PI;
+    const float factor_pot = M_PI / alpha2;
+
+    /* Zero everything */
+    bzero(fewald_x, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float));
+    bzero(fewald_y, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float));
+    bzero(fewald_z, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float));
+    bzero(potewald, (Newald + 1) * (Newald + 1) * (Newald + 1) * sizeof(float));
+
+    potewald[0][0][0] = 2.8372975f;
+
+    /* Compute the values in one of the octants */
+    for (int i = 0; i <= Newald; ++i) {
+      for (int j = 0; j <= Newald; ++j) {
+        for (int k = 0; k <= Newald; ++k) {
+
+          if (i == 0 && j == 0 && k == 0) continue;
+
+          /* Distance vector */
+          const float r_x = 0.5f * ((float)i) / Newald;
+          const float r_y = 0.5f * ((float)j) / Newald;
+          const float r_z = 0.5f * ((float)k) / Newald;
+
+          /* Norm of distance vector */
+          const float r2 = r_x * r_x + r_y * r_y + r_z * r_z;
+          const float r_inv = 1.f / sqrtf(r2);
+          const float r_inv3 = r_inv * r_inv * r_inv;
+
+          /* Normal gravity potential term */
+          float f_x = r_x * r_inv3;
+          float f_y = r_y * r_inv3;
+          float f_z = r_z * r_inv3;
+          float pot = r_inv + factor_pot;
+
+          for (int n_i = -4; n_i <= 4; ++n_i) {
+            for (int n_j = -4; n_j <= 4; ++n_j) {
+              for (int n_k = -4; n_k <= 4; ++n_k) {
+
+                const float d_x = r_x - n_i;
+                const float d_y = r_y - n_j;
+                const float d_z = r_z - n_k;
+
+                /* Discretised distance */
+                const float r_tilde2 = d_x * d_x + d_y * d_y + d_z * d_z;
+                const float r_tilde_inv = 1.f / sqrtf(r_tilde2);
+                const float r_tilde = r_tilde_inv * r_tilde2;
+                const float r_tilde_inv3 =
+                    r_tilde_inv * r_tilde_inv * r_tilde_inv;
+
+                const float val_pot = erfcf(alpha * r_tilde);
+
+                const float val_f =
+                    val_pot + factor_exp1 * r_tilde * expf(-alpha2 * r_tilde2);
+
+                /* First correction term */
+                const float f = val_f * r_tilde_inv3;
+                f_x -= f * d_x;
+                f_y -= f * d_y;
+                f_z -= f * d_z;
+                pot -= val_pot * r_tilde_inv;
+              }
             }
           }
-        }
 
-        for (int h_i = -4; h_i <= 4; ++h_i) {
-          for (int h_j = -4; h_j <= 4; ++h_j) {
-            for (int h_k = -4; h_k <= 4; ++h_k) {
+          for (int h_i = -4; h_i <= 4; ++h_i) {
+            for (int h_j = -4; h_j <= 4; ++h_j) {
+              for (int h_k = -4; h_k <= 4; ++h_k) {
 
-              const float h2 = h_i * h_i + h_j * h_j + h_k * h_k;
+                const float h2 = h_i * h_i + h_j * h_j + h_k * h_k;
 
-              if (h2 == 0.f) continue;
+                if (h2 == 0.f) continue;
 
-              const float h2_inv = 1.f / (h2 + FLT_MIN);
-              const float h_dot_x = h_i * r_x + h_j * r_y + h_k * r_z;
+                const float h2_inv = 1.f / (h2 + FLT_MIN);
+                const float h_dot_x = h_i * r_x + h_j * r_y + h_k * r_z;
 
-              const float common = h2_inv * expf(h2 * factor_exp2);
+                const float common = h2_inv * expf(h2 * factor_exp2);
 
-              const float val_pot =
-                  (float)M_1_PI * common * cosf(factor_cos * h_dot_x);
+                const float val_pot =
+                    (float)M_1_PI * common * cosf(factor_cos * h_dot_x);
 
-              const float val_f = 2.f * common * sinf(factor_sin * h_dot_x);
+                const float val_f = 2.f * common * sinf(factor_sin * h_dot_x);
 
-              /* Second correction term */
-              f_x -= val_f * h_i;
-              f_y -= val_f * h_j;
-              f_z -= val_f * h_k;
-              pot -= val_pot;
+                /* Second correction term */
+                f_x -= val_f * h_i;
+                f_y -= val_f * h_j;
+                f_z -= val_f * h_k;
+                pot -= val_pot;
+              }
             }
           }
-        }
 
-        /* Save back to memory */
-        fewald_x[i][j][k] = f_x;
-        fewald_y[i][j][k] = f_y;
-        fewald_z[i][j][k] = f_z;
-        potewald[i][j][k] = pot;
+          /* Save back to memory */
+          fewald_x[i][j][k] = f_x;
+          fewald_y[i][j][k] = f_y;
+          fewald_z[i][j][k] = f_z;
+          potewald[i][j][k] = pot;
+        }
       }
     }
-  }
 
 /* Dump the Ewald table to a file */
 #ifdef HAVE_HDF5
-  hid_t h_file =
-      H5Fcreate("Ewald.hdf5", H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
-  if (h_file < 0) error("Error while opening file for Ewald dump.");
-
-  /* Create dataspace */
-  hsize_t dim[3] = {Newald + 1, Newald + 1, Newald + 1};
-  hid_t h_space = H5Screate_simple(3, dim, NULL);
-  hid_t h_data;
-  h_data = H5Dcreate(h_file, "Ewald_x", H5T_NATIVE_FLOAT, h_space, H5P_DEFAULT,
-                     H5P_DEFAULT, H5P_DEFAULT);
-  H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT,
-           &(fewald_x[0][0][0]));
-  H5Dclose(h_data);
-  h_data = H5Dcreate(h_file, "Ewald_y", H5T_NATIVE_FLOAT, h_space, H5P_DEFAULT,
-                     H5P_DEFAULT, H5P_DEFAULT);
-  H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT,
-           &(fewald_y[0][0][0]));
-  H5Dclose(h_data);
-  h_data = H5Dcreate(h_file, "Ewald_z", H5T_NATIVE_FLOAT, h_space, H5P_DEFAULT,
-                     H5P_DEFAULT, H5P_DEFAULT);
-  H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT,
-           &(fewald_z[0][0][0]));
-  H5Dclose(h_data);
-  h_data = H5Dcreate(h_file, "Ewald_pot", H5T_NATIVE_FLOAT, h_space,
-                     H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
-  H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT,
-           &(potewald[0][0][0]));
-  H5Dclose(h_data);
-  H5Sclose(h_space);
-  H5Fclose(h_file);
+    hid_t h_file =
+        H5Fcreate("Ewald.hdf5", H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+    if (h_file < 0) error("Error while opening file for Ewald dump.");
+
+    /* Write the Ewald table size */
+    const int size = Newald;
+    const hid_t h_grp = H5Gcreate1(h_file, "Info", 0);
+    const hid_t h_aspace = H5Screate(H5S_SCALAR);
+    hid_t h_att =
+        H5Acreate1(h_grp, "Ewald_size", H5T_NATIVE_INT, h_aspace, H5P_DEFAULT);
+    H5Awrite(h_att, H5T_NATIVE_INT, &size);
+    H5Aclose(h_att);
+    H5Gclose(h_grp);
+    H5Sclose(h_aspace);
+
+    /* Create dataspace and write arrays */
+    hsize_t dim[3] = {Newald + 1, Newald + 1, Newald + 1};
+    hid_t h_space = H5Screate_simple(3, dim, NULL);
+    hid_t h_data;
+    h_data = H5Dcreate(h_file, "Ewald_x", H5T_NATIVE_FLOAT, h_space,
+                       H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+    H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT,
+             &(fewald_x[0][0][0]));
+    H5Dclose(h_data);
+    h_data = H5Dcreate(h_file, "Ewald_y", H5T_NATIVE_FLOAT, h_space,
+                       H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+    H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT,
+             &(fewald_y[0][0][0]));
+    H5Dclose(h_data);
+    h_data = H5Dcreate(h_file, "Ewald_z", H5T_NATIVE_FLOAT, h_space,
+                       H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+    H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT,
+             &(fewald_z[0][0][0]));
+    H5Dclose(h_data);
+    h_data = H5Dcreate(h_file, "Ewald_pot", H5T_NATIVE_FLOAT, h_space,
+                       H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+    H5Dwrite(h_data, H5T_NATIVE_FLOAT, h_space, H5S_ALL, H5P_DEFAULT,
+             &(potewald[0][0][0]));
+    H5Dclose(h_data);
+    H5Sclose(h_space);
+    H5Fclose(h_file);
 #endif
 
+    /* Report time this took */
+    message("Ewald correction table computed (took %.3f %s). ",
+            clocks_from_ticks(getticks() - tic), clocks_getunit());
+  }
+
+  /* Ewald factor to access the table */
+  ewald_fac = (double)(2 * Newald) / boxSize;
+
   /* Apply the box-size correction */
   for (int i = 0; i <= Newald; ++i) {
     for (int j = 0; j <= Newald; ++j) {
@@ -229,20 +303,15 @@ void gravity_exact_force_ewald_init(double boxSize) {
         fewald_x[i][j][k] *= boxSize_inv2;
         fewald_y[i][j][k] *= boxSize_inv2;
         fewald_z[i][j][k] *= boxSize_inv2;
-        potewald[i][j][k] *= boxSize_inv2;
+        potewald[i][j][k] *= boxSize_inv;
       }
     }
   }
-
-  /* Say goodbye */
-  message("Ewald correction table computed (took %.3f %s). ",
-          clocks_from_ticks(getticks() - tic), clocks_getunit());
 #else
   error("Gravity checking function called without the corresponding flag.");
 #endif
 }
 
-#ifdef SWIFT_GRAVITY_FORCE_CHECKS
 /**
  * @brief Compute the Ewald correction for a given distance vector r.
  *
@@ -255,9 +324,10 @@ void gravity_exact_force_ewald_init(double boxSize) {
  * @param corr_f (return) The Ewald correction for the force.
  * @param corr_p (return) The Ewald correction for the potential.
  */
-__attribute__((always_inline)) INLINE static void
-gravity_exact_force_ewald_evaluate(double rx, double ry, double rz,
-                                   double corr_f[3], double *corr_p) {
+void gravity_exact_force_ewald_evaluate(double rx, double ry, double rz,
+                                        double corr_f[3], double *corr_p) {
+
+#ifdef SWIFT_GRAVITY_FORCE_CHECKS
 
   const double s_x = (rx < 0.) ? 1. : -1.;
   const double s_y = (ry < 0.) ? 1. : -1.;
@@ -327,8 +397,11 @@ gravity_exact_force_ewald_evaluate(double rx, double ry, double rz,
   *corr_p += potewald[i + 1][j + 0][k + 1] * dx * ty * dz;
   *corr_p += potewald[i + 1][j + 1][k + 0] * dx * dy * tz;
   *corr_p += potewald[i + 1][j + 1][k + 1] * dx * dy * dz;
-}
+
+#else
+  error("Gravity checking function called without the corresponding flag.");
 #endif
+}
 
 /**
  * @brief Checks whether the file containing the exact accelerations for
@@ -343,9 +416,9 @@ int gravity_exact_force_file_exits(const struct engine *e) {
   /* File name */
   char file_name[100];
   if (e->s->periodic)
-    sprintf(file_name, "gravity_checks_exact_periodic_step%d.dat", e->step);
+    sprintf(file_name, "gravity_checks_exact_periodic_step%.4d.dat", e->step);
   else
-    sprintf(file_name, "gravity_checks_exact_step%d.dat", e->step);
+    sprintf(file_name, "gravity_checks_exact_step%.4d.dat", e->step);
 
   /* Does the file exist ? */
   if (access(file_name, R_OK | W_OK) == 0) {
@@ -436,7 +509,7 @@ void gravity_exact_force_compute_mapper(void *map_data, int nr_gparts,
       /* Interact it with all other particles in the space.*/
       for (int j = 0; j < (int)s->nr_gparts; ++j) {
 
-        struct gpart *gpj = &s->gparts[j];
+        const struct gpart *gpj = &s->gparts[j];
 
         /* No self interaction */
         if (gpi == gpj) continue;
@@ -574,7 +647,7 @@ void gravity_exact_force_check(struct space *s, const struct engine *e,
 
   /* File name */
   char file_name_swift[100];
-  sprintf(file_name_swift, "gravity_checks_swift_step%d_order%d.dat", e->step,
+  sprintf(file_name_swift, "gravity_checks_swift_step%.4d_order%d.dat", e->step,
           SELF_GRAVITY_MULTIPOLE_ORDER);
 
   /* Creare files and write header */
@@ -630,10 +703,10 @@ void gravity_exact_force_check(struct space *s, const struct engine *e,
 
     char file_name_exact[100];
     if (s->periodic)
-      sprintf(file_name_exact, "gravity_checks_exact_periodic_step%d.dat",
+      sprintf(file_name_exact, "gravity_checks_exact_periodic_step%.4d.dat",
               e->step);
     else
-      sprintf(file_name_exact, "gravity_checks_exact_step%d.dat", e->step);
+      sprintf(file_name_exact, "gravity_checks_exact_step%.4d.dat", e->step);
 
     FILE *file_exact = fopen(file_name_exact, "w");
     fprintf(file_exact, "# Gravity accuracy test - EXACT FORCES\n");
diff --git a/src/gravity.h b/src/gravity.h
index 33bef8ba329aa1264334e3319b6250c01b974338..6497de8294dfa3f207332ff696ddb992c875eb28 100644
--- a/src/gravity.h
+++ b/src/gravity.h
@@ -36,7 +36,9 @@ struct engine;
 struct space;
 
 void gravity_exact_force_ewald_init(double boxSize);
-void gravity_exact_force_ewald_free();
+void gravity_exact_force_ewald_free(void);
+void gravity_exact_force_ewald_evaluate(double rx, double ry, double rz,
+                                        double corr_f[3], double *corr_p);
 void gravity_exact_force_compute(struct space *s, const struct engine *e);
 void gravity_exact_force_check(struct space *s, const struct engine *e,
                                float rel_tol);
diff --git a/src/gravity/Default/gravity_iact.h b/src/gravity/Default/gravity_iact.h
index 99f4ec420178ec75ad0bfa6562acf4681b8b3b0f..ad477c54f49f9d30ee492d84012d7a9228401c5f 100644
--- a/src/gravity/Default/gravity_iact.h
+++ b/src/gravity/Default/gravity_iact.h
@@ -44,8 +44,8 @@
  * @param pot_ij (return) The potential.
  */
 __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_full(
-    float r2, float h2, float h_inv, float h_inv3, float mass, float *f_ij,
-    float *pot_ij) {
+    const float r2, const float h2, const float h_inv, const float h_inv3,
+    const float mass, float *f_ij, float *pot_ij) {
 
   /* Get the inverse distance */
   const float r_inv = 1.f / sqrtf(r2 + FLT_MIN);
@@ -84,13 +84,13 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_full(
  * @param h_inv Inverse of the softening length.
  * @param h_inv3 Cube of the inverse of the softening length.
  * @param mass Mass of the point-mass.
- * @param rlr_inv Inverse of the mesh smoothing scale.
+ * @param r_s_inv Inverse of the mesh smoothing scale.
  * @param f_ij (return) The force intensity.
  * @param pot_ij (return) The potential.
  */
 __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated(
-    float r2, float h2, float h_inv, float h_inv3, float mass, float rlr_inv,
-    float *f_ij, float *pot_ij) {
+    const float r2, const float h2, const float h_inv, const float h_inv3,
+    const float mass, const float r_s_inv, float *f_ij, float *pot_ij) {
 
   /* Get the inverse distance */
   const float r_inv = 1.f / sqrtf(r2 + FLT_MIN);
@@ -117,7 +117,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated(
   }
 
   /* Get long-range correction */
-  const float u_lr = r * rlr_inv;
+  const float u_lr = r * r_s_inv;
   float corr_f_lr, corr_pot_lr;
   kernel_long_grav_force_eval(u_lr, &corr_f_lr);
   kernel_long_grav_pot_eval(u_lr, &corr_pot_lr);
@@ -126,10 +126,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated(
 }
 
 /**
- * @brief Computes the force at a point generated by a multipole.
+ * @brief Computes the forces at a point generated by a multipole.
  *
- * This uses the quadrupole terms only and defaults to the monopole if
- * the code is compiled with low-order gravity only.
+ * This assumes M_100 == M_010 == M_001 == 0.
+ * This uses the quadrupole and trace of the octupole terms only and defaults to
+ * the monopole if the code is compiled with low-order gravity only.
  *
  * @param r_x x-component of the distance vector to the multipole.
  * @param r_y y-component of the distance vector to the multipole.
@@ -143,42 +144,206 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated(
  * @param f_z (return) The z-component of the acceleration.
  * @param pot (return) The potential.
  */
-__attribute__((always_inline)) INLINE static void runner_iact_grav_pm(
-    float r_x, float r_y, float r_z, float r2, float h, float h_inv,
-    const struct multipole *m, float *f_x, float *f_y, float *f_z, float *pot) {
+__attribute__((always_inline)) INLINE static void runner_iact_grav_pm_full(
+    const float r_x, const float r_y, const float r_z, const float r2,
+    const float h, const float h_inv, const struct multipole *m, float *f_x,
+    float *f_y, float *f_z, float *pot) {
 
+/* In the case where the order is < 3, then there is only a monopole term left.
+ * We can default to the normal P-P interaction with the mass of the multipole
+ * and its CoM as the "particle" property */
 #if SELF_GRAVITY_MULTIPOLE_ORDER < 3
-  float f_ij;
+
+  float f_ij, pot_ij;
   runner_iact_grav_pp_full(r2, h * h, h_inv, h_inv * h_inv * h_inv, m->M_000,
-                           &f_ij, pot);
-  *f_x = -f_ij * r_x;
-  *f_y = -f_ij * r_y;
-  *f_z = -f_ij * r_z;
+                           &f_ij, &pot_ij);
+  *f_x = f_ij * r_x;
+  *f_y = f_ij * r_y;
+  *f_z = f_ij * r_z;
+  *pot = pot_ij;
+
 #else
 
   /* Get the inverse distance */
   const float r_inv = 1.f / sqrtf(r2);
 
+  /* Compute the derivatives of the potential */
   struct potential_derivatives_M2P d;
-  compute_potential_derivatives_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, &d);
+  compute_potential_derivatives_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, 0, 0.f,
+                                    &d);
 
-  /* 1st order terms (monopole) */
+  /* 0th order contributions */
   *f_x = m->M_000 * d.D_100;
   *f_y = m->M_000 * d.D_010;
   *f_z = m->M_000 * d.D_001;
-  *pot = -m->M_000 * d.D_000;
+  *pot = m->M_000 * d.D_000;
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 0
+
+/* 1st order contributions */
+
+/* 1st order contributions are all 0 since the dipole is 0 */
+
+/* *f_x = m->M_001 * d.D_101 + m->M_010 * d.D_110 + m->M_100 * d.D_200 ; */
+/* *f_y = m->M_001 * d.D_011 + m->M_010 * d.D_020 + m->M_100 * d.D_110 ; */
+/* *f_z = m->M_001 * d.D_002 + m->M_010 * d.D_011 + m->M_100 * d.D_101 ; */
+/* *pot = m->M_001 * d.D_001 + m->M_010 * d.D_010 + m->M_100 * d.D_100 ; */
+
+#endif
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 2
+
+  /* 2nd order contributions */
+  *f_x += m->M_002 * d.D_102 + m->M_011 * d.D_111 + m->M_020 * d.D_120 +
+          m->M_101 * d.D_201 + m->M_110 * d.D_210 + m->M_200 * d.D_300;
+  *f_y += m->M_002 * d.D_012 + m->M_011 * d.D_021 + m->M_020 * d.D_030 +
+          m->M_101 * d.D_111 + m->M_110 * d.D_120 + m->M_200 * d.D_210;
+  *f_z += m->M_002 * d.D_003 + m->M_011 * d.D_012 + m->M_020 * d.D_021 +
+          m->M_101 * d.D_102 + m->M_110 * d.D_111 + m->M_200 * d.D_201;
+  *pot += m->M_002 * d.D_002 + m->M_011 * d.D_011 + m->M_020 * d.D_020 +
+          m->M_101 * d.D_101 + m->M_110 * d.D_110 + m->M_200 * d.D_200;
+
+#endif
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+
+  /* 3rd order contributions */
+  *f_x += m->M_003 * d.D_103 + m->M_012 * d.D_112 + m->M_021 * d.D_121 +
+          m->M_030 * d.D_130 + m->M_102 * d.D_202 + m->M_111 * d.D_211 +
+          m->M_120 * d.D_220 + m->M_201 * d.D_301 + m->M_210 * d.D_310 +
+          m->M_300 * d.D_400;
+  *f_y += m->M_003 * d.D_013 + m->M_012 * d.D_022 + m->M_021 * d.D_031 +
+          m->M_030 * d.D_040 + m->M_102 * d.D_112 + m->M_111 * d.D_121 +
+          m->M_120 * d.D_130 + m->M_201 * d.D_211 + m->M_210 * d.D_220 +
+          m->M_300 * d.D_310;
+  *f_z += m->M_003 * d.D_004 + m->M_012 * d.D_013 + m->M_021 * d.D_022 +
+          m->M_030 * d.D_031 + m->M_102 * d.D_103 + m->M_111 * d.D_112 +
+          m->M_120 * d.D_121 + m->M_201 * d.D_202 + m->M_210 * d.D_211 +
+          m->M_300 * d.D_301;
+  *pot += m->M_003 * d.D_003 + m->M_012 * d.D_012 + m->M_021 * d.D_021 +
+          m->M_030 * d.D_030 + m->M_102 * d.D_102 + m->M_111 * d.D_111 +
+          m->M_120 * d.D_120 + m->M_201 * d.D_201 + m->M_210 * d.D_210 +
+          m->M_300 * d.D_300;
+
+#endif
 
-  /* 3rd order terms (quadrupole) */
-  *f_x += m->M_200 * d.D_300 + m->M_020 * d.D_120 + m->M_002 * d.D_102;
-  *f_y += m->M_200 * d.D_210 + m->M_020 * d.D_030 + m->M_002 * d.D_012;
-  *f_z += m->M_200 * d.D_201 + m->M_020 * d.D_021 + m->M_002 * d.D_003;
-  *pot -= m->M_200 * d.D_100 + m->M_020 * d.D_020 + m->M_002 * d.D_002;
+  /* Take care of the the sign convention */
+  *f_x *= -1.f;
+  *f_y *= -1.f;
+  *f_z *= -1.f;
+  *pot *= -1.f;
+#endif
+}
 
-  *f_x += m->M_110 * d.D_210 + m->M_101 * d.D_201 + m->M_011 * d.D_111;
-  *f_y += m->M_110 * d.D_120 + m->M_101 * d.D_111 + m->M_011 * d.D_021;
-  *f_z += m->M_110 * d.D_111 + m->M_101 * d.D_102 + m->M_011 * d.D_012;
-  *pot -= m->M_110 * d.D_110 + m->M_101 * d.D_101 + m->M_011 * d.D_011;
+/**
+ * @brief Computes the forces at a point generated by a multipole, truncated for
+ * long-range periodicity.
+ *
+ * This assumes M_100 == M_010 == M_001 == 0.
+ * This uses the quadrupole term and trace of the octupole terms only and
+ * defaults to the monopole if the code is compiled with low-order gravity only.
+ *
+ * @param r_x x-component of the distance vector to the multipole.
+ * @param r_y y-component of the distance vector to the multipole.
+ * @param r_z z-component of the distance vector to the multipole.
+ * @param r2 Square of the distance vector to the multipole.
+ * @param h The softening length.
+ * @param h_inv Inverse of the softening length.
+ * @param r_s_inv The inverse of the gravity mesh-smoothing scale.
+ * @param m The multipole.
+ * @param f_x (return) The x-component of the acceleration.
+ * @param f_y (return) The y-component of the acceleration.
+ * @param f_z (return) The z-component of the acceleration.
+ * @param pot (return) The potential.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_grav_pm_truncated(
+    const float r_x, const float r_y, const float r_z, const float r2,
+    const float h, const float h_inv, const float r_s_inv,
+    const struct multipole *m, float *f_x, float *f_y, float *f_z, float *pot) {
+
+/* In the case where the order is < 3, then there is only a monopole term left.
+ * We can default to the normal P-P interaction with the mass of the multipole
+ * and its CoM as the "particle" property */
+#if SELF_GRAVITY_MULTIPOLE_ORDER < 3
+
+  float f_ij, pot_ij;
+  runner_iact_grav_pp_truncated(r2, h * h, h_inv, h_inv * h_inv * h_inv,
+                                m->M_000, r_s_inv, &f_ij, &pot_ij);
+  *f_x = f_ij * r_x;
+  *f_y = f_ij * r_y;
+  *f_z = f_ij * r_z;
+  *pot = -pot_ij;
+
+#else
+
+  /* Get the inverse distance */
+  const float r_inv = 1.f / sqrtf(r2);
+
+  /* Compute the derivatives of the potential */
+  struct potential_derivatives_M2P d;
+  compute_potential_derivatives_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, 1,
+                                    r_s_inv, &d);
+
+  /* 0th order contributions */
+  *f_x = m->M_000 * d.D_100;
+  *f_y = m->M_000 * d.D_010;
+  *f_z = m->M_000 * d.D_001;
+  *pot = m->M_000 * d.D_000;
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 0
+
+/* 1st order contributions */
+
+/* 1st order contributions are all 0 since the dipole is 0 */
+
+/* *f_x = m->M_001 * d.D_101 + m->M_010 * d.D_110 + m->M_100 * d.D_200 ; */
+/* *f_y = m->M_001 * d.D_011 + m->M_010 * d.D_020 + m->M_100 * d.D_110 ; */
+/* *f_z = m->M_001 * d.D_002 + m->M_010 * d.D_011 + m->M_100 * d.D_101 ; */
+/* *pot = m->M_001 * d.D_001 + m->M_010 * d.D_010 + m->M_100 * d.D_100 ; */
+
+#endif
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 2
+
+  /* 2nd order contributions */
+  *f_x += m->M_002 * d.D_102 + m->M_011 * d.D_111 + m->M_020 * d.D_120 +
+          m->M_101 * d.D_201 + m->M_110 * d.D_210 + m->M_200 * d.D_300;
+  *f_y += m->M_002 * d.D_012 + m->M_011 * d.D_021 + m->M_020 * d.D_030 +
+          m->M_101 * d.D_111 + m->M_110 * d.D_120 + m->M_200 * d.D_210;
+  *f_z += m->M_002 * d.D_003 + m->M_011 * d.D_012 + m->M_020 * d.D_021 +
+          m->M_101 * d.D_102 + m->M_110 * d.D_111 + m->M_200 * d.D_201;
+  *pot += m->M_002 * d.D_002 + m->M_011 * d.D_011 + m->M_020 * d.D_020 +
+          m->M_101 * d.D_101 + m->M_110 * d.D_110 + m->M_200 * d.D_200;
+
+#endif
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+
+  /* 3rd order contributions */
+  *f_x += m->M_003 * d.D_103 + m->M_012 * d.D_112 + m->M_021 * d.D_121 +
+          m->M_030 * d.D_130 + m->M_102 * d.D_202 + m->M_111 * d.D_211 +
+          m->M_120 * d.D_220 + m->M_201 * d.D_301 + m->M_210 * d.D_310 +
+          m->M_300 * d.D_400;
+  *f_y += m->M_003 * d.D_013 + m->M_012 * d.D_022 + m->M_021 * d.D_031 +
+          m->M_030 * d.D_040 + m->M_102 * d.D_112 + m->M_111 * d.D_121 +
+          m->M_120 * d.D_130 + m->M_201 * d.D_211 + m->M_210 * d.D_220 +
+          m->M_300 * d.D_310;
+  *f_z += m->M_003 * d.D_004 + m->M_012 * d.D_013 + m->M_021 * d.D_022 +
+          m->M_030 * d.D_031 + m->M_102 * d.D_103 + m->M_111 * d.D_112 +
+          m->M_120 * d.D_121 + m->M_201 * d.D_202 + m->M_210 * d.D_211 +
+          m->M_300 * d.D_301;
+  *pot += m->M_003 * d.D_003 + m->M_012 * d.D_012 + m->M_021 * d.D_021 +
+          m->M_030 * d.D_030 + m->M_102 * d.D_102 + m->M_111 * d.D_111 +
+          m->M_120 * d.D_120 + m->M_201 * d.D_201 + m->M_210 * d.D_210 +
+          m->M_300 * d.D_300;
+
+#endif
 
+  /* Take care of the the sign convention */
+  *f_x *= -1.f;
+  *f_y *= -1.f;
+  *f_z *= -1.f;
+  *pot *= -1.f;
 #endif
 }
 
diff --git a/src/gravity/Default/gravity_io.h b/src/gravity/Default/gravity_io.h
index 7b8ec2c8fef04cc4a4fc6836d8bb895b24d3c41f..7f453179641e2ba16b30e3172ddd7853245a1d2f 100644
--- a/src/gravity/Default/gravity_io.h
+++ b/src/gravity/Default/gravity_io.h
@@ -21,8 +21,8 @@
 
 #include "io_properties.h"
 
-void convert_gpart_pos(const struct engine* e, const struct gpart* gp,
-                       double* ret) {
+INLINE static void convert_gpart_pos(const struct engine* e,
+                                     const struct gpart* gp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(gp->x[0], 0.0, e->s->dim[0]);
@@ -35,8 +35,8 @@ void convert_gpart_pos(const struct engine* e, const struct gpart* gp,
   }
 }
 
-void convert_gpart_vel(const struct engine* e, const struct gpart* gp,
-                       float* ret) {
+INLINE static void convert_gpart_vel(const struct engine* e,
+                                     const struct gpart* gp, float* ret) {
 
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const struct cosmology* cosmo = e->cosmology;
@@ -62,9 +62,9 @@ void convert_gpart_vel(const struct engine* e, const struct gpart* gp,
   ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav;
 
   /* Conversion from internal units to peculiar velocities */
-  ret[0] *= cosmo->a2_inv;
-  ret[1] *= cosmo->a2_inv;
-  ret[2] *= cosmo->a2_inv;
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
 }
 
 /**
@@ -74,8 +74,9 @@ void convert_gpart_vel(const struct engine* e, const struct gpart* gp,
  * @param list The list of i/o properties to read.
  * @param num_fields The number of i/o fields to read.
  */
-void darkmatter_read_particles(struct gpart* gparts, struct io_props* list,
-                               int* num_fields) {
+INLINE static void darkmatter_read_particles(struct gpart* gparts,
+                                             struct io_props* list,
+                                             int* num_fields) {
 
   /* Say how much we want to read */
   *num_fields = 4;
@@ -98,8 +99,9 @@ void darkmatter_read_particles(struct gpart* gparts, struct io_props* list,
  * @param list The list of i/o properties to write.
  * @param num_fields The number of i/o fields to write.
  */
-void darkmatter_write_particles(const struct gpart* gparts,
-                                struct io_props* list, int* num_fields) {
+INLINE static void darkmatter_write_particles(const struct gpart* gparts,
+                                              struct io_props* list,
+                                              int* num_fields) {
 
   /* Say how much we want to write */
   *num_fields = 5;
diff --git a/src/gravity_cache.h b/src/gravity_cache.h
index 0012be193c657abccd5d83ed872e4d21764065a0..9101fc763d69ace10c57536ebf59f655744b212b 100644
--- a/src/gravity_cache.h
+++ b/src/gravity_cache.h
@@ -105,7 +105,8 @@ static INLINE void gravity_cache_clean(struct gravity_cache *c) {
  * @param count The number of #gpart to allocated for (space_splitsize is a good
  * choice).
  */
-static INLINE void gravity_cache_init(struct gravity_cache *c, int count) {
+static INLINE void gravity_cache_init(struct gravity_cache *c,
+                                      const int count) {
 
   /* Size of the gravity cache */
   const int padded_count = count - (count % VEC_SIZE) + VEC_SIZE;
@@ -134,6 +135,35 @@ static INLINE void gravity_cache_init(struct gravity_cache *c, int count) {
   c->count = padded_count;
 }
 
+/**
+ * @brief Zero all the output fields (acceleration and potential) of a
+ * #gravity_cache.
+ *
+ * @param c The #gravity_cache to zero.
+ * @param gcount_padded The padded size of the cache arrays.
+ */
+__attribute__((always_inline)) INLINE static void gravity_cache_zero_output(
+    struct gravity_cache *c, const int gcount_padded) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (gcount_padded % VEC_SIZE != 0)
+    error("Padded gcount size not a multiple of the vector length");
+#endif
+
+  /* Make the compiler understand we are in happy vectorization land */
+  swift_declare_aligned_ptr(float, a_x, c->a_x, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, a_y, c->a_y, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, a_z, c->a_z, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, pot, c->pot, SWIFT_CACHE_ALIGNMENT);
+  swift_assume_size(gcount_padded, VEC_SIZE);
+
+  /* Zero everything */
+  bzero(a_x, gcount_padded * sizeof(float));
+  bzero(a_y, gcount_padded * sizeof(float));
+  bzero(a_z, gcount_padded * sizeof(float));
+  bzero(pot, gcount_padded * sizeof(float));
+}
+
 /**
  * @brief Fills a #gravity_cache structure with some #gpart and shift them.
  *
@@ -141,6 +171,9 @@ static INLINE void gravity_cache_init(struct gravity_cache *c, int count) {
  * more expensive P2P.
  *
  * @param max_active_bin The largest active bin in the current time-step.
+ * @param allow_mpole Are we allowing the use of multipoles?
+ * @param periodic Are we using periodic BCs ?
+ * @param dim The size of the simulation volume along each dimension.
  * @param c The #gravity_cache to fill.
  * @param gparts The #gpart array to read from.
  * @param gcount The number of particles to read.
@@ -153,10 +186,12 @@ static INLINE void gravity_cache_init(struct gravity_cache *c, int count) {
  * @param grav_props The global gravity properties.
  */
 __attribute__((always_inline)) INLINE static void gravity_cache_populate(
-    timebin_t max_active_bin, struct gravity_cache *c,
-    const struct gpart *restrict gparts, int gcount, int gcount_padded,
-    const double shift[3], const float CoM[3], float r_max2,
-    const struct cell *cell, const struct gravity_props *grav_props) {
+    const timebin_t max_active_bin, const int allow_mpole, const int periodic,
+    const float dim[3], struct gravity_cache *c,
+    const struct gpart *restrict gparts, const int gcount,
+    const int gcount_padded, const double shift[3], const float CoM[3],
+    const float r_max2, const struct cell *cell,
+    const struct gravity_props *grav_props) {
 
   const float theta_crit2 = grav_props->theta_crit2;
 
@@ -180,12 +215,21 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate(
     m[i] = gparts[i].mass;
     active[i] = (int)(gparts[i].time_bin <= max_active_bin);
 
-    /* Check whether we can use the multipole instead of P-P */
-    const float dx = x[i] - CoM[0];
-    const float dy = y[i] - CoM[1];
-    const float dz = z[i] - CoM[2];
+    /* Distance to the CoM of the other cell. */
+    float dx = x[i] - CoM[0];
+    float dy = y[i] - CoM[1];
+    float dz = z[i] - CoM[2];
+
+    /* Apply periodic BC */
+    if (periodic) {
+      dx = nearestf(dx, dim[0]);
+      dy = nearestf(dy, dim[1]);
+      dz = nearestf(dz, dim[2]);
+    }
     const float r2 = dx * dx + dy * dy + dz * dz;
-    use_mpole[i] = gravity_M2P_accept(r_max2, theta_crit2, r2);
+
+    /* Check whether we can use the multipole instead of P-P */
+    use_mpole[i] = allow_mpole && gravity_M2P_accept(r_max2, theta_crit2, r2);
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -209,6 +253,9 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate(
     active[i] = 0;
     use_mpole[i] = 0;
   }
+
+  /* Zero the output as well */
+  gravity_cache_zero_output(c, gcount_padded);
 }
 
 /**
@@ -225,11 +272,11 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate(
  * @param grav_props The global gravity properties.
  */
 __attribute__((always_inline)) INLINE static void
-gravity_cache_populate_no_mpole(timebin_t max_active_bin,
+gravity_cache_populate_no_mpole(const timebin_t max_active_bin,
                                 struct gravity_cache *c,
-                                const struct gpart *restrict gparts, int gcount,
-                                int gcount_padded, const double shift[3],
-                                const struct cell *cell,
+                                const struct gpart *restrict gparts,
+                                const int gcount, const int gcount_padded,
+                                const double shift[3], const struct cell *cell,
                                 const struct gravity_props *grav_props) {
 
   /* Make the compiler understand we are in happy vectorization land */
@@ -271,17 +318,120 @@ gravity_cache_populate_no_mpole(timebin_t max_active_bin,
     m[i] = 0.f;
     active[i] = 0;
   }
+
+  /* Zero the output as well */
+  gravity_cache_zero_output(c, gcount_padded);
 }
 
 /**
- * @brief Write the output cache values back to the #gpart.
+ * @brief Fills a #gravity_cache structure with some #gpart and make them use
+ * the multi-pole.
+ *
+ * @param max_active_bin The largest active bin in the current time-step.
+ * @param periodic Are we using periodic BCs ?
+ * @param dim The size of the simulation volume along each dimension.
+ * @param c The #gravity_cache to fill.
+ * @param gparts The #gpart array to read from.
+ * @param gcount The number of particles to read.
+ * @param gcount_padded The number of particle to read padded to the next
+ * multiple of the vector length.
+ * @param cell The cell we play with (to get reasonable padding positions).
+ * @param CoM The position of the multipole.
+ * @param r_max2 The square of the multipole radius.
+ * @param grav_props The global gravity properties.
+ */
+__attribute__((always_inline)) INLINE static void
+gravity_cache_populate_all_mpole(const timebin_t max_active_bin,
+                                 const int periodic, const float dim[3],
+                                 struct gravity_cache *c,
+                                 const struct gpart *restrict gparts,
+                                 const int gcount, const int gcount_padded,
+                                 const struct cell *cell, const float CoM[3],
+                                 const float r_max2,
+                                 const struct gravity_props *grav_props) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  const float theta_crit2 = grav_props->theta_crit2;
+#endif
+
+  /* Make the compiler understand we are in happy vectorization land */
+  swift_declare_aligned_ptr(float, x, c->x, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, y, c->y, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, z, c->z, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, epsilon, c->epsilon, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, m, c->m, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(int, active, c->active, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(int, use_mpole, c->use_mpole,
+                            SWIFT_CACHE_ALIGNMENT);
+  swift_assume_size(gcount_padded, VEC_SIZE);
+
+  /* Fill the input caches */
+  for (int i = 0; i < gcount; ++i) {
+    x[i] = (float)(gparts[i].x[0]);
+    y[i] = (float)(gparts[i].x[1]);
+    z[i] = (float)(gparts[i].x[2]);
+    epsilon[i] = gravity_get_softening(&gparts[i], grav_props);
+    m[i] = gparts[i].mass;
+    active[i] = (int)(gparts[i].time_bin <= max_active_bin);
+    use_mpole[i] = 1;
+
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Distance to the CoM of the other cell. */
+    float dx = x[i] - CoM[0];
+    float dy = y[i] - CoM[1];
+    float dz = z[i] - CoM[2];
+
+    /* Apply periodic BC */
+    if (periodic) {
+      dx = nearestf(dx, dim[0]);
+      dy = nearestf(dy, dim[1]);
+      dz = nearestf(dz, dim[2]);
+    }
+    const float r2 = dx * dx + dy * dy + dz * dz;
+
+    if (!gravity_M2P_accept(r_max2, theta_crit2, r2))
+      error("Using m-pole where the test fails");
+#endif
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (gcount_padded < gcount) error("Padded counter smaller than counter");
+#endif
+
+  /* Particles used for padding should get impossible positions
+   * that have a reasonable magnitude. We use the cell width for this */
+  const float pos_padded[3] = {-2.f * (float)cell->width[0],
+                               -2.f * (float)cell->width[1],
+                               -2.f * (float)cell->width[2]};
+  const float eps_padded = epsilon[0];
+
+  /* Pad the caches */
+  for (int i = gcount; i < gcount_padded; ++i) {
+    x[i] = pos_padded[0];
+    y[i] = pos_padded[1];
+    z[i] = pos_padded[2];
+    epsilon[i] = eps_padded;
+    m[i] = 0.f;
+    active[i] = 0;
+    use_mpole[i] = 0;
+  }
+
+  /* Zero the output as well */
+  gravity_cache_zero_output(c, gcount_padded);
+}
+
+/**
+ * @brief Write the output cache values back to the active #gpart.
+ *
+ * This function obviously omits the padded values in the cache.
  *
  * @param c The #gravity_cache to read from.
  * @param gparts The #gpart array to write to.
  * @param gcount The number of particles to write.
  */
 __attribute__((always_inline)) INLINE void gravity_cache_write_back(
-    const struct gravity_cache *c, struct gpart *restrict gparts, int gcount) {
+    const struct gravity_cache *c, struct gpart *restrict gparts,
+    const int gcount) {
 
   /* Make the compiler understand we are in happy vectorization land */
   swift_declare_aligned_ptr(float, a_x, c->a_x, SWIFT_CACHE_ALIGNMENT);
diff --git a/src/gravity_derivatives.h b/src/gravity_derivatives.h
index d698c66e418a120e5e7ebbe1cba0a5a9af8cd1f1..756fb7af66d4cb695ba014452e424843b1c7c25b 100644
--- a/src/gravity_derivatives.h
+++ b/src/gravity_derivatives.h
@@ -33,6 +33,7 @@
 /* Local headers. */
 #include "inline.h"
 #include "kernel_gravity.h"
+#include "kernel_long_gravity.h"
 
 /**
  * @brief Structure containing all the derivatives of the potential field
@@ -40,7 +41,7 @@
  */
 struct potential_derivatives_M2L {
 
-  /* 0th order terms */
+  /* 0th order term */
   float D_000;
 
 #if SELF_GRAVITY_MULTIPOLE_ORDER > 0
@@ -95,7 +96,7 @@ struct potential_derivatives_M2L {
  */
 struct potential_derivatives_M2P {
 
-  /* 0th order terms */
+  /* 0th order term */
   float D_000;
 
   /* 1st order terms */
@@ -111,6 +112,17 @@ struct potential_derivatives_M2P {
   float D_120, D_021;
   float D_102, D_012;
   float D_111;
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+
+  /* 4th order terms */
+  float D_400, D_040, D_004;
+  float D_310, D_301;
+  float D_130, D_031;
+  float D_103, D_013;
+  float D_220, D_202, D_022;
+  float D_211, D_121, D_112;
+#endif
 };
 
 /**
@@ -124,11 +136,16 @@ struct potential_derivatives_M2P {
  * @param r_inv Inverse norm of distance vector
  * @param eps Softening length.
  * @param eps_inv Inverse of softening length.
+ * @param periodic Is the calculation periodic ?
+ * @param r_s_inv Inverse of the long-range gravity mesh smoothing length.
  * @param pot (return) The structure containing all the derivatives.
  */
 __attribute__((always_inline)) INLINE static void
-compute_potential_derivatives_M2L(float r_x, float r_y, float r_z, float r2,
-                                  float r_inv, float eps, float eps_inv,
+compute_potential_derivatives_M2L(const float r_x, const float r_y,
+                                  const float r_z, const float r2,
+                                  const float r_inv, const float eps,
+                                  const float eps_inv, const int periodic,
+                                  const float r_s_inv,
                                   struct potential_derivatives_M2L *pot) {
 
   float Dt_1;
@@ -148,8 +165,8 @@ compute_potential_derivatives_M2L(float r_x, float r_y, float r_z, float r2,
   float Dt_11;
 #endif
 
-  /* Un-softened case */
-  if (r2 > eps * eps) {
+  /* Un-softened un-truncated case (Newtonian potential) */
+  if (!periodic && r2 > eps * eps) {
 
     Dt_1 = r_inv;
 #if SELF_GRAVITY_MULTIPOLE_ORDER > 0
@@ -172,6 +189,52 @@ compute_potential_derivatives_M2L(float r_x, float r_y, float r_z, float r2,
 #error "Missing implementation for order >5"
 #endif
 
+    /* Un-softened truncated case */
+  } else if (periodic && r2 > eps * eps) {
+
+    /* Get the derivatives of the truncated potential */
+    const float r = r2 * r_inv;
+    struct chi_derivatives derivs;
+    kernel_long_grav_derivatives(r, r_s_inv, &derivs);
+
+    Dt_1 = derivs.chi_0 * r_inv;
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 0
+    const float r_inv2 = r_inv * r_inv;
+    const float r_inv3 = r_inv2 * r_inv;
+    Dt_3 = (r * derivs.chi_1 - derivs.chi_0) * r_inv3;
+#endif
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 1
+    const float r_inv5 = r_inv2 * r_inv3;
+    Dt_5 =
+        (r * r * derivs.chi_2 - 3.f * r * derivs.chi_1 + 3.f * derivs.chi_0) *
+        r_inv5;
+#endif
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 2
+    const float r_inv7 = r_inv2 * r_inv5;
+    Dt_7 = (r * r * r * derivs.chi_3 - 6.f * r * r * derivs.chi_2 +
+            15.f * r * derivs.chi_1 - 15.f * derivs.chi_0) *
+           r_inv7;
+#endif
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+    const float r_inv9 = r_inv2 * r_inv7;
+    Dt_9 = (r * r * r * r * derivs.chi_4 - 10.f * r * r * r * derivs.chi_3 +
+            45.f * r * r * derivs.chi_2 - 105.f * r * derivs.chi_1 +
+            105.f * derivs.chi_0) *
+           r_inv9;
+#endif
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 4
+    const float r_inv11 = r_inv2 * r_inv9;
+    Dt_11 = (r * r * r * r * r * derivs.chi_5 -
+             15.f * r * r * r * r * derivs.chi_4 +
+             105.f * r * r * r * derivs.chi_3 - 420.f * r * r * derivs.chi_2 +
+             945.f * r * derivs.chi_1 - 945.f * derivs.chi_0) *
+            r_inv11;
+#endif
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 5
+#error "Missing implementation for order >5"
+#endif
+
+    /* Softened case */
   } else {
     const float r = r2 * r_inv;
     const float u = r * eps_inv;
@@ -329,20 +392,28 @@ compute_potential_derivatives_M2L(float r_x, float r_y, float r_z, float r2,
  * @param r_inv Inverse norm of distance vector
  * @param eps Softening length.
  * @param eps_inv Inverse of softening length.
+ * @param periodic Is the calculation using periodic BCs?
+ * @param r_s_inv The inverse of the gravity mesh-smoothing scale.
  * @param pot (return) The structure containing all the derivatives.
  */
 __attribute__((always_inline)) INLINE static void
-compute_potential_derivatives_M2P(float r_x, float r_y, float r_z, float r2,
-                                  float r_inv, float eps, float eps_inv,
+compute_potential_derivatives_M2P(const float r_x, const float r_y,
+                                  const float r_z, const float r2,
+                                  const float r_inv, const float eps,
+                                  const float eps_inv, const int periodic,
+                                  const float r_s_inv,
                                   struct potential_derivatives_M2P *pot) {
 
   float Dt_1;
   float Dt_3;
   float Dt_5;
   float Dt_7;
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+  float Dt_9;
+#endif
 
-  /* Un-softened case */
-  if (r2 > eps * eps) {
+  /* Un-softened un-truncated case (Newtonian potential) */
+  if (!periodic && r2 > eps * eps) {
 
     const float r_inv2 = r_inv * r_inv;
 
@@ -350,21 +421,62 @@ compute_potential_derivatives_M2P(float r_x, float r_y, float r_z, float r2,
     Dt_3 = -1.f * Dt_1 * r_inv2; /* -1 / r^3 */
     Dt_5 = -3.f * Dt_3 * r_inv2; /* 3 / r^5 */
     Dt_7 = -5.f * Dt_5 * r_inv2; /* -15 / r^7 */
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+    Dt_9 = -7.f * Dt_7 * r_inv2; /* -105 / r^9 */
+#endif
+
+    /* Un-softened truncated case */
+  } else if (periodic && r2 > eps * eps) {
+
+    /* Get the derivatives of the truncated potential */
+    const float r = r2 * r_inv;
+    struct chi_derivatives d;
+    kernel_long_grav_derivatives(r, r_s_inv, &d);
+
+    const float r_inv2 = r_inv * r_inv;
+    Dt_1 = d.chi_0 * r_inv;
+
+    const float r_inv3 = r_inv2 * r_inv;
+    Dt_3 = (r * d.chi_1 - d.chi_0) * r_inv3;
+
+    const float r_inv5 = r_inv2 * r_inv3;
+    Dt_5 = (r * r * d.chi_2 - 3.f * r * d.chi_1 + 3.f * d.chi_0) * r_inv5;
+
+    const float r_inv7 = r_inv2 * r_inv5;
+    Dt_7 = (r * r * r * d.chi_3 - 6.f * r * r * d.chi_2 + 15.f * r * d.chi_1 -
+            15.f * d.chi_0) *
+           r_inv7;
 
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+    const float r_inv9 = r_inv2 * r_inv7;
+    Dt_9 = (r * r * r * r * d.chi_4 - 10.f * r * r * r * d.chi_3 +
+            45.f * r * r * d.chi_2 - 105.f * r * d.chi_1 + 105.f * d.chi_0) *
+           r_inv9;
+#endif
+
+    /* Softened case */
   } else {
 
     const float r = r2 * r_inv;
     const float u = r * eps_inv;
     const float u_inv = r_inv * eps;
     const float eps_inv2 = eps_inv * eps_inv;
-    const float eps_inv3 = eps_inv * eps_inv2;
-    const float eps_inv5 = eps_inv3 * eps_inv2;
-    const float eps_inv7 = eps_inv5 * eps_inv2;
 
     Dt_1 = eps_inv * D_soft_1(u, u_inv);
+
+    const float eps_inv3 = eps_inv * eps_inv2;
     Dt_3 = -eps_inv3 * D_soft_3(u, u_inv);
+
+    const float eps_inv5 = eps_inv3 * eps_inv2;
     Dt_5 = eps_inv5 * D_soft_5(u, u_inv);
+
+    const float eps_inv7 = eps_inv5 * eps_inv2;
     Dt_7 = -eps_inv7 * D_soft_7(u, u_inv);
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+    const float eps_inv9 = eps_inv7 * eps_inv2;
+    Dt_9 = eps_inv9 * D_soft_9(u, u_inv);
+#endif
   }
 
   /* Compute some powers of r_x, r_y and r_z */
@@ -374,6 +486,11 @@ compute_potential_derivatives_M2P(float r_x, float r_y, float r_z, float r2,
   const float r_x3 = r_x2 * r_x;
   const float r_y3 = r_y2 * r_y;
   const float r_z3 = r_z2 * r_z;
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+  const float r_x4 = r_x3 * r_x;
+  const float r_y4 = r_y3 * r_y;
+  const float r_z4 = r_z3 * r_z;
+#endif
 
   /* 0th order derivative */
   pot->D_000 = Dt_1;
@@ -402,6 +519,25 @@ compute_potential_derivatives_M2P(float r_x, float r_y, float r_z, float r2,
   pot->D_102 = r_z2 * r_x * Dt_7 + r_x * Dt_5;
   pot->D_012 = r_z2 * r_y * Dt_7 + r_y * Dt_5;
   pot->D_111 = r_x * r_y * r_z * Dt_7;
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 3
+  /* 4th order derivatives */
+  pot->D_400 = r_x4 * Dt_9 + 6.f * r_x2 * Dt_7 + 3.f * Dt_5;
+  pot->D_040 = r_y4 * Dt_9 + 6.f * r_y2 * Dt_7 + 3.f * Dt_5;
+  pot->D_004 = r_z4 * Dt_9 + 6.f * r_z2 * Dt_7 + 3.f * Dt_5;
+  pot->D_310 = r_x3 * r_y * Dt_9 + 3.f * r_x * r_y * Dt_7;
+  pot->D_301 = r_x3 * r_z * Dt_9 + 3.f * r_x * r_z * Dt_7;
+  pot->D_130 = r_y3 * r_x * Dt_9 + 3.f * r_y * r_x * Dt_7;
+  pot->D_031 = r_y3 * r_z * Dt_9 + 3.f * r_y * r_z * Dt_7;
+  pot->D_103 = r_z3 * r_x * Dt_9 + 3.f * r_z * r_x * Dt_7;
+  pot->D_013 = r_z3 * r_y * Dt_9 + 3.f * r_z * r_y * Dt_7;
+  pot->D_220 = r_x2 * r_y2 * Dt_9 + r_x2 * Dt_7 + r_y2 * Dt_7 + Dt_5;
+  pot->D_202 = r_x2 * r_z2 * Dt_9 + r_x2 * Dt_7 + r_z2 * Dt_7 + Dt_5;
+  pot->D_022 = r_y2 * r_z2 * Dt_9 + r_y2 * Dt_7 + r_z2 * Dt_7 + Dt_5;
+  pot->D_211 = r_x2 * r_y * r_z * Dt_9 + r_y * r_z * Dt_7;
+  pot->D_121 = r_y2 * r_x * r_z * Dt_9 + r_x * r_z * Dt_7;
+  pot->D_112 = r_z2 * r_x * r_y * Dt_9 + r_x * r_y * Dt_7;
+#endif
 }
 
 #endif /* SWIFT_GRAVITY_DERIVATIVE_H */
diff --git a/src/gravity_properties.c b/src/gravity_properties.c
index 79be11a8bab2389c19c58a59851379ea23d5fb3f..22856c25fbbaebd1dd74c5592ce8fd914e76b61c 100644
--- a/src/gravity_properties.c
+++ b/src/gravity_properties.c
@@ -31,22 +31,38 @@
 #include "error.h"
 #include "gravity.h"
 #include "kernel_gravity.h"
+#include "kernel_long_gravity.h"
 
 #define gravity_props_default_a_smooth 1.25f
 #define gravity_props_default_r_cut_max 4.5f
 #define gravity_props_default_r_cut_min 0.1f
+#define gravity_props_default_rebuild_frequency 0.01f
 
-void gravity_props_init(struct gravity_props *p,
-                        const struct swift_params *params,
-                        const struct cosmology *cosmo) {
+void gravity_props_init(struct gravity_props *p, struct swift_params *params,
+                        const struct cosmology *cosmo, int with_cosmology) {
+
+  /* Tree updates */
+  p->rebuild_frequency =
+      parser_get_opt_param_float(params, "Gravity:rebuild_frequency",
+                                 gravity_props_default_rebuild_frequency);
+
+  if (p->rebuild_frequency < 0.f || p->rebuild_frequency > 1.f)
+    error("Invalid tree rebuild frequency. Must be in [0., 1.]");
 
   /* Tree-PM parameters */
+  p->mesh_size = parser_get_param_int(params, "Gravity:mesh_side_length");
   p->a_smooth = parser_get_opt_param_float(params, "Gravity:a_smooth",
                                            gravity_props_default_a_smooth);
-  p->r_cut_max = parser_get_opt_param_float(params, "Gravity:r_cut_max",
-                                            gravity_props_default_r_cut_max);
-  p->r_cut_min = parser_get_opt_param_float(params, "Gravity:r_cut_min",
-                                            gravity_props_default_r_cut_min);
+  p->r_cut_max_ratio = parser_get_opt_param_float(
+      params, "Gravity:r_cut_max", gravity_props_default_r_cut_max);
+  p->r_cut_min_ratio = parser_get_opt_param_float(
+      params, "Gravity:r_cut_min", gravity_props_default_r_cut_min);
+
+  if (p->mesh_size % 2 != 0)
+    error("The mesh side-length must be an even number.");
+
+  if (p->a_smooth <= 0.)
+    error("The mesh smoothing scale 'a_smooth' must be > 0.");
 
   /* Time integration */
   p->eta = parser_get_param_float(params, "Gravity:eta");
@@ -58,10 +74,16 @@ void gravity_props_init(struct gravity_props *p,
   p->theta_crit_inv = 1. / p->theta_crit;
 
   /* Softening parameters */
-  p->epsilon_comoving =
-      parser_get_param_double(params, "Gravity:comoving_softening");
-  p->epsilon_max_physical =
-      parser_get_param_double(params, "Gravity:max_physical_softening");
+  if (with_cosmology) {
+    p->epsilon_comoving =
+        parser_get_param_double(params, "Gravity:comoving_softening");
+    p->epsilon_max_physical =
+        parser_get_param_double(params, "Gravity:max_physical_softening");
+  } else {
+    p->epsilon_max_physical =
+        parser_get_param_double(params, "Gravity:max_physical_softening");
+    p->epsilon_comoving = p->epsilon_max_physical;
+  }
 
   /* Set the softening to the current time */
   gravity_update(p, cosmo);
@@ -97,6 +119,9 @@ void gravity_props_print(const struct gravity_props *p) {
 
   message("Self-gravity opening angle:  theta=%.4f", p->theta_crit);
 
+  message("Self-gravity softening functional form: %s",
+          kernel_gravity_softening_name);
+
   message(
       "Self-gravity comoving softening:    epsilon=%.4f (Plummer equivalent: "
       "%.4f)",
@@ -109,10 +134,17 @@ void gravity_props_print(const struct gravity_props *p) {
       p->epsilon_max_physical * kernel_gravity_softening_plummer_equivalent,
       p->epsilon_max_physical);
 
+  message("Self-gravity mesh side-length: N=%d", p->mesh_size);
   message("Self-gravity mesh smoothing-scale: a_smooth=%f", p->a_smooth);
 
-  message("Self-gravity tree cut-off: r_cut_max=%f", p->r_cut_max);
-  message("Self-gravity truncation cut-off: r_cut_min=%f", p->r_cut_min);
+  message("Self-gravity tree cut-off ratio: r_cut_max=%f", p->r_cut_max_ratio);
+  message("Self-gravity truncation cut-off ratio: r_cut_min=%f",
+          p->r_cut_min_ratio);
+
+  message("Self-gravity mesh truncation function: %s",
+          kernel_long_gravity_truncation_name);
+
+  message("Self-gravity tree update frequency: f=%f", p->rebuild_frequency);
 }
 
 #if defined(HAVE_HDF5)
@@ -120,6 +152,8 @@ void gravity_props_print_snapshot(hid_t h_grpgrav,
                                   const struct gravity_props *p) {
 
   io_write_attribute_f(h_grpgrav, "Time integration eta", p->eta);
+  io_write_attribute_s(h_grpgrav, "Softening style",
+                       kernel_gravity_softening_name);
   io_write_attribute_f(
       h_grpgrav, "Comoving softening length",
       p->epsilon_comoving * kernel_gravity_softening_plummer_equivalent);
@@ -135,8 +169,12 @@ void gravity_props_print_snapshot(hid_t h_grpgrav,
   io_write_attribute_f(h_grpgrav, "Opening angle", p->theta_crit);
   io_write_attribute_d(h_grpgrav, "MM order", SELF_GRAVITY_MULTIPOLE_ORDER);
   io_write_attribute_f(h_grpgrav, "Mesh a_smooth", p->a_smooth);
-  io_write_attribute_f(h_grpgrav, "Mesh r_cut_max", p->r_cut_max);
-  io_write_attribute_f(h_grpgrav, "Mesh r_cut_min", p->r_cut_min);
+  io_write_attribute_f(h_grpgrav, "Mesh r_cut_max ratio", p->r_cut_max_ratio);
+  io_write_attribute_f(h_grpgrav, "Mesh r_cut_min ratio", p->r_cut_min_ratio);
+  io_write_attribute_f(h_grpgrav, "Tree update frequency",
+                       p->rebuild_frequency);
+  io_write_attribute_s(h_grpgrav, "Mesh truncation function",
+                       kernel_long_gravity_truncation_name);
 }
 #endif
 
@@ -158,7 +196,7 @@ void gravity_props_struct_dump(const struct gravity_props *p, FILE *stream) {
  * @param p the struct
  * @param stream the file stream
  */
-void gravity_props_struct_restore(const struct gravity_props *p, FILE *stream) {
+void gravity_props_struct_restore(struct gravity_props *p, FILE *stream) {
   restart_read_blocks((void *)p, sizeof(struct gravity_props), 1, stream, NULL,
                       "gravity props");
 }
diff --git a/src/gravity_properties.h b/src/gravity_properties.h
index f36a39a6e187b8b397a5f597692f5a37c982aa7a..62dbab3605fb2dcfc4ae65e54c0b5f913d714c16 100644
--- a/src/gravity_properties.h
+++ b/src/gravity_properties.h
@@ -27,25 +27,33 @@
 #endif
 
 /* Local includes. */
-#include "cosmology.h"
-#include "parser.h"
 #include "restart.h"
 
+/* Forward declarations */
+struct cosmology;
+struct swift_params;
+
 /**
  * @brief Contains all the constants and parameters of the self-gravity scheme
  */
 struct gravity_props {
 
+  /*! Frequency of tree-rebuild in units of #gpart updates. */
+  float rebuild_frequency;
+
+  /*! Periodic long-range mesh side-length */
+  int mesh_size;
+
   /*! Mesh smoothing scale in units of top-level cell size */
   float a_smooth;
 
   /*! Distance below which the truncated mesh force is Newtonian in units of
    * a_smooth */
-  float r_cut_min;
+  float r_cut_min_ratio;
 
   /*! Distance above which the truncated mesh force is negligible in units of
    * a_smooth */
-  float r_cut_max;
+  float r_cut_max_ratio;
 
   /*! Time integration dimensionless multiplier */
   float eta;
@@ -79,9 +87,8 @@ struct gravity_props {
 };
 
 void gravity_props_print(const struct gravity_props *p);
-void gravity_props_init(struct gravity_props *p,
-                        const struct swift_params *params,
-                        const struct cosmology *cosmo);
+void gravity_props_init(struct gravity_props *p, struct swift_params *params,
+                        const struct cosmology *cosmo, int with_cosmology);
 void gravity_update(struct gravity_props *p, const struct cosmology *cosmo);
 
 #if defined(HAVE_HDF5)
@@ -91,6 +98,6 @@ void gravity_props_print_snapshot(hid_t h_grpsph,
 
 /* Dump/restore. */
 void gravity_props_struct_dump(const struct gravity_props *p, FILE *stream);
-void gravity_props_struct_restore(const struct gravity_props *p, FILE *stream);
+void gravity_props_struct_restore(struct gravity_props *p, FILE *stream);
 
 #endif /* SWIFT_GRAVITY_PROPERTIES */
diff --git a/src/hydro.h b/src/hydro.h
index 78ae7d178ff93b01fa2eeebe09f34b718ce9a826..950f63526a1590fa0fdcf2bfb5e650a2dfe14431 100644
--- a/src/hydro.h
+++ b/src/hydro.h
@@ -41,19 +41,31 @@
 #include "./hydro/PressureEntropy/hydro.h"
 #include "./hydro/PressureEntropy/hydro_iact.h"
 #define SPH_IMPLEMENTATION "Pressure-Entropy SPH (Hopkins 2013)"
+#elif defined(HOPKINS_PU_SPH)
+#include "./hydro/PressureEnergy/hydro.h"
+#include "./hydro/PressureEnergy/hydro_iact.h"
+#define SPH_IMPLEMENTATION "Pressure-Energy SPH (Hopkins 2013)"
 #elif defined(DEFAULT_SPH)
 #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)"
+#elif defined(GIZMO_MFV_SPH)
+#include "./hydro/GizmoMFV/hydro.h"
+#include "./hydro/GizmoMFV/hydro_iact.h"
+#define SPH_IMPLEMENTATION "GIZMO MFV (Hopkins 2015)"
+#elif defined(GIZMO_MFM_SPH)
+#include "./hydro/GizmoMFM/hydro.h"
+#include "./hydro/GizmoMFM/hydro_iact.h"
+#define SPH_IMPLEMENTATION "GIZMO MFM (Hopkins 2015)"
 #elif defined(SHADOWFAX_SPH)
 #include "./hydro/Shadowswift/hydro.h"
 #include "./hydro/Shadowswift/hydro_iact.h"
 #define SPH_IMPLEMENTATION \
   "Shadowfax moving mesh (Vandenbroucke and De Rijcke 2016)"
+#elif defined(MINIMAL_MULTI_MAT_SPH)
+#include "./hydro/MinimalMultiMat/hydro.h"
+#include "./hydro/MinimalMultiMat/hydro_iact.h"
+#define SPH_IMPLEMENTATION "Minimal version of SPH with multiple materials"
 #else
 #error "Invalid choice of SPH variant"
 #endif
diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h
index b1a999b63143437cab8518cfdd96885533d7401e..2c3a9c46f0500fb20aa3cfa2e5feb682b3dcec63 100644
--- a/src/hydro/Default/hydro.h
+++ b/src/hydro/Default/hydro.h
@@ -339,7 +339,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
 
   /* Re-set problematic values */
   p->rho = p->mass * kernel_root * h_inv_dim;
-  p->density.wcount = kernel_root * kernel_norm * h_inv_dim;
+  p->density.wcount = kernel_root * h_inv_dim;
   p->rho_dh = 0.f;
   p->density.wcount_dh = 0.f;
   p->density.div_v = 0.f;
@@ -423,7 +423,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
   /* Reset the time derivatives. */
   p->force.u_dt = 0.0f;
   p->force.h_dt = 0.0f;
-  p->force.v_sig = 0.0f;
+  p->force.v_sig = p->force.soundspeed;
 }
 
 /**
diff --git a/src/hydro/Default/hydro_io.h b/src/hydro/Default/hydro_io.h
index 542cc21d41741a203adacb4560c6bd701e3af758..d47c96fbf32e1ee00346888aaf2e8afabc22abc3 100644
--- a/src/hydro/Default/hydro_io.h
+++ b/src/hydro/Default/hydro_io.h
@@ -31,8 +31,9 @@
  * @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) {
+INLINE static void hydro_read_particles(struct part* parts,
+                                        struct io_props* list,
+                                        int* num_fields) {
 
   *num_fields = 8;
 
@@ -55,8 +56,9 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
                                 UNIT_CONV_DENSITY, parts, rho);
 }
 
-void convert_part_pos(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, double* ret) {
+INLINE static void convert_part_pos(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -69,8 +71,9 @@ void convert_part_pos(const struct engine* e, const struct part* p,
   }
 }
 
-void convert_part_vel(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, float* ret) {
+INLINE static void convert_part_vel(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, float* ret) {
 
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const struct cosmology* cosmo = e->cosmology;
@@ -98,13 +101,14 @@ void convert_part_vel(const struct engine* e, const struct part* p,
   hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret);
 
   /* Conversion from internal units to peculiar velocities */
-  ret[0] *= cosmo->a2_inv;
-  ret[1] *= cosmo->a2_inv;
-  ret[2] *= cosmo->a2_inv;
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
 }
 
-void convert_part_potential(const struct engine* e, const struct part* p,
-                            const struct xpart* xp, float* ret) {
+INLINE static void convert_part_potential(const struct engine* e,
+                                          const struct part* p,
+                                          const struct xpart* xp, float* ret) {
 
   if (p->gpart != NULL)
     ret[0] = gravity_get_comoving_potential(p->gpart);
@@ -119,8 +123,10 @@ void convert_part_potential(const struct engine* e, const struct part* p,
  * @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(const struct part* parts, const struct xpart* xparts,
-                           struct io_props* list, int* num_fields) {
+INLINE static void hydro_write_particles(const struct part* parts,
+                                         const struct xpart* xparts,
+                                         struct io_props* list,
+                                         int* num_fields) {
 
   *num_fields = 8;
 
@@ -149,7 +155,7 @@ void hydro_write_particles(const struct part* parts, const struct xpart* xparts,
  * @brief Writes the current model of SPH to the file
  * @param h_grpsph The HDF5 group in which to write
  */
-void hydro_write_flavour(hid_t h_grpsph) {
+INLINE static void hydro_write_flavour(hid_t h_grpsph) {
 
   /* Viscosity and thermal conduction */
   io_write_attribute_s(h_grpsph, "Thermal Conductivity Model",
@@ -178,6 +184,6 @@ void hydro_write_flavour(hid_t h_grpsph) {
  *
  * @return 1 if entropy is in 'internal energy', 0 otherwise.
  */
-int writeEntropyFlag() { return 0; }
+INLINE static int writeEntropyFlag(void) { return 0; }
 
 #endif /* SWIFT_DEFAULT_HYDRO_IO_H */
diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h
index bc06a24e2a8245556a1042f2459273b8d750489e..26e3bf97dd1924abbe7380d1eaadce75213344df 100644
--- a/src/hydro/Gadget2/hydro.h
+++ b/src/hydro/Gadget2/hydro.h
@@ -349,7 +349,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
 
   /* Re-set problematic values */
   p->rho = p->mass * kernel_root * h_inv_dim;
-  p->density.wcount = kernel_root * kernel_norm * h_inv_dim;
+  p->density.wcount = kernel_root * h_inv_dim;
   p->density.rho_dh = 0.f;
   p->density.wcount_dh = 0.f;
   p->density.div_v = 0.f;
diff --git a/src/hydro/Gadget2/hydro_io.h b/src/hydro/Gadget2/hydro_io.h
index 28c0eea4772f51fab35a08d43c0564472694eeeb..3f2af41dc7f0cc8f60992a15a0f09f3c90f764fe 100644
--- a/src/hydro/Gadget2/hydro_io.h
+++ b/src/hydro/Gadget2/hydro_io.h
@@ -31,8 +31,9 @@
  * @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) {
+INLINE static void hydro_read_particles(struct part* parts,
+                                        struct io_props* list,
+                                        int* num_fields) {
 
   *num_fields = 8;
 
@@ -55,20 +56,21 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
                                 UNIT_CONV_DENSITY, parts, rho);
 }
 
-void convert_part_u(const struct engine* e, const struct part* p,
-                    const struct xpart* xp, float* ret) {
+INLINE static void convert_part_u(const struct engine* e, const struct part* p,
+                                  const struct xpart* xp, float* ret) {
 
   ret[0] = hydro_get_comoving_internal_energy(p);
 }
 
-void convert_part_P(const struct engine* e, const struct part* p,
-                    const struct xpart* xp, float* ret) {
+INLINE static void convert_part_P(const struct engine* e, const struct part* p,
+                                  const struct xpart* xp, float* ret) {
 
   ret[0] = hydro_get_comoving_pressure(p);
 }
 
-void convert_part_pos(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, double* ret) {
+INLINE static void convert_part_pos(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -81,8 +83,9 @@ void convert_part_pos(const struct engine* e, const struct part* p,
   }
 }
 
-void convert_part_vel(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, float* ret) {
+INLINE static void convert_part_vel(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, float* ret) {
 
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const struct cosmology* cosmo = e->cosmology;
@@ -110,13 +113,14 @@ void convert_part_vel(const struct engine* e, const struct part* p,
   hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret);
 
   /* Conversion from internal units to peculiar velocities */
-  ret[0] *= cosmo->a2_inv;
-  ret[1] *= cosmo->a2_inv;
-  ret[2] *= cosmo->a2_inv;
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
 }
 
-void convert_part_potential(const struct engine* e, const struct part* p,
-                            const struct xpart* xp, float* ret) {
+INLINE static void convert_part_potential(const struct engine* e,
+                                          const struct part* p,
+                                          const struct xpart* xp, float* ret) {
 
   if (p->gpart != NULL)
     ret[0] = gravity_get_comoving_potential(p->gpart);
@@ -131,8 +135,10 @@ void convert_part_potential(const struct engine* e, const struct part* p,
  * @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(const struct part* parts, const struct xpart* xparts,
-                           struct io_props* list, int* num_fields) {
+INLINE static void hydro_write_particles(const struct part* parts,
+                                         const struct xpart* xparts,
+                                         struct io_props* list,
+                                         int* num_fields) {
 
   *num_fields = 10;
 
@@ -185,7 +191,7 @@ void hydro_write_particles(const struct part* parts, const struct xpart* xparts,
  * @brief Writes the current model of SPH to the file
  * @param h_grpsph The HDF5 group in which to write
  */
-void hydro_write_flavour(hid_t h_grpsph) {
+INLINE static void hydro_write_flavour(hid_t h_grpsph) {
 
   /* Viscosity and thermal conduction */
   io_write_attribute_s(h_grpsph, "Thermal Conductivity Model",
@@ -202,6 +208,6 @@ void hydro_write_flavour(hid_t h_grpsph) {
  *
  * @return 1 if entropy is in 'internal energy', 0 otherwise.
  */
-int writeEntropyFlag() { return 0; }
+INLINE static int writeEntropyFlag(void) { return 0; }
 
 #endif /* SWIFT_GADGET2_HYDRO_IO_H */
diff --git a/src/hydro/GizmoMFM/hydro.h b/src/hydro/GizmoMFM/hydro.h
new file mode 100644
index 0000000000000000000000000000000000000000..1ab142740b641bdc9a0dff5a02b19479bae8257e
--- /dev/null
+++ b/src/hydro/GizmoMFM/hydro.h
@@ -0,0 +1,1003 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2016, 2017 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_GIZMO_MFM_HYDRO_H
+#define SWIFT_GIZMO_MFM_HYDRO_H
+
+#include "adiabatic_index.h"
+#include "approx_math.h"
+#include "cosmology.h"
+#include "equation_of_state.h"
+#include "hydro_gradients.h"
+#include "hydro_properties.h"
+#include "hydro_space.h"
+#include "hydro_unphysical.h"
+#include "hydro_velocities.h"
+#include "minmax.h"
+#include "riemann.h"
+
+#include <float.h>
+
+//#define GIZMO_LLOYD_ITERATION
+
+/**
+ * @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 hydro_properties Pointer to the hydro parameters.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_compute_timestep(
+    const struct part* restrict p, const struct xpart* restrict xp,
+    const struct hydro_props* restrict hydro_properties,
+    const struct cosmology* restrict cosmo) {
+
+  const float CFL_condition = hydro_properties->CFL_condition;
+
+#ifdef GIZMO_LLOYD_ITERATION
+  return CFL_condition;
+#endif
+
+  /* v_full is the actual velocity of the particle, primitives.v is its
+     hydrodynamical velocity. The time step depends on the relative difference
+     of the two. */
+  float vrel[3];
+  vrel[0] = p->primitives.v[0] - xp->v_full[0];
+  vrel[1] = p->primitives.v[1] - xp->v_full[1];
+  vrel[2] = p->primitives.v[2] - xp->v_full[2];
+  float vmax =
+      sqrtf(vrel[0] * vrel[0] + vrel[1] * vrel[1] + vrel[2] * vrel[2]) +
+      sqrtf(hydro_gamma * p->primitives.P / p->primitives.rho);
+  vmax = max(vmax, p->timestepvars.vmax);
+
+  // MATTHIEU: Bert is this correct? Do we need more cosmology terms here?
+  const float psize =
+      cosmo->a * powf(p->geometry.volume / hydro_dimension_unit_sphere,
+                      hydro_dimension_inv);
+  float dt = FLT_MAX;
+  if (vmax > 0.) {
+    dt = psize / vmax;
+  }
+  return CFL_condition * dt;
+}
+
+/**
+ * @brief Does some extra hydro operations once the actual physical time step
+ * for the particle is known.
+ *
+ * This method is no longer used, as Gizmo is now unaware of the actual particle
+ * time step.
+ *
+ * @param p The particle to act upon.
+ * @param dt Physical time step of the particle during the next step.
+ */
+__attribute__((always_inline)) INLINE static void hydro_timestep_extra(
+    struct part* p, float dt) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (dt == 0.) {
+    error("Zero time step assigned to particle!");
+  }
+
+  if (dt != dt) {
+    error("NaN time step assigned to particle!");
+  }
+#endif
+}
+
+/**
+ * @brief Initialises the particles for the first time
+ *
+ * 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) {
+
+  const float mass = p->conserved.mass;
+
+  p->primitives.v[0] = p->v[0];
+  p->primitives.v[1] = p->v[1];
+  p->primitives.v[2] = p->v[2];
+
+  /* we can already initialize the momentum */
+  p->conserved.momentum[0] = mass * p->primitives.v[0];
+  p->conserved.momentum[1] = mass * p->primitives.v[1];
+  p->conserved.momentum[2] = mass * p->primitives.v[2];
+
+/* and the thermal energy */
+/* remember that we store the total thermal energy, not the specific thermal
+   energy (as in Gadget) */
+#if defined(EOS_ISOTHERMAL_GAS)
+  /* this overwrites the internal energy from the initial condition file
+   * Note that we call the EoS function just to get the constant u here. */
+  p->conserved.energy = mass * gas_internal_energy_from_entropy(0.f, 0.f);
+#else
+  p->conserved.energy *= mass;
+#endif
+
+#ifdef GIZMO_TOTAL_ENERGY
+  /* add the total kinetic energy */
+  p->conserved.energy += 0.5f * (p->conserved.momentum[0] * p->primitives.v[0] +
+                                 p->conserved.momentum[1] * p->primitives.v[1] +
+                                 p->conserved.momentum[2] * p->primitives.v[2]);
+#endif
+
+#ifdef GIZMO_LLOYD_ITERATION
+  /* overwrite all variables to make sure they have safe values */
+  p->primitives.rho = 1.;
+  p->primitives.v[0] = 0.;
+  p->primitives.v[1] = 0.;
+  p->primitives.v[2] = 0.;
+  p->primitives.P = 1.;
+
+  p->conserved.mass = 1.;
+  p->conserved.momentum[0] = 0.;
+  p->conserved.momentum[1] = 0.;
+  p->conserved.momentum[2] = 0.;
+  p->conserved.energy = 1.;
+
+  p->v[0] = 0.;
+  p->v[1] = 0.;
+  p->v[2] = 0.;
+#endif
+
+  /* initialize the particle velocity based on the primitive fluid velocity */
+  hydro_velocities_init(p, xp);
+
+  /* ignore accelerations present in the initial condition */
+  p->a_hydro[0] = 0.0f;
+  p->a_hydro[1] = 0.0f;
+  p->a_hydro[2] = 0.0f;
+
+  /* we cannot initialize wcorr in init_part, as init_part gets called every
+     time the density loop is repeated, and the whole point of storing wcorr
+     is to have a way of remembering that we need more neighbours for this
+     particle */
+  p->density.wcorr = 1.0f;
+}
+
+/**
+ * @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
+ * @param hs #hydro_space containing hydro specific space information.
+ */
+__attribute__((always_inline)) INLINE static void hydro_init_part(
+    struct part* p, const struct hydro_space* hs) {
+
+  p->density.wcount = 0.0f;
+  p->density.wcount_dh = 0.0f;
+  p->geometry.volume = 0.0f;
+  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;
+  p->geometry.centroid[0] = 0.0f;
+  p->geometry.centroid[1] = 0.0f;
+  p->geometry.centroid[2] = 0.0f;
+}
+
+/**
+ * @brief Finishes the volume calculation.
+ *
+ * Multiplies the density and number of neighbours by the appropiate constants
+ * 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.
+ *
+ * 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.
+ *
+ * @param p The particle to act upon.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_end_density(
+    struct part* restrict p, const struct cosmology* cosmo) {
+
+  /* Some smoothing length multiples. */
+  const float h = p->h;
+  const float ih = 1.0f / h;
+  const float ihdim = pow_dimension(ih);
+  const float ihdim_plus_one = ihdim * ih;
+
+  /* Final operation on the density. */
+  p->density.wcount += kernel_root;
+  p->density.wcount *= ihdim;
+
+  p->density.wcount_dh -= hydro_dimension * kernel_root;
+  p->density.wcount_dh *= ihdim_plus_one;
+
+  /* Final operation on the geometry. */
+  /* we multiply with the smoothing kernel normalization ih3 and calculate the
+   * volume */
+  const float volume = 1.f / (ihdim * (p->geometry.volume + kernel_root));
+  p->geometry.volume = volume;
+
+  /* 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];
+
+  p->geometry.centroid[0] *= kernel_norm;
+  p->geometry.centroid[1] *= kernel_norm;
+  p->geometry.centroid[2] *= kernel_norm;
+
+  p->geometry.centroid[0] /= p->density.wcount;
+  p->geometry.centroid[1] /= p->density.wcount;
+  p->geometry.centroid[2] /= p->density.wcount;
+
+  /* Check the condition number to see if we have a stable geometry. */
+  float condition_number_E = 0.0f;
+  int i, j;
+  for (i = 0; i < 3; ++i) {
+    for (j = 0; j < 3; ++j) {
+      condition_number_E +=
+          p->geometry.matrix_E[i][j] * p->geometry.matrix_E[i][j];
+    }
+  }
+
+  invert_dimension_by_dimension_matrix(p->geometry.matrix_E);
+
+  float condition_number_Einv = 0.0f;
+  for (i = 0; i < 3; ++i) {
+    for (j = 0; j < 3; ++j) {
+      condition_number_Einv +=
+          p->geometry.matrix_E[i][j] * p->geometry.matrix_E[i][j];
+    }
+  }
+
+  float condition_number =
+      hydro_dimension_inv * sqrtf(condition_number_E * condition_number_Einv);
+
+  if (condition_number > const_gizmo_max_condition_number &&
+      p->density.wcorr > const_gizmo_min_wcorr) {
+#ifdef GIZMO_PATHOLOGICAL_ERROR
+    error("Condition number larger than %g (%g)!",
+          const_gizmo_max_condition_number, condition_number);
+#endif
+#ifdef GIZMO_PATHOLOGICAL_WARNING
+    message("Condition number too large: %g (> %g, p->id: %llu)!",
+            condition_number, const_gizmo_max_condition_number, p->id);
+#endif
+    /* add a correction to the number of neighbours for this particle */
+    p->density.wcorr *= const_gizmo_w_correction_factor;
+  }
+
+  hydro_gradients_init(p);
+
+  /* compute primitive variables */
+  /* eqns (3)-(5) */
+  const float m = p->conserved.mass;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (m < 0.) {
+    error("Mass is negative!");
+  }
+
+  if (volume == 0.) {
+    error("Volume is 0!");
+  }
+#endif
+
+  // MATTHIEU: Bert is this correct? Do we need cosmology terms here?
+  float momentum[3];
+  momentum[0] = p->conserved.momentum[0];
+  momentum[1] = p->conserved.momentum[1];
+  momentum[2] = p->conserved.momentum[2];
+  p->primitives.rho = m / volume;
+  if (m == 0.) {
+    p->primitives.v[0] = 0.;
+    p->primitives.v[1] = 0.;
+    p->primitives.v[2] = 0.;
+  } else {
+    p->primitives.v[0] = momentum[0] / m;
+    p->primitives.v[1] = momentum[1] / m;
+    p->primitives.v[2] = momentum[2] / m;
+  }
+
+#ifdef EOS_ISOTHERMAL_GAS
+  /* although the pressure is not formally used anywhere if an isothermal eos
+     has been selected, we still make sure it is set to the correct value */
+  p->primitives.P = gas_pressure_from_internal_energy(p->primitives.rho, 0.);
+#else
+
+  float energy = p->conserved.energy;
+
+#ifdef GIZMO_TOTAL_ENERGY
+  /* subtract the kinetic energy; we want the thermal energy */
+  energy -= 0.5f * (momentum[0] * p->primitives.v[0] +
+                    momentum[1] * p->primitives.v[1] +
+                    momentum[2] * p->primitives.v[2]);
+#endif
+
+  /* energy contains the total thermal energy, we want the specific energy.
+     this is why we divide by the volume, and not by the density */
+  p->primitives.P = hydro_gamma_minus_one * energy / volume;
+#endif
+
+  /* sanity checks */
+  gizmo_check_physical_quantities("density", "pressure", p->primitives.rho,
+                                  p->primitives.v[0], p->primitives.v[1],
+                                  p->primitives.v[2], p->primitives.P);
+
+#ifdef GIZMO_LLOYD_ITERATION
+  /* overwrite primitive variables to make sure they still have safe values */
+  p->primitives.rho = 1.;
+  p->primitives.v[0] = 0.;
+  p->primitives.v[1] = 0.;
+  p->primitives.v[2] = 0.;
+  p->primitives.P = 1.;
+#endif
+
+  /* Add a correction factor to wcount (to force a neighbour number increase if
+     the geometry matrix is close to singular) */
+  p->density.wcount *= p->density.wcorr;
+  p->density.wcount_dh *= p->density.wcorr;
+}
+
+/**
+ * @brief Sets all particle fields to sensible values when the #part has 0 ngbs.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct cosmology* cosmo) {
+
+  /* Some smoothing length multiples. */
+  const float h = p->h;
+  const float h_inv = 1.0f / h;                 /* 1/h */
+  const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */
+
+  /* Re-set problematic values */
+  p->density.wcount = kernel_root * h_inv_dim;
+  p->density.wcount_dh = 0.f;
+  p->geometry.volume = 1.0f;
+  p->geometry.matrix_E[0][0] = 1.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] = 1.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] = 1.0f;
+  /* centroid is relative w.r.t. particle position */
+  /* by setting the centroid to 0.0f, we make sure no velocity correction is
+     applied */
+  p->geometry.centroid[0] = 0.0f;
+  p->geometry.centroid[1] = 0.0f;
+  p->geometry.centroid[2] = 0.0f;
+}
+
+/**
+ * @brief Prepare a particle for the gradient calculation.
+ *
+ * This function is 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 cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_prepare_gradient(
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct cosmology* cosmo) {
+
+  /* Initialize time step criterion variables */
+  p->timestepvars.vmax = 0.;
+
+  // MATTHIEU: Bert is this correct? Do we need cosmology terms here?
+
+  /* Set the actual velocity of the particle */
+  hydro_velocities_prepare_force(p, xp);
+}
+
+/**
+ * @brief Resets the variables that are required for a gradient calculation.
+ *
+ * This function is called after hydro_prepare_gradient.
+ *
+ * @param p The particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_reset_gradient(
+    struct part* restrict p) {}
+
+/**
+ * @brief Finishes the gradient calculation.
+ *
+ * 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_end_gradient(
+    struct part* p) {
+
+  hydro_gradients_finalize(p);
+
+#ifdef GIZMO_LLOYD_ITERATION
+  /* reset the gradients to zero, as we don't want them */
+  hydro_gradients_init(p);
+#endif
+}
+
+/**
+ * @brief Prepare a particle for the force calculation.
+ *
+ * This function is called in the extra_ghost task to convert some quantities
+ * coming from the gradient loop over neighbours into quantities ready to be
+ * used in the force loop over neighbours.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_prepare_force(
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct cosmology* cosmo) {
+
+  /* Initialise values that are used in the force loop */
+  p->gravity.mflux[0] = 0.0f;
+  p->gravity.mflux[1] = 0.0f;
+  p->gravity.mflux[2] = 0.0f;
+
+  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 Reset acceleration fields of a particle
+ *
+ * This is actually not necessary for GIZMO, since we just set the accelerations
+ * after the flux calculation.
+ *
+ * @param p The particle to act upon.
+ */
+__attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
+    struct part* p) {
+
+  /* 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 Sets the values to be predicted in the drifts to their values at a
+ * kick time
+ *
+ * @param p The particle.
+ * @param xp The extended data of this particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
+    struct part* restrict p, const struct xpart* restrict xp) {}
+
+/**
+ * @brief Converts the hydrodynamic variables from the initial condition file to
+ * conserved variables that can be used during the integration
+ *
+ * We no longer do this, as the mass needs to be provided in the initial
+ * condition file, and the mass alone is enough to initialize all conserved
+ * variables. This is now done in hydro_first_init_part.
+ *
+ * @param p The particle to act upon.
+ */
+__attribute__((always_inline)) INLINE static void hydro_convert_quantities(
+    struct part* p, struct xpart* xp, const struct cosmology* cosmo) {}
+
+/**
+ * @brief Extra operations to be done during the drift
+ *
+ * @param p Particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param dt_drift The drift time-step for positions.
+ * @param dt_therm The drift time-step for thermal quantities.
+ */
+__attribute__((always_inline)) INLINE static void hydro_predict_extra(
+    struct part* p, struct xpart* xp, float dt_drift, float dt_therm) {
+
+#ifdef GIZMO_LLOYD_ITERATION
+  return;
+#endif
+
+  const float h_inv = 1.0f / p->h;
+
+  /* Predict smoothing length */
+  const float w1 = p->force.h_dt * h_inv * dt_drift;
+  float h_corr;
+  if (fabsf(w1) < 0.2f)
+    h_corr = approx_expf(w1); /* 4th order expansion of exp(w) */
+  else
+    h_corr = expf(w1);
+
+  /* Limit the smoothing length correction (and make sure it is always
+     positive). */
+  if (h_corr < 2.0f && h_corr > 0.) {
+    p->h *= h_corr;
+  }
+
+  /* drift the primitive variables based on the old fluxes */
+  if (p->geometry.volume > 0.) {
+    p->primitives.rho += p->conserved.flux.mass * dt_drift / p->geometry.volume;
+  }
+
+  if (p->conserved.mass > 0.) {
+    p->primitives.v[0] +=
+        p->conserved.flux.momentum[0] * dt_drift / p->conserved.mass;
+    p->primitives.v[1] +=
+        p->conserved.flux.momentum[1] * dt_drift / p->conserved.mass;
+    p->primitives.v[2] +=
+        p->conserved.flux.momentum[2] * dt_drift / p->conserved.mass;
+
+#if !defined(EOS_ISOTHERMAL_GAS)
+    const float u = p->conserved.energy + p->conserved.flux.energy * dt_therm;
+    p->primitives.P =
+        hydro_gamma_minus_one * u * p->primitives.rho / p->conserved.mass;
+#endif
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (p->h <= 0.) {
+    error("Zero or negative smoothing length (%g)!", p->h);
+  }
+#endif
+
+  gizmo_check_physical_quantities("density", "pressure", p->primitives.rho,
+                                  p->primitives.v[0], p->primitives.v[1],
+                                  p->primitives.v[2], p->primitives.P);
+}
+
+/**
+ * @brief Set the particle acceleration after the flux loop
+ *
+ * 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.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_end_force(
+    struct part* p, const struct cosmology* cosmo) {
+
+  /* set the variables that are used to drift the primitive variables */
+
+  // MATTHIEU: Bert is this correct? Do we need cosmology terms here?
+  hydro_velocities_end_force(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, const struct cosmology* cosmo,
+    const struct hydro_props* hydro_props) {
+
+  float a_grav[3];
+
+  /* Update conserved variables. */
+  p->conserved.mass += p->conserved.flux.mass * dt;
+  p->conserved.momentum[0] += p->conserved.flux.momentum[0] * dt;
+  p->conserved.momentum[1] += p->conserved.flux.momentum[1] * dt;
+  p->conserved.momentum[2] += p->conserved.flux.momentum[2] * dt;
+#if defined(EOS_ISOTHERMAL_GAS)
+  /* We use the EoS equation in a sneaky way here just to get the constant u */
+  p->conserved.energy =
+      p->conserved.mass * gas_internal_energy_from_entropy(0.f, 0.f);
+#else
+  p->conserved.energy += p->conserved.flux.energy * dt;
+#endif
+
+  /* Apply the minimal energy limit */
+  const float min_energy =
+      hydro_props->minimal_internal_energy * cosmo->a_factor_internal_energy;
+  if (p->conserved.energy < min_energy * p->conserved.mass) {
+    p->conserved.energy = min_energy * p->conserved.mass;
+    p->conserved.flux.energy = 0.f;
+  }
+
+  gizmo_check_physical_quantities(
+      "mass", "energy", p->conserved.mass, p->conserved.momentum[0],
+      p->conserved.momentum[1], p->conserved.momentum[2], p->conserved.energy);
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Note that this check will only have effect if no GIZMO_UNPHYSICAL option
+     was selected. */
+  if (p->conserved.mass < 0.) {
+    error(
+        "Negative mass after conserved variables update (mass: %g, dmass: %g)!",
+        p->conserved.mass, p->conserved.flux.mass);
+  }
+
+  if (p->conserved.energy < 0.) {
+    error(
+        "Negative energy after conserved variables update (energy: %g, "
+        "denergy: %g)!",
+        p->conserved.energy, p->conserved.flux.energy);
+  }
+#endif
+
+  /* Add gravity. We only do this if we have gravity activated. */
+  if (p->gpart) {
+    /* 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.*/
+    a_grav[0] = p->gpart->a_grav[0];
+    a_grav[1] = p->gpart->a_grav[1];
+    a_grav[2] = p->gpart->a_grav[2];
+
+    /* Make sure the gpart knows the mass has changed. */
+    p->gpart->mass = p->conserved.mass;
+
+    /* Kick the momentum for half a time step */
+    /* Note that this also affects the particle movement, as the velocity for
+       the particles is set after this. */
+    p->conserved.momentum[0] += dt * p->conserved.mass * a_grav[0];
+    p->conserved.momentum[1] += dt * p->conserved.mass * a_grav[1];
+    p->conserved.momentum[2] += dt * p->conserved.mass * a_grav[2];
+
+    p->conserved.energy += dt * (p->gravity.mflux[0] * a_grav[0] +
+                                 p->gravity.mflux[1] * a_grav[1] +
+                                 p->gravity.mflux[2] * a_grav[2]);
+  }
+
+  hydro_velocities_set(p, xp);
+
+#ifdef GIZMO_LLOYD_ITERATION
+  /* reset conserved variables to safe values */
+  p->conserved.mass = 1.;
+  p->conserved.momentum[0] = 0.;
+  p->conserved.momentum[1] = 0.;
+  p->conserved.momentum[2] = 0.;
+  p->conserved.energy = 1.;
+
+  /* set the particle velocities to the Lloyd velocities */
+  /* note that centroid is the relative position of the centroid w.r.t. the
+     particle position (position - centroid) */
+  xp->v_full[0] = -p->geometry.centroid[0] / p->force.dt;
+  xp->v_full[1] = -p->geometry.centroid[1] / p->force.dt;
+  xp->v_full[2] = -p->geometry.centroid[2] / p->force.dt;
+  p->v[0] = xp->v_full[0];
+  p->v[1] = xp->v_full[1];
+  p->v[2] = xp->v_full[2];
+#endif
+
+  /* reset wcorr */
+  p->density.wcorr = 1.0f;
+}
+
+/**
+ * @brief Returns the comoving internal energy of a particle
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy(const struct part* restrict p) {
+
+  if (p->primitives.rho > 0.)
+    return gas_internal_energy_from_pressure(p->primitives.rho,
+                                             p->primitives.P);
+  else
+    return 0.;
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy(const struct part* restrict p,
+                                   const struct cosmology* cosmo) {
+
+  return cosmo->a_factor_internal_energy *
+         hydro_get_comoving_internal_energy(p);
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
+    const struct part* restrict p) {
+
+  if (p->primitives.rho > 0.) {
+    return gas_entropy_from_pressure(p->primitives.rho, p->primitives.P);
+  } else {
+    return 0.;
+  }
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
+    const struct part* restrict p, const struct cosmology* cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return hydro_get_comoving_entropy(p);
+}
+
+/**
+ * @brief Returns the sound speed of a particle
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_soundspeed(const struct part* restrict p) {
+
+  if (p->primitives.rho > 0.)
+    return gas_soundspeed_from_pressure(p->primitives.rho, p->primitives.P);
+  else
+    return 0.;
+}
+
+/**
+ * @brief Returns the physical sound speed of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_soundspeed(const struct part* restrict p,
+                              const struct cosmology* cosmo) {
+
+  return cosmo->a_factor_sound_speed * hydro_get_comoving_soundspeed(p);
+}
+
+/**
+ * @brief Returns the comoving pressure of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure(
+    const struct part* restrict p) {
+
+  return p->primitives.P;
+}
+
+/**
+ * @brief Returns the comoving pressure of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
+    const struct part* restrict p, const struct cosmology* cosmo) {
+
+  return cosmo->a_factor_pressure * 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 Sets the mass of a particle
+ *
+ * @param p The particle of interest
+ * @param m The mass to set.
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_mass(
+    struct part* restrict p, float m) {
+
+  p->conserved.mass = m;
+}
+
+/**
+ * @brief Returns the velocities drifted to the current time of a particle.
+ *
+ * @param p The particle of interest
+ * @param xp The extended data of the particle.
+ * @param dt_kick_hydro The time (for hydro accelerations) since the last kick.
+ * @param dt_kick_grav The time (for gravity accelerations) since the last kick.
+ * @param v (return) The velocities at the current time.
+ */
+__attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
+    const struct part* restrict p, const struct xpart* xp, float dt_kick_hydro,
+    float dt_kick_grav, float v[3]) {
+
+  if (p->conserved.mass > 0.) {
+    v[0] = p->primitives.v[0] +
+           p->conserved.flux.momentum[0] * dt_kick_hydro / p->conserved.mass;
+    v[1] = p->primitives.v[1] +
+           p->conserved.flux.momentum[1] * dt_kick_hydro / p->conserved.mass;
+    v[2] = p->primitives.v[2] +
+           p->conserved.flux.momentum[2] * dt_kick_hydro / p->conserved.mass;
+  } else {
+    v[0] = p->primitives.v[0];
+    v[1] = p->primitives.v[1];
+    v[2] = p->primitives.v[2];
+  }
+
+  // MATTHIEU: Bert is this correct?
+  v[0] += xp->a_grav[0] * dt_kick_grav;
+  v[1] += xp->a_grav[1] * dt_kick_grav;
+  v[2] += xp->a_grav[2] * dt_kick_grav;
+}
+
+/**
+ * @brief Returns the comoving density of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_density(
+    const struct part* restrict p) {
+
+  return p->primitives.rho;
+}
+
+/**
+ * @brief Returns the physical density of a particle
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_density(
+    const struct part* restrict p, const struct cosmology* cosmo) {
+
+  return cosmo->a3_inv * p->primitives.rho;
+}
+
+/**
+ * @brief Modifies the thermal state of a particle to the imposed internal
+ * energy
+ *
+ * This overrides the current state of the particle but does *not* change its
+ * time-derivatives
+ *
+ * @param p The particle
+ * @param u The new internal energy
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_internal_energy(
+    struct part* restrict p, float u) {
+
+  /* conserved.energy is NOT the specific energy (u), but the total thermal
+     energy (u*m) */
+  p->conserved.energy = u * p->conserved.mass;
+#ifdef GIZMO_TOTAL_ENERGY
+  /* add the kinetic energy */
+  p->conserved.energy += 0.5f * p->conserved.mass *
+                         (p->conserved.momentum[0] * p->primitives.v[0] +
+                          p->conserved.momentum[1] * p->primitives.v[1] +
+                          p->conserved.momentum[2] * p->primitives.v[2]);
+#endif
+  p->primitives.P = hydro_gamma_minus_one * p->primitives.rho * u;
+}
+
+/**
+ * @brief Modifies the thermal state of a particle to the imposed entropy
+ *
+ * This overrides the current state of the particle but does *not* change its
+ * time-derivatives
+ *
+ * @param p The particle
+ * @param S The new entropy
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_entropy(
+    struct part* restrict p, float S) {
+
+  p->conserved.energy = S * pow_gamma_minus_one(p->primitives.rho) *
+                        hydro_one_over_gamma_minus_one * p->conserved.mass;
+#ifdef GIZMO_TOTAL_ENERGY
+  /* add the kinetic energy */
+  p->conserved.energy += 0.5f * p->conserved.mass *
+                         (p->conserved.momentum[0] * p->primitives.v[0] +
+                          p->conserved.momentum[1] * p->primitives.v[1] +
+                          p->conserved.momentum[2] * p->primitives.v[2]);
+#endif
+  p->primitives.P = S * pow_gamma(p->primitives.rho);
+}
+
+/**
+ * @brief Overwrite the initial internal energy of a particle.
+ *
+ * Note that in the cases where the thermodynamic variable is not
+ * internal energy but gets converted later, we must overwrite that
+ * field. The conversion to the actual variable happens later after
+ * the initial fake time-step.
+ *
+ * @param p The #part to write to.
+ * @param u_init The new initial internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_init_internal_energy(struct part* p, float u_init) {
+
+  p->conserved.energy = u_init * p->conserved.mass;
+#ifdef GIZMO_TOTAL_ENERGY
+  /* add the kinetic energy */
+  p->conserved.energy += 0.5f * p->conserved.mass *
+                         (p->conserved.momentum[0] * p->primitives.v[0] +
+                          p->conserved.momentum[1] * p->primitives.v[1] +
+                          p->conserved.momentum[2] * p->primitives.v[2]);
+#endif
+  p->primitives.P = hydro_gamma_minus_one * p->primitives.rho * u_init;
+}
+
+#endif /* SWIFT_GIZMO_MFM_HYDRO_H */
diff --git a/src/hydro/Gizmo/hydro_debug.h b/src/hydro/GizmoMFM/hydro_debug.h
similarity index 96%
rename from src/hydro/Gizmo/hydro_debug.h
rename to src/hydro/GizmoMFM/hydro_debug.h
index 0516068d3452e179c076f40a7c2db859be1c73c6..6603bc216b986b40513383120587d3caec1adc87 100644
--- a/src/hydro/Gizmo/hydro_debug.h
+++ b/src/hydro/GizmoMFM/hydro_debug.h
@@ -16,8 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_GIZMO_HYDRO_DEBUG_H
-#define SWIFT_GIZMO_HYDRO_DEBUG_H
+#ifndef SWIFT_GIZMO_MFM_HYDRO_DEBUG_H
+#define SWIFT_GIZMO_MFM_HYDRO_DEBUG_H
 
 __attribute__((always_inline)) INLINE static void hydro_debug_particle(
     const struct part* p, const struct xpart* xp) {
@@ -78,4 +78,4 @@ __attribute__((always_inline)) INLINE static void hydro_debug_particle(
       p->timestepvars.vmax, p->density.wcount_dh, p->density.wcount);
 }
 
-#endif /* SWIFT_GIZMO_HYDRO_DEBUG_H */
+#endif /* SWIFT_GIZMO_MFM_HYDRO_DEBUG_H */
diff --git a/src/hydro/Gizmo/hydro_gradients.h b/src/hydro/GizmoMFM/hydro_gradients.h
similarity index 97%
rename from src/hydro/Gizmo/hydro_gradients.h
rename to src/hydro/GizmoMFM/hydro_gradients.h
index c5d95e4dab4f574dd20e8355aacdd176530ec63d..964a2adcfe09b95c2a221af540e5e3ff0830dd67 100644
--- a/src/hydro/Gizmo/hydro_gradients.h
+++ b/src/hydro/GizmoMFM/hydro_gradients.h
@@ -17,8 +17,8 @@
  *
  ******************************************************************************/
 
-#ifndef SWIFT_HYDRO_GIZMO_GRADIENTS_H
-#define SWIFT_HYDRO_GIZMO_GRADIENTS_H
+#ifndef SWIFT_HYDRO_GIZMO_MFM_GRADIENTS_H
+#define SWIFT_HYDRO_GIZMO_MFM_GRADIENTS_H
 
 #include "hydro_slope_limiters.h"
 #include "hydro_unphysical.h"
@@ -156,4 +156,4 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_predict(
                                   Wj[3], Wj[4]);
 }
 
-#endif /* SWIFT_HYDRO_GIZMO_GRADIENTS_H */
+#endif /* SWIFT_HYDRO_GIZMO_MFM_GRADIENTS_H */
diff --git a/src/hydro/Gizmo/hydro_gradients_gizmo.h b/src/hydro/GizmoMFM/hydro_gradients_gizmo.h
similarity index 99%
rename from src/hydro/Gizmo/hydro_gradients_gizmo.h
rename to src/hydro/GizmoMFM/hydro_gradients_gizmo.h
index 9f23f82e870cffb82a73964707bd28fa68ed00d7..1c3b68bb28375259628e09f16730710fbbd80149 100644
--- a/src/hydro/Gizmo/hydro_gradients_gizmo.h
+++ b/src/hydro/GizmoMFM/hydro_gradients_gizmo.h
@@ -22,8 +22,8 @@
  *
  * @param p Particle.
  */
-#ifndef SWIFT_GIZMO_HYDRO_GRADIENTS_H
-#define SWIFT_GIZMO_HYDRO_GRADIENTS_H
+#ifndef SWIFT_GIZMO_MFM_HYDRO_GRADIENTS_H
+#define SWIFT_GIZMO_MFM_HYDRO_GRADIENTS_H
 
 __attribute__((always_inline)) INLINE static void hydro_gradients_init(
     struct part *p) {
@@ -485,4 +485,4 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_finalize(
   hydro_slope_limit_cell(p);
 }
 
-#endif /* SWIFT_GIZMO_HYDRO_GRADIENTS_H */
+#endif /* SWIFT_GIZMO_MFM_HYDRO_GRADIENTS_H */
diff --git a/src/hydro/Gizmo/hydro_gradients_sph.h b/src/hydro/GizmoMFM/hydro_gradients_sph.h
similarity index 98%
rename from src/hydro/Gizmo/hydro_gradients_sph.h
rename to src/hydro/GizmoMFM/hydro_gradients_sph.h
index fbaa056443df56691f1414f2d661ba9edc459734..169bed74f0b1b7e966f9880248f811d100bec13b 100644
--- a/src/hydro/Gizmo/hydro_gradients_sph.h
+++ b/src/hydro/GizmoMFM/hydro_gradients_sph.h
@@ -22,8 +22,8 @@
  *
  * @param p Particle.
  */
-#ifndef SWIFT_GIZMO_HYDRO_SPH_GRADIENTS_H
-#define SWIFT_GIZMO_HYDRO_SPH_GRADIENTS_H
+#ifndef SWIFT_GIZMO_MFM_HYDRO_SPH_GRADIENTS_H
+#define SWIFT_GIZMO_MFM_HYDRO_SPH_GRADIENTS_H
 
 __attribute__((always_inline)) INLINE static void hydro_gradients_init(
     struct part *p) {
@@ -253,4 +253,4 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_finalize(
   hydro_slope_limit_cell(p);
 }
 
-#endif /* SWIFT_GIZMO_HYDRO_SPH_GRADIENTS_H */
+#endif /* SWIFT_GIZMO_MFM_HYDRO_SPH_GRADIENTS_H */
diff --git a/src/hydro/GizmoMFM/hydro_iact.h b/src/hydro/GizmoMFM/hydro_iact.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1a82e514baa60d5895343ea84ef1c3aedc8a6b7
--- /dev/null
+++ b/src/hydro/GizmoMFM/hydro_iact.h
@@ -0,0 +1,517 @@
+/*******************************************************************************
+ * 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)
+ *
+ * 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_GIZMO_MFM_HYDRO_IACT_H
+#define SWIFT_GIZMO_MFM_HYDRO_IACT_H
+
+#include "adiabatic_index.h"
+#include "hydro_gradients.h"
+#include "riemann.h"
+
+#define GIZMO_VOLUME_CORRECTION
+
+/**
+ * @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 Comoving squared distance between particle i and particle j.
+ * @param dx Comoving distance vector between the particles (dx = pi->x -
+ * pj->x).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_density(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
+
+  float wi, wj, wi_dx, wj_dx;
+
+  /* Get r and h inverse. */
+  const float r = sqrtf(r2);
+
+  /* Compute density of pi. */
+  const float hi_inv = 1.f / hi;
+  const float xi = r * hi_inv;
+  kernel_deval(xi, &wi, &wi_dx);
+
+  pi->density.wcount += wi;
+  pi->density.wcount_dh -= (hydro_dimension * wi + xi * wi_dx);
+
+  /* these are eqns. (1) and (2) in the summary */
+  pi->geometry.volume += wi;
+  for (int k = 0; k < 3; k++)
+    for (int l = 0; l < 3; l++)
+      pi->geometry.matrix_E[k][l] += dx[k] * dx[l] * wi;
+
+  pi->geometry.centroid[0] -= dx[0] * wi;
+  pi->geometry.centroid[1] -= dx[1] * wi;
+  pi->geometry.centroid[2] -= dx[2] * wi;
+
+  /* Compute density of pj. */
+  const float hj_inv = 1.f / hj;
+  const float xj = r * hj_inv;
+  kernel_deval(xj, &wj, &wj_dx);
+
+  pj->density.wcount += wj;
+  pj->density.wcount_dh -= (hydro_dimension * wj + xj * wj_dx);
+
+  /* these are eqns. (1) and (2) in the summary */
+  pj->geometry.volume += wj;
+  for (int k = 0; k < 3; k++)
+    for (int l = 0; l < 3; l++)
+      pj->geometry.matrix_E[k][l] += dx[k] * dx[l] * wj;
+
+  pj->geometry.centroid[0] += dx[0] * wj;
+  pj->geometry.centroid[1] += dx[1] * wj;
+  pj->geometry.centroid[2] += dx[2] * wj;
+}
+
+/**
+ * @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 Comoving squared distance between particle i and particle j.
+ * @param dx Comoving distance vector between the particles (dx = pi->x -
+ * pj->x).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    const struct part *restrict pj, float a, float H) {
+
+  float wi, wi_dx;
+
+  /* Get r and h inverse. */
+  const float r = sqrtf(r2);
+
+  const float hi_inv = 1.f / hi;
+  const float xi = r * hi_inv;
+  kernel_deval(xi, &wi, &wi_dx);
+
+  pi->density.wcount += wi;
+  pi->density.wcount_dh -= (hydro_dimension * wi + xi * wi_dx);
+
+  /* these are eqns. (1) and (2) in the summary */
+  pi->geometry.volume += wi;
+  for (int k = 0; k < 3; k++)
+    for (int l = 0; l < 3; l++)
+      pi->geometry.matrix_E[k][l] += dx[k] * dx[l] * wi;
+
+  pi->geometry.centroid[0] -= dx[0] * wi;
+  pi->geometry.centroid[1] -= dx[1] * wi;
+  pi->geometry.centroid[2] -= dx[2] * wi;
+}
+
+/**
+ * @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 Comoving squared distance between particle i and particle j.
+ * @param dx Comoving distance vector between the particles (dx = pi->x -
+ * pj->x).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_gradient(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
+
+  hydro_gradients_collect(r2, dx, hi, hj, pi, pj);
+}
+
+/**
+ * @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 Comoving squared distance between particle i and particle j.
+ * @param dx Comoving distance vector between the particles (dx = pi->x -
+ * pj->x).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
+
+  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), 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 Comoving squared distance between particle i and particle j.
+ * @param dx Comoving distance vector between the particles (dx = pi->x -
+ * pj->x).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, int mode, float a, float H) {
+
+  const float r_inv = 1.f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  /* Initialize local variables */
+  float Bi[3][3];
+  float Bj[3][3];
+  float vi[3], vj[3];
+  for (int k = 0; k < 3; k++) {
+    for (int l = 0; l < 3; l++) {
+      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];
+  }
+  const float Vi = pi->geometry.volume;
+  const float Vj = pj->geometry.volume;
+  float Wi[5], Wj[5];
+  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;
+
+  /* calculate the maximal signal velocity */
+  float vmax;
+  if (Wi[0] > 0.0f && Wj[0] > 0.0f) {
+    const float ci = gas_soundspeed_from_pressure(Wi[0], Wi[4]);
+    const float cj = gas_soundspeed_from_pressure(Wj[0], Wj[4]);
+    vmax = ci + cj;
+  } else
+    vmax = 0.0f;
+
+  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];
+
+  /* Velocity on the axis linking the particles */
+  float dvdotdx = (Wi[1] - Wj[1]) * dx[0] + (Wi[2] - Wj[2]) * dx[1] +
+                  (Wi[3] - Wj[3]) * dx[2];
+  dvdotdx = min(dvdotdx, dvdr);
+
+  /* We only care about this velocity for particles moving towards each others
+   */
+  dvdotdx = min(dvdotdx, 0.f);
+
+  /* Get the signal velocity */
+  /* the magical factor 3 also appears in Gadget2 */
+  vmax -= 3.f * dvdotdx * r_inv;
+
+  /* Store the signal velocity */
+  pi->timestepvars.vmax = max(pi->timestepvars.vmax, vmax);
+  if (mode == 1) pj->timestepvars.vmax = max(pj->timestepvars.vmax, vmax);
+
+  /* Compute kernel of pi. */
+  float wi, wi_dx;
+  const float hi_inv = 1.f / hi;
+  const float hi_inv_dim = pow_dimension(hi_inv);
+  const float xi = r * hi_inv;
+  kernel_deval(xi, &wi, &wi_dx);
+
+  /* Compute kernel of pj. */
+  float wj, wj_dx;
+  const float hj_inv = 1.f / hj;
+  const float hj_inv_dim = pow_dimension(hj_inv);
+  const float 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 */
+  const float hidp1 = pow_dimension_plus_one(hi_inv);
+  const float hjdp1 = pow_dimension_plus_one(hj_inv);
+  const float wi_dr = hidp1 * wi_dx;
+  const float wj_dr = hjdp1 * wj_dx;
+  dvdr *= r_inv;
+  if (pj->primitives.rho > 0.)
+    pi->force.h_dt -= pj->conserved.mass * dvdr / pj->primitives.rho * wi_dr;
+  if (mode == 1 && pi->primitives.rho > 0.)
+    pj->force.h_dt -= pi->conserved.mass * dvdr / pi->primitives.rho * wj_dr;
+
+  /* Compute (square of) area */
+  /* eqn. (7) */
+  float Anorm2 = 0.0f;
+  float A[3];
+  if (pi->density.wcorr > const_gizmo_min_wcorr &&
+      pj->density.wcorr > const_gizmo_min_wcorr) {
+    /* in principle, we use Vi and Vj as weights for the left and right
+       contributions to the generalized surface vector.
+       However, if Vi and Vj are very different (because they have very
+       different
+       smoothing lengths), then the expressions below are more stable. */
+    float Xi = Vi;
+    float Xj = Vj;
+#ifdef GIZMO_VOLUME_CORRECTION
+    if (fabsf(Vi - Vj) / min(Vi, Vj) > 1.5f * hydro_dimension) {
+      Xi = (Vi * hj + Vj * hi) / (hi + hj);
+      Xj = Xi;
+    }
+#endif
+    for (int k = 0; k < 3; k++) {
+      /* we add a minus sign since dx is pi->x - pj->x */
+      A[k] = -Xi * (Bi[k][0] * dx[0] + Bi[k][1] * dx[1] + Bi[k][2] * dx[2]) *
+                 wi * hi_inv_dim -
+             Xj * (Bj[k][0] * dx[0] + Bj[k][1] * dx[1] + Bj[k][2] * dx[2]) *
+                 wj * hj_inv_dim;
+      Anorm2 += A[k] * A[k];
+    }
+  } else {
+    /* ill condition gradient matrix: revert to SPH face area */
+    const float Anorm =
+        -(hidp1 * Vi * Vi * wi_dx + hjdp1 * Vj * Vj * wj_dx) * r_inv;
+    A[0] = -Anorm * dx[0];
+    A[1] = -Anorm * dx[1];
+    A[2] = -Anorm * dx[2];
+    Anorm2 = Anorm * Anorm * r2;
+  }
+
+  /* if the interface has no area, nothing happens and we return */
+  /* continuing results in dividing by zero and NaN's... */
+  if (Anorm2 == 0.f) return;
+
+  /* Compute the area */
+  const float Anorm_inv = 1. / sqrtf(Anorm2);
+  const float Anorm = Anorm2 * Anorm_inv;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* For stability reasons, we do require A and dx to have opposite
+     directions (basically meaning that the surface normal for the surface
+     always points from particle i to particle j, as it would in a real
+     moving-mesh code). If not, our scheme is no longer upwind and hence can
+     become unstable. */
+  const float dA_dot_dx = A[0] * dx[0] + A[1] * dx[1] + A[2] * dx[2];
+  /* In GIZMO, Phil Hopkins reverts to an SPH integration scheme if this
+     happens. We curently just ignore this case and display a message. */
+  const float rdim = pow_dimension(r);
+  if (dA_dot_dx > 1.e-6f * rdim) {
+    message("Ill conditioned gradient matrix (%g %g %g %g %g)!", dA_dot_dx,
+            Anorm, Vi, Vj, r);
+  }
+#endif
+
+  /* compute the normal vector of the interface */
+  const float n_unit[3] = {A[0] * Anorm_inv, A[1] * Anorm_inv,
+                           A[2] * Anorm_inv};
+
+  /* Compute interface position (relative to pi, since we don't need the actual
+   * position) eqn. (8) */
+  const float xfac = -hi / (hi + hj);
+  const float xij_i[3] = {xfac * dx[0], xfac * dx[1], xfac * dx[2]};
+
+  /* Compute interface velocity */
+  /* eqn. (9) */
+  float xijdotdx = xij_i[0] * dx[0] + xij_i[1] * dx[1] + xij_i[2] * dx[2];
+  xijdotdx *= r_inv * r_inv;
+  const float vij[3] = {vi[0] + (vi[0] - vj[0]) * xijdotdx,
+                        vi[1] + (vi[1] - vj[1]) * xijdotdx,
+                        vi[2] + (vi[2] - vj[2]) * xijdotdx};
+
+  /* complete calculation of position of interface */
+  /* NOTE: dx is not necessarily just pi->x - pj->x but can also contain
+           correction terms for periodicity. If we do the interpolation,
+           we have to use xij w.r.t. the actual particle.
+           => we need a separate xij for pi and pj... */
+  /* tldr: we do not need the code below, but we do need the same code as above
+     but then with i and j swapped */
+  //    for ( k = 0 ; k < 3 ; k++ )
+  //      xij[k] += pi->x[k];
+
+  /* Boost the primitive variables to the frame of reference of the interface */
+  /* Note that velocities are indices 1-3 in W */
+  Wi[1] -= vij[0];
+  Wi[2] -= vij[1];
+  Wi[3] -= vij[2];
+  Wj[1] -= vij[0];
+  Wj[2] -= vij[1];
+  Wj[3] -= vij[2];
+
+  hydro_gradients_predict(pi, pj, hi, hj, dx, r, xij_i, Wi, Wj);
+
+  /* we don't need to rotate, we can use the unit vector in the Riemann problem
+   * itself (see GIZMO) */
+
+  float totflux[5];
+  riemann_solve_for_middle_state_flux(Wi, Wj, n_unit, vij, totflux);
+
+  /* Multiply with the interface surface area */
+  totflux[0] *= Anorm;
+  totflux[1] *= Anorm;
+  totflux[2] *= Anorm;
+  totflux[3] *= Anorm;
+  totflux[4] *= Anorm;
+
+  /* Store mass flux */
+  const float mflux_i = totflux[0];
+  pi->gravity.mflux[0] += mflux_i * dx[0];
+  pi->gravity.mflux[1] += mflux_i * dx[1];
+  pi->gravity.mflux[2] += mflux_i * dx[2];
+
+  /* Update conserved variables */
+  /* eqn. (16) */
+  pi->conserved.flux.mass -= totflux[0];
+  pi->conserved.flux.momentum[0] -= totflux[1];
+  pi->conserved.flux.momentum[1] -= totflux[2];
+  pi->conserved.flux.momentum[2] -= totflux[3];
+  pi->conserved.flux.energy -= totflux[4];
+
+#ifndef GIZMO_TOTAL_ENERGY
+  const float ekin_i = 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 += totflux[1] * pi->primitives.v[0];
+  pi->conserved.flux.energy += totflux[2] * pi->primitives.v[1];
+  pi->conserved.flux.energy += totflux[3] * pi->primitives.v[2];
+  pi->conserved.flux.energy -= totflux[0] * ekin_i;
+#endif
+
+  /* Note that this used to be much more complicated in early implementations of
+   * the GIZMO scheme, as we wanted manifest conservation of conserved variables
+   * and had to do symmetric flux exchanges. Now we don't care about manifest
+   * conservation anymore and just assume the current fluxes are representative
+   * for the flux over the entire time step. */
+  if (mode == 1) {
+    /* Store mass flux */
+    const float mflux_j = totflux[0];
+    pj->gravity.mflux[0] -= mflux_j * dx[0];
+    pj->gravity.mflux[1] -= mflux_j * dx[1];
+    pj->gravity.mflux[2] -= mflux_j * dx[2];
+
+    pj->conserved.flux.mass += totflux[0];
+    pj->conserved.flux.momentum[0] += totflux[1];
+    pj->conserved.flux.momentum[1] += totflux[2];
+    pj->conserved.flux.momentum[2] += totflux[3];
+    pj->conserved.flux.energy += totflux[4];
+
+#ifndef GIZMO_TOTAL_ENERGY
+    const float ekin_j = 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 -= totflux[1] * pj->primitives.v[0];
+    pj->conserved.flux.energy -= totflux[2] * pj->primitives.v[1];
+    pj->conserved.flux.energy -= totflux[3] * pj->primitives.v[2];
+    pj->conserved.flux.energy += totflux[0] * ekin_j;
+#endif
+  }
+}
+
+/**
+ * @brief Flux calculation between particle i and particle j
+ *
+ * This method calls runner_iact_fluxes_common with mode 1.
+ *
+ * @param r2 Comoving squared distance between particle i and particle j.
+ * @param dx Comoving distance vector between the particles (dx = pi->x -
+ * pj->x).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_force(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
+
+  runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 1, a, H);
+}
+
+/**
+ * @brief Flux calculation between particle i and particle j: non-symmetric
+ * version
+ *
+ * This method calls runner_iact_fluxes_common with mode 0.
+ *
+ * @param r2 Comoving squared distance between particle i and particle j.
+ * @param dx Comoving distance vector between the particles (dx = pi->x -
+ * pj->x).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi Particle i.
+ * @param pj Particle j.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
+
+  runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0, a, H);
+}
+
+#endif /* SWIFT_GIZMO_MFM_HYDRO_IACT_H */
diff --git a/src/hydro/Gizmo/hydro_io.h b/src/hydro/GizmoMFM/hydro_io.h
similarity index 82%
rename from src/hydro/Gizmo/hydro_io.h
rename to src/hydro/GizmoMFM/hydro_io.h
index 7aa56b3bde5247f4839b8f4f60cdb3de67253392..59d579f70cd4aedc728dbf42038eff78d4c507d5 100644
--- a/src/hydro/Gizmo/hydro_io.h
+++ b/src/hydro/GizmoMFM/hydro_io.h
@@ -16,8 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_GIZMO_HYDRO_IO_H
-#define SWIFT_GIZMO_HYDRO_IO_H
+#ifndef SWIFT_GIZMO_MFM_HYDRO_IO_H
+#define SWIFT_GIZMO_MFM_HYDRO_IO_H
 
 #include "adiabatic_index.h"
 #include "hydro.h"
@@ -40,8 +40,9 @@
  * @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) {
+INLINE static void hydro_read_particles(struct part* parts,
+                                        struct io_props* list,
+                                        int* num_fields) {
 
   *num_fields = 8;
 
@@ -72,8 +73,8 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
  * @param p Particle.
  * @param ret (return) Internal energy of the particle
  */
-void convert_u(const struct engine* e, const struct part* p,
-               const struct xpart* xp, float* ret) {
+INLINE static void convert_u(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
 
   ret[0] = hydro_get_comoving_internal_energy(p);
 }
@@ -85,8 +86,8 @@ void convert_u(const struct engine* e, const struct part* p,
  * @param p Particle.
  * @param ret (return) Entropic function of the particle
  */
-void convert_A(const struct engine* e, const struct part* p,
-               const struct xpart* xp, float* ret) {
+INLINE static void convert_A(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
   ret[0] = hydro_get_comoving_entropy(p);
 }
 
@@ -97,8 +98,8 @@ void convert_A(const struct engine* e, const struct part* p,
  * @param p Particle.
  * @return Total energy of the particle
  */
-void convert_Etot(const struct engine* e, const struct part* p,
-                  const struct xpart* xp, float* ret) {
+INLINE static void convert_Etot(const struct engine* e, const struct part* p,
+                                const struct xpart* xp, float* ret) {
 #ifdef GIZMO_TOTAL_ENERGY
   ret[0] = p->conserved.energy;
 #else
@@ -112,8 +113,9 @@ void convert_Etot(const struct engine* e, const struct part* p,
 #endif
 }
 
-void convert_part_pos(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, double* ret) {
+INLINE static void convert_part_pos(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -126,8 +128,9 @@ void convert_part_pos(const struct engine* e, const struct part* p,
   }
 }
 
-void convert_part_vel(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, float* ret) {
+INLINE static void convert_part_vel(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, float* ret) {
 
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const struct cosmology* cosmo = e->cosmology;
@@ -155,13 +158,14 @@ void convert_part_vel(const struct engine* e, const struct part* p,
   hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret);
 
   /* Conversion from internal units to peculiar velocities */
-  ret[0] *= cosmo->a2_inv;
-  ret[1] *= cosmo->a2_inv;
-  ret[2] *= cosmo->a2_inv;
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
 }
 
-void convert_part_potential(const struct engine* e, const struct part* p,
-                            const struct xpart* xp, float* ret) {
+INLINE static void convert_part_potential(const struct engine* e,
+                                          const struct part* p,
+                                          const struct xpart* xp, float* ret) {
 
   if (p->gpart != NULL)
     ret[0] = gravity_get_comoving_potential(p->gpart);
@@ -176,8 +180,10 @@ void convert_part_potential(const struct engine* e, const struct part* p,
  * @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(const struct part* parts, const struct xpart* xparts,
-                           struct io_props* list, int* num_fields) {
+INLINE static void hydro_write_particles(const struct part* parts,
+                                         const struct xpart* xparts,
+                                         struct io_props* list,
+                                         int* num_fields) {
 
   *num_fields = 11;
 
@@ -215,7 +221,7 @@ void hydro_write_particles(const struct part* parts, const struct xpart* xparts,
  * @brief Writes the current model of SPH to the file
  * @param h_grpsph The HDF5 group in which to write
  */
-void hydro_write_flavour(hid_t h_grpsph) {
+INLINE static void hydro_write_flavour(hid_t h_grpsph) {
   /* Gradient information */
   io_write_attribute_s(h_grpsph, "Gradient reconstruction model",
                        HYDRO_GRADIENT_IMPLEMENTATION);
@@ -239,6 +245,6 @@ void hydro_write_flavour(hid_t h_grpsph) {
  *
  * @return 1 if entropy is in 'internal energy', 0 otherwise.
  */
-int writeEntropyFlag() { return 0; }
+INLINE static int writeEntropyFlag(void) { return 0; }
 
-#endif /* SWIFT_GIZMO_HYDRO_IO_H */
+#endif /* SWIFT_GIZMO_MFM_HYDRO_IO_H */
diff --git a/src/hydro/Gizmo/hydro_part.h b/src/hydro/GizmoMFM/hydro_part.h
similarity index 97%
rename from src/hydro/Gizmo/hydro_part.h
rename to src/hydro/GizmoMFM/hydro_part.h
index d5bc3f84238fecbf3b2f120f031996f55bbd65fe..857c429ec933ad3eea730e8f0b6f830782cdf77b 100644
--- a/src/hydro/Gizmo/hydro_part.h
+++ b/src/hydro/GizmoMFM/hydro_part.h
@@ -16,8 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_GIZMO_HYDRO_PART_H
-#define SWIFT_GIZMO_HYDRO_PART_H
+#ifndef SWIFT_GIZMO_MFM_HYDRO_PART_H
+#define SWIFT_GIZMO_MFM_HYDRO_PART_H
 
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
@@ -210,4 +210,4 @@ struct part {
 
 } SWIFT_STRUCT_ALIGN;
 
-#endif /* SWIFT_GIZMO_HYDRO_PART_H */
+#endif /* SWIFT_GIZMO_MFM_HYDRO_PART_H */
diff --git a/src/hydro/Gizmo/hydro_slope_limiters.h b/src/hydro/GizmoMFM/hydro_slope_limiters.h
similarity index 100%
rename from src/hydro/Gizmo/hydro_slope_limiters.h
rename to src/hydro/GizmoMFM/hydro_slope_limiters.h
diff --git a/src/hydro/Gizmo/hydro_slope_limiters_cell.h b/src/hydro/GizmoMFM/hydro_slope_limiters_cell.h
similarity index 98%
rename from src/hydro/Gizmo/hydro_slope_limiters_cell.h
rename to src/hydro/GizmoMFM/hydro_slope_limiters_cell.h
index 599be632b64e3b06ab1e0339a6ad4b560b2abece..7dec6f499da31de1f10652a31781a788166957cc 100644
--- a/src/hydro/Gizmo/hydro_slope_limiters_cell.h
+++ b/src/hydro/GizmoMFM/hydro_slope_limiters_cell.h
@@ -16,8 +16,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_GIZMO_SLOPE_LIMITER_CELL_H
-#define SWIFT_GIZMO_SLOPE_LIMITER_CELL_H
+#ifndef SWIFT_GIZMO_MFM_SLOPE_LIMITER_CELL_H
+#define SWIFT_GIZMO_MFM_SLOPE_LIMITER_CELL_H
 
 #include <float.h>
 
@@ -183,4 +183,4 @@ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell(
   }
 }
 
-#endif /* SWIFT_GIZMO_SLOPE_LIMITER_CELL_H */
+#endif /* SWIFT_GIZMO_MFM_SLOPE_LIMITER_CELL_H */
diff --git a/src/hydro/Gizmo/hydro_slope_limiters_face.h b/src/hydro/GizmoMFM/hydro_slope_limiters_face.h
similarity index 97%
rename from src/hydro/Gizmo/hydro_slope_limiters_face.h
rename to src/hydro/GizmoMFM/hydro_slope_limiters_face.h
index 39a453b3d21383fbf075a3c1985dfb182d9a2dcc..8f0636c992ccbd615cc62ca09c1b0ca9b08ea56b 100644
--- a/src/hydro/Gizmo/hydro_slope_limiters_face.h
+++ b/src/hydro/GizmoMFM/hydro_slope_limiters_face.h
@@ -29,8 +29,8 @@
  * @return The slope limited difference between the quantity at the particle
  * position and the quantity at the interface position.
  */
-#ifndef SWIFT_GIZMO_SLOPE_LIMITER_FACE_H
-#define SWIFT_GIZMO_SLOPE_LIMITER_FACE_H
+#ifndef SWIFT_GIZMO_MFM_SLOPE_LIMITER_FACE_H
+#define SWIFT_GIZMO_MFM_SLOPE_LIMITER_FACE_H
 
 /* Some standard headers. */
 #include <float.h>
@@ -125,4 +125,4 @@ __attribute__((always_inline)) INLINE static void hydro_slope_limit_face(
                                            xij_j_norm, r_inv);
 }
 
-#endif /* SWIFT_GIZMO_SLOPE_LIMITER_FACE_H */
+#endif /* SWIFT_GIZMO_MFM_SLOPE_LIMITER_FACE_H */
diff --git a/src/hydro/Gizmo/hydro_unphysical.h b/src/hydro/GizmoMFM/hydro_unphysical.h
similarity index 100%
rename from src/hydro/Gizmo/hydro_unphysical.h
rename to src/hydro/GizmoMFM/hydro_unphysical.h
diff --git a/src/hydro/Gizmo/hydro_velocities.h b/src/hydro/GizmoMFM/hydro_velocities.h
similarity index 100%
rename from src/hydro/Gizmo/hydro_velocities.h
rename to src/hydro/GizmoMFM/hydro_velocities.h
diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/GizmoMFV/hydro.h
similarity index 96%
rename from src/hydro/Gizmo/hydro.h
rename to src/hydro/GizmoMFV/hydro.h
index 079e66669eb4e3b574742b09ca6b7e80d5386fb7..6916fe33272692316354385b723ce9969606b6a2 100644
--- a/src/hydro/Gizmo/hydro.h
+++ b/src/hydro/GizmoMFV/hydro.h
@@ -17,8 +17,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_GIZMO_HYDRO_H
-#define SWIFT_GIZMO_HYDRO_H
+#ifndef SWIFT_GIZMO_MFV_HYDRO_H
+#define SWIFT_GIZMO_MFV_HYDRO_H
 
 #include "adiabatic_index.h"
 #include "approx_math.h"
@@ -398,7 +398,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
   const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */
 
   /* Re-set problematic values */
-  p->density.wcount = kernel_root * kernel_norm * h_inv_dim;
+  p->density.wcount = kernel_root * h_inv_dim;
   p->density.wcount_dh = 0.f;
   p->geometry.volume = 1.0f;
   p->geometry.matrix_E[0][0] = 1.0f;
@@ -421,8 +421,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
 /**
  * @brief Prepare a particle for the gradient calculation.
  *
- * The name of this method is confusing, as this method is really called after
- * the density loop and before the gradient loop.
+ * This function is 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
@@ -433,7 +432,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param xp The extended particle data to act upon.
  * @param cosmo The cosmological model.
  */
-__attribute__((always_inline)) INLINE static void hydro_prepare_force(
+__attribute__((always_inline)) INLINE static void hydro_prepare_gradient(
     struct part* restrict p, struct xpart* restrict xp,
     const struct cosmology* cosmo) {
 
@@ -452,8 +451,6 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
  * 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_end_gradient(
@@ -461,6 +458,28 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient(
 
   hydro_gradients_finalize(p);
 
+#ifdef GIZMO_LLOYD_ITERATION
+  /* reset the gradients to zero, as we don't want them */
+  hydro_gradients_init(p);
+#endif
+}
+
+/**
+ * @brief Prepare a particle for the force calculation.
+ *
+ * This function is called in the extra_ghost task to convert some quantities
+ * coming from the gradient loop over neighbours into quantities ready to be
+ * used in the force loop over neighbours.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_prepare_force(
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct cosmology* cosmo) {
+
+  /* Initialise values that are used in the force loop */
   p->gravity.mflux[0] = 0.0f;
   p->gravity.mflux[1] = 0.0f;
   p->gravity.mflux[2] = 0.0f;
@@ -470,11 +489,6 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient(
   p->conserved.flux.momentum[1] = 0.0f;
   p->conserved.flux.momentum[2] = 0.0f;
   p->conserved.flux.energy = 0.0f;
-
-#ifdef GIZMO_LLOYD_ITERATION
-  /* reset the gradients to zero, as we don't want them */
-  hydro_gradients_init(p);
-#endif
 }
 
 /**
@@ -497,6 +511,18 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
   p->force.h_dt = 0.0f;
 }
 
+/**
+ * @brief Resets the variables that are required for a gradient calculation.
+ *
+ * This function is called after hydro_prepare_gradient.
+ *
+ * @param p The particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_reset_gradient(
+    struct part* restrict p) {}
+
 /**
  * @brief Sets the values to be predicted in the drifts to their values at a
  * kick time
@@ -972,4 +998,4 @@ hydro_set_init_internal_energy(struct part* p, float u_init) {
   p->primitives.P = hydro_gamma_minus_one * p->primitives.rho * u_init;
 }
 
-#endif /* SWIFT_GIZMO_HYDRO_H */
+#endif /* SWIFT_GIZMO_MFV_HYDRO_H */
diff --git a/src/hydro/GizmoMFV/hydro_debug.h b/src/hydro/GizmoMFV/hydro_debug.h
new file mode 100644
index 0000000000000000000000000000000000000000..8af3f824666529efad833c3bd520ace779718449
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_debug.h
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_GIZMO_MFV_HYDRO_DEBUG_H
+#define SWIFT_GIZMO_MFV_HYDRO_DEBUG_H
+
+__attribute__((always_inline)) INLINE static void hydro_debug_particle(
+    const struct part* p, const struct xpart* xp) {
+  printf(
+      "x=[%.16e,%.16e,%.16e], "
+      "v=[%.3e,%.3e,%.3e], "
+      "a=[%.3e,%.3e,%.3e], "
+      "h=%.3e, "
+      "time_bin=%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={"
+      "wcount_dh=%.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->h, p->time_bin, 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.wcount_dh, p->density.wcount);
+}
+
+#endif /* SWIFT_GIZMO_MFV_HYDRO_DEBUG_H */
diff --git a/src/hydro/GizmoMFV/hydro_gradients.h b/src/hydro/GizmoMFV/hydro_gradients.h
new file mode 100644
index 0000000000000000000000000000000000000000..387c263775ddfb37e4c7cb31a624ba5dc673beb2
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_gradients.h
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * 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_GIZMO_MFV_GRADIENTS_H
+#define SWIFT_HYDRO_GIZMO_MFV_GRADIENTS_H
+
+#include "hydro_slope_limiters.h"
+#include "hydro_unphysical.h"
+#include "riemann.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, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict 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, const float *dx, float hi, float hj,
+                               struct part *restrict pi,
+                               struct part *restrict 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* restrict pi, struct part* restrict pj, float hi, float hj,
+    const float* dx, float r, const float* xij_i, float* Wi, float* Wj) {
+
+  /* perform gradient reconstruction in space and time */
+  /* Compute interface position (relative to pj, since we don't need the actual
+   * position) eqn. (8) */
+  const float xfac = hj / (hi + hj);
+  const float xij_j[3] = {xfac * dx[0], xfac * dx[1], xfac * dx[2]};
+
+  float dWi[5];
+  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];
+
+  float dWj[5];
+  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];
+
+  /* Apply the slope limiter at this interface */
+  hydro_slope_limit_face(Wi, Wj, dWi, dWj, xij_i, xij_j, r);
+
+  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];
+
+  gizmo_check_physical_quantities("density", "pressure", Wi[0], Wi[1], Wi[2],
+                                  Wi[3], Wi[4]);
+  gizmo_check_physical_quantities("density", "pressure", Wj[0], Wj[1], Wj[2],
+                                  Wj[3], Wj[4]);
+}
+
+#endif /* SWIFT_HYDRO_GIZMO_MFV_GRADIENTS_H */
diff --git a/src/hydro/GizmoMFV/hydro_gradients_gizmo.h b/src/hydro/GizmoMFV/hydro_gradients_gizmo.h
new file mode 100644
index 0000000000000000000000000000000000000000..2592f46da9a8c118d18beba933f98f477ec5a3b2
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_gradients_gizmo.h
@@ -0,0 +1,488 @@
+/*******************************************************************************
+ * 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.
+ */
+#ifndef SWIFT_GIZMO_MFV_HYDRO_GRADIENTS_H
+#define SWIFT_GIZMO_MFV_HYDRO_GRADIENTS_H
+
+__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, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj) {
+
+  const float r_inv = 1.f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  float wi, wj, wi_dx, wj_dx;
+  float Bi[3][3];
+  float Bj[3][3];
+  float Wi[5], Wj[5];
+
+  /* Initialize local variables */
+  for (int k = 0; k < 3; k++) {
+    for (int 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. */
+  const float hi_inv = 1.f / hi;
+  const float xi = r * hi_inv;
+  kernel_deval(xi, &wi, &wi_dx);
+
+  if (pi->density.wcorr > const_gizmo_min_wcorr) {
+    /* 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]);
+
+  } else {
+    /* The gradient matrix was not well-behaved, switch to SPH gradients */
+
+    pi->primitives.gradients.rho[0] -=
+        wi_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+    pi->primitives.gradients.rho[1] -=
+        wi_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+    pi->primitives.gradients.rho[2] -=
+        wi_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+
+    pi->primitives.gradients.v[0][0] -=
+        wi_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+    pi->primitives.gradients.v[0][1] -=
+        wi_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+    pi->primitives.gradients.v[0][2] -=
+        wi_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+
+    pi->primitives.gradients.v[1][0] -=
+        wi_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+    pi->primitives.gradients.v[1][1] -=
+        wi_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+    pi->primitives.gradients.v[1][2] -=
+        wi_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+
+    pi->primitives.gradients.v[2][0] -=
+        wi_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+    pi->primitives.gradients.v[2][1] -=
+        wi_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+    pi->primitives.gradients.v[2][2] -=
+        wi_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+
+    pi->primitives.gradients.P[0] -=
+        wi_dx * dx[0] * (pi->primitives.P - pj->primitives.P) * r_inv;
+    pi->primitives.gradients.P[1] -=
+        wi_dx * dx[1] * (pi->primitives.P - pj->primitives.P) * r_inv;
+    pi->primitives.gradients.P[2] -=
+        wi_dx * dx[2] * (pi->primitives.P - pj->primitives.P) * r_inv;
+  }
+
+  hydro_slope_limit_cell_collect(pi, pj, r);
+
+  /* Compute kernel of pj. */
+  const float hj_inv = 1.f / hj;
+  const float xj = r * hj_inv;
+  kernel_deval(xj, &wj, &wj_dx);
+
+  if (pj->density.wcorr > const_gizmo_min_wcorr) {
+    /* 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]);
+
+  } else {
+    /* SPH gradients */
+
+    pj->primitives.gradients.rho[0] -=
+        wj_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+    pj->primitives.gradients.rho[1] -=
+        wj_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+    pj->primitives.gradients.rho[2] -=
+        wj_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+
+    pj->primitives.gradients.v[0][0] -=
+        wj_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+    pj->primitives.gradients.v[0][1] -=
+        wj_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+    pj->primitives.gradients.v[0][2] -=
+        wj_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+
+    pj->primitives.gradients.v[1][0] -=
+        wj_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+    pj->primitives.gradients.v[1][1] -=
+        wj_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+    pj->primitives.gradients.v[1][2] -=
+        wj_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+    pj->primitives.gradients.v[2][0] -=
+        wj_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+    pj->primitives.gradients.v[2][1] -=
+        wj_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+    pj->primitives.gradients.v[2][2] -=
+        wj_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+
+    pj->primitives.gradients.P[0] -=
+        wj_dx * dx[0] * (pi->primitives.P - pj->primitives.P) * r_inv;
+    pj->primitives.gradients.P[1] -=
+        wj_dx * dx[1] * (pi->primitives.P - pj->primitives.P) * r_inv;
+    pj->primitives.gradients.P[2] -=
+        wj_dx * dx[2] * (pi->primitives.P - pj->primitives.P) * r_inv;
+  }
+
+  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, const float *dx, float hi, float hj,
+                               struct part *restrict pi,
+                               struct part *restrict pj) {
+
+  const float r_inv = 1.f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  float Bi[3][3];
+  float Wi[5], Wj[5];
+
+  /* Initialize local variables */
+  for (int k = 0; k < 3; k++) {
+    for (int 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. */
+  float wi, wi_dx;
+  const float hi_inv = 1.f / hi;
+  const float xi = r * hi_inv;
+  kernel_deval(xi, &wi, &wi_dx);
+
+  if (pi->density.wcorr > const_gizmo_min_wcorr) {
+    /* 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]);
+
+  } else {
+    /* Gradient matrix is not well-behaved, switch to SPH gradients */
+
+    pi->primitives.gradients.rho[0] -=
+        wi_dx * dx[0] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+    pi->primitives.gradients.rho[1] -=
+        wi_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+    pi->primitives.gradients.rho[2] -=
+        wi_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+
+    pi->primitives.gradients.v[0][0] -=
+        wi_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+    pi->primitives.gradients.v[0][1] -=
+        wi_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+    pi->primitives.gradients.v[0][2] -=
+        wi_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+    pi->primitives.gradients.v[1][0] -=
+        wi_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+    pi->primitives.gradients.v[1][1] -=
+        wi_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+    pi->primitives.gradients.v[1][2] -=
+        wi_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+
+    pi->primitives.gradients.v[2][0] -=
+        wi_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+    pi->primitives.gradients.v[2][1] -=
+        wi_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+    pi->primitives.gradients.v[2][2] -=
+        wi_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+
+    pi->primitives.gradients.P[0] -=
+        wi_dx * dx[0] * (pi->primitives.P - pj->primitives.P) * r_inv;
+    pi->primitives.gradients.P[1] -=
+        wi_dx * dx[1] * (pi->primitives.P - pj->primitives.P) * r_inv;
+    pi->primitives.gradients.P[2] -=
+        wi_dx * dx[2] * (pi->primitives.P - pj->primitives.P) * r_inv;
+  }
+
+  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) {
+
+  /* add kernel normalization to gradients */
+  const float volume = p->geometry.volume;
+  const float h = p->h;
+  const float h_inv = 1.0f / h;
+  const float ihdim = pow_dimension(h_inv);
+  const float ihdimp1 = pow_dimension_plus_one(h_inv);
+
+  if (p->density.wcorr > const_gizmo_min_wcorr) {
+    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;
+
+  } else {
+
+    /* 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);
+}
+
+#endif /* SWIFT_GIZMO_MFV_HYDRO_GRADIENTS_H */
diff --git a/src/hydro/GizmoMFV/hydro_gradients_sph.h b/src/hydro/GizmoMFV/hydro_gradients_sph.h
new file mode 100644
index 0000000000000000000000000000000000000000..7b2b89ae688622ff017e4188f9e8fd2c7cee9d7a
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_gradients_sph.h
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * 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.
+ */
+#ifndef SWIFT_GIZMO_MFV_HYDRO_SPH_GRADIENTS_H
+#define SWIFT_GIZMO_MFV_HYDRO_SPH_GRADIENTS_H
+
+__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, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj) {
+
+  const float r_inv = 1.f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  float wi, wi_dx;
+  const float hi_inv = 1.0f / hi;
+  const float 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_inv;
+  pi->primitives.gradients.rho[1] -=
+      wi_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+  pi->primitives.gradients.rho[2] -=
+      wi_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+
+  pi->primitives.gradients.v[0][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+  pi->primitives.gradients.v[0][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+  pi->primitives.gradients.v[0][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+
+  pi->primitives.gradients.v[1][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+  pi->primitives.gradients.v[1][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+  pi->primitives.gradients.v[1][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+
+  pi->primitives.gradients.v[2][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+  pi->primitives.gradients.v[2][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+  pi->primitives.gradients.v[2][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+
+  pi->primitives.gradients.P[0] -=
+      wi_dx * dx[0] * (pi->primitives.P - pj->primitives.P) * r_inv;
+  pi->primitives.gradients.P[1] -=
+      wi_dx * dx[1] * (pi->primitives.P - pj->primitives.P) * r_inv;
+  pi->primitives.gradients.P[2] -=
+      wi_dx * dx[2] * (pi->primitives.P - pj->primitives.P) * r_inv;
+
+  hydro_slope_limit_cell_collect(pi, pj, r);
+
+  float wj, wj_dx;
+  const float hj_inv = 1.0f / hj;
+  const float 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_inv;
+  pj->primitives.gradients.rho[1] -=
+      wj_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+  pj->primitives.gradients.rho[2] -=
+      wj_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+
+  pj->primitives.gradients.v[0][0] -=
+      wj_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+  pj->primitives.gradients.v[0][1] -=
+      wj_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+  pj->primitives.gradients.v[0][2] -=
+      wj_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+
+  pj->primitives.gradients.v[1][0] -=
+      wj_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+  pj->primitives.gradients.v[1][1] -=
+      wj_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+  pj->primitives.gradients.v[1][2] -=
+      wj_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+  pj->primitives.gradients.v[2][0] -=
+      wj_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+  pj->primitives.gradients.v[2][1] -=
+      wj_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+  pj->primitives.gradients.v[2][2] -=
+      wj_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+
+  pj->primitives.gradients.P[0] -=
+      wj_dx * dx[0] * (pi->primitives.P - pj->primitives.P) * r_inv;
+  pj->primitives.gradients.P[1] -=
+      wj_dx * dx[1] * (pi->primitives.P - pj->primitives.P) * r_inv;
+  pj->primitives.gradients.P[2] -=
+      wj_dx * dx[2] * (pi->primitives.P - pj->primitives.P) * r_inv;
+
+  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, const float *dx, float hi, float hj,
+                               struct part *restrict pi,
+                               struct part *restrict pj) {
+
+  const float r_inv = 1.f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  float wi, wi_dx;
+  const float hi_inv = 1.0f / hi;
+  const float 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_inv;
+  pi->primitives.gradients.rho[1] -=
+      wi_dx * dx[1] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+  pi->primitives.gradients.rho[2] -=
+      wi_dx * dx[2] * (pi->primitives.rho - pj->primitives.rho) * r_inv;
+
+  pi->primitives.gradients.v[0][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+  pi->primitives.gradients.v[0][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+  pi->primitives.gradients.v[0][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[0] - pj->primitives.v[0]) * r_inv;
+
+  pi->primitives.gradients.v[1][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+  pi->primitives.gradients.v[1][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+  pi->primitives.gradients.v[1][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[1] - pj->primitives.v[1]) * r_inv;
+
+  pi->primitives.gradients.v[2][0] -=
+      wi_dx * dx[0] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+  pi->primitives.gradients.v[2][1] -=
+      wi_dx * dx[1] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+  pi->primitives.gradients.v[2][2] -=
+      wi_dx * dx[2] * (pi->primitives.v[2] - pj->primitives.v[2]) * r_inv;
+
+  pi->primitives.gradients.P[0] -=
+      wi_dx * dx[0] * (pi->primitives.P - pj->primitives.P) * r_inv;
+  pi->primitives.gradients.P[1] -=
+      wi_dx * dx[1] * (pi->primitives.P - pj->primitives.P) * r_inv;
+  pi->primitives.gradients.P[2] -=
+      wi_dx * dx[2] * (pi->primitives.P - pj->primitives.P) * r_inv;
+
+  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);
+  const 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);
+}
+
+#endif /* SWIFT_GIZMO_MFV_HYDRO_SPH_GRADIENTS_H */
diff --git a/src/hydro/Gizmo/hydro_iact.h b/src/hydro/GizmoMFV/hydro_iact.h
similarity index 99%
rename from src/hydro/Gizmo/hydro_iact.h
rename to src/hydro/GizmoMFV/hydro_iact.h
index 0c888e4159c6da6eb07eb0afbf758efce1c697ee..bb835094acd285b109383c1f5d04a6f5e2d936df 100644
--- a/src/hydro/Gizmo/hydro_iact.h
+++ b/src/hydro/GizmoMFV/hydro_iact.h
@@ -18,8 +18,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_GIZMO_HYDRO_IACT_H
-#define SWIFT_GIZMO_HYDRO_IACT_H
+#ifndef SWIFT_GIZMO_MFV_HYDRO_IACT_H
+#define SWIFT_GIZMO_MFV_HYDRO_IACT_H
 
 #include "adiabatic_index.h"
 #include "hydro_gradients.h"
@@ -514,4 +514,4 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0, a, H);
 }
 
-#endif /* SWIFT_GIZMO_HYDRO_IACT_H */
+#endif /* SWIFT_GIZMO_MFV_HYDRO_IACT_H */
diff --git a/src/hydro/GizmoMFV/hydro_io.h b/src/hydro/GizmoMFV/hydro_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..92e4378f071cb71678929716be86588a3405f40e
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_io.h
@@ -0,0 +1,250 @@
+/*******************************************************************************
+ * 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_GIZMO_MFV_HYDRO_IO_H
+#define SWIFT_GIZMO_MFV_HYDRO_IO_H
+
+#include "adiabatic_index.h"
+#include "hydro.h"
+#include "hydro_gradients.h"
+#include "hydro_slope_limiters.h"
+#include "io_properties.h"
+#include "riemann.h"
+
+/* Set the description of the particle movement. */
+#if defined(GIZMO_FIX_PARTICLES)
+#define GIZMO_PARTICLE_MOVEMENT "Fixed particles."
+#else
+#define GIZMO_PARTICLE_MOVEMENT "Particles move with flow velocity."
+#endif
+
+/**
+ * @brief Specifies which particle fields to read from a dataset
+ *
+ * @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.
+ */
+INLINE static 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.
+ * @param ret (return) Internal energy of the particle
+ */
+INLINE static void convert_u(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
+
+  ret[0] = hydro_get_comoving_internal_energy(p);
+}
+
+/**
+ * @brief Get the entropic function of a particle
+ *
+ * @param e #engine.
+ * @param p Particle.
+ * @param ret (return) Entropic function of the particle
+ */
+INLINE static void convert_A(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
+  ret[0] = hydro_get_comoving_entropy(p);
+}
+
+/**
+ * @brief Get the total energy of a particle
+ *
+ * @param e #engine.
+ * @param p Particle.
+ * @return Total energy of the particle
+ */
+INLINE static void convert_Etot(const struct engine* e, const struct part* p,
+                                const struct xpart* xp, float* ret) {
+#ifdef GIZMO_TOTAL_ENERGY
+  ret[0] = p->conserved.energy;
+#else
+  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];
+
+  ret[0] = p->conserved.energy + 0.5f * momentum2 / p->conserved.mass;
+#endif
+}
+
+INLINE static void convert_part_pos(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, double* ret) {
+
+  if (e->s->periodic) {
+    ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
+    ret[1] = box_wrap(p->x[1], 0.0, e->s->dim[1]);
+    ret[2] = box_wrap(p->x[2], 0.0, e->s->dim[2]);
+  } else {
+    ret[0] = p->x[0];
+    ret[1] = p->x[1];
+    ret[2] = p->x[2];
+  }
+}
+
+INLINE static void convert_part_vel(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, float* ret) {
+
+  const int with_cosmology = (e->policy & engine_policy_cosmology);
+  const struct cosmology* cosmo = e->cosmology;
+  const integertime_t ti_current = e->ti_current;
+  const double time_base = e->time_base;
+
+  const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin);
+  const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin);
+
+  /* Get time-step since the last kick */
+  float dt_kick_grav, dt_kick_hydro;
+  if (with_cosmology) {
+    dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current);
+    dt_kick_grav -=
+        cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2);
+    dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current);
+    dt_kick_hydro -=
+        cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2);
+  } else {
+    dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base;
+    dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base;
+  }
+
+  /* Extrapolate the velocites to the current time */
+  hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret);
+
+  /* Conversion from internal units to peculiar velocities */
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
+}
+
+INLINE static void convert_part_potential(const struct engine* e,
+                                          const struct part* p,
+                                          const struct xpart* xp, float* ret) {
+
+  if (p->gpart != NULL)
+    ret[0] = gravity_get_comoving_potential(p->gpart);
+  else
+    ret[0] = 0.f;
+}
+
+/**
+ * @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.
+ */
+INLINE static void hydro_write_particles(const struct part* parts,
+                                         const struct xpart* xparts,
+                                         struct io_props* list,
+                                         int* num_fields) {
+
+  *num_fields = 11;
+
+  /* List what we want to write */
+  list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3,
+                                              UNIT_CONV_LENGTH, parts, xparts,
+                                              convert_part_pos);
+  list[1] = io_make_output_field_convert_part(
+      "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel);
+
+  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, xparts, convert_u);
+  list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
+                                 UNIT_CONV_NO_UNITS, parts, id);
+  list[6] = io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts,
+                                 primitives.rho);
+  list[7] = io_make_output_field_convert_part(
+      "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY, parts, xparts, convert_A);
+  list[8] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE,
+                                 parts, primitives.P);
+  list[9] = io_make_output_field_convert_part(
+      "TotEnergy", FLOAT, 1, UNIT_CONV_ENERGY, parts, xparts, convert_Etot);
+
+  list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1,
+                                               UNIT_CONV_POTENTIAL, parts,
+                                               xparts, convert_part_potential);
+}
+
+/**
+ * @brief Writes the current model of SPH to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+INLINE static void hydro_write_flavour(hid_t h_grpsph) {
+  /* Gradient information */
+  io_write_attribute_s(h_grpsph, "Gradient reconstruction model",
+                       HYDRO_GRADIENT_IMPLEMENTATION);
+
+  /* Slope limiter information */
+  io_write_attribute_s(h_grpsph, "Cell wide slope limiter model",
+                       HYDRO_SLOPE_LIMITER_CELL_IMPLEMENTATION);
+  io_write_attribute_s(h_grpsph, "Piecewise slope limiter model",
+                       HYDRO_SLOPE_LIMITER_FACE_IMPLEMENTATION);
+
+  /* Riemann solver information */
+  io_write_attribute_s(h_grpsph, "Riemann solver type",
+                       RIEMANN_SOLVER_IMPLEMENTATION);
+
+  /* Particle movement information */
+  io_write_attribute_s(h_grpsph, "Particle movement", GIZMO_PARTICLE_MOVEMENT);
+}
+
+/**
+ * @brief Are we writing entropy in the internal energy field ?
+ *
+ * @return 1 if entropy is in 'internal energy', 0 otherwise.
+ */
+INLINE static int writeEntropyFlag(void) { return 0; }
+
+#endif /* SWIFT_GIZMO_MFV_HYDRO_IO_H */
diff --git a/src/hydro/GizmoMFV/hydro_part.h b/src/hydro/GizmoMFV/hydro_part.h
new file mode 100644
index 0000000000000000000000000000000000000000..6248ddb11daf39a65be9a57fe51e40386ecda50b
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_part.h
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * 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
+ * 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_GIZMO_MFV_HYDRO_PART_H
+#define SWIFT_GIZMO_MFV_HYDRO_PART_H
+
+#include "chemistry_struct.h"
+#include "cooling_struct.h"
+
+/* Extra particle data not needed during the computation. */
+struct xpart {
+
+  /* Offset between current position and position at last tree rebuild. */
+  float x_diff[3];
+
+  /* Offset between the current position and position at the last sort. */
+  float x_diff_sort[3];
+
+  /* Velocity at the last full step. */
+  float v_full[3];
+
+  /* Gravitational acceleration at the last full step. */
+  float a_grav[3];
+
+  /* Additional data used to record cooling information */
+  struct cooling_xpart_data cooling_data;
+
+} SWIFT_STRUCT_ALIGN;
+
+/* Data of a single particle. */
+struct part {
+
+  /* Particle ID. */
+  long long id;
+
+  /* Associated gravitas. */
+  struct gpart *gpart;
+
+  /* Particle position. */
+  double x[3];
+
+  /* Particle predicted velocity. */
+  float v[3];
+
+  /* Particle acceleration. */
+  float a_hydro[3];
+
+  /* Particle smoothing length. */
+  float h;
+
+  /* The primitive hydrodynamical variables. */
+  struct {
+
+    /* Density. */
+    float rho;
+
+    /* Fluid velocity. */
+    float v[3];
+
+    /* Pressure. */
+    float P;
+
+    /* Gradients of the primitive variables. */
+    struct {
+
+      /* Density gradients. */
+      float rho[3];
+
+      /* Fluid velocity gradients. */
+      float v[3][3];
+
+      /* Pressure gradients. */
+      float P[3];
+
+    } gradients;
+
+    /* Quantities needed by the slope limiter. */
+    struct {
+
+      /* Extreme values of the density among the neighbours. */
+      float rho[2];
+
+      /* Extreme values of the fluid velocity among the neighbours. */
+      float v[3][2];
+
+      /* Extreme values of the pressure among the neighbours. */
+      float P[2];
+
+      /* Maximal distance to all neighbouring faces. */
+      float maxr;
+
+    } limiter;
+
+  } primitives;
+
+  /* The conserved hydrodynamical variables. */
+  struct {
+
+    /* Fluid mass */
+    float mass;
+
+    /* Fluid momentum. */
+    float momentum[3];
+
+    /* Fluid thermal energy (not per unit mass!). */
+    float energy;
+
+    /* Fluxes. */
+    struct {
+
+      /* Mass flux. */
+      float mass;
+
+      /* Momentum flux. */
+      float momentum[3];
+
+      /* Energy flux. */
+      float energy;
+
+    } flux;
+
+  } conserved;
+
+  /* Geometrical quantities used for hydro. */
+  struct {
+
+    /* Volume of the particle. */
+    float volume;
+
+    /* Geometrical shear matrix used to calculate second order accurate
+       gradients */
+    float matrix_E[3][3];
+
+    /* Centroid of the "cell". */
+    float centroid[3];
+
+  } geometry;
+
+  /* Variables used for timestep calculation. */
+  struct {
+
+    /* Maximum signal velocity among all the neighbours of the particle. The
+     * signal velocity encodes information about the relative fluid velocities
+     * AND particle velocities of the neighbour and this particle, as well as
+     * the sound speed of both particles. */
+    float vmax;
+
+  } timestepvars;
+
+  /* Quantities used during the volume (=density) loop. */
+  struct {
+
+    /* Derivative of particle number density. */
+    float wcount_dh;
+
+    /* Particle number density. */
+    float wcount;
+
+    /* Correction factor for wcount. */
+    float wcorr;
+
+  } density;
+
+  /* Quantities used during the force loop. */
+  struct {
+
+    /* Needed to drift the primitive variables. */
+    float h_dt;
+
+  } force;
+
+  /* Specific stuff for the gravity-hydro coupling. */
+  struct {
+
+    /* Current value of the mass flux vector. */
+    float mflux[3];
+
+  } gravity;
+
+  /* Chemistry information */
+  struct chemistry_part_data chemistry_data;
+
+  /* Time-step length */
+  timebin_t time_bin;
+
+#ifdef SWIFT_DEBUG_CHECKS
+
+  /* Time of the last drift */
+  integertime_t ti_drift;
+
+  /* Time of the last kick */
+  integertime_t ti_kick;
+
+#endif
+
+} SWIFT_STRUCT_ALIGN;
+
+#endif /* SWIFT_GIZMO_MFV_HYDRO_PART_H */
diff --git a/src/hydro/GizmoMFV/hydro_slope_limiters.h b/src/hydro/GizmoMFV/hydro_slope_limiters.h
new file mode 100644
index 0000000000000000000000000000000000000000..78f2785cdae5dc2334d37e3924dd5b259cca8c05
--- /dev/null
+++ b/src/hydro/GizmoMFV/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/GizmoMFV/hydro_slope_limiters_cell.h b/src/hydro/GizmoMFV/hydro_slope_limiters_cell.h
new file mode 100644
index 0000000000000000000000000000000000000000..130a87def95a3198f0871ce485f0a9d377a96cd5
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_slope_limiters_cell.h
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * 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_GIZMO_MFV_SLOPE_LIMITER_CELL_H
+#define SWIFT_GIZMO_MFV_SLOPE_LIMITER_CELL_H
+
+#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] =
+      min(pj->primitives.rho, pi->primitives.limiter.rho[0]);
+  pi->primitives.limiter.rho[1] =
+      max(pj->primitives.rho, pi->primitives.limiter.rho[1]);
+
+  pi->primitives.limiter.v[0][0] =
+      min(pj->primitives.v[0], pi->primitives.limiter.v[0][0]);
+  pi->primitives.limiter.v[0][1] =
+      max(pj->primitives.v[0], pi->primitives.limiter.v[0][1]);
+  pi->primitives.limiter.v[1][0] =
+      min(pj->primitives.v[1], pi->primitives.limiter.v[1][0]);
+  pi->primitives.limiter.v[1][1] =
+      max(pj->primitives.v[1], pi->primitives.limiter.v[1][1]);
+  pi->primitives.limiter.v[2][0] =
+      min(pj->primitives.v[2], pi->primitives.limiter.v[2][0]);
+  pi->primitives.limiter.v[2][1] =
+      max(pj->primitives.v[2], pi->primitives.limiter.v[2][1]);
+
+  pi->primitives.limiter.P[0] =
+      min(pj->primitives.P, pi->primitives.limiter.P[0]);
+  pi->primitives.limiter.P[1] =
+      max(pj->primitives.P, pi->primitives.limiter.P[1]);
+
+  pi->primitives.limiter.maxr = max(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 gradtrue, gradrho[3], gradv[3][3], gradP[3];
+
+  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;
+    const float gradmax = p->primitives.limiter.rho[1] - p->primitives.rho;
+    const float gradmin = p->primitives.rho - p->primitives.limiter.rho[0];
+    const float gradtrue_inv = 1.f / gradtrue;
+    const float alpha =
+        min3(1.0f, gradmax * gradtrue_inv, gradmin * gradtrue_inv);
+    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;
+    const float gradmax = p->primitives.limiter.v[0][1] - p->primitives.v[0];
+    const float gradmin = p->primitives.v[0] - p->primitives.limiter.v[0][0];
+    const float gradtrue_inv = 1.f / gradtrue;
+    const float alpha =
+        min3(1.0f, gradmax * gradtrue_inv, gradmin * gradtrue_inv);
+    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;
+    const float gradmax = p->primitives.limiter.v[1][1] - p->primitives.v[1];
+    const float gradmin = p->primitives.v[1] - p->primitives.limiter.v[1][0];
+    const float gradtrue_inv = 1.f / gradtrue;
+    const float alpha =
+        min3(1.0f, gradmax * gradtrue_inv, gradmin * gradtrue_inv);
+    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;
+    const float gradmax = p->primitives.limiter.v[2][1] - p->primitives.v[2];
+    const float gradmin = p->primitives.v[2] - p->primitives.limiter.v[2][0];
+    const float gradtrue_inv = 1.f / gradtrue;
+    const float alpha =
+        min3(1.0f, gradmax * gradtrue_inv, gradmin * gradtrue_inv);
+    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;
+    const float gradmax = p->primitives.limiter.P[1] - p->primitives.P;
+    const float gradmin = p->primitives.P - p->primitives.limiter.P[0];
+    const float gradtrue_inv = 1.f / gradtrue;
+    const float alpha =
+        min3(1.0f, gradmax * gradtrue_inv, gradmin * gradtrue_inv);
+    p->primitives.gradients.P[0] *= alpha;
+    p->primitives.gradients.P[1] *= alpha;
+    p->primitives.gradients.P[2] *= alpha;
+  }
+}
+
+#endif /* SWIFT_GIZMO_MFV_SLOPE_LIMITER_CELL_H */
diff --git a/src/hydro/GizmoMFV/hydro_slope_limiters_face.h b/src/hydro/GizmoMFV/hydro_slope_limiters_face.h
new file mode 100644
index 0000000000000000000000000000000000000000..b12f15590f51c16f494978d9501401709b2cbf59
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_slope_limiters_face.h
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * 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.
+ */
+#ifndef SWIFT_GIZMO_MFV_SLOPE_LIMITER_FACE_H
+#define SWIFT_GIZMO_MFV_SLOPE_LIMITER_FACE_H
+
+/* Some standard headers. */
+#include <float.h>
+
+/* Local headers. */
+#include "minmax.h"
+#include "sign.h"
+
+__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_inv) {
+
+  const float psi1 = 0.5f;
+  const float psi2 = 0.25f;
+
+  const float delta1 = psi1 * fabsf(phi_i - phi_j);
+  const float delta2 = psi2 * fabsf(phi_i - phi_j);
+
+  const float phimin = min(phi_i, phi_j);
+  const float phimax = max(phi_i, phi_j);
+
+  const float phibar = phi_i + xij_norm * r_inv * (phi_j - phi_i);
+
+  float phiplus, phiminus, phi_mid;
+
+  if (same_signf(phimax + delta1, phimax))
+    phiplus = phimax + delta1;
+  else
+    phiplus = phimax / (1.0f + delta1 / (fabsf(phimax) + FLT_MIN));
+
+  if (same_signf(phimin - delta1, phimin))
+    phiminus = phimin - delta1;
+  else
+    phiminus = phimin / (1.0f + delta1 / (fabsf(phimin) + FLT_MIN));
+
+  if (phi_i < phi_j) {
+    const float temp = min(phibar + delta2, phi_mid0);
+    phi_mid = max(phiminus, temp);
+  } else {
+    const float temp = max(phibar - delta2, phi_mid0);
+    phi_mid = min(phiplus, temp);
+  }
+
+  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, const float *xij_i,
+    const float *xij_j, float r) {
+
+  const float xij_i_norm =
+      sqrtf(xij_i[0] * xij_i[0] + xij_i[1] * xij_i[1] + xij_i[2] * xij_i[2]);
+
+  const float xij_j_norm =
+      sqrtf(xij_j[0] * xij_j[0] + xij_j[1] * xij_j[1] + xij_j[2] * xij_j[2]);
+
+  const float r_inv = 1.f / r;
+
+  dWi[0] = hydro_slope_limit_face_quantity(Wi[0], Wj[0], Wi[0] + dWi[0],
+                                           xij_i_norm, r_inv);
+  dWi[1] = hydro_slope_limit_face_quantity(Wi[1], Wj[1], Wi[1] + dWi[1],
+                                           xij_i_norm, r_inv);
+  dWi[2] = hydro_slope_limit_face_quantity(Wi[2], Wj[2], Wi[2] + dWi[2],
+                                           xij_i_norm, r_inv);
+  dWi[3] = hydro_slope_limit_face_quantity(Wi[3], Wj[3], Wi[3] + dWi[3],
+                                           xij_i_norm, r_inv);
+  dWi[4] = hydro_slope_limit_face_quantity(Wi[4], Wj[4], Wi[4] + dWi[4],
+                                           xij_i_norm, r_inv);
+
+  dWj[0] = hydro_slope_limit_face_quantity(Wj[0], Wi[0], Wj[0] + dWj[0],
+                                           xij_j_norm, r_inv);
+  dWj[1] = hydro_slope_limit_face_quantity(Wj[1], Wi[1], Wj[1] + dWj[1],
+                                           xij_j_norm, r_inv);
+  dWj[2] = hydro_slope_limit_face_quantity(Wj[2], Wi[2], Wj[2] + dWj[2],
+                                           xij_j_norm, r_inv);
+  dWj[3] = hydro_slope_limit_face_quantity(Wj[3], Wi[3], Wj[3] + dWj[3],
+                                           xij_j_norm, r_inv);
+  dWj[4] = hydro_slope_limit_face_quantity(Wj[4], Wi[4], Wj[4] + dWj[4],
+                                           xij_j_norm, r_inv);
+}
+
+#endif /* SWIFT_GIZMO_MFV_SLOPE_LIMITER_FACE_H */
diff --git a/src/hydro/GizmoMFV/hydro_unphysical.h b/src/hydro/GizmoMFV/hydro_unphysical.h
new file mode 100644
index 0000000000000000000000000000000000000000..81c644e6cce00e0d871aafc39140e420e042a926
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_unphysical.h
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2017 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_UNPHYSICAL_H
+#define SWIFT_HYDRO_UNPHYSICAL_H
+
+#if defined(GIZMO_UNPHYSICAL_ERROR) || defined(GIZMO_UNPHYSICAL_RESCUE)
+
+#if defined(GIZMO_UNPHYSICAL_ERROR)
+
+/*! @brief Crash whenever an unphysical value is detected. */
+#define gizmo_unphysical_message(name, quantity) \
+  error("Unphysical " name " detected (%g)!", quantity);
+
+#elif defined(GIZMO_UNPHYSICAL_WARNING)
+
+/*! @brief Show a warning whenever an unphysical value is detected. */
+#define gizmo_unphysical_message(name, quantity) \
+  message("Unphysical " name " detected (%g), reset to 0!", quantity);
+
+#else
+
+/*! @brief Don't tell anyone an unphysical value was detected. */
+#define gizmo_unphysical_message(name, quantity)
+
+#endif
+
+#define gizmo_check_physical_quantity(name, quantity) \
+  if (quantity < 0.f) {                               \
+    gizmo_unphysical_message(name, quantity);         \
+    quantity = 0.f;                                   \
+  }
+
+#define gizmo_check_physical_quantities(                                      \
+    mass_name, energy_name, mass, momentum_x, momentum_y, momentum_z, energy) \
+  gizmo_check_physical_quantity(mass_name, mass);                             \
+  gizmo_check_physical_quantity(energy_name, energy);                         \
+  /* now check for vacuum and make sure we have a real vacuum */              \
+  if (mass == 0.f || energy == 0.f) {                                         \
+    mass = 0.f;                                                               \
+    momentum_x = 0.f;                                                         \
+    momentum_y = 0.f;                                                         \
+    momentum_z = 0.f;                                                         \
+    energy = 0.f;                                                             \
+  }
+
+#else  // defined(GIZMO_UNPHYSICAL_ERROR) || defined(GIZMO_UNPHYSICAL_RESCUE)
+
+#define gizmo_check_physical_quantities( \
+    mass_name, energy_name, mass, momentum_x, momentum_y, momentum_z, energy)
+
+#endif  // defined(GIZMO_UNPHYSICAL_ERROR) || defined(GIZMO_UNPHYSICAL_RESCUE)
+
+#endif /* SWIFT_HYDRO_UNPHYSICAL_H */
diff --git a/src/hydro/GizmoMFV/hydro_velocities.h b/src/hydro/GizmoMFV/hydro_velocities.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea30d5a6c9c74d34ffd73fa6ab941640f37b02e4
--- /dev/null
+++ b/src/hydro/GizmoMFV/hydro_velocities.h
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2017 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_VELOCITIES_H
+#define SWIFT_HYDRO_VELOCITIES_H
+
+/**
+ * @brief Initialize the GIZMO particle velocities before the start of the
+ * actual run based on the initial value of the primitive velocity.
+ *
+ * @param p The particle to act upon.
+ * @param xp The extended particle data to act upon.
+ */
+__attribute__((always_inline)) INLINE static void hydro_velocities_init(
+    struct part* restrict p, struct xpart* restrict xp) {
+
+#ifdef GIZMO_FIX_PARTICLES
+  p->v[0] = 0.f;
+  p->v[1] = 0.f;
+  p->v[2] = 0.f;
+#else
+  p->v[0] = p->primitives.v[0];
+  p->v[1] = p->primitives.v[1];
+  p->v[2] = p->primitives.v[2];
+#endif
+
+  xp->v_full[0] = p->v[0];
+  xp->v_full[1] = p->v[1];
+  xp->v_full[2] = p->v[2];
+}
+
+/**
+ * @brief Set the particle velocity field that will be used to deboost fluid
+ * velocities during the force loop.
+ *
+ * @param p The particle to act upon.
+ * @param xp The extended particle data to act upon.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_velocities_prepare_force(struct part* restrict p,
+                               const struct xpart* restrict xp) {}
+
+/**
+ * @brief Set the variables that will be used to update the smoothing length
+ * during the drift (these will depend on the movement of the particles).
+ *
+ * @param p The particle to act upon.
+ */
+__attribute__((always_inline)) INLINE static void hydro_velocities_end_force(
+    struct part* restrict p) {
+
+#ifdef GIZMO_FIX_PARTICLES
+  /* disable the smoothing length update, since the smoothing lengths should
+     stay the same for all steps (particles don't move) */
+  p->force.h_dt = 0.0f;
+#else
+  /* Add normalization to h_dt. */
+  p->force.h_dt *= p->h * hydro_dimension_inv;
+#endif
+}
+
+/**
+ * @brief Set the velocity of a GIZMO particle, based on the values of its
+ * primitive variables and the geometry of its mesh-free "cell".
+ *
+ * @param p The particle to act upon.
+ * @param xp The extended particle data to act upon.
+ */
+__attribute__((always_inline)) INLINE static void hydro_velocities_set(
+    struct part* restrict p, struct xpart* restrict xp) {
+
+/* We first set the particle velocity. */
+#ifdef GIZMO_FIX_PARTICLES
+
+  p->v[0] = 0.f;
+  p->v[1] = 0.f;
+  p->v[2] = 0.f;
+
+#else  // GIZMO_FIX_PARTICLES
+
+  if (p->conserved.mass > 0.f && p->primitives.rho > 0.f) {
+
+    const float inverse_mass = 1.f / p->conserved.mass;
+
+    /* Normal case: set particle velocity to fluid velocity. */
+    p->v[0] = p->conserved.momentum[0] * inverse_mass;
+    p->v[1] = p->conserved.momentum[1] * inverse_mass;
+    p->v[2] = p->conserved.momentum[2] * inverse_mass;
+
+#ifdef GIZMO_STEER_MOTION
+
+    /* Add a correction to the velocity to keep particle positions close enough
+       to
+       the centroid of their mesh-free "cell". */
+    /* The correction term below is the same one described in Springel (2010).
+     */
+    float ds[3];
+    ds[0] = p->geometry.centroid[0];
+    ds[1] = p->geometry.centroid[1];
+    ds[2] = p->geometry.centroid[2];
+    const float d = sqrtf(ds[0] * ds[0] + ds[1] * ds[1] + ds[2] * ds[2]);
+    const float R = get_radius_dimension_sphere(p->geometry.volume);
+    const float eta = 0.25f;
+    const float etaR = eta * R;
+    const float xi = 1.f;
+    const float soundspeed =
+        sqrtf(hydro_gamma * p->primitives.P / p->primitives.rho);
+    /* We only apply the correction if the offset between centroid and position
+       is too large. */
+    if (d > 0.9f * etaR) {
+      float fac = xi * soundspeed / d;
+      if (d < 1.1f * etaR) {
+        fac *= 5.f * (d - 0.9f * etaR) / etaR;
+      }
+      p->v[0] -= ds[0] * fac;
+      p->v[1] -= ds[1] * fac;
+      p->v[2] -= ds[2] * fac;
+    }
+
+#endif  // GIZMO_STEER_MOTION
+  } else {
+    /* Vacuum particles have no fluid velocity. */
+    p->v[0] = 0.f;
+    p->v[1] = 0.f;
+    p->v[2] = 0.f;
+  }
+
+#endif  // GIZMO_FIX_PARTICLES
+
+  /* Now make sure all velocity variables are up to date. */
+  xp->v_full[0] = p->v[0];
+  xp->v_full[1] = p->v[1];
+  xp->v_full[2] = p->v[2];
+
+  if (p->gpart) {
+    p->gpart->v_full[0] = p->v[0];
+    p->gpart->v_full[1] = p->v[1];
+    p->gpart->v_full[2] = p->v[2];
+  }
+}
+
+#endif /* SWIFT_HYDRO_VELOCITIES_H */
diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h
index 3f9d99683bde4ed6db64d8aaa5b111e2f67f0969..812f8ad72de55ad7990ee6ef88223a401780bc4b 100644
--- a/src/hydro/Minimal/hydro.h
+++ b/src/hydro/Minimal/hydro.h
@@ -367,7 +367,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
 
   /* Re-set problematic values */
   p->rho = p->mass * kernel_root * h_inv_dim;
-  p->density.wcount = kernel_root * kernel_norm * h_inv_dim;
+  p->density.wcount = kernel_root * h_inv_dim;
   p->density.rho_dh = 0.f;
   p->density.wcount_dh = 0.f;
 }
@@ -426,7 +426,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
   /* Reset the time derivatives. */
   p->u_dt = 0.0f;
   p->force.h_dt = 0.0f;
-  p->force.v_sig = 0.0f;
+  p->force.v_sig = p->force.soundspeed;
 }
 
 /**
diff --git a/src/hydro/Minimal/hydro_debug.h b/src/hydro/Minimal/hydro_debug.h
index 541029ee06dd2799443fc89b688d7baca3fae0f8..73ffc26b8acf687a5445591ddccd72ea8e8fa8ae 100644
--- a/src/hydro/Minimal/hydro_debug.h
+++ b/src/hydro/Minimal/hydro_debug.h
@@ -36,16 +36,17 @@
 __attribute__((always_inline)) INLINE static void hydro_debug_particle(
     const struct part* p, const struct xpart* xp) {
   printf(
-      "x=[%.3e,%.3e,%.3e], "
-      "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e], "
-      "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n"
-      "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, "
-      "time_bin=%d\n",
+      "\n "
+      "x=[%.6g, %.6g, %.6g], v=[%.3g, %.3g, %.3g], \n "
+      "v_full=[%.3g, %.3g, %.3g], a=[%.3g, %.3g, %.3g], \n "
+      "m=%.3g, u=%.3g, du/dt=%.3g, P=%.3g, c_s=%.3g, \n "
+      "v_sig=%.3g, h=%.3g, dh/dt=%.3g, wcount=%.3g, rho=%.3g, \n "
+      "dh_drho=%.3g, time_bin=%d \n",
       p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0],
       xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2],
-      p->u, p->u_dt, p->force.v_sig, hydro_get_comoving_pressure(p), p->h,
-      p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho,
-      p->time_bin);
+      p->mass, p->u, p->u_dt, hydro_get_comoving_pressure(p),
+      p->force.soundspeed, p->force.v_sig, p->h, p->force.h_dt,
+      p->density.wcount, p->rho, p->density.rho_dh, p->time_bin);
 }
 
 #endif /* SWIFT_MINIMAL_HYDRO_DEBUG_H */
diff --git a/src/hydro/Minimal/hydro_io.h b/src/hydro/Minimal/hydro_io.h
index 380d6120e05acf2e015ece6f133df02fad3b761d..879255640fc1a1d6a06a666c80d3860c9c31ab64 100644
--- a/src/hydro/Minimal/hydro_io.h
+++ b/src/hydro/Minimal/hydro_io.h
@@ -45,8 +45,9 @@
  * @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) {
+INLINE static void hydro_read_particles(struct part* parts,
+                                        struct io_props* list,
+                                        int* num_fields) {
 
   *num_fields = 8;
 
@@ -69,20 +70,21 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
                                 UNIT_CONV_DENSITY, parts, rho);
 }
 
-void convert_S(const struct engine* e, const struct part* p,
-               const struct xpart* xp, float* ret) {
+INLINE static void convert_S(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
 
   ret[0] = hydro_get_comoving_entropy(p);
 }
 
-void convert_P(const struct engine* e, const struct part* p,
-               const struct xpart* xp, float* ret) {
+INLINE static void convert_P(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
 
   ret[0] = hydro_get_comoving_pressure(p);
 }
 
-void convert_part_pos(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, double* ret) {
+INLINE static void convert_part_pos(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -95,8 +97,9 @@ void convert_part_pos(const struct engine* e, const struct part* p,
   }
 }
 
-void convert_part_vel(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, float* ret) {
+INLINE static void convert_part_vel(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, float* ret) {
 
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const struct cosmology* cosmo = e->cosmology;
@@ -124,13 +127,14 @@ void convert_part_vel(const struct engine* e, const struct part* p,
   hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret);
 
   /* Conversion from internal units to peculiar velocities */
-  ret[0] *= cosmo->a2_inv;
-  ret[1] *= cosmo->a2_inv;
-  ret[2] *= cosmo->a2_inv;
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
 }
 
-void convert_part_potential(const struct engine* e, const struct part* p,
-                            const struct xpart* xp, float* ret) {
+INLINE static void convert_part_potential(const struct engine* e,
+                                          const struct part* p,
+                                          const struct xpart* xp, float* ret) {
 
   if (p->gpart != NULL)
     ret[0] = gravity_get_comoving_potential(p->gpart);
@@ -146,8 +150,10 @@ void convert_part_potential(const struct engine* e, const struct part* p,
  * @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(const struct part* parts, const struct xpart* xparts,
-                           struct io_props* list, int* num_fields) {
+INLINE static void hydro_write_particles(const struct part* parts,
+                                         const struct xpart* xparts,
+                                         struct io_props* list,
+                                         int* num_fields) {
 
   *num_fields = 10;
 
@@ -182,7 +188,7 @@ void hydro_write_particles(const struct part* parts, const struct xpart* xparts,
  * @brief Writes the current model of SPH to the file
  * @param h_grpsph The HDF5 group in which to write
  */
-void hydro_write_flavour(hid_t h_grpsph) {
+INLINE static void hydro_write_flavour(hid_t h_grpsph) {
 
   /* Viscosity and thermal conduction */
   /* Nothing in this minimal model... */
@@ -200,6 +206,6 @@ void hydro_write_flavour(hid_t h_grpsph) {
  *
  * @return 1 if entropy is in 'internal energy', 0 otherwise.
  */
-int writeEntropyFlag() { return 0; }
+INLINE static int writeEntropyFlag(void) { return 0; }
 
 #endif /* SWIFT_MINIMAL_HYDRO_IO_H */
diff --git a/src/hydro/MinimalMultiMat/hydro.h b/src/hydro/MinimalMultiMat/hydro.h
new file mode 100644
index 0000000000000000000000000000000000000000..cfad6b2b2b389da9f423540cb30f1df4cebc5416
--- /dev/null
+++ b/src/hydro/MinimalMultiMat/hydro.h
@@ -0,0 +1,634 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2018   Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_MINIMAL_MULTI_MAT_HYDRO_H
+#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_H
+
+/**
+ * @file MinimalMultiMat/hydro.h
+ * @brief Minimal conservative implementation of SPH (Non-neighbour loop
+ * equations) with multiple materials.
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
+ *
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational
+ * Physics, 2012, Volume 231, Issue 3, pp. 759-794.
+ */
+
+#include "adiabatic_index.h"
+#include "approx_math.h"
+#include "cosmology.h"
+#include "dimension.h"
+#include "equation_of_state.h"
+#include "hydro_properties.h"
+#include "hydro_space.h"
+#include "kernel_hydro.h"
+#include "minmax.h"
+
+/**
+ * @brief Returns the comoving internal energy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not internal energy, this function computes the internal
+ * energy from the thermodynamic variable.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy(const struct part *restrict p) {
+
+  return p->u;
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not internal energy, this function computes the internal
+ * energy from the thermodynamic variable and converts it to
+ * physical coordinates.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy(const struct part *restrict p,
+                                   const struct cosmology *cosmo) {
+
+  return p->u * cosmo->a_factor_internal_energy;
+}
+
+/**
+ * @brief Returns the comoving pressure of a particle
+ *
+ * Computes the pressure based on the particle's properties.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure(
+    const struct part *restrict p) {
+
+  return gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id);
+}
+
+/**
+ * @brief Returns the physical pressure of a particle
+ *
+ * Computes the pressure based on the particle's properties and
+ * convert it to physical coordinates.
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
+    const struct part *restrict p, const struct cosmology *cosmo) {
+
+  return cosmo->a_factor_pressure *
+         gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id);
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not entropy, this function computes the entropy from
+ * the thermodynamic variable.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
+    const struct part *restrict p) {
+
+  return gas_entropy_from_internal_energy(p->rho, p->u, p->mat_id);
+}
+
+/**
+ * @brief Returns the physical entropy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not entropy, this function computes the entropy from
+ * the thermodynamic variable and converts it to
+ * physical coordinates.
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
+    const struct part *restrict p, const struct cosmology *cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return gas_entropy_from_internal_energy(p->rho, p->u, p->mat_id);
+}
+
+/**
+ * @brief Returns the comoving sound speed of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_soundspeed(const struct part *restrict p) {
+
+  return p->force.soundspeed;
+}
+
+/**
+ * @brief Returns the physical sound speed of a particle
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_soundspeed(const struct part *restrict p,
+                              const struct cosmology *cosmo) {
+
+  return cosmo->a_factor_sound_speed * p->force.soundspeed;
+}
+
+/**
+ * @brief Returns the comoving density of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_density(
+    const struct part *restrict p) {
+
+  return p->rho;
+}
+
+/**
+ * @brief Returns the comoving density of a particle.
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_density(
+    const struct part *restrict p, const struct cosmology *cosmo) {
+
+  return cosmo->a3_inv * 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 Sets the mass of a particle
+ *
+ * @param p The particle of interest
+ * @param m The mass to set.
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_mass(
+    struct part *restrict p, float m) {
+
+  p->mass = m;
+}
+
+/**
+ * @brief Returns the velocities drifted to the current time of a particle.
+ *
+ * @param p The particle of interest
+ * @param xp The extended data of the particle.
+ * @param dt_kick_hydro The time (for hydro accelerations) since the last kick.
+ * @param dt_kick_grav The time (for gravity accelerations) since the last kick.
+ * @param v (return) The velocities at the current time.
+ */
+__attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
+    const struct part *restrict p, const struct xpart *xp, float dt_kick_hydro,
+    float dt_kick_grav, float v[3]) {
+
+  v[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro +
+         xp->a_grav[0] * dt_kick_grav;
+  v[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro +
+         xp->a_grav[1] * dt_kick_grav;
+  v[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro +
+         xp->a_grav[2] * dt_kick_grav;
+}
+
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
+    const struct part *restrict p) {
+
+  return p->u_dt;
+}
+
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest.
+ * @param du_dt The new time derivative of the internal energy.
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt(
+    struct part *restrict p, float du_dt) {
+
+  p->u_dt = du_dt;
+}
+/**
+ * @brief Computes the hydro time-step of a given particle
+ *
+ * This function returns the time-step of a particle given its hydro-dynamical
+ * state. A typical time-step calculation would be the use of the CFL condition.
+ *
+ * @param p Pointer to the particle data
+ * @param xp Pointer to the extended particle data
+ * @param hydro_properties The SPH parameters
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_compute_timestep(
+    const struct part *restrict p, const struct xpart *restrict xp,
+    const struct hydro_props *restrict hydro_properties,
+    const struct cosmology *restrict cosmo) {
+
+  const float CFL_condition = hydro_properties->CFL_condition;
+
+  /* CFL condition */
+  const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h /
+                       (cosmo->a_factor_sound_speed * p->force.v_sig);
+
+  return dt_cfl;
+}
+
+/**
+ * @brief Does some extra hydro operations once the actual physical time step
+ * for the particle is known.
+ *
+ * @param p The particle to act upon.
+ * @param dt Physical time step of the particle during the next step.
+ */
+__attribute__((always_inline)) INLINE static void hydro_timestep_extra(
+    struct part *p, float dt) {}
+
+/**
+ * @brief Prepares a particle for the density calculation.
+ *
+ * Zeroes all the relevant arrays in preparation for the sums taking place in
+ * the various density loop over neighbours. Typically, all fields of the
+ * density sub-structure of a particle get zeroed in here.
+ *
+ * @param p The particle to act upon
+ * @param hs #hydro_space containing hydro specific space information.
+ */
+__attribute__((always_inline)) INLINE static void hydro_init_part(
+    struct part *restrict p, const struct hydro_space *hs) {
+
+  p->density.wcount = 0.f;
+  p->density.wcount_dh = 0.f;
+  p->rho = 0.f;
+  p->density.rho_dh = 0.f;
+}
+
+/**
+ * @brief Finishes the density calculation.
+ *
+ * Multiplies the density and number of neighbours by the appropiate constants
+ * and add the self-contribution term.
+ * Additional quantities such as velocity gradients will also get the final
+ * terms added to them here.
+ *
+ * Also adds/multiplies the cosmological terms if need be.
+ *
+ * @param p The particle to act upon
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_end_density(
+    struct part *restrict p, const struct cosmology *cosmo) {
+
+  /* Some smoothing length multiples. */
+  const float h = p->h;
+  const float h_inv = 1.0f / h;                       /* 1/h */
+  const float h_inv_dim = pow_dimension(h_inv);       /* 1/h^d */
+  const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */
+
+  /* Final operation on the density (add self-contribution). */
+  p->rho += p->mass * kernel_root;
+  p->density.rho_dh -= hydro_dimension * p->mass * kernel_root;
+  p->density.wcount += kernel_root;
+  p->density.wcount_dh -= hydro_dimension * kernel_root;
+
+  /* Finish the calculation by inserting the missing h-factors */
+  p->rho *= h_inv_dim;
+  p->density.rho_dh *= h_inv_dim_plus_one;
+  p->density.wcount *= h_inv_dim;
+  p->density.wcount_dh *= h_inv_dim_plus_one;
+}
+
+/**
+ * @brief Sets all particle fields to sensible values when the #part has 0 ngbs.
+ *
+ * In the desperate case where a particle has no neighbours (likely because
+ * of the h_max ceiling), set the particle fields to something sensible to avoid
+ * NaNs in the next calculations.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
+    struct part *restrict p, struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Some smoothing length multiples. */
+  const float h = p->h;
+  const float h_inv = 1.0f / h;                 /* 1/h */
+  const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */
+
+  /* Re-set problematic values */
+  p->rho = p->mass * kernel_root * h_inv_dim;
+  p->density.wcount = kernel_root * h_inv_dim;
+  p->density.rho_dh = 0.f;
+  p->density.wcount_dh = 0.f;
+}
+
+/**
+ * @brief Prepare a particle for the force calculation.
+ *
+ * This function is called in the ghost task to convert some quantities coming
+ * from the density loop over neighbours into quantities ready to be used in the
+ * force loop over neighbours. Quantities are typically read from the density
+ * sub-structure and written to the force sub-structure.
+ * Examples of calculations done here include the calculation of viscosity term
+ * constants, thermal conduction terms, hydro conversions, etc.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_prepare_force(
+    struct part *restrict p, struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Compute the pressure */
+  const float pressure =
+      gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id);
+
+  /* Compute the sound speed */
+  const float soundspeed =
+      gas_soundspeed_from_pressure(p->rho, pressure, p->mat_id);
+
+  /* Compute the "grad h" term */
+  const float rho_inv = 1.f / p->rho;
+  const float grad_h_term =
+      1.f / (1.f + hydro_dimension_inv * p->h * p->density.rho_dh * rho_inv);
+
+  /* Update variables. */
+  p->force.f = grad_h_term;
+  p->force.pressure = pressure;
+  p->force.soundspeed = soundspeed;
+}
+
+/**
+ * @brief Reset acceleration fields of a particle
+ *
+ * Resets all hydro acceleration and time derivative fields in preparation
+ * for the sums taking  place in the various force tasks.
+ *
+ * @param p The particle to act upon
+ */
+__attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
+    struct part *restrict p) {
+
+  /* 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->u_dt = 0.0f;
+  p->force.h_dt = 0.0f;
+  p->force.v_sig = p->force.soundspeed;
+}
+
+/**
+ * @brief Sets the values to be predicted in the drifts to their values at a
+ * kick time
+ *
+ * @param p The particle.
+ * @param xp The extended data of this particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
+    struct part *restrict p, const struct xpart *restrict xp) {
+
+  /* Re-set the predicted velocities */
+  p->v[0] = xp->v_full[0];
+  p->v[1] = xp->v_full[1];
+  p->v[2] = xp->v_full[2];
+
+  /* Re-set the entropy */
+  p->u = xp->u_full;
+}
+
+/**
+ * @brief Predict additional particle fields forward in time when drifting
+ *
+ * Additional hydrodynamic quantites are drifted forward in time here. These
+ * include thermal quantities (thermal energy or total energy or entropy, ...).
+ *
+ * Note the different time-step sizes used for the different quantities as they
+ * include cosmological factors.
+ *
+ * @param p The particle.
+ * @param xp The extended data of the particle.
+ * @param dt_drift The drift time-step for positions.
+ * @param dt_therm The drift time-step for thermal quantities.
+ */
+__attribute__((always_inline)) INLINE static void hydro_predict_extra(
+    struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
+    float dt_therm) {
+
+  const float h_inv = 1.f / p->h;
+
+  /* Predict smoothing length */
+  const float w1 = p->force.h_dt * h_inv * dt_drift;
+  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 internal energy */
+  p->u += p->u_dt * dt_therm;
+
+  /* Compute the new pressure */
+  const float pressure =
+      gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id);
+
+  /* Compute the new sound speed */
+  const float soundspeed =
+      gas_soundspeed_from_pressure(p->rho, pressure, p->mat_id);
+
+  p->force.pressure = pressure;
+  p->force.soundspeed = soundspeed;
+}
+
+/**
+ * @brief Finishes the force calculation.
+ *
+ * Multiplies the force and accelerations by the appropiate constants
+ * and add the self-contribution term. In most cases, there is little
+ * to do here.
+ *
+ * Cosmological terms are also added/multiplied here.
+ *
+ * @param p The particle to act upon
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_end_force(
+    struct part *restrict p, const struct cosmology *cosmo) {
+
+  p->force.h_dt *= p->h * hydro_dimension_inv;
+}
+
+/**
+ * @brief Kick the additional variables
+ *
+ * Additional hydrodynamic quantites are kicked forward in time here. These
+ * include thermal quantities (thermal energy or total energy or entropy, ...).
+ *
+ * @param p The particle to act upon.
+ * @param xp The particle extended data to act upon.
+ * @param dt_therm The time-step for this kick (for thermodynamic quantities).
+ * @param cosmo The cosmological model.
+ * @param hydro_props The constants used in the scheme
+ */
+__attribute__((always_inline)) INLINE static void hydro_kick_extra(
+    struct part *restrict p, struct xpart *restrict xp, float dt_therm,
+    const struct cosmology *cosmo, const struct hydro_props *hydro_props) {
+
+  /* Do not decrease the energy by more than a factor of 2*/
+  if (dt_therm > 0. && p->u_dt * dt_therm < -0.5f * xp->u_full) {
+    p->u_dt = -0.5f * xp->u_full / dt_therm;
+  }
+  xp->u_full += p->u_dt * dt_therm;
+
+  /* Apply the minimal energy limit */
+  const float min_energy =
+      hydro_props->minimal_internal_energy * cosmo->a_factor_internal_energy;
+  if (xp->u_full < min_energy) {
+    xp->u_full = min_energy;
+    p->u_dt = 0.f;
+  }
+
+  /* Compute the pressure */
+  const float pressure =
+      gas_pressure_from_internal_energy(p->rho, xp->u_full, p->mat_id);
+
+  /* Compute the sound speed */
+  const float soundspeed =
+      gas_soundspeed_from_internal_energy(p->rho, p->u, p->mat_id);
+
+  p->force.pressure = pressure;
+  p->force.soundspeed = soundspeed;
+}
+
+/**
+ * @brief Converts hydro quantity of a particle at the start of a run
+ *
+ * This function is called once at the end of the engine_init_particle()
+ * routine (at the start of a calculation) after the densities of
+ * particles have been computed.
+ * This can be used to convert internal energy into entropy for instance.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle to act upon
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_convert_quantities(
+    struct part *restrict p, struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Compute the pressure */
+  const float pressure =
+      gas_pressure_from_internal_energy(p->rho, p->u, p->mat_id);
+
+  /* Compute the sound speed */
+  const float soundspeed =
+      gas_soundspeed_from_internal_energy(p->rho, p->u, p->mat_id);
+
+  p->force.pressure = pressure;
+  p->force.soundspeed = soundspeed;
+}
+
+/**
+ * @brief Initialises the particles for the first time
+ *
+ * This function is called only once just after the ICs have been
+ * read in to do some conversions or assignments between the particle
+ * and extended particle fields.
+ *
+ * @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 *restrict p, struct xpart *restrict xp) {
+
+  p->time_bin = 0;
+  xp->v_full[0] = p->v[0];
+  xp->v_full[1] = p->v[1];
+  xp->v_full[2] = p->v[2];
+  xp->a_grav[0] = 0.f;
+  xp->a_grav[1] = 0.f;
+  xp->a_grav[2] = 0.f;
+  xp->u_full = p->u;
+
+  hydro_reset_acceleration(p);
+  hydro_init_part(p, NULL);
+}
+
+/**
+ * @brief Overwrite the initial internal energy of a particle.
+ *
+ * Note that in the cases where the thermodynamic variable is not
+ * internal energy but gets converted later, we must overwrite that
+ * field. The conversion to the actual variable happens later after
+ * the initial fake time-step.
+ *
+ * @param p The #part to write to.
+ * @param u_init The new initial internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_init_internal_energy(struct part *p, float u_init) {
+
+  p->u = u_init;
+}
+
+#endif /* SWIFT_MINIMAL_MULTI_MAT_HYDRO_H */
diff --git a/src/hydro/MinimalMultiMat/hydro_debug.h b/src/hydro/MinimalMultiMat/hydro_debug.h
new file mode 100644
index 0000000000000000000000000000000000000000..17b624ad0f660152be4ba685905a3c855e1761f8
--- /dev/null
+++ b/src/hydro/MinimalMultiMat/hydro_debug.h
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2018   Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_MINIMAL_MULTI_MAT_HYDRO_DEBUG_H
+#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_DEBUG_H
+
+/**
+ * @file MinimalMultiMat/hydro_debug.h
+ * @brief MinimalMultiMat conservative implementation of SPH (Debugging
+ * routines)
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
+ *
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of
+ * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3,
+ * pp. 759-794.
+ */
+
+__attribute__((always_inline)) INLINE static void hydro_debug_particle(
+    const struct part* p, const struct xpart* xp) {
+  printf(
+      "\n "
+      "x=[%.6g, %.6g, %.6g], v=[%.3g, %.3g, %.3g], \n "
+      "v_full=[%.3g, %.3g, %.3g], a=[%.3g, %.3g, %.3g], \n "
+      "m=%.3g, u=%.3g, du/dt=%.3g, P=%.3g, c_s=%.3g, \n "
+      "v_sig=%.3g, h=%.3g, dh/dt=%.3g, wcount=%.3g, rho=%.3g, \n "
+      "dh_drho=%.3g, time_bin=%d, mat_id=%d \n",
+      p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0],
+      xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2],
+      p->mass, p->u, p->u_dt, hydro_get_comoving_pressure(p),
+      p->force.soundspeed, p->force.v_sig, p->h, p->force.h_dt,
+      p->density.wcount, p->rho, p->density.rho_dh, p->time_bin, p->mat_id);
+}
+
+#endif /* SWIFT_MINIMAL_MULTI_MAT_HYDRO_DEBUG_H */
diff --git a/src/hydro/MinimalMultiMat/hydro_iact.h b/src/hydro/MinimalMultiMat/hydro_iact.h
new file mode 100644
index 0000000000000000000000000000000000000000..5984c1c483546d87800792ced0ffcc41e0aaa408
--- /dev/null
+++ b/src/hydro/MinimalMultiMat/hydro_iact.h
@@ -0,0 +1,340 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_MINIMAL_MULTI_MAT_HYDRO_IACT_H
+#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_IACT_H
+
+/**
+ * @file MinimalMultiMat/hydro_iact.h
+ * @brief MinimalMultiMat conservative implementation of SPH (Neighbour loop
+ * equations)
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
+ *
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational
+ * Physics, 2012, Volume 231, Issue 3, pp. 759-794.
+ */
+
+#include "adiabatic_index.h"
+#include "const.h"
+#include "minmax.h"
+
+/**
+ * @brief Density interaction between two particles.
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi First particle.
+ * @param pj Second particle.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_density(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
+
+  float wi, wj, wi_dx, wj_dx;
+
+  /* Get r. */
+  const float r_inv = 1.0f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  /* Get the masses. */
+  const float mi = pi->mass;
+  const float mj = pj->mass;
+
+  /* Compute density of pi. */
+  const float hi_inv = 1.f / hi;
+  const float ui = r * hi_inv;
+  kernel_deval(ui, &wi, &wi_dx);
+
+  pi->rho += mj * wi;
+  pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx);
+  pi->density.wcount += wi;
+  pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx);
+
+  /* Compute density of pj. */
+  const float hj_inv = 1.f / hj;
+  const float uj = r * hj_inv;
+  kernel_deval(uj, &wj, &wj_dx);
+
+  pj->rho += mi * wj;
+  pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx);
+  pj->density.wcount += wj;
+  pj->density.wcount_dh -= (hydro_dimension * wj + uj * wj_dx);
+}
+
+/**
+ * @brief Density interaction between two particles (non-symmetric).
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi First particle.
+ * @param pj Second particle (not updated).
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    const struct part *restrict pj, float a, float H) {
+
+  float wi, wi_dx;
+
+  /* Get the masses. */
+  const float mj = pj->mass;
+
+  /* Get r. */
+  const float r_inv = 1.0f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  const float h_inv = 1.f / hi;
+  const float ui = r * h_inv;
+  kernel_deval(ui, &wi, &wi_dx);
+
+  pi->rho += mj * wi;
+  pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx);
+  pi->density.wcount += wi;
+  pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx);
+}
+
+/**
+ * @brief Force interaction between two particles.
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi First particle.
+ * @param pj Second particle.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_force(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
+
+  /* Cosmological factors entering the EoMs */
+  const float fac_mu = pow_three_gamma_minus_five_over_two(a);
+  const float a2_Hubble = a * a * H;
+
+  /* Get r and r inverse. */
+  const float r_inv = 1.0f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  /* Recover some data */
+  const float mi = pi->mass;
+  const float mj = pj->mass;
+  const float rhoi = pi->rho;
+  const float rhoj = pj->rho;
+  const float pressurei = pi->force.pressure;
+  const float pressurej = pj->force.pressure;
+
+  /* Get the kernel for hi. */
+  const float hi_inv = 1.0f / hi;
+  const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
+  const float xi = r * hi_inv;
+  float wi, wi_dx;
+  kernel_deval(xi, &wi, &wi_dx);
+  const float wi_dr = hid_inv * wi_dx;
+
+  /* Get the kernel for hj. */
+  const float hj_inv = 1.0f / hj;
+  const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
+  const float xj = r * hj_inv;
+  float wj, wj_dx;
+  kernel_deval(xj, &wj, &wj_dx);
+  const float wj_dr = hjd_inv * wj_dx;
+
+  /* Compute gradient terms */
+  const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->force.f;
+  const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->force.f;
+
+  /* Compute dv dot r. */
+  const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] +
+                     (pi->v[1] - pj->v[1]) * dx[1] +
+                     (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2;
+
+  /* Are the particles moving towards each others ? */
+  const float omega_ij = min(dvdr, 0.f);
+  const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
+
+  /* Compute sound speeds and signal velocity */
+  const float ci = pi->force.soundspeed;
+  const float cj = pj->force.soundspeed;
+  const float v_sig = ci + cj - 3.f * mu_ij;
+
+  /* Construct the full viscosity term */
+  const float rho_ij = 0.5f * (rhoi + rhoj);
+  const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij;
+
+  /* Convolve with the kernel */
+  const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
+
+  /* SPH acceleration term */
+  const float sph_acc_term =
+      (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv;
+
+  /* Assemble the acceleration */
+  const float acc = sph_acc_term + visc_acc_term;
+
+  /* Use the force Luke ! */
+  pi->a_hydro[0] -= mj * acc * dx[0];
+  pi->a_hydro[1] -= mj * acc * dx[1];
+  pi->a_hydro[2] -= mj * acc * dx[2];
+
+  pj->a_hydro[0] += mi * acc * dx[0];
+  pj->a_hydro[1] += mi * acc * dx[1];
+  pj->a_hydro[2] += mi * acc * dx[2];
+
+  /* Get the time derivative for u. */
+  const float sph_du_term_i = P_over_rho2_i * dvdr * r_inv * wi_dr;
+  const float sph_du_term_j = P_over_rho2_j * dvdr * r_inv * wj_dr;
+
+  /* Viscosity term */
+  const float visc_du_term = 0.5f * visc_acc_term * dvdr;
+
+  /* Assemble the energy equation term */
+  const float du_dt_i = sph_du_term_i + visc_du_term;
+  const float du_dt_j = sph_du_term_j + visc_du_term;
+
+  /* Internal energy time derivatibe */
+  pi->u_dt += du_dt_i * mj;
+  pj->u_dt += du_dt_j * mi;
+
+  /* Get the time derivative for h. */
+  pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr;
+  pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr;
+
+  /* Update the signal velocity. */
+  pi->force.v_sig = max(pi->force.v_sig, v_sig);
+  pj->force.v_sig = max(pj->force.v_sig, v_sig);
+}
+
+/**
+ * @brief Force interaction between two particles (non-symmetric).
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param pi First particle.
+ * @param pj Second particle (not updated).
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    const struct part *restrict pj, float a, float H) {
+
+  /* Cosmological factors entering the EoMs */
+  const float fac_mu = pow_three_gamma_minus_five_over_two(a);
+  const float a2_Hubble = a * a * H;
+
+  /* Get r and r inverse. */
+  const float r_inv = 1.0f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  /* Recover some data */
+  // const float mi = pi->mass;
+  const float mj = pj->mass;
+  const float rhoi = pi->rho;
+  const float rhoj = pj->rho;
+  const float pressurei = pi->force.pressure;
+  const float pressurej = pj->force.pressure;
+
+  /* Get the kernel for hi. */
+  const float hi_inv = 1.0f / hi;
+  const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
+  const float xi = r * hi_inv;
+  float wi, wi_dx;
+  kernel_deval(xi, &wi, &wi_dx);
+  const float wi_dr = hid_inv * wi_dx;
+
+  /* Get the kernel for hj. */
+  const float hj_inv = 1.0f / hj;
+  const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
+  const float xj = r * hj_inv;
+  float wj, wj_dx;
+  kernel_deval(xj, &wj, &wj_dx);
+  const float wj_dr = hjd_inv * wj_dx;
+
+  /* Compute gradient terms */
+  const float P_over_rho2_i = pressurei / (rhoi * rhoi) * pi->force.f;
+  const float P_over_rho2_j = pressurej / (rhoj * rhoj) * pj->force.f;
+
+  /* Compute dv dot r. */
+  const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] +
+                     (pi->v[1] - pj->v[1]) * dx[1] +
+                     (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2;
+
+  /* Are the particles moving towards each others ? */
+  const float omega_ij = min(dvdr, 0.f);
+  const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
+
+  /* Compute sound speeds and signal velocity */
+  const float ci = pi->force.soundspeed;
+  const float cj = pj->force.soundspeed;
+  const float v_sig = ci + cj - 3.f * mu_ij;
+
+  /* Construct the full viscosity term */
+  const float rho_ij = 0.5f * (rhoi + rhoj);
+  const float visc = -0.5f * const_viscosity_alpha * v_sig * mu_ij / rho_ij;
+
+  /* Convolve with the kernel */
+  const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
+
+  /* SPH acceleration term */
+  const float sph_acc_term =
+      (P_over_rho2_i * wi_dr + P_over_rho2_j * wj_dr) * r_inv;
+
+  /* Assemble the acceleration */
+  const float acc = sph_acc_term + visc_acc_term;
+
+  /* Use the force Luke ! */
+  pi->a_hydro[0] -= mj * acc * dx[0];
+  pi->a_hydro[1] -= mj * acc * dx[1];
+  pi->a_hydro[2] -= mj * acc * dx[2];
+
+  /* Get the time derivative for u. */
+  const float sph_du_term_i = P_over_rho2_i * dvdr * r_inv * wi_dr;
+
+  /* Viscosity term */
+  const float visc_du_term = 0.5f * visc_acc_term * dvdr;
+
+  /* Assemble the energy equation term */
+  const float du_dt_i = sph_du_term_i + visc_du_term;
+
+  /* Internal energy time derivatibe */
+  pi->u_dt += du_dt_i * mj;
+
+  /* Get the time derivative for h. */
+  pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr;
+
+  /* Update the signal velocity. */
+  pi->force.v_sig = max(pi->force.v_sig, v_sig);
+}
+
+#endif /* SWIFT_MINIMAL_MULTI_MAT_HYDRO_IACT_H */
diff --git a/src/hydro/MinimalMultiMat/hydro_io.h b/src/hydro/MinimalMultiMat/hydro_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..7f41f5e227b6c8a8904b5546a2568b4700109abd
--- /dev/null
+++ b/src/hydro/MinimalMultiMat/hydro_io.h
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_MINIMAL_MULTI_MAT_HYDRO_IO_H
+#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_IO_H
+
+/**
+ * @file MinimalMultiMat/hydro_io.h
+ * @brief MinimalMultiMat conservative implementation of SPH (i/o routines)
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
+ *
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of
+ * Price, D., Journal of Computational Physics, 2012, Volume 231, Issue 3,
+ * pp. 759-794.
+ */
+
+#include "adiabatic_index.h"
+#include "hydro.h"
+#include "io_properties.h"
+#include "kernel_hydro.h"
+
+/**
+ * @brief Specifies which particle fields to read from a dataset
+ *
+ * @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.
+ */
+INLINE static void hydro_read_particles(struct part* parts,
+                                        struct io_props* list,
+                                        int* num_fields) {
+
+  *num_fields = 9;
+
+  /* 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, 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, u);
+  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, rho);
+  list[8] = io_make_input_field("MaterialID", INT, 1, COMPULSORY,
+                                UNIT_CONV_NO_UNITS, parts, mat_id);
+}
+
+INLINE static void convert_S(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
+
+  ret[0] = hydro_get_comoving_entropy(p);
+}
+
+INLINE static void convert_P(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
+
+  ret[0] = hydro_get_comoving_pressure(p);
+}
+
+INLINE static void convert_part_pos(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, double* ret) {
+
+  if (e->s->periodic) {
+    ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
+    ret[1] = box_wrap(p->x[1], 0.0, e->s->dim[1]);
+    ret[2] = box_wrap(p->x[2], 0.0, e->s->dim[2]);
+  } else {
+    ret[0] = p->x[0];
+    ret[1] = p->x[1];
+    ret[2] = p->x[2];
+  }
+}
+
+INLINE static void convert_part_vel(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, float* ret) {
+
+  const int with_cosmology = (e->policy & engine_policy_cosmology);
+  const struct cosmology* cosmo = e->cosmology;
+  const integertime_t ti_current = e->ti_current;
+  const double time_base = e->time_base;
+
+  const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin);
+  const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin);
+
+  /* Get time-step since the last kick */
+  float dt_kick_grav, dt_kick_hydro;
+  if (with_cosmology) {
+    dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current);
+    dt_kick_grav -=
+        cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2);
+    dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current);
+    dt_kick_hydro -=
+        cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2);
+  } else {
+    dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base;
+    dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base;
+  }
+
+  /* Extrapolate the velocites to the current time */
+  hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret);
+
+  /* Conversion from internal units to peculiar velocities */
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
+}
+
+INLINE static void convert_part_potential(const struct engine* e,
+                                          const struct part* p,
+                                          const struct xpart* xp, float* ret) {
+
+  if (p->gpart != NULL)
+    ret[0] = gravity_get_comoving_potential(p->gpart);
+  else
+    ret[0] = 0.f;
+}
+
+/**
+ * @brief Specifies which particle fields to write to a dataset
+ *
+ * @param parts The particle array.
+ * @param xparts The extended particle array.
+ * @param list The list of i/o properties to write.
+ * @param num_fields The number of i/o fields to write.
+ */
+INLINE static void hydro_write_particles(const struct part* parts,
+                                         const struct xpart* xparts,
+                                         struct io_props* list,
+                                         int* num_fields) {
+
+  *num_fields = 11;
+
+  /* List what we want to write */
+  list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3,
+                                              UNIT_CONV_LENGTH, parts, xparts,
+                                              convert_part_pos);
+  list[1] = io_make_output_field_convert_part(
+      "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel);
+  list[2] =
+      io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass);
+  list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH,
+                                 parts, h);
+  list[4] = io_make_output_field("InternalEnergy", FLOAT, 1,
+                                 UNIT_CONV_ENERGY_PER_UNIT_MASS, parts, u);
+  list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
+                                 UNIT_CONV_NO_UNITS, parts, id);
+  list[6] =
+      io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho);
+  list[7] = io_make_output_field_convert_part("Entropy", FLOAT, 1,
+                                              UNIT_CONV_ENTROPY_PER_UNIT_MASS,
+                                              parts, xparts, convert_S);
+  list[8] = io_make_output_field("MaterialID", INT, 1, UNIT_CONV_NO_UNITS,
+                                 parts, mat_id);
+  list[9] = io_make_output_field_convert_part(
+      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P);
+  list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1,
+                                               UNIT_CONV_POTENTIAL, parts,
+                                               xparts, convert_part_potential);
+}
+
+/**
+ * @brief Writes the current model of SPH to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+INLINE static void hydro_write_flavour(hid_t h_grpsph) {
+
+  /* Viscosity and thermal conduction */
+  /* Nothing in this minimal model... */
+  io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment");
+  io_write_attribute_s(h_grpsph, "Viscosity Model",
+                       "Minimal treatment as in Monaghan (1992)");
+
+  /* Time integration properties */
+  io_write_attribute_f(h_grpsph, "Maximal Delta u change over dt",
+                       const_max_u_change);
+}
+
+/**
+ * @brief Are we writing entropy in the internal energy field ?
+ *
+ * @return 1 if entropy is in 'internal energy', 0 otherwise.
+ */
+INLINE static int writeEntropyFlag(void) { return 0; }
+
+#endif /* SWIFT_MINIMAL_MULTI_MAT_HYDRO_IO_H */
diff --git a/src/hydro/MinimalMultiMat/hydro_part.h b/src/hydro/MinimalMultiMat/hydro_part.h
new file mode 100644
index 0000000000000000000000000000000000000000..dad13e889aa531636e34846825109086177b3dae
--- /dev/null
+++ b/src/hydro/MinimalMultiMat/hydro_part.h
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2018   Jacob Kegerreis (jacob.kegerreis@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_MINIMAL_MULTI_MAT_HYDRO_PART_H
+#define SWIFT_MINIMAL_MULTI_MAT_HYDRO_PART_H
+
+/**
+ * @file MinimalMultiMat/hydro_part.h
+ * @brief MinimalMultiMat conservative implementation of SPH (Particle
+ * definition)
+ *
+ * The thermal variable is the internal energy (u). Simple constant
+ * viscosity term without switches is implemented. No thermal conduction
+ * term is implemented.
+ *
+ * This corresponds to equations (43), (44), (45), (101), (103)  and (104) with
+ * \f$\beta=3\f$ and \f$\alpha_u=0\f$ of Price, D., Journal of Computational
+ * Physics, 2012, Volume 231, Issue 3, pp. 759-794.
+ */
+
+#include "chemistry_struct.h"
+#include "cooling_struct.h"
+#include "equation_of_state.h"  // For enum material_id
+
+/**
+ * @brief Particle fields not needed during the SPH loops over neighbours.
+ *
+ * This structure contains the particle fields that are not used in the
+ * density or force loops. Quantities should be used in the kick, drift and
+ * potentially ghost tasks only.
+ */
+struct xpart {
+
+  /*! Offset between current position and position at last tree rebuild. */
+  float x_diff[3];
+
+  /*! Offset between the current position and position at the last sort. */
+  float x_diff_sort[3];
+
+  /*! Velocity at the last full step. */
+  float v_full[3];
+
+  /*! Gravitational acceleration at the last full step. */
+  float a_grav[3];
+
+  /*! Internal energy at the last full step. */
+  float u_full;
+
+  /*! Additional data used to record cooling information */
+  struct cooling_xpart_data cooling_data;
+
+} SWIFT_STRUCT_ALIGN;
+
+/**
+ * @brief Particle fields for the SPH particles
+ *
+ * The density and force substructures are used to contain variables only used
+ * within the density and force loops over neighbours. All more permanent
+ * variables should be declared in the main part of the part structure,
+ */
+struct part {
+
+  /*! Particle unique ID. */
+  long long id;
+
+  /*! Pointer to corresponding gravity part. */
+  struct gpart* gpart;
+
+  /*! Particle position. */
+  double x[3];
+
+  /*! Particle predicted velocity. */
+  float v[3];
+
+  /*! Particle acceleration. */
+  float a_hydro[3];
+
+  /*! Particle mass. */
+  float mass;
+
+  /*! Particle smoothing length. */
+  float h;
+
+  /*! Particle internal energy. */
+  float u;
+
+  /*! Time derivative of the internal energy. */
+  float u_dt;
+
+  /*! Particle density. */
+  float rho;
+
+  /* Store density/force specific stuff. */
+  union {
+
+    /**
+     * @brief Structure for the variables only used in the density loop over
+     * neighbours.
+     *
+     * Quantities in this sub-structure should only be accessed in the density
+     * loop over neighbours and the ghost task.
+     */
+    struct {
+
+      /*! Neighbour number count. */
+      float wcount;
+
+      /*! Derivative of the neighbour number with respect to h. */
+      float wcount_dh;
+
+      /*! Derivative of density with respect to h */
+      float rho_dh;
+
+    } density;
+
+    /**
+     * @brief Structure for the variables only used in the force loop over
+     * neighbours.
+     *
+     * Quantities in this sub-structure should only be accessed in the force
+     * loop over neighbours and the ghost, drift and kick tasks.
+     */
+    struct {
+
+      /*! "Grad h" term */
+      float f;
+
+      /*! Particle pressure. */
+      float pressure;
+
+      /*! Particle soundspeed. */
+      float soundspeed;
+
+      /*! Particle signal velocity */
+      float v_sig;
+
+      /*! Time derivative of smoothing length  */
+      float h_dt;
+
+    } force;
+  };
+
+  /* Chemistry information */
+  struct chemistry_part_data chemistry_data;
+
+  /*! Material identifier flag */
+  enum eos_planetary_material_id mat_id;
+
+  /*! Time-step length */
+  timebin_t time_bin;
+
+#ifdef SWIFT_DEBUG_CHECKS
+
+  /* Time of the last drift */
+  integertime_t ti_drift;
+
+  /* Time of the last kick */
+  integertime_t ti_kick;
+
+#endif
+
+} SWIFT_STRUCT_ALIGN;
+
+#endif /* SWIFT_MINIMAL_MULTI_MAT_HYDRO_PART_H */
diff --git a/src/hydro/PressureEnergy/hydro.h b/src/hydro/PressureEnergy/hydro.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea086daeeb1e93d7f1476302564fb4182a6fb611
--- /dev/null
+++ b/src/hydro/PressureEnergy/hydro.h
@@ -0,0 +1,657 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) &
+ *                    Josh Borrow (joshua.borrow@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_PRESSURE_ENERGY_HYDRO_H
+#define SWIFT_PRESSURE_ENERGY_HYDRO_H
+
+/**
+ * @file PressureEnergy/hydro.h
+ * @brief P-U conservative implementation of SPH (Non-neighbour loop
+ * equations)
+ *
+ * The thermal variable is the internal energy (u). A simple constant
+ * viscosity term with a Balsara switch is implemented.
+ *
+ * No thermal conduction term is implemented.
+ *
+ * This implementation corresponds to the one presented in the SWIFT
+ * documentation and in Hopkins, "A general class of Lagrangian smoothed
+ * particle hydrodynamics methods and implications for fluid mixing problems",
+ * MNRAS, 2013.
+ */
+
+#include "adiabatic_index.h"
+#include "approx_math.h"
+#include "cosmology.h"
+#include "dimension.h"
+#include "equation_of_state.h"
+#include "hydro_properties.h"
+#include "hydro_space.h"
+#include "kernel_hydro.h"
+#include "minmax.h"
+
+#include <float.h>
+
+/**
+ * @brief Returns the comoving internal energy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not internal energy, this function computes the internal
+ * energy from the thermodynamic variable.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy(const struct part *restrict p) {
+
+  return p->u;
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not internal energy, this function computes the internal
+ * energy from the thermodynamic variable and converts it to
+ * physical coordinates.
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy(const struct part *restrict p,
+                                   const struct cosmology *cosmo) {
+
+  return p->u * cosmo->a_factor_internal_energy;
+}
+
+/**
+ * @brief Returns the comoving pressure of a particle
+ *
+ * Computes the pressure based on the particle's properties.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure(
+    const struct part *restrict p) {
+
+  return p->pressure_bar;
+}
+
+/**
+ * @brief Returns the physical pressure of a particle
+ *
+ * Computes the pressure based on the particle's properties and
+ * convert it to physical coordinates.
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
+    const struct part *restrict p, const struct cosmology *cosmo) {
+
+  return cosmo->a_factor_pressure * p->pressure_bar;
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not entropy, this function computes the entropy from
+ * the thermodynamic variable.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
+    const struct part *restrict p) {
+
+  return gas_entropy_from_internal_energy(p->rho, p->u);
+}
+
+/**
+ * @brief Returns the physical entropy of a particle
+ *
+ * For implementations where the main thermodynamic variable
+ * is not entropy, this function computes the entropy from
+ * the thermodynamic variable and converts it to
+ * physical coordinates.
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
+    const struct part *restrict p, const struct cosmology *cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return gas_entropy_from_internal_energy(p->rho, p->u);
+}
+
+/**
+ * @brief Returns the comoving sound speed of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_soundspeed(const struct part *restrict p) {
+
+  /* Compute the sound speed -- see theory section for justification */
+  /* IDEAL GAS ONLY -- P-U does not work with generic EoS. */
+  const float square_rooted = sqrtf(hydro_gamma * p->pressure_bar / p->rho);
+
+  return square_rooted;
+}
+
+/**
+ * @brief Returns the physical sound speed of a particle
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_soundspeed(const struct part *restrict p,
+                              const struct cosmology *cosmo) {
+
+  return cosmo->a_factor_sound_speed * p->force.soundspeed;
+}
+
+/**
+ * @brief Returns the comoving density of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_density(
+    const struct part *restrict p) {
+
+  return p->rho;
+}
+
+/**
+ * @brief Returns the comoving density of a particle.
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_density(
+    const struct part *restrict p, const struct cosmology *cosmo) {
+
+  return cosmo->a3_inv * 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 Sets the mass of a particle
+ *
+ * @param p The particle of interest
+ * @param m The mass to set.
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_mass(
+    struct part *restrict p, float m) {
+
+  p->mass = m;
+}
+
+/**
+ * @brief Returns the velocities drifted to the current time of a particle.
+ *
+ * @param p The particle of interest
+ * @param xp The extended data of the particle.
+ * @param dt_kick_hydro The time (for hydro accelerations) since the last kick.
+ * @param dt_kick_grav The time (for gravity accelerations) since the last kick.
+ * @param v (return) The velocities at the current time.
+ */
+__attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
+    const struct part *restrict p, const struct xpart *xp, float dt_kick_hydro,
+    float dt_kick_grav, float v[3]) {
+
+  v[0] = xp->v_full[0] + p->a_hydro[0] * dt_kick_hydro +
+         xp->a_grav[0] * dt_kick_grav;
+  v[1] = xp->v_full[1] + p->a_hydro[1] * dt_kick_hydro +
+         xp->a_grav[1] * dt_kick_grav;
+  v[2] = xp->v_full[2] + p->a_hydro[2] * dt_kick_hydro +
+         xp->a_grav[2] * dt_kick_grav;
+}
+
+/**
+ * @brief Returns the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_internal_energy_dt(
+    const struct part *restrict p) {
+
+  return p->u_dt;
+}
+
+/**
+ * @brief Sets the time derivative of internal energy of a particle
+ *
+ * We assume a constant density.
+ *
+ * @param p The particle of interest.
+ * @param du_dt The new time derivative of the internal energy.
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_internal_energy_dt(
+    struct part *restrict p, float du_dt) {
+
+  p->u_dt = du_dt;
+}
+
+/**
+ * @brief Computes the hydro time-step of a given particle
+ *
+ * This function returns the time-step of a particle given its hydro-dynamical
+ * state. A typical time-step calculation would be the use of the CFL condition.
+ *
+ * @param p Pointer to the particle data
+ * @param xp Pointer to the extended particle data
+ * @param hydro_properties The SPH parameters
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_compute_timestep(
+    const struct part *restrict p, const struct xpart *restrict xp,
+    const struct hydro_props *restrict hydro_properties,
+    const struct cosmology *restrict cosmo) {
+
+  const float CFL_condition = hydro_properties->CFL_condition;
+
+  /* CFL condition */
+  const float dt_cfl = 2.f * kernel_gamma * CFL_condition * cosmo->a * p->h /
+                       (cosmo->a_factor_sound_speed * p->force.v_sig);
+
+  const float dt_u_change =
+      (p->u_dt != 0.0f) ? fabsf(const_max_u_change * p->u / p->u_dt) : FLT_MAX;
+
+  return fminf(dt_cfl, dt_u_change);
+}
+
+/**
+ * @brief Does some extra hydro operations once the actual physical time step
+ * for the particle is known.
+ *
+ * @param p The particle to act upon.
+ * @param dt Physical time step of the particle during the next step.
+ */
+__attribute__((always_inline)) INLINE static void hydro_timestep_extra(
+    struct part *p, float dt) {}
+
+/**
+ * @brief Prepares a particle for the density calculation.
+ *
+ * Zeroes all the relevant arrays in preparation for the sums taking place in
+ * the various density loop over neighbours. Typically, all fields of the
+ * density sub-structure of a particle get zeroed in here.
+ *
+ * @param p The particle to act upon
+ * @param hs #hydro_space containing hydro specific space information.
+ */
+__attribute__((always_inline)) INLINE static void hydro_init_part(
+    struct part *restrict p, const struct hydro_space *hs) {
+
+  p->density.wcount = 0.f;
+  p->density.wcount_dh = 0.f;
+  p->rho = 0.f;
+  p->density.rho_dh = 0.f;
+  p->pressure_bar = 0.f;
+  p->density.pressure_bar_dh = 0.f;
+
+  p->density.div_v = 0.f;
+  p->density.rot_v[0] = 0.f;
+  p->density.rot_v[1] = 0.f;
+  p->density.rot_v[2] = 0.f;
+}
+
+/**
+ * @brief Finishes the density calculation.
+ *
+ * Multiplies the density and number of neighbours by the appropiate constants
+ * and add the self-contribution term.
+ * Additional quantities such as velocity gradients will also get the final
+ * terms added to them here.
+ *
+ * Also adds/multiplies the cosmological terms if need be.
+ *
+ * @param p The particle to act upon
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_end_density(
+    struct part *restrict p, const struct cosmology *cosmo) {
+
+  /* Some smoothing length multiples. */
+  const float h = p->h;
+  const float h_inv = 1.0f / h;                       /* 1/h */
+  const float h_inv_dim = pow_dimension(h_inv);       /* 1/h^d */
+  const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */
+
+  /* Final operation on the density (add self-contribution). */
+  p->rho += p->mass * kernel_root;
+  p->density.rho_dh -= hydro_dimension * p->mass * kernel_root;
+  p->pressure_bar += p->mass * p->u * kernel_root;
+  p->density.pressure_bar_dh -= hydro_dimension * p->mass * p->u * kernel_root;
+  p->density.wcount += kernel_root;
+  p->density.wcount_dh -= hydro_dimension * kernel_root;
+
+  /* Finish the calculation by inserting the missing h-factors */
+  p->rho *= h_inv_dim;
+  p->density.rho_dh *= h_inv_dim_plus_one;
+  p->pressure_bar *= (h_inv_dim * hydro_gamma_minus_one);
+  p->density.pressure_bar_dh *= (h_inv_dim_plus_one * hydro_gamma_minus_one);
+  p->density.wcount *= h_inv_dim;
+  p->density.wcount_dh *= h_inv_dim_plus_one;
+
+  const float rho_inv = 1.f / p->rho;
+  const float a_inv2 = cosmo->a2_inv;
+
+  /* Finish calculation of the velocity curl components */
+  p->density.rot_v[0] *= h_inv_dim_plus_one * a_inv2 * rho_inv;
+  p->density.rot_v[1] *= h_inv_dim_plus_one * a_inv2 * rho_inv;
+  p->density.rot_v[2] *= h_inv_dim_plus_one * a_inv2 * rho_inv;
+
+  /* Finish calculation of the velocity divergence */
+  p->density.div_v *= h_inv_dim_plus_one * rho_inv * a_inv2;
+}
+
+/**
+ * @brief Sets all particle fields to sensible values when the #part has 0 ngbs.
+ *
+ * In the desperate case where a particle has no neighbours (likely because
+ * of the h_max ceiling), set the particle fields to something sensible to avoid
+ * NaNs in the next calculations.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
+    struct part *restrict p, struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  /* Some smoothing length multiples. */
+  const float h = p->h;
+  const float h_inv = 1.0f / h;                 /* 1/h */
+  const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */
+
+  /* Re-set problematic values */
+  p->rho = p->mass * kernel_root * h_inv_dim;
+  p->pressure_bar =
+      p->mass * p->u * hydro_gamma_minus_one * kernel_root * h_inv_dim;
+  p->density.wcount = kernel_root * h_inv_dim;
+  p->density.rho_dh = 0.f;
+  p->density.wcount_dh = 0.f;
+  p->density.pressure_bar_dh = 0.f;
+
+  p->density.div_v = 0.f;
+  p->density.rot_v[0] = 0.f;
+  p->density.rot_v[1] = 0.f;
+  p->density.rot_v[2] = 0.f;
+}
+
+/**
+ * @brief Prepare a particle for the force calculation.
+ *
+ * This function is called in the ghost task to convert some quantities coming
+ * from the density loop over neighbours into quantities ready to be used in the
+ * force loop over neighbours. Quantities are typically read from the density
+ * sub-structure and written to the force sub-structure.
+ * Examples of calculations done here include the calculation of viscosity term
+ * constants, thermal conduction terms, hydro conversions, etc.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle data to act upon
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_prepare_force(
+    struct part *restrict p, struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
+
+  const float fac_mu = cosmo->a_factor_mu;
+
+  /* Compute the norm of the curl */
+  const float curl_v = sqrtf(p->density.rot_v[0] * p->density.rot_v[0] +
+                             p->density.rot_v[1] * p->density.rot_v[1] +
+                             p->density.rot_v[2] * p->density.rot_v[2]);
+
+  /* Compute the norm of div v */
+  const float abs_div_v = fabsf(p->density.div_v);
+
+  /* Compute the sound speed -- see theory section for justification */
+  const float soundspeed = hydro_get_comoving_soundspeed(p);
+
+  /* Compute the Balsara switch */
+  const float balsara =
+      abs_div_v / (abs_div_v + curl_v + 0.0001f * soundspeed * fac_mu / p->h);
+
+  /* Compute the "grad h" term */
+  const float common_factor = p->h / (hydro_dimension * p->density.wcount);
+  const float grad_h_term = (p->density.pressure_bar_dh * common_factor *
+                             hydro_one_over_gamma_minus_one) /
+                            (1.f + common_factor * p->density.wcount_dh);
+
+  /* Update variables. */
+  p->force.f = grad_h_term;
+  p->force.soundspeed = soundspeed;
+  p->force.balsara = balsara;
+}
+
+/**
+ * @brief Reset acceleration fields of a particle
+ *
+ * Resets all hydro acceleration and time derivative fields in preparation
+ * for the sums taking  place in the various force tasks.
+ *
+ * @param p The particle to act upon
+ */
+__attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
+    struct part *restrict p) {
+
+  /* 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->u_dt = 0.0f;
+  p->force.h_dt = 0.0f;
+  p->force.v_sig = p->force.soundspeed;
+}
+
+/**
+ * @brief Sets the values to be predicted in the drifts to their values at a
+ * kick time
+ *
+ * @param p The particle.
+ * @param xp The extended data of this particle.
+ */
+__attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
+    struct part *restrict p, const struct xpart *restrict xp) {
+
+  /* Re-set the predicted velocities */
+  p->v[0] = xp->v_full[0];
+  p->v[1] = xp->v_full[1];
+  p->v[2] = xp->v_full[2];
+
+  /* Re-set the entropy */
+  p->u = xp->u_full;
+}
+
+/**
+ * @brief Predict additional particle fields forward in time when drifting
+ *
+ * Additional hydrodynamic quantites are drifted forward in time here. These
+ * include thermal quantities (thermal energy or total energy or entropy, ...).
+ *
+ * Note the different time-step sizes used for the different quantities as they
+ * include cosmological factors.
+ *
+ * @param p The particle.
+ * @param xp The extended data of the particle.
+ * @param dt_drift The drift time-step for positions.
+ * @param dt_therm The drift time-step for thermal quantities.
+ */
+__attribute__((always_inline)) INLINE static void hydro_predict_extra(
+    struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
+    float dt_therm) {
+
+  const float h_inv = 1.f / p->h;
+
+  /* Predict smoothing length */
+  const float w1 = p->force.h_dt * h_inv * dt_drift;
+  if (fabsf(w1) < 0.2f)
+    p->h *= approx_expf(w1); /* 4th order expansion of exp(w) */
+  else
+    p->h *= expf(w1);
+
+  /* Predict density and weighted pressure */
+  const float w2 = -hydro_dimension * w1;
+  if (fabsf(w2) < 0.2f) {
+    const float expf_approx =
+        approx_expf(w2); /* 4th order expansion of exp(w) */
+    p->rho *= expf_approx;
+    p->pressure_bar *= expf_approx;
+  } else {
+    const float expf_exact = expf(w2);
+    p->rho *= expf_exact;
+    p->pressure_bar *= expf_exact;
+  }
+
+  /* Predict the internal energy */
+  p->u += p->u_dt * dt_therm;
+
+  /* Compute the new sound speed */
+  const float soundspeed = hydro_get_comoving_soundspeed(p);
+
+  p->force.soundspeed = soundspeed;
+}
+
+/**
+ * @brief Finishes the force calculation.
+ *
+ * Multiplies the force and accelerations by the appropiate constants
+ * and add the self-contribution term. In most cases, there is little
+ * to do here.
+ *
+ * Cosmological terms are also added/multiplied here.
+ *
+ * @param p The particle to act upon
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_end_force(
+    struct part *restrict p, const struct cosmology *cosmo) {
+
+  p->force.h_dt *= p->h * hydro_dimension_inv;
+}
+
+/**
+ * @brief Kick the additional variables
+ *
+ * Additional hydrodynamic quantites are kicked forward in time here. These
+ * include thermal quantities (thermal energy or total energy or entropy, ...).
+ *
+ * @param p The particle to act upon.
+ * @param xp The particle extended data to act upon.
+ * @param dt_therm The time-step for this kick (for thermodynamic quantities).
+ */
+__attribute__((always_inline)) INLINE static void hydro_kick_extra(
+    struct part *restrict p, struct xpart *restrict xp, float dt_therm,
+    const struct cosmology *cosmo,
+    const struct hydro_props *restrict hydro_properties) {
+
+  /* Do not decrease the energy by more than a factor of 2*/
+  if (dt_therm > 0. && p->u_dt * dt_therm < -0.5f * xp->u_full) {
+    p->u_dt = -0.5f * xp->u_full / dt_therm;
+  }
+  xp->u_full += p->u_dt * dt_therm;
+
+  /* Compute the sound speed */
+  const float soundspeed = hydro_get_comoving_soundspeed(p);
+
+  p->force.soundspeed = soundspeed;
+}
+
+/**
+ * @brief Converts hydro quantity of a particle at the start of a run
+ *
+ * This function is called once at the end of the engine_init_particle()
+ * routine (at the start of a calculation) after the densities of
+ * particles have been computed.
+ * This can be used to convert internal energy into entropy for instance.
+ *
+ * @param p The particle to act upon
+ * @param xp The extended particle to act upon
+ */
+__attribute__((always_inline)) INLINE static void hydro_convert_quantities(
+    struct part *restrict p, struct xpart *restrict xp,
+    const struct cosmology *cosmo) {}
+
+/**
+ * @brief Initialises the particles for the first time
+ *
+ * This function is called only once just after the ICs have been
+ * read in to do some conversions or assignments between the particle
+ * and extended particle fields.
+ *
+ * @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 *restrict p, struct xpart *restrict xp) {
+
+  p->time_bin = 0;
+  xp->v_full[0] = p->v[0];
+  xp->v_full[1] = p->v[1];
+  xp->v_full[2] = p->v[2];
+  xp->a_grav[0] = 0.f;
+  xp->a_grav[1] = 0.f;
+  xp->a_grav[2] = 0.f;
+  xp->u_full = p->u;
+
+  hydro_reset_acceleration(p);
+  hydro_init_part(p, NULL);
+}
+
+/**
+ * @brief Overwrite the initial internal energy of a particle.
+ *
+ * Note that in the cases where the thermodynamic variable is not
+ * internal energy but gets converted later, we must overwrite that
+ * field. The conversion to the actual variable happens later after
+ * the initial fake time-step.
+ *
+ * @param p The #part to write to.
+ * @param u_init The new initial internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_init_internal_energy(struct part *p, float u_init) {
+
+  p->u = u_init;
+}
+
+#endif /* SWIFT_MINIMAL_HYDRO_H */
diff --git a/src/hydro/PressureEnergy/hydro_debug.h b/src/hydro/PressureEnergy/hydro_debug.h
new file mode 100644
index 0000000000000000000000000000000000000000..6324167f12726e155eeaa3359be9741aca3a1e42
--- /dev/null
+++ b/src/hydro/PressureEnergy/hydro_debug.h
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) &
+ *                    Josh Borrow (joshua.borrow@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_PRESSURE_ENERGY_HYDRO_DEBUG_H
+#define SWIFT_PRESSURE_ENERGY_HYDRO_DEBUG_H
+/**
+ * @file PressureEnergy/hydro_debug.h
+ * @brief P-U conservative implementation of SPH (Debugging routines)
+ */
+
+__attribute__((always_inline)) INLINE static void hydro_debug_particle(
+    const struct part* p, const struct xpart* xp) {
+  printf(
+      "x=[%.3e,%.3e,%.3e], "
+      "v=[%.3e,%.3e,%.3e],v_full=[%.3e,%.3e,%.3e] \n a=[%.3e,%.3e,%.3e], "
+      "u=%.3e, du/dt=%.3e v_sig=%.3e, P=%.3e\n"
+      "h=%.3e, dh/dt=%.3e wcount=%d, m=%.3e, dh_drho=%.3e, rho=%.3e, \n"
+      "p_dh=%.3e, p_bar=%.3e \n"
+      "time_bin=%d\n",
+      p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], xp->v_full[0],
+      xp->v_full[1], xp->v_full[2], p->a_hydro[0], p->a_hydro[1], p->a_hydro[2],
+      p->u, p->u_dt, p->force.v_sig, hydro_get_comoving_pressure(p), p->h,
+      p->force.h_dt, (int)p->density.wcount, p->mass, p->density.rho_dh, p->rho,
+      p->density.pressure_bar_dh, p->pressure_bar, p->time_bin);
+}
+
+#endif /* SWIFT_MINIMAL_HYDRO_DEBUG_H */
diff --git a/src/hydro/PressureEnergy/hydro_iact.h b/src/hydro/PressureEnergy/hydro_iact.h
new file mode 100644
index 0000000000000000000000000000000000000000..65c46a55554d4a8f09b32bb6eb1deb1fdcfc932a
--- /dev/null
+++ b/src/hydro/PressureEnergy/hydro_iact.h
@@ -0,0 +1,418 @@
+/*******************************************************************************
+ * This file is part* of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) &
+ *                    Josh Borrow (joshua.borrow@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_MINIMAL_HYDRO_IACT_H
+#define SWIFT_MINIMAL_HYDRO_IACT_H
+
+/**
+ * @file PressureEnergy/hydro_iact.h
+ * @brief P-U implementation of SPH (Neighbour loop equations)
+ *
+ * The thermal variable is the internal energy (u). A simple constant
+ * viscosity term with a Balsara switch is implemented.
+ *
+ * No thermal conduction term is implemented.
+ *
+ * See PressureEnergy/hydro.h for references.
+ */
+
+#include "adiabatic_index.h"
+#include "minmax.h"
+
+/**
+ * @brief Density interaction between two part*icles.
+ *
+ * @param r2 Comoving square distance between the two part*icles.
+ * @param dx Comoving vector separating both part*icles (pi - pj).
+ * @param hi Comoving smoothing-length of part*icle i.
+ * @param hj Comoving smoothing-length of part*icle j.
+ * @param pi First part*icle.
+ * @param pj Second part*icle.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_density(
+    float r2, const float* dx, float hi, float hj, struct part* pi,
+    struct part* pj, float a, float H) {
+
+  float wi, wj, wi_dx, wj_dx;
+  float dv[3], curlvr[3];
+
+  const float r = sqrtf(r2);
+
+  /* Get the masses. */
+  const float mi = pi->mass;
+  const float mj = pj->mass;
+
+  /* Compute density of pi. */
+  const float hi_inv = 1.f / hi;
+  const float ui = r * hi_inv;
+
+  kernel_deval(ui, &wi, &wi_dx);
+
+  pi->rho += mj * wi;
+  pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx);
+
+  pi->pressure_bar += mj * wi * pj->u;
+  pi->density.pressure_bar_dh -=
+      mj * pj->u * (hydro_dimension * wi + ui * wi_dx);
+  pi->density.wcount += wi;
+  pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx);
+
+  /* Compute density of pj. */
+  const float hj_inv = 1.f / hj;
+  const float uj = r * hj_inv;
+  kernel_deval(uj, &wj, &wj_dx);
+
+  pj->rho += mi * wj;
+  pj->density.rho_dh -= mi * (hydro_dimension * wj + uj * wj_dx);
+  pj->pressure_bar += mi * wj * pi->u;
+  pj->density.pressure_bar_dh -=
+      mi * pi->u * (hydro_dimension * wj + uj * wj_dx);
+  pj->density.wcount += wj;
+  pj->density.wcount_dh -= (hydro_dimension * wj + uj * wj_dx);
+
+  /* Now we need to compute the div terms */
+  const float r_inv = 1.f / r;
+  const float faci = mj * wi_dx * r_inv;
+  const float facj = mi * wj_dx * r_inv;
+
+  /* Compute dv dot r */
+  dv[0] = pi->v[0] - pj->v[0];
+  dv[1] = pi->v[1] - pj->v[1];
+  dv[2] = pi->v[2] - pj->v[2];
+  const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2];
+
+  pi->density.div_v -= faci * dvdr;
+  pj->density.div_v -= facj * dvdr;
+
+  /* Compute dv cross r */
+  curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1];
+  curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2];
+  curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0];
+
+  pi->density.rot_v[0] += faci * curlvr[0];
+  pi->density.rot_v[1] += faci * curlvr[1];
+  pi->density.rot_v[2] += faci * curlvr[2];
+
+  /* Negative because of the change in sign of dx & dv. */
+  pj->density.rot_v[0] += facj * curlvr[0];
+  pj->density.rot_v[1] += facj * curlvr[1];
+  pj->density.rot_v[2] += facj * curlvr[2];
+}
+
+/**
+ * @brief Density interaction between two part*icles (non-symmetric).
+ *
+ * @param r2 Comoving square distance between the two part*icles.
+ * @param dx Comoving vector separating both part*icles (pi - pj).
+ * @param hi Comoving smoothing-length of part*icle i.
+ * @param hj Comoving smoothing-length of part*icle j.
+ * @param pi First part*icle.
+ * @param pj Second part*icle (not updated).
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
+    float r2, const float* dx, float hi, float hj, struct part* pi,
+    const struct part* pj, float a, float H) {
+
+  float wi, wi_dx;
+  float dv[3], curlvr[3];
+
+  /* Get the masses. */
+  const float mj = pj->mass;
+
+  /* Get r and r inverse. */
+  const float r = sqrtf(r2);
+
+  const float h_inv = 1.f / hi;
+  const float ui = r * h_inv;
+  kernel_deval(ui, &wi, &wi_dx);
+
+  pi->rho += mj * wi;
+  pi->density.rho_dh -= mj * (hydro_dimension * wi + ui * wi_dx);
+
+  pi->pressure_bar += mj * wi * pj->u;
+
+  pi->density.pressure_bar_dh -=
+      mj * pj->u * (hydro_dimension * wi + ui * wi_dx);
+  pi->density.wcount += wi;
+  pi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx);
+
+  const float r_inv = 1.f / r;
+  const float faci = mj * wi_dx * r_inv;
+
+  /* Compute dv dot r */
+  dv[0] = pi->v[0] - pj->v[0];
+  dv[1] = pi->v[1] - pj->v[1];
+  dv[2] = pi->v[2] - pj->v[2];
+  const float dvdr = dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2];
+
+  pi->density.div_v -= faci * dvdr;
+
+  /* Compute dv cross r */
+  curlvr[0] = dv[1] * dx[2] - dv[2] * dx[1];
+  curlvr[1] = dv[2] * dx[0] - dv[0] * dx[2];
+  curlvr[2] = dv[0] * dx[1] - dv[1] * dx[0];
+
+  pi->density.rot_v[0] += faci * curlvr[0];
+  pi->density.rot_v[1] += faci * curlvr[1];
+  pi->density.rot_v[2] += faci * curlvr[2];
+}
+
+/**
+ * @brief Force interaction between two part*icles.
+ *
+ * @param r2 Comoving square distance between the two part*icles.
+ * @param dx Comoving vector separating both part*icles (pi - pj).
+ * @param hi Comoving smoothing-length of part*icle i.
+ * @param hj Comoving smoothing-length of part*icle j.
+ * @param pi First part*icle.
+ * @param pj Second part*icle.
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_force(
+    float r2, const float* dx, float hi, float hj, struct part* pi,
+    struct part* pj, float a, float H) {
+
+  /* Cosmological factors entering the EoMs */
+  const float fac_mu = pow_three_gamma_minus_five_over_two(a);
+  const float a2_Hubble = a * a * H;
+
+  const float r = sqrtf(r2);
+  const float r_inv = 1.0f / r;
+
+  /* Recover some data */
+  const float mj = pj->mass;
+  const float mi = pi->mass;
+
+  const float miui = mi * pi->u;
+  const float mjuj = mj * pj->u;
+
+  const float rhoi = pi->rho;
+  const float rhoj = pj->rho;
+  /* Compute gradient terms */
+  const float f_ij = 1.f - (pi->force.f / mjuj);
+  const float f_ji = 1.f - (pj->force.f / miui);
+
+  /* Get the kernel for hi. */
+  const float hi_inv = 1.0f / hi;
+  const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
+  const float xi = r * hi_inv;
+  float wi, wi_dx;
+  kernel_deval(xi, &wi, &wi_dx);
+  const float wi_dr = hid_inv * wi_dx;
+
+  /* Get the kernel for hj. */
+  const float hj_inv = 1.0f / hj;
+  const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
+  const float xj = r * hj_inv;
+  float wj, wj_dx;
+  kernel_deval(xj, &wj, &wj_dx);
+  const float wj_dr = hjd_inv * wj_dx;
+
+  /* Compute dv dot r. */
+  const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] +
+                     (pi->v[1] - pj->v[1]) * dx[1] +
+                     (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2;
+
+  /* Are the part*icles moving towards each others ? */
+  const float omega_ij = min(dvdr, 0.f);
+  const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
+
+  /* Compute sound speeds and signal velocity */
+  const float ci = pi->force.soundspeed;
+  const float cj = pj->force.soundspeed;
+  const float v_sig = ci + cj - 3.f * mu_ij;
+
+  /* Balsara term */
+  const float balsara_i = pi->force.balsara;
+  const float balsara_j = pj->force.balsara;
+
+  /* Construct the full viscosity term */
+  const float rho_ij = 0.5f * (rhoi + rhoj);
+  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
+                     (balsara_i + balsara_j) / rho_ij;
+
+  /* Convolve with the kernel */
+  const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
+
+  /* SPH acceleration term */
+  const float sph_acc_term =
+      pj->u * pi->u * hydro_gamma_minus_one * hydro_gamma_minus_one *
+      ((f_ij / pi->pressure_bar) * wi_dr + (f_ji / pj->pressure_bar) * wj_dr) *
+      r_inv;
+
+  /* Assemble the acceleration */
+  const float acc = sph_acc_term + visc_acc_term;
+
+  /* Use the force Luke ! */
+  pi->a_hydro[0] -= mj * acc * dx[0];
+  pi->a_hydro[1] -= mj * acc * dx[1];
+  pi->a_hydro[2] -= mj * acc * dx[2];
+
+  pj->a_hydro[0] += mi * acc * dx[0];
+  pj->a_hydro[1] += mi * acc * dx[1];
+  pj->a_hydro[2] += mi * acc * dx[2];
+
+  /* Get the time derivative for u. */
+  const float sph_du_term_i = hydro_gamma_minus_one * hydro_gamma_minus_one *
+                              pj->u * pi->u * (f_ij / pi->pressure_bar) *
+                              wi_dr * dvdr * r_inv;
+  const float sph_du_term_j = hydro_gamma_minus_one * hydro_gamma_minus_one *
+                              pi->u * pj->u * (f_ji / pj->pressure_bar) *
+                              wj_dr * dvdr * r_inv;
+
+  /* Viscosity term */
+  const float visc_du_term = 0.5f * visc_acc_term * dvdr;
+
+  /* Assemble the energy equation term */
+  const float du_dt_i = sph_du_term_i + visc_du_term;
+  const float du_dt_j = sph_du_term_j + visc_du_term;
+
+  /* Internal energy time derivative */
+  pi->u_dt += du_dt_i * mj;
+  pj->u_dt += du_dt_j * mi;
+
+  /* Get the time derivative for h. */
+  pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr;
+  pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr;
+
+  /* Update the signal velocity. */
+  pi->force.v_sig = max(pi->force.v_sig, v_sig);
+  pj->force.v_sig = max(pj->force.v_sig, v_sig);
+}
+
+/**
+ * @brief Force interaction between two part*icles (non-symmetric).
+ *
+ * @param r2 Comoving square distance between the two part*icles.
+ * @param dx Comoving vector separating both part*icles (pi - pj).
+ * @param hi Comoving smoothing-length of part*icle i.
+ * @param hj Comoving smoothing-length of part*icle j.
+ * @param pi First part*icle.
+ * @param pj Second part*icle (not updated).
+ * @param a Current scale factor.
+ * @param H Current Hubble parameter.
+ */
+__attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
+    float r2, const float* dx, float hi, float hj, struct part* pi,
+    const struct part* pj, float a, float H) {
+
+  /* Cosmological factors entering the EoMs */
+  const float fac_mu = pow_three_gamma_minus_five_over_two(a);
+  const float a2_Hubble = a * a * H;
+
+  const float r = sqrtf(r2);
+  const float r_inv = 1.0f / r;
+
+  /* Recover some data */
+  // const float mi = pi->mass;
+  const float mj = pj->mass;
+  const float mi = pi->mass;
+
+  const float miui = mi * pi->u;
+  const float mjuj = mj * pj->u;
+
+  const float rhoi = pi->rho;
+  const float rhoj = pj->rho;
+  /* Compute gradient terms */
+  const float f_ij = 1.f - (pi->force.f / mjuj);
+  const float f_ji = 1.f - (pj->force.f / miui);
+
+  /* Get the kernel for hi. */
+  const float hi_inv = 1.0f / hi;
+  const float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
+  const float xi = r * hi_inv;
+  float wi, wi_dx;
+  kernel_deval(xi, &wi, &wi_dx);
+  const float wi_dr = hid_inv * wi_dx;
+
+  /* Get the kernel for hj. */
+  const float hj_inv = 1.0f / hj;
+  const float hjd_inv = pow_dimension_plus_one(hj_inv); /* 1/h^(d+1) */
+  const float xj = r * hj_inv;
+  float wj, wj_dx;
+  kernel_deval(xj, &wj, &wj_dx);
+  const float wj_dr = hjd_inv * wj_dx;
+
+  /* Compute dv dot r. */
+  const float dvdr = (pi->v[0] - pj->v[0]) * dx[0] +
+                     (pi->v[1] - pj->v[1]) * dx[1] +
+                     (pi->v[2] - pj->v[2]) * dx[2] + a2_Hubble * r2;
+
+  /* Are the part*icles moving towards each others ? */
+  const float omega_ij = min(dvdr, 0.f);
+  const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
+
+  /* Compute sound speeds and signal velocity */
+  const float ci = pi->force.soundspeed;
+  const float cj = pj->force.soundspeed;
+  const float v_sig = ci + cj - 3.f * mu_ij;
+
+  /* Balsara term */
+  const float balsara_i = pi->force.balsara;
+  const float balsara_j = pj->force.balsara;
+
+  /* Construct the full viscosity term */
+  const float rho_ij = 0.5f * (rhoi + rhoj);
+  const float visc = -0.25f * const_viscosity_alpha * v_sig * mu_ij *
+                     (balsara_i + balsara_j) / rho_ij;
+
+  /* Convolve with the kernel */
+  const float visc_acc_term = 0.5f * visc * (wi_dr + wj_dr) * r_inv;
+
+  /* SPH acceleration term */
+  const float sph_acc_term =
+      pj->u * pi->u * hydro_gamma_minus_one * hydro_gamma_minus_one *
+      ((f_ij / pi->pressure_bar) * wi_dr + (f_ji / pj->pressure_bar) * wj_dr) *
+      r_inv;
+
+  /* Assemble the acceleration */
+  const float acc = sph_acc_term + visc_acc_term;
+
+  /* Use the force Luke ! */
+  pi->a_hydro[0] -= mj * acc * dx[0];
+  pi->a_hydro[1] -= mj * acc * dx[1];
+  pi->a_hydro[2] -= mj * acc * dx[2];
+
+  /* Get the time derivative for u. */
+  const float sph_du_term_i = hydro_gamma_minus_one * hydro_gamma_minus_one *
+                              pj->u * pi->u * (f_ij / pi->pressure_bar) *
+                              wi_dr * dvdr * r_inv;
+
+  /* Viscosity term */
+  const float visc_du_term = 0.5f * visc_acc_term * dvdr;
+
+  /* Assemble the energy equation term */
+  const float du_dt_i = sph_du_term_i + visc_du_term;
+
+  /* Internal energy time derivatibe */
+  pi->u_dt += du_dt_i * mj;
+
+  /* Get the time derivative for h. */
+  pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr;
+
+  /* Update the signal velocity. */
+  pi->force.v_sig = max(pi->force.v_sig, v_sig);
+}
+
+#endif /* SWIFT_MINIMAL_HYDRO_IACT_H */
diff --git a/src/hydro/PressureEnergy/hydro_io.h b/src/hydro/PressureEnergy/hydro_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..78967faec218f0efffbb624c4e8d25af214aad94
--- /dev/null
+++ b/src/hydro/PressureEnergy/hydro_io.h
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) &
+ *                    Josh Borrow (joshua.borrow@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_PRESSURE_ENERGY_HYDRO_IACT_H
+#define SWIFT_PRESSURE_ENERGY_HYDRO_IACT_H
+/**
+ * @file PressureEnergy/hydro_io.h
+ * @brief P-U implementation of SPH (i/o routines)
+ *
+ * The thermal variable is the internal energy (u). A simple constant
+ * viscosity term with a Balsara switch is implemented.
+ *
+ * No thermal conduction term is implemented.
+ *
+ * See PressureEnergy/hydro.h for references.
+ */
+
+#include "adiabatic_index.h"
+#include "hydro.h"
+#include "io_properties.h"
+#include "kernel_hydro.h"
+
+/**
+ * @brief Specifies which particle fields to read from a dataset
+ *
+ * @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.
+ */
+INLINE static 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, 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, u);
+  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, rho);
+}
+
+INLINE static void convert_u(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
+
+  ret[0] = hydro_get_comoving_internal_energy(p);
+}
+
+INLINE static void convert_S(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
+
+  ret[0] = hydro_get_comoving_entropy(p);
+}
+
+INLINE static void convert_P(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
+
+  ret[0] = hydro_get_comoving_pressure(p);
+}
+
+INLINE static void convert_part_pos(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, double* ret) {
+
+  if (e->s->periodic) {
+    ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
+    ret[1] = box_wrap(p->x[1], 0.0, e->s->dim[1]);
+    ret[2] = box_wrap(p->x[2], 0.0, e->s->dim[2]);
+  } else {
+    ret[0] = p->x[0];
+    ret[1] = p->x[1];
+    ret[2] = p->x[2];
+  }
+}
+
+INLINE static void convert_part_vel(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, float* ret) {
+
+  const int with_cosmology = (e->policy & engine_policy_cosmology);
+  const struct cosmology* cosmo = e->cosmology;
+  const integertime_t ti_current = e->ti_current;
+  const double time_base = e->time_base;
+
+  const integertime_t ti_beg = get_integer_time_begin(ti_current, p->time_bin);
+  const integertime_t ti_end = get_integer_time_end(ti_current, p->time_bin);
+
+  /* Get time-step since the last kick */
+  float dt_kick_grav, dt_kick_hydro;
+  if (with_cosmology) {
+    dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current);
+    dt_kick_grav -=
+        cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2);
+    dt_kick_hydro = cosmology_get_hydro_kick_factor(cosmo, ti_beg, ti_current);
+    dt_kick_hydro -=
+        cosmology_get_hydro_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2);
+  } else {
+    dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base;
+    dt_kick_hydro = (ti_current - ((ti_beg + ti_end) / 2)) * time_base;
+  }
+
+  /* Extrapolate the velocites to the current time */
+  hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret);
+
+  /* Conversion from internal units to peculiar velocities */
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
+}
+
+/**
+ * @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.
+ */
+INLINE static void hydro_write_particles(const struct part* parts,
+                                         const struct xpart* xparts,
+                                         struct io_props* list,
+                                         int* num_fields) {
+
+  *num_fields = 9;
+
+  /* List what we want to write */
+  list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3,
+                                              UNIT_CONV_LENGTH, parts, xparts,
+                                              convert_part_pos);
+  list[1] = io_make_output_field_convert_part(
+      "Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, xparts, convert_part_vel);
+  list[2] =
+      io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, parts, mass);
+  list[3] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH,
+                                 parts, h);
+  list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1,
+                                              UNIT_CONV_ENERGY_PER_UNIT_MASS,
+                                              parts, xparts, convert_u);
+  list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
+                                 UNIT_CONV_NO_UNITS, parts, id);
+  list[6] =
+      io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho);
+  list[7] = io_make_output_field("Pressure", FLOAT, 1, UNIT_CONV_PRESSURE,
+                                 parts, pressure_bar);
+  list[8] = io_make_output_field_convert_part("Entropy", FLOAT, 1,
+                                              UNIT_CONV_ENTROPY_PER_UNIT_MASS,
+                                              parts, xparts, convert_S);
+}
+
+/**
+ * @brief Writes the current model of SPH to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+INLINE static void hydro_write_flavour(hid_t h_grpsph) {
+
+  /* Viscosity and thermal conduction */
+  /* Nothing in this minimal model... */
+  io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment");
+  io_write_attribute_s(h_grpsph, "Viscosity Model",
+                       "Minimal treatment as in Monaghan (1992)");
+
+  /* Time integration properties */
+  io_write_attribute_f(h_grpsph, "Maximal Delta u change over dt",
+                       const_max_u_change);
+}
+
+/**
+ * @brief Are we writing entropy in the internal energy field ?
+ *
+ * @return 1 if entropy is in 'internal energy', 0 otherwise.
+ */
+INLINE static int writeEntropyFlag(void) { return 0; }
+
+#endif /* SWIFT_MINIMAL_HYDRO_IO_H */
diff --git a/src/hydro/PressureEnergy/hydro_part.h b/src/hydro/PressureEnergy/hydro_part.h
new file mode 100644
index 0000000000000000000000000000000000000000..bc7d14b612556dc722ecca67dd6ce823192e00f0
--- /dev/null
+++ b/src/hydro/PressureEnergy/hydro_part.h
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk) &
+ *                    Josh Borrow (joshua.borrow@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_PRESSURE_ENERGY_HYDRO_PART_H
+#define SWIFT_PRESSURE_ENERGY_HYDRO_PART_H
+/**
+ * @file PressureEnergy/hydro_part.h
+ * @brief P-U implementation of SPH (Particle definition)
+ *
+ * The thermal variable is the internal energy (u). A simple constant
+ * viscosity term with a Balsara switch is implemented.
+ *
+ * No thermal conduction term is implemented.
+ *
+ * See PressureEnergy/hydro.h for references.
+ */
+
+#include "chemistry_struct.h"
+#include "cooling_struct.h"
+
+/**
+ * @brief Particle fields not needed during the SPH loops over neighbours.
+ *
+ * This structure contains the particle fields that are not used in the
+ * density or force loops. Quantities should be used in the kick, drift and
+ * potentially ghost tasks only.
+ */
+struct xpart {
+
+  /*! Offset between current position and position at last tree rebuild. */
+  float x_diff[3];
+
+  /*! Offset between the current position and position at the last sort. */
+  float x_diff_sort[3];
+
+  /*! Velocity at the last full step. */
+  float v_full[3];
+
+  /*! Gravitational acceleration at the last full step. */
+  float a_grav[3];
+
+  /*! Internal energy at the last full step. */
+  float u_full;
+
+  /*! Additional data used to record cooling information */
+  struct cooling_xpart_data cooling_data;
+
+} SWIFT_STRUCT_ALIGN;
+
+/**
+ * @brief Particle fields for the SPH particles
+ *
+ * The density and force substructures are used to contain variables only used
+ * within the density and force loops over neighbours. All more permanent
+ * variables should be declared in the main part of the part structure,
+ */
+struct part {
+
+  /*! Particle unique ID. */
+  long long id;
+
+  /*! Pointer to corresponding gravity part. */
+  struct gpart* gpart;
+
+  /*! Particle position. */
+  double x[3];
+
+  /*! Particle predicted velocity. */
+  float v[3];
+
+  /*! Particle acceleration. */
+  float a_hydro[3];
+
+  /*! Particle mass. */
+  float mass;
+
+  /*! Particle smoothing length. */
+  float h;
+
+  /*! Particle internal energy. */
+  float u;
+
+  /*! Time derivative of the internal energy. */
+  float u_dt;
+
+  /*! Particle density. */
+  float rho;
+
+  /*! Particle pressure (weighted) */
+  float pressure_bar;
+
+  /* Store density/force specific stuff. */
+  union {
+
+    /**
+     * @brief Structure for the variables only used in the density loop over
+     * neighbours.
+     *
+     * Quantities in this sub-structure should only be accessed in the density
+     * loop over neighbours and the ghost task.
+     */
+    struct {
+
+      /*! Neighbour number count. */
+      float wcount;
+
+      /*! Derivative of the neighbour number with respect to h. */
+      float wcount_dh;
+
+      /*! Derivative of density with respect to h */
+      float rho_dh;
+
+      /*! Derivative of the weighted pressure with respect to h */
+      float pressure_bar_dh;
+
+      /*! Particle velocity curl. */
+      float rot_v[3];
+
+      /*! Particle velocity divergence. */
+      float div_v;
+    } density;
+
+    /**
+     * @brief Structure for the variables only used in the force loop over
+     * neighbours.
+     *
+     * Quantities in this sub-structure should only be accessed in the force
+     * loop over neighbours and the ghost, drift and kick tasks.
+     */
+    struct {
+
+      /*! "Grad h" term -- only partial in P-U */
+      float f;
+
+      /*! Particle soundspeed. */
+      float soundspeed;
+
+      /*! Particle signal velocity */
+      float v_sig;
+
+      /*! Time derivative of smoothing length  */
+      float h_dt;
+
+      /*! Balsara switch */
+      float balsara;
+    } force;
+  };
+
+  /* Chemistry information */
+  struct chemistry_part_data chemistry_data;
+
+  /*! Time-step length */
+  timebin_t time_bin;
+
+#ifdef SWIFT_DEBUG_CHECKS
+
+  /* Time of the last drift */
+  integertime_t ti_drift;
+
+  /* Time of the last kick */
+  integertime_t ti_kick;
+
+#endif
+
+} SWIFT_STRUCT_ALIGN;
+
+#endif /* SWIFT_MINIMAL_HYDRO_PART_H */
diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h
index 87d46c6d43f0d4f6de6d18f5400b38f0fc4d0f55..e4b7cf06e083638a94526cc1f9e7212cf19dfad4 100644
--- a/src/hydro/PressureEntropy/hydro.h
+++ b/src/hydro/PressureEntropy/hydro.h
@@ -357,7 +357,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
   /* Re-set problematic values */
   p->rho = p->mass * kernel_root * h_inv_dim;
   p->rho_bar = p->mass * kernel_root * h_inv_dim;
-  p->density.wcount = kernel_root * kernel_norm * h_inv_dim;
+  p->density.wcount = kernel_root * h_inv_dim;
   p->density.rho_dh = 0.f;
   p->density.wcount_dh = 0.f;
   p->density.pressure_dh = 0.f;
@@ -441,7 +441,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_acceleration(
   p->force.h_dt = 0.0f;
 
   /* Reset maximal signal velocity */
-  p->force.v_sig = 0.0f;
+  p->force.v_sig = p->force.soundspeed;
 }
 
 /**
diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h
index 78371c1eb21fafed56e89d46690d0cf1e0f2a0f0..8c11bf6e334e18b10217e90f6573a42e40880955 100644
--- a/src/hydro/PressureEntropy/hydro_io.h
+++ b/src/hydro/PressureEntropy/hydro_io.h
@@ -42,8 +42,9 @@
  * @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) {
+INLINE static void hydro_read_particles(struct part* parts,
+                                        struct io_props* list,
+                                        int* num_fields) {
 
   *num_fields = 8;
 
@@ -67,20 +68,21 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
                                 UNIT_CONV_DENSITY, parts, rho);
 }
 
-void convert_u(const struct engine* e, const struct part* p,
-               const struct xpart* xp, float* ret) {
+INLINE static void convert_u(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
 
   ret[0] = hydro_get_comoving_internal_energy(p);
 }
 
-void convert_P(const struct engine* e, const struct part* p,
-               const struct xpart* xp, float* ret) {
+INLINE static void convert_P(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
 
   ret[0] = hydro_get_comoving_pressure(p);
 }
 
-void convert_part_pos(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, double* ret) {
+INLINE static void convert_part_pos(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -93,8 +95,9 @@ void convert_part_pos(const struct engine* e, const struct part* p,
   }
 }
 
-void convert_part_vel(const struct engine* e, const struct part* p,
-                      const struct xpart* xp, float* ret) {
+INLINE static void convert_part_vel(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, float* ret) {
 
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const struct cosmology* cosmo = e->cosmology;
@@ -122,13 +125,14 @@ void convert_part_vel(const struct engine* e, const struct part* p,
   hydro_get_drifted_velocities(p, xp, dt_kick_hydro, dt_kick_grav, ret);
 
   /* Conversion from internal units to peculiar velocities */
-  ret[0] *= cosmo->a2_inv;
-  ret[1] *= cosmo->a2_inv;
-  ret[2] *= cosmo->a2_inv;
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
 }
 
-void convert_part_potential(const struct engine* e, const struct part* p,
-                            const struct xpart* xp, float* ret) {
+INLINE static void convert_part_potential(const struct engine* e,
+                                          const struct part* p,
+                                          const struct xpart* xp, float* ret) {
 
   if (p->gpart != NULL)
     ret[0] = gravity_get_comoving_potential(p->gpart);
@@ -143,8 +147,10 @@ void convert_part_potential(const struct engine* e, const struct part* p,
  * @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(const struct part* parts, const struct xpart* xparts,
-                           struct io_props* list, int* num_fields) {
+INLINE static void hydro_write_particles(const struct part* parts,
+                                         const struct xpart* xparts,
+                                         struct io_props* list,
+                                         int* num_fields) {
 
   *num_fields = 11;
 
@@ -180,7 +186,7 @@ void hydro_write_particles(const struct part* parts, const struct xpart* xparts,
  * @brief Writes the current model of SPH to the file
  * @param h_grpsph The HDF5 group in which to write
  */
-void hydro_write_flavour(hid_t h_grpsph) {
+INLINE static void hydro_write_flavour(hid_t h_grpsph) {
 
   /* Viscosity and thermal conduction */
   /* Nothing in this minimal model... */
@@ -201,6 +207,6 @@ void hydro_write_flavour(hid_t h_grpsph) {
  *
  * @return 1 if entropy is in 'internal energy', 0 otherwise.
  */
-int writeEntropyFlag() { return 0; }
+INLINE static int writeEntropyFlag(void) { return 0; }
 
 #endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_IO_H */
diff --git a/src/hydro/Shadowswift/hydro.h b/src/hydro/Shadowswift/hydro.h
index e4ea0d971cd89862d3ea6b99f0b04930666a154a..d70d58c6ba508ba4282ac9dd32565478afb40692 100644
--- a/src/hydro/Shadowswift/hydro.h
+++ b/src/hydro/Shadowswift/hydro.h
@@ -16,10 +16,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#ifndef SWIFT_SHADOWSWIFT_HYDRO_H
+#define SWIFT_SHADOWSWIFT_HYDRO_H
 
 #include <float.h>
 #include "adiabatic_index.h"
 #include "approx_math.h"
+#include "cosmology.h"
 #include "equation_of_state.h"
 #include "hydro_gradients.h"
 #include "hydro_properties.h"
@@ -35,19 +38,28 @@
  */
 __attribute__((always_inline)) INLINE static float hydro_compute_timestep(
     const struct part* restrict p, const struct xpart* restrict xp,
-    const struct hydro_props* restrict hydro_properties) {
+    const struct hydro_props* restrict hydro_properties,
+    const struct cosmology* restrict cosmo) {
 
   const float CFL_condition = hydro_properties->CFL_condition;
 
-  float R = get_radius_dimension_sphere(p->cell.volume);
-
-  if (p->timestepvars.vmax == 0.) {
-    /* vmax can be zero in vacuum. We force the time step to become the maximal
-       time step in this case */
-    return FLT_MAX;
-  } else {
-    return CFL_condition * R / fabsf(p->timestepvars.vmax);
+  float vrel[3];
+  vrel[0] = p->primitives.v[0] - xp->v_full[0];
+  vrel[1] = p->primitives.v[1] - xp->v_full[1];
+  vrel[2] = p->primitives.v[2] - xp->v_full[2];
+  float vmax =
+      sqrtf(vrel[0] * vrel[0] + vrel[1] * vrel[1] + vrel[2] * vrel[2]) +
+      sqrtf(hydro_gamma * p->primitives.P / p->primitives.rho);
+  vmax = max(vmax, p->timestepvars.vmax);
+
+  const float psize =
+      cosmo->a *
+      powf(p->cell.volume / hydro_dimension_unit_sphere, hydro_dimension_inv);
+  float dt = FLT_MAX;
+  if (vmax > 0.) {
+    dt = psize / vmax;
   }
+  return CFL_condition * dt;
 }
 
 /**
@@ -100,7 +112,7 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part(
   p->conserved.momentum[2] = mass * p->primitives.v[2];
 
 #ifdef EOS_ISOTHERMAL_GAS
-  p->conserved.energy = mass * const_isothermal_internal_energy;
+  p->conserved.energy = mass * gas_internal_energy_from_entropy(0.f, 0.f);
 #else
   p->conserved.energy *= mass;
 #endif
@@ -115,12 +127,21 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part(
   p->v[0] = 0.;
   p->v[1] = 0.;
   p->v[2] = 0.;
+#else
+  p->v[0] = p->primitives.v[0];
+  p->v[1] = p->primitives.v[1];
+  p->v[2] = p->primitives.v[2];
 #endif
 
   /* set the initial velocity of the cells */
   xp->v_full[0] = p->v[0];
   xp->v_full[1] = p->v[1];
   xp->v_full[2] = p->v[2];
+
+  /* ignore accelerations present in the initial condition */
+  p->a_hydro[0] = 0.0f;
+  p->a_hydro[1] = 0.0f;
+  p->a_hydro[2] = 0.0f;
 }
 
 /**
@@ -135,7 +156,8 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part(
 __attribute__((always_inline)) INLINE static void hydro_init_part(
     struct part* p, const struct hydro_space* hs) {
 
-  p->density.wcount = 0.0f;
+  /* make sure we don't enter the no neighbour case in runner.c */
+  p->density.wcount = 1.0f;
   p->density.wcount_dh = 0.0f;
 
   voronoi_cell_init(&p->cell, p->x, hs->anchor, hs->side);
@@ -158,7 +180,7 @@ __attribute__((always_inline)) INLINE static void hydro_init_part(
  * @param p The particle to act upon.
  */
 __attribute__((always_inline)) INLINE static void hydro_end_density(
-    struct part* restrict p) {
+    struct part* restrict p, const struct cosmology* cosmo) {
 
   float volume;
   float m, momentum[3], energy;
@@ -178,12 +200,13 @@ __attribute__((always_inline)) INLINE static void hydro_end_density(
   if (hnew < p->h) {
     /* Iteration succesful: we accept, but manually set h to a smaller value
        for the next time step */
-    p->density.wcount = 1.0f;
+    const float hinvdim = pow_dimension(1.0f / p->h);
+    p->density.wcount = hinvdim;
     p->h = 1.1f * hnew;
   } else {
     /* Iteration not succesful: we force h to become 1.1*hnew */
     p->density.wcount = 0.0f;
-    p->density.wcount_dh = 1.0f / (1.1f * hnew - p->h);
+    p->density.wcount_dh = p->h / (1.1f * hnew - p->h);
     return;
   }
   volume = p->cell.volume;
@@ -246,7 +269,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_density(
  * @param xp The extended particle data to act upon
  */
 __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
-    struct part* restrict p, struct xpart* restrict xp) {
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct cosmology* cosmo) {
 
   /* Some smoothing length multiples. */
   const float h = p->h;
@@ -254,7 +278,7 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
   const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */
 
   /* Re-set problematic values */
-  p->density.wcount = kernel_root * kernel_norm * h_inv_dim;
+  p->density.wcount = kernel_root * h_inv_dim;
   p->density.wcount_dh = 0.f;
 }
 
@@ -273,7 +297,8 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param xp The extended particle data to act upon.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
-    struct part* restrict p, struct xpart* restrict xp) {
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct cosmology* cosmo) {
 
   /* Initialize time step criterion variables */
   p->timestepvars.vmax = 0.0f;
@@ -282,8 +307,48 @@ __attribute__((always_inline)) INLINE static void hydro_prepare_force(
   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];
+
+  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 Prepare a particle for the gradient calculation.
+ *
+ * This function is 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 cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_prepare_gradient(
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct cosmology* cosmo) {
+
+  /* Initialize time step criterion variables */
+  p->timestepvars.vmax = 0.;
+}
+
+/**
+ * @brief Resets the variables that are required for a gradient calculation.
+ *
+ * This function is called after hydro_prepare_gradient.
+ *
+ * @param p The particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void hydro_reset_gradient(
+    struct part* restrict p) {}
+
 /**
  * @brief Finishes the gradient calculation.
  *
@@ -346,7 +411,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended particle data to act upon.
  */
 __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
-    struct part* p, struct xpart* xp) {}
+    struct part* p, struct xpart* xp, const struct cosmology* cosmo) {}
 
 /**
  * @brief Extra operations to be done during the drift
@@ -358,7 +423,7 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
  * @param dt The drift time-step.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
-    struct part* p, struct xpart* xp, float dt) {}
+    struct part* p, struct xpart* xp, float dt_drift, float dt_therm) {}
 
 /**
  * @brief Set the particle acceleration after the flux loop.
@@ -366,7 +431,7 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra(
  * @param p Particle to act upon.
  */
 __attribute__((always_inline)) INLINE static void hydro_end_force(
-    struct part* p) {}
+    struct part* p, const struct cosmology* cosmo) {}
 
 /**
  * @brief Extra operations done during the kick
@@ -378,55 +443,39 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param dt Physical time step.
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
-    struct part* p, struct xpart* xp, float dt) {
-
-  float vcell[3];
+    struct part* p, struct xpart* xp, float dt, const struct cosmology* cosmo,
+    const struct hydro_props* hydro_props) {
 
   /* Update the conserved variables. We do this here and not in the kick,
      since we need the updated variables below. */
-  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;
+  p->conserved.mass += p->conserved.flux.mass * dt;
+  p->conserved.momentum[0] += p->conserved.flux.momentum[0] * dt;
+  p->conserved.momentum[1] += p->conserved.flux.momentum[1] * dt;
+  p->conserved.momentum[2] += p->conserved.flux.momentum[2] * dt;
 
 #ifdef EOS_ISOTHERMAL_GAS
   /* reset the thermal energy */
-  p->conserved.energy = p->conserved.mass * const_isothermal_internal_energy;
-
-#ifdef SHADOWFAX_TOTAL_ENERGY
-  p->conserved.energy += 0.5f * (p->conserved.momentum[0] * p->primitives.v[0] +
-                                 p->conserved.momentum[1] * p->primitives.v[1] +
-                                 p->conserved.momentum[2] * p->primitives.v[2]);
+  p->conserved.energy =
+      p->conserved.mass * gas_internal_energy_from_entropy(0.f, 0.f);
+#else
+  p->conserved.energy += p->conserved.flux.energy * dt;
 #endif
 
-#endif
+#if defined(SHADOWFAX_FIX_CELLS)
+  p->v[0] = 0.0f;
+  p->v[1] = 0.0f;
+  p->v[2] = 0.0f;
+#else
+  if (p->conserved.mass > 0.0f && p->primitives.rho > 0.0f) {
 
-  /* 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;
+    const float inverse_mass = 1.f / p->conserved.mass;
 
-  if (p->conserved.mass > 0.) {
-    /* We want the cell velocity to be as close as possible to the fluid
-       velocity */
-    vcell[0] = p->conserved.momentum[0] / p->conserved.mass;
-    vcell[1] = p->conserved.momentum[1] / p->conserved.mass;
-    vcell[2] = p->conserved.momentum[2] / p->conserved.mass;
-  } else {
-    vcell[0] = 0.;
-    vcell[1] = 0.;
-    vcell[2] = 0.;
-  }
+    /* Normal case: set particle velocity to fluid velocity. */
+    p->v[0] = p->conserved.momentum[0] * inverse_mass;
+    p->v[1] = p->conserved.momentum[1] * inverse_mass;
+    p->v[2] = p->conserved.momentum[2] * inverse_mass;
 
 #ifdef SHADOWFAX_STEER_CELL_MOTION
-  /* To prevent stupid things like cell crossovers or generators that move
-     outside their cell, we steer the motion of the cell somewhat */
-  if (p->primitives.rho) {
     float centroid[3], d[3];
     float volume, csnd, R, vfac, fac, dnrm;
     voronoi_get_centroid(&p->cell, centroid);
@@ -444,32 +493,29 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
       } else {
         vfac = csnd / dnrm;
       }
-    } else {
-      vfac = 0.0f;
+      p->v[0] += vfac * d[0];
+      p->v[1] += vfac * d[1];
+      p->v[2] += vfac * d[2];
     }
-    vcell[0] += vfac * d[0];
-    vcell[1] += vfac * d[1];
-    vcell[2] += vfac * d[2];
-  }
 #endif
 
-#if defined(SHADOWFAX_FIX_CELLS)
-  xp->v_full[0] = 0.;
-  xp->v_full[1] = 0.;
-  xp->v_full[2] = 0.;
+  } else {
+    p->v[0] = 0.;
+    p->v[1] = 0.;
+    p->v[2] = 0.;
+  }
+#endif
 
-  p->v[0] = 0.;
-  p->v[1] = 0.;
-  p->v[2] = 0.;
-#else
-  xp->v_full[0] = vcell[0];
-  xp->v_full[1] = vcell[1];
-  xp->v_full[2] = vcell[2];
+  /* Now make sure all velocity variables are up to date. */
+  xp->v_full[0] = p->v[0];
+  xp->v_full[1] = p->v[1];
+  xp->v_full[2] = p->v[2];
 
-  p->v[0] = xp->v_full[0];
-  p->v[1] = xp->v_full[1];
-  p->v[2] = xp->v_full[2];
-#endif
+  if (p->gpart) {
+    p->gpart->v_full[0] = p->v[0];
+    p->gpart->v_full[1] = p->v[1];
+    p->gpart->v_full[2] = p->v[2];
+  }
 }
 
 /**
@@ -553,8 +599,13 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass(
  * @param v (return) The velocities at the current time.
  */
 __attribute__((always_inline)) INLINE static void hydro_get_drifted_velocities(
-    const struct part* restrict p, const struct xpart* xp, float dt,
-    float v[3]) {}
+    const struct part* restrict p, const struct xpart* xp, float dt_kick_hydro,
+    float dt_kick_grav, float v[3]) {
+
+  v[0] = p->v[0];
+  v[1] = p->v[1];
+  v[2] = p->v[2];
+}
 
 /**
  * @brief Returns the density of a particle
@@ -621,3 +672,173 @@ __attribute__((always_inline)) INLINE static void hydro_set_entropy(
     p->primitives.P = gas_pressure_from_entropy(p->primitives.rho, S);
   }
 }
+
+/**
+ * @brief Sets the mass of a particle
+ *
+ * @param p The particle of interest
+ * @param m The mass to set.
+ */
+__attribute__((always_inline)) INLINE static void hydro_set_mass(
+    struct part* restrict p, float m) {
+
+  p->conserved.mass = m;
+}
+
+/**
+ * @brief Overwrite the initial internal energy of a particle.
+ *
+ * Note that in the cases where the thermodynamic variable is not
+ * internal energy but gets converted later, we must overwrite that
+ * field. The conversion to the actual variable happens later after
+ * the initial fake time-step.
+ *
+ * @param p The #part to write to.
+ * @param u_init The new initial internal energy.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_set_init_internal_energy(struct part* p, float u_init) {
+
+  p->conserved.energy = u_init * p->conserved.mass;
+#ifdef GIZMO_TOTAL_ENERGY
+  /* add the kinetic energy */
+  p->conserved.energy += 0.5f * p->conserved.mass *
+                         (p->conserved.momentum[0] * p->primitives.v[0] +
+                          p->conserved.momentum[1] * p->primitives.v[1] +
+                          p->conserved.momentum[2] * p->primitives.v[2]);
+#endif
+  p->primitives.P = hydro_gamma_minus_one * p->primitives.rho * u_init;
+}
+
+/**
+ * @brief Returns the comoving internal energy of a particle
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_internal_energy(const struct part* restrict p) {
+
+  if (p->primitives.rho > 0.)
+    return gas_internal_energy_from_pressure(p->primitives.rho,
+                                             p->primitives.P);
+  else
+    return 0.;
+}
+
+/**
+ * @brief Returns the comoving entropy of a particle
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_entropy(
+    const struct part* restrict p) {
+
+  if (p->primitives.rho > 0.) {
+    return gas_entropy_from_pressure(p->primitives.rho, p->primitives.P);
+  } else {
+    return 0.;
+  }
+}
+
+/**
+ * @brief Returns the sound speed of a particle
+ *
+ * @param p The particle of interest.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_comoving_soundspeed(const struct part* restrict p) {
+
+  if (p->primitives.rho > 0.)
+    return gas_soundspeed_from_pressure(p->primitives.rho, p->primitives.P);
+  else
+    return 0.;
+}
+
+/**
+ * @brief Returns the comoving pressure of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_pressure(
+    const struct part* restrict p) {
+
+  return p->primitives.P;
+}
+
+/**
+ * @brief Returns the comoving density of a particle
+ *
+ * @param p The particle of interest
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_comoving_density(
+    const struct part* restrict p) {
+
+  return p->primitives.rho;
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_internal_energy(const struct part* restrict p,
+                                   const struct cosmology* cosmo) {
+
+  return cosmo->a_factor_internal_energy *
+         hydro_get_comoving_internal_energy(p);
+}
+
+/**
+ * @brief Returns the physical internal energy of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_entropy(
+    const struct part* restrict p, const struct cosmology* cosmo) {
+
+  /* Note: no cosmological conversion required here with our choice of
+   * coordinates. */
+  return hydro_get_comoving_entropy(p);
+}
+
+/**
+ * @brief Returns the physical sound speed of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+hydro_get_physical_soundspeed(const struct part* restrict p,
+                              const struct cosmology* cosmo) {
+
+  return cosmo->a_factor_sound_speed * hydro_get_comoving_soundspeed(p);
+}
+
+/**
+ * @brief Returns the comoving pressure of a particle
+ *
+ * @param p The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_pressure(
+    const struct part* restrict p, const struct cosmology* cosmo) {
+
+  return cosmo->a_factor_pressure * p->primitives.P;
+}
+
+/**
+ * @brief Returns the physical density of a particle
+ *
+ * @param p The particle of interest
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float hydro_get_physical_density(
+    const struct part* restrict p, const struct cosmology* cosmo) {
+
+  return cosmo->a3_inv * p->primitives.rho;
+}
+
+#endif /* SWIFT_SHADOWSWIFT_HYDRO_H */
diff --git a/src/hydro/Shadowswift/hydro_gradients.h b/src/hydro/Shadowswift/hydro_gradients.h
index 1aea49790d998d3912a80fa1376cbd1e183f26f7..4e7a9911d8d4fc586fe7a56687dd4c4ae9ec8de2 100644
--- a/src/hydro/Shadowswift/hydro_gradients.h
+++ b/src/hydro/Shadowswift/hydro_gradients.h
@@ -51,8 +51,8 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_init(
  * @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 r2, const float* dx, float hi, float hj, struct part* restrict pi,
+    struct part* restrict pj) {}
 
 /**
  * @brief Gradient calculations done during the neighbour loop: non-symmetric
@@ -66,8 +66,9 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_collect(
  * @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) {}
+hydro_gradients_nonsym_collect(float r2, const float* dx, float hi, float hj,
+                               struct part* restrict pi,
+                               const struct part* restrict pj) {}
 
 /**
  * @brief Finalize the gradient variables after all data have been collected
@@ -84,8 +85,8 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_finalize(
  * 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) {
+    struct part* pi, struct part* pj, float hi, float hj, const float* dx,
+    float r, float* xij_i, float* Wi, float* Wj) {
 
   float dWi[5], dWj[5];
   float xij_j[3];
@@ -131,59 +132,6 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_predict(
 
   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];
diff --git a/src/hydro/Shadowswift/hydro_gradients_shadowfax.h b/src/hydro/Shadowswift/hydro_gradients_shadowfax.h
index 9ca40a604da3dc12bbb48ac033cd078f0561d8ab..d131731907806536e86a03921b1c701f287077f1 100644
--- a/src/hydro/Shadowswift/hydro_gradients_shadowfax.h
+++ b/src/hydro/Shadowswift/hydro_gradients_shadowfax.h
@@ -65,7 +65,7 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_init(
  * @param grad Current value of the gradient for the quantity (is updated).
  */
 __attribute__((always_inline)) INLINE void hydro_gradients_single_quantity(
-    float qL, float qR, float *cLR, float *xLR, float rLR, float A,
+    float qL, float qR, float *cLR, const float *xLR, float rLR, float A,
     float *grad) {
 
   grad[0] += A * ((qR - qL) * cLR[0] / rLR - 0.5f * (qL + qR) * xLR[0] / rLR);
@@ -84,7 +84,8 @@ __attribute__((always_inline)) INLINE void hydro_gradients_single_quantity(
  * @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 r2, const float *dx, float hi, float hj, struct part *pi,
+    struct part *pj) {
 
   float A, midpoint[3];
 
@@ -148,8 +149,8 @@ __attribute__((always_inline)) INLINE static void hydro_gradients_collect(
  * @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) {
+hydro_gradients_nonsym_collect(float r2, const float *dx, float hi, float hj,
+                               struct part *pi, const struct part *pj) {
 
   float A, midpoint[3];
 
diff --git a/src/hydro/Shadowswift/hydro_iact.h b/src/hydro/Shadowswift/hydro_iact.h
index 15219bf879ea7d34a8a45be217ef81107d3392e6..eda8e3759d9e08dac8073ebed9fb36dd0c5b99f6 100644
--- a/src/hydro/Shadowswift/hydro_iact.h
+++ b/src/hydro/Shadowswift/hydro_iact.h
@@ -35,7 +35,8 @@
  * @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 r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
 
   float mindx[3];
 
@@ -59,7 +60,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_density(
  * @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 r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    const struct part *restrict pj, float a, float H) {
 
   voronoi_cell_interact(&pi->cell, dx, pj->id);
 }
@@ -78,7 +80,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_density(
  * @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) {
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
 
   hydro_gradients_collect(r2, dx, hi, hj, pi, pj);
 }
@@ -98,7 +101,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_gradient(
  * @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) {
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
 
   hydro_gradients_nonsym_collect(r2, dx, hi, hj, pi, pj);
 }
@@ -129,8 +133,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_gradient(
  * @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 r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, int mode, float a, float H) {
 
   float r = sqrtf(r2);
   int k;
@@ -139,7 +143,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   float vmax, dvdotdx;
   float vi[3], vj[3], vij[3];
   float Wi[5], Wj[5];
-  float dti, dtj, mindt;
   float n_unit[3];
 
   A = voronoi_get_face(&pi->cell, pj->id, xij_i);
@@ -164,9 +167,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   Wj[3] = pj->primitives.v[2];
   Wj[4] = pj->primitives.P;
 
-  dti = pi->force.dt;
-  dtj = pj->force.dt;
-
   /* calculate the maximal signal velocity */
   vmax = 0.0f;
   if (Wi[0] > 0.) {
@@ -188,10 +188,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
     pj->timestepvars.vmax = fmaxf(pj->timestepvars.vmax, vmax);
   }
 
-  /* The flux will be exchanged using the smallest time step of the two
-   * particles */
-  mindt = fminf(dti, dtj);
-
   /* compute the normal vector of the interface */
   for (k = 0; k < 3; ++k) {
     n_unit[k] = -dx[k] / r;
@@ -215,13 +211,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   Wj[2] -= vij[1];
   Wj[3] -= vij[2];
 
-  hydro_gradients_predict(pi, pj, hi, hj, dx, r, xij_i, Wi, Wj, mindt);
+  hydro_gradients_predict(pi, pj, hi, hj, dx, r, xij_i, Wi, Wj);
 
   /* we don't need to rotate, we can use the unit vector in the Riemann problem
    * itself (see GIZMO) */
 
   if (Wi[0] < 0.0f || Wj[0] < 0.0f || Wi[4] < 0.0f || Wj[4] < 0.0f) {
-    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
@@ -262,20 +257,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
 
   /* Update conserved variables */
   /* eqn. (16) */
-  pi->conserved.flux.mass -= mindt * A * totflux[0];
-  pi->conserved.flux.momentum[0] -= mindt * A * totflux[1];
-  pi->conserved.flux.momentum[1] -= mindt * A * totflux[2];
-  pi->conserved.flux.momentum[2] -= mindt * A * totflux[3];
-  pi->conserved.flux.energy -= mindt * A * totflux[4];
+  pi->conserved.flux.mass -= A * totflux[0];
+  pi->conserved.flux.momentum[0] -= A * totflux[1];
+  pi->conserved.flux.momentum[1] -= A * totflux[2];
+  pi->conserved.flux.momentum[2] -= A * totflux[3];
+  pi->conserved.flux.energy -= A * totflux[4];
 
 #ifndef SHADOWFAX_TOTAL_ENERGY
   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 += mindt * A * totflux[1] * pi->primitives.v[0];
-  pi->conserved.flux.energy += mindt * A * totflux[2] * pi->primitives.v[1];
-  pi->conserved.flux.energy += mindt * A * totflux[3] * pi->primitives.v[2];
-  pi->conserved.flux.energy -= mindt * A * totflux[0] * ekin;
+  pi->conserved.flux.energy += A * totflux[1] * pi->primitives.v[0];
+  pi->conserved.flux.energy += A * totflux[2] * pi->primitives.v[1];
+  pi->conserved.flux.energy += A * totflux[3] * pi->primitives.v[2];
+  pi->conserved.flux.energy -= A * totflux[0] * ekin;
 #endif
 
   /* here is how it works:
@@ -291,20 +286,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
      ==> we update particle j if (MODE IS 1) OR (j IS INACTIVE)
   */
   if (mode == 1 || pj->force.active == 0) {
-    pj->conserved.flux.mass += mindt * A * totflux[0];
-    pj->conserved.flux.momentum[0] += mindt * A * totflux[1];
-    pj->conserved.flux.momentum[1] += mindt * A * totflux[2];
-    pj->conserved.flux.momentum[2] += mindt * A * totflux[3];
-    pj->conserved.flux.energy += mindt * A * totflux[4];
+    pj->conserved.flux.mass += A * totflux[0];
+    pj->conserved.flux.momentum[0] += A * totflux[1];
+    pj->conserved.flux.momentum[1] += A * totflux[2];
+    pj->conserved.flux.momentum[2] += A * totflux[3];
+    pj->conserved.flux.energy += A * totflux[4];
 
 #ifndef SHADOWFAX_TOTAL_ENERGY
     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 -= mindt * A * totflux[1] * pj->primitives.v[0];
-    pj->conserved.flux.energy -= mindt * A * totflux[2] * pj->primitives.v[1];
-    pj->conserved.flux.energy -= mindt * A * totflux[3] * pj->primitives.v[2];
-    pj->conserved.flux.energy += mindt * A * totflux[0] * ekin;
+    pj->conserved.flux.energy -= A * totflux[1] * pj->primitives.v[0];
+    pj->conserved.flux.energy -= A * totflux[2] * pj->primitives.v[1];
+    pj->conserved.flux.energy -= A * totflux[3] * pj->primitives.v[2];
+    pj->conserved.flux.energy += A * totflux[0] * ekin;
 #endif
   }
 }
@@ -322,9 +317,10 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
  * @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) {
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
 
-  runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 1);
+  runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 1, a, H);
 }
 
 /**
@@ -341,7 +337,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
  * @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) {
+    float r2, const float *dx, float hi, float hj, struct part *restrict pi,
+    struct part *restrict pj, float a, float H) {
 
-  runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0);
+  runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0, a, H);
 }
diff --git a/src/hydro/Shadowswift/hydro_io.h b/src/hydro/Shadowswift/hydro_io.h
index 65cb4ee6b8b1ecb174212bc8867cd7657a213414..1f6bb86e62c6a3359d1242328775c6e4067ef8f2 100644
--- a/src/hydro/Shadowswift/hydro_io.h
+++ b/src/hydro/Shadowswift/hydro_io.h
@@ -32,8 +32,9 @@
  * @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) {
+INLINE static void hydro_read_particles(struct part* parts,
+                                        struct io_props* list,
+                                        int* num_fields) {
 
   *num_fields = 8;
 
@@ -64,7 +65,8 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
  * @param p Particle.
  * @return Internal energy of the particle
  */
-void convert_u(const struct engine* e, const struct part* p, float* ret) {
+INLINE static void convert_u(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
   ret[0] = hydro_get_internal_energy(p);
 }
 
@@ -75,7 +77,8 @@ void convert_u(const struct engine* e, const struct part* p, float* ret) {
  * @param p Particle.
  * @return Entropic function of the particle
  */
-void convert_A(const struct engine* e, const struct part* p, float* ret) {
+INLINE static void convert_A(const struct engine* e, const struct part* p,
+                             const struct xpart* xp, float* ret) {
   ret[0] = hydro_get_entropy(p);
 }
 
@@ -86,7 +89,8 @@ void convert_A(const struct engine* e, const struct part* p, float* ret) {
  * @param p Particle.
  * @return Total energy of the particle
  */
-void convert_Etot(const struct engine* e, const struct part* p, float* ret) {
+INLINE static void convert_Etot(const struct engine* e, const struct part* p,
+                                const struct xpart* xp, float* ret) {
 #ifdef SHADOWFAX_TOTAL_ENERGY
   return p->conserved.energy;
 #else
@@ -104,8 +108,9 @@ void convert_Etot(const struct engine* e, const struct part* p, float* ret) {
 #endif
 }
 
-void convert_part_pos(const struct engine* e, const struct part* p,
-                      double* ret) {
+INLINE static void convert_part_pos(const struct engine* e,
+                                    const struct part* p,
+                                    const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -125,14 +130,17 @@ void convert_part_pos(const struct engine* e, const struct part* p,
  * @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) {
+INLINE static void hydro_write_particles(const struct part* parts,
+                                         const struct xpart* xparts,
+                                         struct io_props* list,
+                                         int* num_fields) {
 
   *num_fields = 13;
 
   /* List what we want to write */
-  list[0] = io_make_output_field_convert_part(
-      "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, parts, convert_part_pos);
+  list[0] = io_make_output_field_convert_part("Coordinates", DOUBLE, 3,
+                                              UNIT_CONV_LENGTH, parts, xparts,
+                                              convert_part_pos);
   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,
@@ -141,7 +149,7 @@ void hydro_write_particles(struct part* parts, struct io_props* list,
                                  parts, h);
   list[4] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1,
                                               UNIT_CONV_ENERGY_PER_UNIT_MASS,
-                                              parts, convert_u);
+                                              parts, xparts, 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,
@@ -153,18 +161,18 @@ void hydro_write_particles(struct part* parts, struct io_props* list,
   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, convert_A);
+      "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY, parts, xparts, 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, convert_Etot);
+      "TotEnergy", FLOAT, 1, UNIT_CONV_ENERGY, parts, xparts, convert_Etot);
 }
 
 /**
  * @brief Writes the current model of SPH to the file
  * @param h_grpsph The HDF5 group in which to write
  */
-void hydro_write_flavour(hid_t h_grpsph) {
+INLINE static void hydro_write_flavour(hid_t h_grpsph) {
   /* Gradient information */
   io_write_attribute_s(h_grpsph, "Gradient reconstruction model",
                        HYDRO_GRADIENT_IMPLEMENTATION);
@@ -185,4 +193,4 @@ void hydro_write_flavour(hid_t h_grpsph) {
  *
  * @return 1 if entropy is in 'internal energy', 0 otherwise.
  */
-int writeEntropyFlag() { return 0; }
+INLINE static int writeEntropyFlag(void) { return 0; }
diff --git a/src/hydro/Shadowswift/hydro_part.h b/src/hydro/Shadowswift/hydro_part.h
index 4f28d7bdc1c4779a9e6f15c3fb789c3f65f32890..a7cc9daf0839216f098ac05c2267adc60ea11fb0 100644
--- a/src/hydro/Shadowswift/hydro_part.h
+++ b/src/hydro/Shadowswift/hydro_part.h
@@ -35,6 +35,9 @@ struct xpart {
   /* Velocity at the last full step. */
   float v_full[3];
 
+  /* Gravitational acceleration at the last full step. */
+  float a_grav[3];
+
   /* Additional data used to record cooling information */
   struct cooling_xpart_data cooling_data;
 
diff --git a/src/hydro/Shadowswift/hydro_slope_limiters_cell.h b/src/hydro/Shadowswift/hydro_slope_limiters_cell.h
index a7b3f7511fa7cd247fd9d2399cd200d8d943630e..d746ffc59538fcc625a2e095245adfd7a946a95e 100644
--- a/src/hydro/Shadowswift/hydro_slope_limiters_cell.h
+++ b/src/hydro/Shadowswift/hydro_slope_limiters_cell.h
@@ -50,7 +50,8 @@ __attribute__((always_inline)) INLINE static void hydro_slope_limit_cell_init(
  * @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) {
+hydro_slope_limit_cell_collect(struct part* pi, const struct part* pj,
+                               float r) {
 
   /* basic slope limiter: collect the maximal and the minimal value for the
    * primitive variables among the ngbs */
diff --git a/src/hydro_io.h b/src/hydro_io.h
index 639c2f3ae640d7b74e6a2507bd4e3d5ad5625171..d752bb8bc03f619fe759fc8f5de32a01b3a61abe 100644
--- a/src/hydro_io.h
+++ b/src/hydro_io.h
@@ -29,12 +29,18 @@
 #include "./hydro/Gadget2/hydro_io.h"
 #elif defined(HOPKINS_PE_SPH)
 #include "./hydro/PressureEntropy/hydro_io.h"
+#elif defined(HOPKINS_PU_SPH)
+#include "./hydro/PressureEnergy/hydro_io.h"
 #elif defined(DEFAULT_SPH)
 #include "./hydro/Default/hydro_io.h"
-#elif defined(GIZMO_SPH)
-#include "./hydro/Gizmo/hydro_io.h"
+#elif defined(GIZMO_MFV_SPH)
+#include "./hydro/GizmoMFV/hydro_io.h"
+#elif defined(GIZMO_MFM_SPH)
+#include "./hydro/GizmoMFM/hydro_io.h"
 #elif defined(SHADOWFAX_SPH)
 #include "./hydro/Shadowswift/hydro_io.h"
+#elif defined(MINIMAL_MULTI_MAT_SPH)
+#include "./hydro/MinimalMultiMat/hydro_io.h"
 #else
 #error "Invalid choice of SPH variant"
 #endif
diff --git a/src/hydro_properties.c b/src/hydro_properties.c
index e63679eaad3c7f61fe67e63326ca59a04c1caffb..5540d8c33c6263213b35697cfed2b6b5b07de2b7 100644
--- a/src/hydro_properties.c
+++ b/src/hydro_properties.c
@@ -52,7 +52,7 @@
 void hydro_props_init(struct hydro_props *p,
                       const struct phys_const *phys_const,
                       const struct unit_system *us,
-                      const struct swift_params *params) {
+                      struct swift_params *params) {
 
   /* Kernel properties */
   p->eta_neighbours = parser_get_param_float(params, "SPH:resolution_eta");
@@ -72,6 +72,7 @@ void hydro_props_init(struct hydro_props *p,
   /* change the meaning of target_neighbours and delta_neighbours */
   p->target_neighbours = 1.0f;
   p->delta_neighbours = 0.0f;
+  p->eta_neighbours = 1.0f;
 #endif
 
   /* Maximal smoothing length */
diff --git a/src/hydro_properties.h b/src/hydro_properties.h
index 2799f6c86eec7f0ebf140643c5ab7fa9b60e6273..64a840692db677704b8617e962d7883505983cc0 100644
--- a/src/hydro_properties.h
+++ b/src/hydro_properties.h
@@ -86,7 +86,7 @@ void hydro_props_print(const struct hydro_props *p);
 void hydro_props_init(struct hydro_props *p,
                       const struct phys_const *phys_const,
                       const struct unit_system *us,
-                      const struct swift_params *params);
+                      struct swift_params *params);
 
 #if defined(HAVE_HDF5)
 void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p);
diff --git a/src/kernel_gravity.h b/src/kernel_gravity.h
index dc9db63f9d59ad0b3e72792b33b38a961ea6c30f..8fc278273f7f152c0e352950a0935ae2f2b0c83b 100644
--- a/src/kernel_gravity.h
+++ b/src/kernel_gravity.h
@@ -26,9 +26,17 @@
 #include "inline.h"
 #include "minmax.h"
 
+#ifdef GADGET2_SOFTENING_CORRECTION
+/*! Conversion factor between Plummer softening and internal softening */
+#define kernel_gravity_softening_plummer_equivalent 2.8
+#define kernel_gravity_softening_plummer_equivalent_inv (1. / 2.8)
+#define kernel_gravity_softening_name "Gadget-2 (spline kernel)"
+#else
 /*! Conversion factor between Plummer softening and internal softening */
 #define kernel_gravity_softening_plummer_equivalent 3.
 #define kernel_gravity_softening_plummer_equivalent_inv (1. / 3.)
+#define kernel_gravity_softening_name "Wendland-C2"
+#endif /* GADGET2_SOFTENING_CORRECTION */
 
 /**
  * @brief Computes the gravity softening function for potential.
@@ -41,6 +49,15 @@
 __attribute__((always_inline)) INLINE static void kernel_grav_pot_eval(
     float u, float *const W) {
 
+#ifdef GADGET2_SOFTENING_CORRECTION
+  if (u < 0.5f)
+    *W = -2.8f + u * u * (5.333333333333f + u * u * (6.4f * u - 9.6f));
+  else
+    *W = -3.2f + 0.066666666667f / u +
+         u * u * (10.666666666667f +
+                  u * (-16.f + u * (9.6f - 2.133333333333f * u)));
+#else
+
   /* W(u) = 3u^7 - 15u^6 + 28u^5 - 21u^4 + 7u^2 - 3 */
   *W = 3.f * u - 15.f;
   *W = *W * u + 28.f;
@@ -49,6 +66,7 @@ __attribute__((always_inline)) INLINE static void kernel_grav_pot_eval(
   *W = *W * u + 7.f;
   *W = *W * u;
   *W = *W * u - 3.f;
+#endif
 }
 
 /**
@@ -62,12 +80,21 @@ __attribute__((always_inline)) INLINE static void kernel_grav_pot_eval(
 __attribute__((always_inline)) INLINE static void kernel_grav_force_eval(
     float u, float *const W) {
 
+#ifdef GADGET2_SOFTENING_CORRECTION
+  if (u < 0.5f)
+    *W = 10.6666667f + u * u * (32.f * u - 38.4f);
+  else
+    *W = 21.3333333f - 48.f * u + 38.4f * u * u - 10.6666667f * u * u * u -
+         0.06666667f / (u * u * u);
+#else
+
   /* W(u) = 21u^5 - 90u^4 + 140u^3 - 84u^2 + 14 */
   *W = 21.f * u - 90.f;
   *W = *W * u + 140.f;
   *W = *W * u - 84.f;
   *W = *W * u;
   *W = *W * u + 14.f;
+#endif
 }
 
 #ifdef SWIFT_GRAVITY_FORCE_CHECKS
@@ -84,6 +111,15 @@ __attribute__((always_inline)) INLINE static void kernel_grav_force_eval(
 __attribute__((always_inline)) INLINE static void kernel_grav_eval_pot_double(
     double u, double *const W) {
 
+#ifdef GADGET2_SOFTENING_CORRECTION
+  if (u < 0.5)
+    *W = -2.8 + u * u * (5.333333333333 + u * u * (6.4 * u - 9.6));
+  else
+    *W = -3.2 + 0.066666666667 / u +
+         u * u *
+             (10.666666666667 + u * (-16.0 + u * (9.6 - 2.133333333333 * u)));
+#else
+
   /* W(u) = 3u^7 - 15u^6 + 28u^5 - 21u^4 + 7u^2 - 3 */
   *W = 3. * u - 15.;
   *W = *W * u + 28.;
@@ -92,6 +128,7 @@ __attribute__((always_inline)) INLINE static void kernel_grav_eval_pot_double(
   *W = *W * u + 7.;
   *W = *W * u;
   *W = *W * u - 3;
+#endif
 }
 
 /**
@@ -106,15 +143,26 @@ __attribute__((always_inline)) INLINE static void kernel_grav_eval_pot_double(
 __attribute__((always_inline)) INLINE static void kernel_grav_eval_force_double(
     double u, double *const W) {
 
+#ifdef GADGET2_SOFTENING_CORRECTION
+  if (u < 0.5)
+    *W = 10.666666666667 + u * u * (32.0 * u - 38.4);
+  else
+    *W = 21.333333333333 - 48.0 * u + 38.4 * u * u -
+         10.666666666667 * u * u * u - 0.066666666667 / (u * u * u);
+#else
+
   /* W(u) = 21u^5 - 90u^4 + 140u^3 - 84u^2 + 14 */
   *W = 21. * u - 90.;
   *W = *W * u + 140.;
   *W = *W * u - 84.;
   *W = *W * u;
   *W = *W * u + 14.;
+#endif
 }
 #endif /* SWIFT_GRAVITY_FORCE_CHECKS */
 
+#undef GADGET2_SOFTENING_CORRECTION
+
 /************************************************/
 /* Derivatives of softening kernel used for FMM */
 /************************************************/
@@ -122,14 +170,14 @@ __attribute__((always_inline)) INLINE static void kernel_grav_eval_force_double(
 __attribute__((always_inline)) INLINE static float D_soft_1(float u,
                                                             float u_inv) {
 
-  /* phi(u) = -3u^7 + 15u^6 - 28u^5 + 21u^4 - 7u^2 + 3 */
-  float phi = -3.f * u + 15.f;
-  phi = phi * u - 28.f;
-  phi = phi * u + 21.f;
+  /* phi(u) = 3u^7 - 15u^6 + 28u^5 - 21u^4 + 7u^2 - 3 */
+  float phi = 3.f * u - 15.f;
+  phi = phi * u + 28.f;
+  phi = phi * u - 21.f;
   phi = phi * u;
-  phi = phi * u - 7.f;
+  phi = phi * u + 7.f;
   phi = phi * u;
-  phi = phi * u + 3.f;
+  phi = phi * u - 3.f;
 
   return phi;
 }
@@ -150,43 +198,27 @@ __attribute__((always_inline)) INLINE static float D_soft_3(float u,
 __attribute__((always_inline)) INLINE static float D_soft_5(float u,
                                                             float u_inv) {
 
-  /* (phi'(u)/u)'/u = -105u^3 + 360u^2 - 420u + 168 */
-  float phi = -105.f * u + 360.f;
-  phi = phi * u - 420.f;
-  phi = phi * u + 168.f;
+  /* (phi'(u)/u)'/u = 105u^3 - 360u^2 + 420u - 168 */
+  float phi = 105.f * u - 360.f;
+  phi = phi * u + 420.f;
+  phi = phi * u - 168.f;
 
   return phi;
 }
 
 __attribute__((always_inline)) INLINE static float D_soft_7(float u,
                                                             float u_inv) {
-
-  /* ((phi'(u)/u)'/u)'/u = 315u - 720 + 420u^-1 */
-  return 315.f * u - 720.f + 420.f * u_inv;
+  return 0.f;
 }
 
 __attribute__((always_inline)) INLINE static float D_soft_9(float u,
                                                             float u_inv) {
-
-  /* (((phi'(u)/u)'/u)'/u)'/u = -315u^-1 + 420u^-3 */
-  float phi = 420.f * u_inv;
-  phi = phi * u_inv - 315.f;
-  phi = phi * u_inv;
-
-  return phi;
+  return 0.f;
 }
 
 __attribute__((always_inline)) INLINE static float D_soft_11(float u,
                                                              float u_inv) {
-
-  /* ((((phi'(u)/u)'/u)'/u)'/u)'/u = 315u^-3 - 1260u^-5 */
-  float phi = -1260.f * u_inv;
-  phi = phi * u_inv + 315.f;
-  phi = phi * u_inv;
-  phi = phi * u_inv;
-  phi = phi * u_inv;
-
-  return phi;
+  return 0.f;
 }
 
 #endif /* SWIFT_KERNEL_GRAVITY_H */
diff --git a/src/kernel_hydro.h b/src/kernel_hydro.h
index d3c905e50ea023ea63629505fd3566cbed3ed9b0..788a9037a6ceb592f4278404ce7ea49ab9f876d1 100644
--- a/src/kernel_hydro.h
+++ b/src/kernel_hydro.h
@@ -38,6 +38,7 @@
 #include "dimension.h"
 #include "error.h"
 #include "inline.h"
+#include "minmax.h"
 #include "vector.h"
 
 /* ------------------------------------------------------------------------- */
@@ -267,6 +268,9 @@ __attribute__((always_inline)) INLINE static void kernel_deval(
     w = x * w + coeffs[k];
   }
 
+  w = max(w, 0.f);
+  dw_dx = min(dw_dx, 0.f);
+
   /* Return everything */
   *W = w * kernel_constant * kernel_gamma_inv_dim;
   *dW_dx = dw_dx * kernel_constant * kernel_gamma_inv_dim_plus_one;
@@ -300,6 +304,8 @@ __attribute__((always_inline)) INLINE static void kernel_eval(
   /* ... and the rest of them */
   for (int k = 2; k <= kernel_degree; k++) w = x * w + coeffs[k];
 
+  w = max(w, 0.f);
+
   /* Return everything */
   *W = w * kernel_constant * kernel_gamma_inv_dim;
 }
@@ -331,9 +337,10 @@ __attribute__((always_inline)) INLINE static void kernel_eval_dWdx(
                 (float)(kernel_degree - 1) * coeffs[1];
 
   /* ... and the rest of them */
-  for (int k = 2; k < kernel_degree; k++) {
+  for (int k = 2; k < kernel_degree; k++)
     dw_dx = dw_dx * x + (float)(kernel_degree - k) * coeffs[k];
-  }
+
+  dw_dx = min(dw_dx, 0.f);
 
   /* Return everything */
   *dW_dx = dw_dx * kernel_constant * kernel_gamma_inv_dim_plus_one;
diff --git a/src/kernel_long_gravity.h b/src/kernel_long_gravity.h
index 578d7c2154f6801e21c004c01cade8bf6bd728ae..1744f2cd046a90499563a182ca68212e43f4a252 100644
--- a/src/kernel_long_gravity.h
+++ b/src/kernel_long_gravity.h
@@ -28,8 +28,123 @@
 #include "inline.h"
 
 /* Standard headers */
+#include <float.h>
 #include <math.h>
 
+#define GADGET2_LONG_RANGE_CORRECTION
+
+#ifdef GADGET2_LONG_RANGE_CORRECTION
+#define kernel_long_gravity_truncation_name "Gadget-like (using erfc())"
+#else
+#define kernel_long_gravity_truncation_name "Exp-based Sigmoid"
+#endif
+
+/**
+ * @brief Derivatives of the long-range truncation function \f$\chi(r,r_s)\f$ up
+ * to 5th order.
+ */
+struct chi_derivatives {
+
+  /*! 0th order derivative \f$\chi(r,r_s)\f$ */
+  float chi_0;
+
+  /*! 1st order derivative \f$\partial_{r}\chi(r,r_s)\f$ */
+  float chi_1;
+
+  /*! 2nd order derivative \f$\partial_{rr}\chi(r,r_s)\f$ */
+  float chi_2;
+
+  /*! 3rd order derivative \f$\partial_{rrr}\chi(r,r_s)\f$ */
+  float chi_3;
+
+  /*! 4th order derivative \f$\partial_{rrrr}\chi(r,r_s)\f$ */
+  float chi_4;
+
+  /*! 5th order derivative \f$\partial_{rrrrr}\chi(r,r_s)\f$ */
+  float chi_5;
+};
+
+/**
+ * @brief Compute the derivatives of the long-range truncation function
+ * \f$\chi(r,r_s)\f$ up to 5th order.
+ *
+ * @param r The distance.
+ * @param r_s_inv The inverse of the long-range gravity mesh scale.
+ * @param derivs (return) The computed #chi_derivatives.
+ */
+__attribute__((always_inline)) INLINE static void kernel_long_grav_derivatives(
+    const float r, const float r_s_inv, struct chi_derivatives *const derivs) {
+
+#ifdef GADGET2_LONG_RANGE_CORRECTION
+
+  /* Powers of u=r/2r_s */
+  const float u = 0.5f * r * r_s_inv;
+  const float u2 = u * u;
+  const float u3 = u2 * u;
+  const float u4 = u3 * u;
+
+  /* Powers of (1/r_s) */
+  const float r_s_inv2 = r_s_inv * r_s_inv;
+  const float r_s_inv3 = r_s_inv2 * r_s_inv;
+  const float r_s_inv4 = r_s_inv3 * r_s_inv;
+  const float r_s_inv5 = r_s_inv4 * r_s_inv;
+
+  /* Derivatives of \chi */
+  derivs->chi_0 = erfcf(u);
+  derivs->chi_1 = -r_s_inv;
+  derivs->chi_2 = r_s_inv2 * u;
+  derivs->chi_3 = -r_s_inv3 * (u2 - 0.5f);
+  derivs->chi_4 = r_s_inv4 * (u3 - 1.5f * u);
+  derivs->chi_5 = -r_s_inv5 * (u4 - 3.f * u2 + 0.75f);
+
+  const float one_over_sqrt_pi = ((float)(M_2_SQRTPI * 0.5));
+  const float common_factor = one_over_sqrt_pi * expf(-u2);
+
+  /* Multiply in the common factors */
+  derivs->chi_1 *= common_factor;
+  derivs->chi_2 *= common_factor;
+  derivs->chi_3 *= common_factor;
+  derivs->chi_4 *= common_factor;
+  derivs->chi_5 *= common_factor;
+
+#else
+
+  /* Powers of 2/r_s */
+  const float c0 = 1.f;
+  const float c1 = 2.f * r_s_inv;
+  const float c2 = c1 * c1;
+  const float c3 = c2 * c1;
+  const float c4 = c3 * c1;
+  const float c5 = c4 * c1;
+
+  /* 2r / r_s */
+  const float x = c1 * r;
+
+  /* e^(2r / r_s) */
+  const float exp_x = expf(x);  // good_approx_expf(x);
+
+  /* 1 / alpha(w) */
+  const float a_inv = 1.f + exp_x;
+
+  /* Powers of alpha */
+  const float a1 = 1.f / a_inv;
+  const float a2 = a1 * a1;
+  const float a3 = a2 * a1;
+  const float a4 = a3 * a1;
+  const float a5 = a4 * a1;
+  const float a6 = a5 * a1;
+
+  /* Derivatives of \chi */
+  derivs->chi_0 = -2.f * exp_x * c0 * a1 + 2.f;
+  derivs->chi_1 = -2.f * exp_x * c1 * a2;
+  derivs->chi_2 = -2.f * exp_x * c2 * (2.f * a3 - a2);
+  derivs->chi_3 = -2.f * exp_x * c3 * (6.f * a4 - 6.f * a3 + a2);
+  derivs->chi_4 = -2.f * exp_x * c4 * (24.f * a5 - 36.f * a4 + 14.f * a3 - a2);
+  derivs->chi_5 = -2.f * exp_x * c5 *
+                  (120.f * a6 - 240.f * a5 + 150.f * a4 - 30.f * a3 + a2);
+#endif
+}
+
 /**
  * @brief Computes the long-range correction term for the potential calculation
  * coming from FFT.
@@ -49,7 +164,7 @@ __attribute__((always_inline)) INLINE static void kernel_long_grav_pot_eval(
 #else
 
   const float x = 2.f * u;
-  const float exp_x = good_approx_expf(x);
+  const float exp_x = expf(x);  // good_approx_expf(x);
   const float alpha = 1.f / (1.f + exp_x);
 
   /* We want 2 - 2 exp(x) * alpha */
@@ -66,27 +181,29 @@ __attribute__((always_inline)) INLINE static void kernel_long_grav_pot_eval(
  * @param W (return) The value of the kernel function.
  */
 __attribute__((always_inline)) INLINE static void kernel_long_grav_force_eval(
-    float u, float *const W) {
+    const float u, float *const W) {
 
 #ifdef GADGET2_LONG_RANGE_CORRECTION
 
   const float one_over_sqrt_pi = ((float)(M_2_SQRTPI * 0.5));
 
   const float arg1 = u * 0.5f;
-  const float arg2 = u * one_over_sqrt_pi;
-  const float arg3 = -arg1 * arg1;
+  const float arg2 = -arg1 * arg1;
 
   const float term1 = erfcf(arg1);
-  const float term2 = arg2 * expf(arg3);
+  const float term2 = u * one_over_sqrt_pi * expf(arg2);
 
   *W = term1 + term2;
 #else
 
-  const float arg = 2.f * u;
-  const float exp_arg = good_approx_expf(arg);
-  const float term = 1.f / (1.f + exp_arg);
+  const float x = 2.f * u;
+  const float exp_x = expf(x);  // good_approx_expf(x);
+  const float alpha = 1.f / (1.f + exp_x);
 
-  *W = arg * exp_arg * term * term - exp_arg * term + 1.f;
+  /* We want 2*(x*alpha - x*alpha^2 - exp(x)*alpha + 1) */
+  *W = 1.f - alpha;
+  *W = *W * x - exp_x;
+  *W = *W * alpha + 1.f;
   *W *= 2.f;
 #endif
 }
@@ -100,7 +217,7 @@ __attribute__((always_inline)) INLINE static void kernel_long_grav_force_eval(
  * @param W (return) The value of the kernel function.
  */
 __attribute__((always_inline)) INLINE static void fourier_kernel_long_grav_eval(
-    double u2, double *const W) {
+    const double u2, double *const W) {
 
 #ifdef GADGET2_LONG_RANGE_CORRECTION
   *W = exp(-u2);
diff --git a/src/memswap.h b/src/memswap.h
index 92c902eeb158978d4a606f5f2a9416d4113fae0b..2f7b9215ed48535fab9e8331303457c2f92859cd 100644
--- a/src/memswap.h
+++ b/src/memswap.h
@@ -49,11 +49,13 @@
  *
  * Keep in mind that this function only works when the underlying data
  * is aligned to the vector length, e.g. with the @c
- * __attribute__((aligned(32)))
- * syntax!
+ * __attribute__((aligned(32))) syntax!
  * Furthermore, register re-labeling only seems to work when the code is
  * compiled with @c -funroll-loops.
  *
+ * Note that GCC (at least until 7.3) produces incorrect AVX512 code here
+ * by automatically assuming alignment.
+ *
  * @param void_a Pointer to the first element.
  * @param void_b Pointer to the second element.
  * @param bytes Size, in bytes, of the data pointed to by @c a and @c b.
@@ -61,7 +63,7 @@
 __attribute__((always_inline)) inline void memswap(void *void_a, void *void_b,
                                                    size_t bytes) {
   char *a = (char *)void_a, *b = (char *)void_b;
-#ifdef __AVX512F__
+#if defined(__AVX512F__) && defined(__INTEL_COMPILER)
   swap_loop(__m512i, a, b, bytes);
 #endif
 #ifdef __AVX__
diff --git a/src/mesh_gravity.c b/src/mesh_gravity.c
new file mode 100644
index 0000000000000000000000000000000000000000..49e66763f7f9ef47ca79d0024fa65b9ce7803fc1
--- /dev/null
+++ b/src/mesh_gravity.c
@@ -0,0 +1,550 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+#ifdef HAVE_FFTW
+#include <fftw3.h>
+#endif
+
+/* This object's header. */
+#include "mesh_gravity.h"
+
+/* Local includes. */
+#include "active.h"
+#include "debug.h"
+#include "engine.h"
+#include "error.h"
+#include "gravity_properties.h"
+#include "kernel_long_gravity.h"
+#include "part.h"
+#include "runner.h"
+#include "space.h"
+
+#ifdef HAVE_FFTW
+
+/**
+ * @brief Returns 1D index of a 3D NxNxN array using row-major style.
+ *
+ * Wraps around in the corresponding dimension if any of the 3 indices is >= N
+ * or < 0.
+ *
+ * @param i Index along x.
+ * @param j Index along y.
+ * @param k Index along z.
+ * @param N Size of the array along one axis.
+ */
+__attribute__((always_inline)) INLINE static int row_major_id_periodic(int i,
+                                                                       int j,
+                                                                       int k,
+                                                                       int N) {
+  return (((i + N) % N) * N * N + ((j + N) % N) * N + ((k + N) % N));
+}
+
+/**
+ * @brief Interpolate values from a the mesh using CIC.
+ *
+ * @param mesh The mesh to read from.
+ * @param i The index of the cell along x
+ * @param j The index of the cell along y
+ * @param k The index of the cell along z
+ * @param tx First CIC coefficient along x
+ * @param ty First CIC coefficient along y
+ * @param tz First CIC coefficient along z
+ * @param dx Second CIC coefficient along x
+ * @param dy Second CIC coefficient along y
+ * @param dz Second CIC coefficient along z
+ */
+__attribute__((always_inline)) INLINE static double CIC_get(
+    double mesh[6][6][6], int i, int j, int k, double tx, double ty, double tz,
+    double dx, double dy, double dz) {
+
+  double temp;
+  temp = mesh[i + 0][j + 0][k + 0] * tx * ty * tz;
+  temp += mesh[i + 0][j + 0][k + 1] * tx * ty * dz;
+  temp += mesh[i + 0][j + 1][k + 0] * tx * dy * tz;
+  temp += mesh[i + 0][j + 1][k + 1] * tx * dy * dz;
+  temp += mesh[i + 1][j + 0][k + 0] * dx * ty * tz;
+  temp += mesh[i + 1][j + 0][k + 1] * dx * ty * dz;
+  temp += mesh[i + 1][j + 1][k + 0] * dx * dy * tz;
+  temp += mesh[i + 1][j + 1][k + 1] * dx * dy * dz;
+
+  return temp;
+}
+
+/**
+ * @brief Interpolate a value to a mesh using CIC.
+ *
+ * @param mesh The mesh to write to
+ * @param N The side-length of the mesh
+ * @param i The index of the cell along x
+ * @param j The index of the cell along y
+ * @param k The index of the cell along z
+ * @param tx First CIC coefficient along x
+ * @param ty First CIC coefficient along y
+ * @param tz First CIC coefficient along z
+ * @param dx Second CIC coefficient along x
+ * @param dy Second CIC coefficient along y
+ * @param dz Second CIC coefficient along z
+ * @param value The value to interpolate.
+ */
+__attribute__((always_inline)) INLINE static void CIC_set(
+    double* mesh, int N, int i, int j, int k, double tx, double ty, double tz,
+    double dx, double dy, double dz, double value) {
+
+  /* Classic CIC interpolation */
+  mesh[row_major_id_periodic(i + 0, j + 0, k + 0, N)] += value * tx * ty * tz;
+  mesh[row_major_id_periodic(i + 0, j + 0, k + 1, N)] += value * tx * ty * dz;
+  mesh[row_major_id_periodic(i + 0, j + 1, k + 0, N)] += value * tx * dy * tz;
+  mesh[row_major_id_periodic(i + 0, j + 1, k + 1, N)] += value * tx * dy * dz;
+  mesh[row_major_id_periodic(i + 1, j + 0, k + 0, N)] += value * dx * ty * tz;
+  mesh[row_major_id_periodic(i + 1, j + 0, k + 1, N)] += value * dx * ty * dz;
+  mesh[row_major_id_periodic(i + 1, j + 1, k + 0, N)] += value * dx * dy * tz;
+  mesh[row_major_id_periodic(i + 1, j + 1, k + 1, N)] += value * dx * dy * dz;
+}
+
+/**
+ * @brief Assigns a given #gpart to a density mesh using the CIC method.
+ *
+ * @param gp The #gpart.
+ * @param rho The density mesh.
+ * @param N the size of the mesh along one axis.
+ * @param fac The width of a mesh cell.
+ * @param dim The dimensions of the simulation box.
+ */
+INLINE static void gpart_to_mesh_CIC(const struct gpart* gp, double* rho, int N,
+                                     double fac, const double dim[3]) {
+
+  /* Box wrap the multipole's position */
+  const double pos_x = box_wrap(gp->x[0], 0., dim[0]);
+  const double pos_y = box_wrap(gp->x[1], 0., dim[1]);
+  const double pos_z = box_wrap(gp->x[2], 0., dim[2]);
+
+  /* Workout the CIC coefficients */
+  int i = (int)(fac * pos_x);
+  if (i >= N) i = N - 1;
+  const double dx = fac * pos_x - i;
+  const double tx = 1. - dx;
+
+  int j = (int)(fac * pos_y);
+  if (j >= N) j = N - 1;
+  const double dy = fac * pos_y - j;
+  const double ty = 1. - dy;
+
+  int k = (int)(fac * pos_z);
+  if (k >= N) k = N - 1;
+  const double dz = fac * pos_z - k;
+  const double tz = 1. - dz;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (i < 0 || i >= N) error("Invalid gpart position in x");
+  if (j < 0 || j >= N) error("Invalid gpart position in y");
+  if (k < 0 || k >= N) error("Invalid gpart position in z");
+#endif
+
+  const double mass = gp->mass;
+
+  /* CIC ! */
+  CIC_set(rho, N, i, j, k, tx, ty, tz, dx, dy, dz, mass);
+}
+
+/**
+ * @brief Computes the potential on a gpart from a given mesh using the CIC
+ * method.
+ *
+ * Debugging routine.
+ *
+ * @param gp The #gpart.
+ * @param pot The potential mesh.
+ * @param N the size of the mesh along one axis.
+ * @param fac width of a mesh cell.
+ * @param dim The dimensions of the simulation box.
+ */
+void mesh_to_gparts_CIC(struct gpart* gp, const double* pot, int N, double fac,
+                        const double dim[3]) {
+
+  /* Box wrap the gpart's position */
+  const double pos_x = box_wrap(gp->x[0], 0., dim[0]);
+  const double pos_y = box_wrap(gp->x[1], 0., dim[1]);
+  const double pos_z = box_wrap(gp->x[2], 0., dim[2]);
+
+  int i = (int)(fac * pos_x);
+  if (i >= N) i = N - 1;
+  const double dx = fac * pos_x - i;
+  const double tx = 1. - dx;
+
+  int j = (int)(fac * pos_y);
+  if (j >= N) j = N - 1;
+  const double dy = fac * pos_y - j;
+  const double ty = 1. - dy;
+
+  int k = (int)(fac * pos_z);
+  if (k >= N) k = N - 1;
+  const double dz = fac * pos_z - k;
+  const double tz = 1. - dz;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (i < 0 || i >= N) error("Invalid gpart position in x");
+  if (j < 0 || j >= N) error("Invalid gpart position in y");
+  if (k < 0 || k >= N) error("Invalid gpart position in z");
+#endif
+
+#ifdef SWIFT_GRAVITY_FORCE_CHECKS
+  if (gp->a_grav_PM[0] != 0. || gp->potential_PM != 0.)
+    error("Particle with non-initalised stuff");
+#endif
+
+  /* First, copy the necessary part of the mesh for stencil operations */
+  /* This includes box-wrapping in all 3 dimensions. */
+  double phi[6][6][6];
+  for (int iii = -2; iii <= 3; ++iii) {
+    for (int jjj = -2; jjj <= 3; ++jjj) {
+      for (int kkk = -2; kkk <= 3; ++kkk) {
+        phi[iii + 2][jjj + 2][kkk + 2] =
+            pot[row_major_id_periodic(i + iii, j + jjj, k + kkk, N)];
+      }
+    }
+  }
+
+  /* Some local accumulators */
+  double p = 0.;
+  double a[3] = {0.};
+
+  /* Indices of (i,j,k) in the local copy of the mesh */
+  const int ii = 2, jj = 2, kk = 2;
+
+  /* Simple CIC for the potential itself */
+  p += CIC_get(phi, ii, jj, kk, tx, ty, tz, dx, dy, dz);
+
+  /* ---- */
+
+  /* 5-point stencil along each axis for the accelerations */
+  a[0] += (1. / 12.) * CIC_get(phi, ii + 2, jj, kk, tx, ty, tz, dx, dy, dz);
+  a[0] -= (2. / 3.) * CIC_get(phi, ii + 1, jj, kk, tx, ty, tz, dx, dy, dz);
+  a[0] += (2. / 3.) * CIC_get(phi, ii - 1, jj, kk, tx, ty, tz, dx, dy, dz);
+  a[0] -= (1. / 12.) * CIC_get(phi, ii - 2, jj, kk, tx, ty, tz, dx, dy, dz);
+
+  a[1] += (1. / 12.) * CIC_get(phi, ii, jj + 2, kk, tx, ty, tz, dx, dy, dz);
+  a[1] -= (2. / 3.) * CIC_get(phi, ii, jj + 1, kk, tx, ty, tz, dx, dy, dz);
+  a[1] += (2. / 3.) * CIC_get(phi, ii, jj - 1, kk, tx, ty, tz, dx, dy, dz);
+  a[1] -= (1. / 12.) * CIC_get(phi, ii, jj - 2, kk, tx, ty, tz, dx, dy, dz);
+
+  a[2] += (1. / 12.) * CIC_get(phi, ii, jj, kk + 2, tx, ty, tz, dx, dy, dz);
+  a[2] -= (2. / 3.) * CIC_get(phi, ii, jj, kk + 1, tx, ty, tz, dx, dy, dz);
+  a[2] += (2. / 3.) * CIC_get(phi, ii, jj, kk - 1, tx, ty, tz, dx, dy, dz);
+  a[2] -= (1. / 12.) * CIC_get(phi, ii, jj, kk - 2, tx, ty, tz, dx, dy, dz);
+
+  /* ---- */
+
+  /* Store things back */
+  gp->potential += p;
+  gp->a_grav[0] += fac * a[0];
+  gp->a_grav[1] += fac * a[1];
+  gp->a_grav[2] += fac * a[2];
+#ifdef SWIFT_GRAVITY_FORCE_CHECKS
+  gp->potential_PM = p;
+  gp->a_grav_PM[0] = fac * a[0];
+  gp->a_grav_PM[1] = fac * a[1];
+  gp->a_grav_PM[2] = fac * a[2];
+#endif
+}
+
+#endif
+
+/**
+ * @brief Compute the potential, including periodic correction on the mesh.
+ *
+ * Interpolates the top-level multipoles on-to a mesh, move to Fourier space,
+ * compute the potential including short-range correction and move back
+ * to real space. We use CIC for the interpolation.
+ *
+ * Note that there is no multiplication by G_newton at this stage.
+ *
+ * @param mesh The #pm_mesh used to store the potential.
+ * @param e The #engine from which to compute the forces.
+ */
+void pm_mesh_compute_potential(struct pm_mesh* mesh, const struct engine* e) {
+
+#ifdef HAVE_FFTW
+
+  const struct space* s = e->s;
+  const double r_s = mesh->r_s;
+  const double box_size = s->dim[0];
+  const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
+
+  if (r_s <= 0.) error("Invalid value of a_smooth");
+
+  /* Some useful constants */
+  const int N = mesh->N;
+  const int N_half = N / 2;
+  const double cell_fac = N / box_size;
+
+  /* Use the memory allocated for the potential to temporarily store rho */
+  double* restrict rho = mesh->potential;
+  if (rho == NULL) error("Error allocating memory for density mesh");
+  bzero(rho, N * N * N * sizeof(double));
+
+  /* Allocates some memory for the mesh in Fourier space */
+  fftw_complex* restrict frho =
+      (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N * N * (N_half + 1));
+  if (frho == NULL)
+    error("Error allocating memory for transform of density mesh");
+
+  /* Prepare the FFT library */
+  fftw_plan forward_plan = fftw_plan_dft_r2c_3d(
+      N, N, N, rho, frho, FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
+  fftw_plan inverse_plan = fftw_plan_dft_c2r_3d(
+      N, N, N, frho, rho, FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
+
+  bzero(rho, N * N * N * sizeof(double));
+
+  /* Do a CIC mesh assignment of the gparts */
+  for (size_t i = 0; i < e->s->nr_gparts; ++i)
+    gpart_to_mesh_CIC(&e->s->gparts[i], rho, N, cell_fac, dim);
+
+  /* print_array(rho, N); */
+
+  /* Fourier transform to go to magic-land */
+  fftw_execute(forward_plan);
+
+  /* frho now contains the Fourier transform of the density field */
+  /* frho contains NxNx(N/2+1) complex numbers */
+
+  /* Some common factors */
+  const double green_fac = -1. / (M_PI * box_size);
+  const double a_smooth2 = 4. * M_PI * M_PI * r_s * r_s / (box_size * box_size);
+  const double k_fac = M_PI / (double)N;
+
+  /* Now de-convolve the CIC kernel and apply the Green function */
+  for (int i = 0; i < N; ++i) {
+
+    /* kx component of vector in Fourier space and 1/sinc(kx) */
+    const int kx = (i > N_half ? i - N : i);
+    const double kx_d = (double)kx;
+    const double fx = k_fac * kx_d;
+    const double sinc_kx_inv = (kx != 0) ? fx / sin(fx) : 1.;
+
+    for (int j = 0; j < N; ++j) {
+
+      /* ky component of vector in Fourier space and 1/sinc(ky) */
+      const int ky = (j > N_half ? j - N : j);
+      const double ky_d = (double)ky;
+      const double fy = k_fac * ky_d;
+      const double sinc_ky_inv = (ky != 0) ? fy / sin(fy) : 1.;
+
+      for (int k = 0; k < N_half + 1; ++k) {
+
+        /* kz component of vector in Fourier space and 1/sinc(kz) */
+        const int kz = (k > N_half ? k - N : k);
+        const double kz_d = (double)kz;
+        const double fz = k_fac * kz_d;
+        const double sinc_kz_inv = (kz != 0) ? fz / (sin(fz) + FLT_MIN) : 1.;
+
+        /* Norm of vector in Fourier space */
+        const double k2 = (kx_d * kx_d + ky_d * ky_d + kz_d * kz_d);
+
+        /* Avoid FPEs... */
+        if (k2 == 0.) continue;
+
+        /* Green function */
+        double W = 1.;
+        fourier_kernel_long_grav_eval(k2 * a_smooth2, &W);
+        const double green_cor = green_fac * W / (k2 + FLT_MIN);
+
+        /* Deconvolution of CIC */
+        const double CIC_cor = sinc_kx_inv * sinc_ky_inv * sinc_kz_inv;
+        const double CIC_cor2 = CIC_cor * CIC_cor;
+        const double CIC_cor4 = CIC_cor2 * CIC_cor2;
+
+        /* Combined correction */
+        const double total_cor = green_cor * CIC_cor4;
+
+        /* Apply to the mesh */
+        const int index = N * (N_half + 1) * i + (N_half + 1) * j + k;
+        frho[index][0] *= total_cor;
+        frho[index][1] *= total_cor;
+      }
+    }
+  }
+
+  /* Correct singularity at (0,0,0) */
+  frho[0][0] = 0.;
+  frho[0][1] = 0.;
+
+  /* Fourier transform to come back from magic-land */
+  fftw_execute(inverse_plan);
+
+  /* rho now contains the potential */
+  /* This array is now again NxNxN real numbers */
+  /* Let's store it in the structure */
+  mesh->potential = rho;
+
+  /* message("\n\n\n POTENTIAL"); */
+  /* print_array(potential, N); */
+
+  /* Clean-up the mess */
+  fftw_destroy_plan(forward_plan);
+  fftw_destroy_plan(inverse_plan);
+  fftw_free(frho);
+
+#else
+  error("No FFTW library found. Cannot compute periodic long-range forces.");
+#endif
+}
+
+/**
+ * @brief Interpolate the forces and potential from the mesh to the #gpart.
+ *
+ * We use CIC interpolation. The resulting accelerations and potential must
+ * be multiplied by G_newton.
+ *
+ * @param mesh The #pm_mesh (containing the potential) to interpolate from.
+ * @param e The #engine (to check active status).
+ * @param gparts The #gpart to interpolate to.
+ * @param gcount The number of #gpart.
+ */
+void pm_mesh_interpolate_forces(const struct pm_mesh* mesh,
+                                const struct engine* e, struct gpart* gparts,
+                                int gcount) {
+
+#ifdef HAVE_FFTW
+
+  const int N = mesh->N;
+  const double cell_fac = mesh->cell_fac;
+  const double* potential = mesh->potential;
+  const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]};
+
+  /* Get the potential from the mesh to the active gparts using CIC */
+  for (int i = 0; i < gcount; ++i) {
+    struct gpart* gp = &gparts[i];
+
+    if (gpart_is_active(gp, e))
+      mesh_to_gparts_CIC(gp, potential, N, cell_fac, dim);
+  }
+#else
+  error("No FFTW library found. Cannot compute periodic long-range forces.");
+#endif
+}
+
+/**
+ * @brief Initialisses the mesh used for the long-range periodic forces
+ *
+ * @param mesh The #pm_mesh to initialise.
+ * @param props The propoerties of the gravity scheme.
+ * @param dim The (comoving) side-lengths of the simulation volume.
+ */
+void pm_mesh_init(struct pm_mesh* mesh, const struct gravity_props* props,
+                  double dim[3]) {
+
+#ifdef HAVE_FFTW
+
+  if (dim[0] != dim[1] || dim[0] != dim[1])
+    error("Doing mesh-gravity on a non-cubic domain");
+
+  const int N = props->mesh_size;
+  const double box_size = dim[0];
+
+  mesh->periodic = 1;
+  mesh->N = N;
+  mesh->dim[0] = dim[0];
+  mesh->dim[1] = dim[1];
+  mesh->dim[2] = dim[2];
+  mesh->cell_fac = N / box_size;
+  mesh->r_s = props->a_smooth * box_size / N;
+  mesh->r_s_inv = 1. / mesh->r_s;
+  mesh->r_cut_max = mesh->r_s * props->r_cut_max_ratio;
+  mesh->r_cut_min = mesh->r_s * props->r_cut_min_ratio;
+
+  /* Allocate the memory for the combined density and potential array */
+  mesh->potential = (double*)fftw_malloc(sizeof(double) * N * N * N);
+  if (mesh->potential == NULL)
+    error("Error allocating memory for the long-range gravity mesh.");
+#else
+  error("No FFTW library found. Cannot compute periodic long-range forces.");
+#endif
+}
+
+/**
+ * @brief Initialises the mesh for the case where we don't do mesh gravity
+ * calculations
+ *
+ * Crucially this set the 'periodic' propoerty to 0 and all the relevant values
+ * to a
+ * state where all calculations will default to pure non-periodic Newtonian.
+ *
+ * @param mesh The #pm_mesh to initialise.
+ * @param dim The (comoving) side-lengths of the simulation volume.
+ */
+void pm_mesh_init_no_mesh(struct pm_mesh* mesh, double dim[3]) {
+
+  bzero(mesh, sizeof(struct pm_mesh));
+
+  /* Fill in non-zero properties */
+  mesh->dim[0] = dim[0];
+  mesh->dim[1] = dim[1];
+  mesh->dim[2] = dim[2];
+  mesh->r_s = FLT_MAX;
+  mesh->r_cut_min = FLT_MAX;
+  mesh->r_cut_max = FLT_MAX;
+}
+
+/**
+ * @brief Frees the memory allocated for the long-range mesh.
+ */
+void pm_mesh_clean(struct pm_mesh* mesh) {
+
+  if (mesh->potential) free(mesh->potential);
+  mesh->potential = 0;
+}
+
+/**
+ * @brief Write a #pm_mesh struct to the given FILE as a stream of bytes.
+ *
+ * @param mesh the struct
+ * @param stream the file stream
+ */
+void pm_mesh_struct_dump(const struct pm_mesh* mesh, FILE* stream) {
+  restart_write_blocks((void*)mesh, sizeof(struct pm_mesh), 1, stream,
+                       "gravity", "gravity props");
+}
+
+/**
+ * @brief Restore a #pm_mesh struct from the given FILE as a stream of
+ * bytes.
+ *
+ * @param mesh the struct
+ * @param stream the file stream
+ */
+void pm_mesh_struct_restore(struct pm_mesh* mesh, FILE* stream) {
+
+  restart_read_blocks((void*)mesh, sizeof(struct pm_mesh), 1, stream, NULL,
+                      "gravity props");
+#ifdef HAVE_FFTW
+  const int N = mesh->N;
+
+  /* Allocate the memory for the combined density and potential array */
+  mesh->potential = (double*)fftw_malloc(sizeof(double) * N * N * N);
+  if (mesh->potential == NULL)
+    error("Error allocating memory for the long-range gravity mesh.");
+
+#else
+  error("No FFTW library found. Cannot compute periodic long-range forces.");
+#endif
+}
diff --git a/src/mesh_gravity.h b/src/mesh_gravity.h
new file mode 100644
index 0000000000000000000000000000000000000000..8cc4cd753ccfb24f80f997dd389f2e5569c7dce1
--- /dev/null
+++ b/src/mesh_gravity.h
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_MESH_GRAVITY_H
+#define SWIFT_MESH_GRAVITY_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Local headers */
+#include "gravity_properties.h"
+#include "restart.h"
+
+/* Forward declarations */
+struct engine;
+struct gpart;
+
+/**
+ * @brief Data structure for the long-range periodic forces using a mesh
+ */
+struct pm_mesh {
+
+  /*! Is the calculation using periodic BCs? */
+  int periodic;
+
+  /*! Side-length of the mesh */
+  int N;
+
+  /*! Conversion factor between box and mesh size */
+  double cell_fac;
+
+  /*! (Comoving) side-length of the box along the three axis */
+  double dim[3];
+
+  /*! Scale over which we smooth the forces */
+  double r_s;
+
+  /*! Inverse of the scale over which we smooth the forces */
+  double r_s_inv;
+
+  /*! Distance beyond which tree forces are neglected */
+  double r_cut_max;
+
+  /*! Distance below which tree forces are Newtonian */
+  double r_cut_min;
+
+  /*! Potential field */
+  double *potential;
+};
+
+void pm_mesh_init(struct pm_mesh *mesh, const struct gravity_props *props,
+                  double dim[3]);
+void pm_mesh_init_no_mesh(struct pm_mesh *mesh, double dim[3]);
+void pm_mesh_compute_potential(struct pm_mesh *mesh, const struct engine *e);
+void pm_mesh_interpolate_forces(const struct pm_mesh *mesh,
+                                const struct engine *e, struct gpart *gparts,
+                                int gcount);
+void pm_mesh_clean(struct pm_mesh *mesh);
+
+/* Dump/restore. */
+void pm_mesh_struct_dump(const struct pm_mesh *p, FILE *stream);
+void pm_mesh_struct_restore(struct pm_mesh *p, FILE *stream);
+
+#endif /* SWIFT_MESH_GRAVITY_H */
diff --git a/src/multipole.h b/src/multipole.h
index dd74c4a40d79cc393e252bfe061591dd868f2a55..ac746728ee39d597c92bba8c260140c10adbddd1 100644
--- a/src/multipole.h
+++ b/src/multipole.h
@@ -1516,12 +1516,13 @@ INLINE static void gravity_M2M(struct multipole *m_a,
  * @param props The #gravity_props of this calculation.
  * @param periodic Is the calculation periodic ?
  * @param dim The size of the simulation box.
+ * @param rs_inv The inverse of the gravity mesh-smoothing scale.
  */
 INLINE static void gravity_M2L(struct grav_tensor *l_b,
                                const struct multipole *m_a,
                                const double pos_b[3], const double pos_a[3],
                                const struct gravity_props *props, int periodic,
-                               const double dim[3]) {
+                               const double dim[3], float rs_inv) {
 
   /* Recover some constants */
   const float eps = props->epsilon_cur;
@@ -1545,7 +1546,8 @@ INLINE static void gravity_M2L(struct grav_tensor *l_b,
 
   /* Compute all derivatives */
   struct potential_derivatives_M2L pot;
-  compute_potential_derivatives_M2L(dx, dy, dz, r2, r_inv, eps, eps_inv, &pot);
+  compute_potential_derivatives_M2L(dx, dy, dz, r2, r_inv, eps, eps_inv,
+                                    periodic, rs_inv, &pot);
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Count interactions */
@@ -2394,8 +2396,9 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb,
  * @param r2 Square of the distance (periodically wrapped) between the
  * multipoles.
  */
-__attribute__((always_inline)) INLINE static int gravity_M2L_accept(
-    double r_crit_a, double r_crit_b, double theta_crit2, double r2) {
+__attribute__((always_inline, const)) INLINE static int gravity_M2L_accept(
+    const double r_crit_a, const double r_crit_b, const double theta_crit2,
+    const double r2) {
 
   const double size = r_crit_a + r_crit_b;
   const double size2 = size * size;
@@ -2408,8 +2411,7 @@ __attribute__((always_inline)) INLINE static int gravity_M2L_accept(
 
 /**
  * @brief Checks whether a particle-cell interaction can be appromixated by a
- * M2P
- * interaction using the distance and cell radius.
+ * M2P interaction using the distance and cell radius.
  *
  * We use the multipole acceptance criterion of Dehnen, 2002, JCoPh, Volume 179,
  * Issue 1, pp.27-42, equation 10.
@@ -2417,10 +2419,10 @@ __attribute__((always_inline)) INLINE static int gravity_M2L_accept(
  * @param r_max2 The square of the size of the multipole.
  * @param theta_crit2 The square of the critical opening angle.
  * @param r2 Square of the distance (periodically wrapped) between the
- * multipoles.
+ * particle and the multipole.
  */
-__attribute__((always_inline)) INLINE static int gravity_M2P_accept(
-    float r_max2, float theta_crit2, float r2) {
+__attribute__((always_inline, const)) INLINE static int gravity_M2P_accept(
+    const float r_max2, const float theta_crit2, const float r2) {
 
   // MATTHIEU: Make this mass-dependent ?
 
diff --git a/src/parallel_io.c b/src/parallel_io.c
index 8d5a97a2ad92a9bfa7e3378534aa2df7b168fb47..7e892154231cb09ffca1f5f93f56240b043995ba 100644
--- a/src/parallel_io.c
+++ b/src/parallel_io.c
@@ -49,12 +49,13 @@
 #include "io_properties.h"
 #include "kernel_hydro.h"
 #include "part.h"
+#include "part_type.h"
 #include "stars_io.h"
 #include "units.h"
 #include "xmf.h"
 
 /* The current limit of ROMIO (the underlying MPI-IO layer) is 2GB */
-#define HDF5_PARALLEL_IO_MAX_BYTES 2000000000LL
+#define HDF5_PARALLEL_IO_MAX_BYTES 2147000000LL
 
 /* Are we timing the i/o? */
 //#define IO_SPEED_MEASUREMENT
@@ -70,13 +71,16 @@
  * @param internal_units The #unit_system used internally.
  * @param ic_units The #unit_system used in the snapshots.
  * @param cleanup_h Are we removing h-factors from the ICs?
+ * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget
+ * IC velocities?
  * @param h The value of the reduced Hubble constant to use for cleaning.
+ * @param a The current value of the scale-factor.
  */
 void readArray_chunk(hid_t h_data, hid_t h_plist_id,
                      const struct io_props props, size_t N, long long offset,
                      const struct unit_system* internal_units,
                      const struct unit_system* ic_units, int cleanup_h,
-                     double h) {
+                     int cleanup_sqrt_a, double h, double a) {
 
   const size_t typeSize = io_sizeof_type(props.type);
   const size_t copySize = typeSize * props.dimension;
@@ -84,7 +88,7 @@ void readArray_chunk(hid_t h_data, hid_t h_plist_id,
 
   /* Can't handle writes of more than 2GB */
   if (N * props.dimension * typeSize > HDF5_PARALLEL_IO_MAX_BYTES)
-    error("Dataset too large to be written in one pass!");
+    error("Dataset too large to be read in one pass!");
 
   /* Allocate temporary buffer */
   void* temp = malloc(num_elements * typeSize);
@@ -140,20 +144,35 @@ void readArray_chunk(hid_t h_data, hid_t h_plist_id,
   /* Clean-up h if necessary */
   const float h_factor_exp = units_h_factor(internal_units, props.units);
   if (cleanup_h && h_factor_exp != 0.f) {
-    const double h_factor = pow(h, h_factor_exp);
 
     /* message("Multipltying '%s' by h^%f=%f", props.name, h_factor_exp,
      * h_factor); */
 
     if (io_is_double_precision(props.type)) {
       double* temp_d = (double*)temp;
+      const double h_factor = pow(h, h_factor_exp);
       for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= h_factor;
     } else {
       float* temp_f = (float*)temp;
+      const float h_factor = pow(h, h_factor_exp);
       for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= h_factor;
     }
   }
 
+  /* Clean-up a if necessary */
+  if (cleanup_sqrt_a && a != 1. && (strcmp(props.name, "Velocities") == 0)) {
+
+    if (io_is_double_precision(props.type)) {
+      double* temp_d = (double*)temp;
+      const double vel_factor = sqrt(a);
+      for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= vel_factor;
+    } else {
+      float* temp_f = (float*)temp;
+      const float vel_factor = sqrt(a);
+      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= vel_factor;
+    }
+  }
+
   /* Copy temporary buffer to particle data */
   char* temp_c = (char*)temp;
   for (size_t i = 0; i < N; ++i)
@@ -177,12 +196,16 @@ void readArray_chunk(hid_t h_data, hid_t h_plist_id,
  * @param internal_units The #unit_system used internally.
  * @param ic_units The #unit_system used in the ICs.
  * @param cleanup_h Are we removing h-factors from the ICs?
+ * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget
+ * IC velocities?
  * @param h The value of the reduced Hubble constant to use for cleaning.
+ * @param a The current value of the scale-factor.
  */
 void readArray(hid_t grp, struct io_props props, size_t N, long long N_total,
                int mpi_rank, long long offset,
                const struct unit_system* internal_units,
-               const struct unit_system* ic_units, int cleanup_h, double h) {
+               const struct unit_system* ic_units, int cleanup_h,
+               int cleanup_sqrt_a, double h, double a) {
 
   const size_t typeSize = io_sizeof_type(props.type);
   const size_t copySize = typeSize * props.dimension;
@@ -205,6 +228,57 @@ void readArray(hid_t grp, struct io_props props, size_t N, long long N_total,
   const hid_t h_data = H5Dopen2(grp, props.name, H5P_DEFAULT);
   if (h_data < 0) error("Error while opening data space '%s'.", props.name);
 
+/* Parallel-HDF5 1.10.2 incorrectly reads data that was compressed */
+/* We detect this here and crash with an error message instead of  */
+/* continuing with garbage data.                                   */
+#if H5_VERSION_LE(1, 10, 2) && H5_VERSION_GE(1, 10, 2)
+  if (mpi_rank == 0) {
+
+    /* Recover the list of filters that were applied to the data */
+    const hid_t h_plist = H5Dget_create_plist(h_data);
+    if (h_plist < 0)
+      error("Error getting property list for data set '%s'", props.name);
+
+    /* Recover the number of filters in the list */
+    const int n_filters = H5Pget_nfilters(h_plist);
+
+    for (int n = 0; n < n_filters; ++n) {
+
+      unsigned int flag;
+      size_t cd_nelmts = 32;
+      unsigned int* cd_values = malloc(cd_nelmts * sizeof(unsigned int));
+      size_t namelen = 256;
+      char* name = calloc(namelen, sizeof(char));
+      unsigned int filter_config;
+
+      /* Recover the n^th filter in the list */
+      const H5Z_filter_t filter =
+          H5Pget_filter(h_plist, n, &flag, &cd_nelmts, cd_values, namelen, name,
+                        &filter_config);
+      if (filter < 0)
+        error("Error retrieving %d^th (%d) filter for data set '%s'", n,
+              n_filters, props.name);
+
+      /* Now check whether the deflate filter had been applied */
+      if (filter == H5Z_FILTER_DEFLATE)
+        error(
+            "HDF5 1.10.2 cannot correctly read data that was compressed with "
+            "the 'deflate' filter.\nThe field '%s' has had this filter applied "
+            "and the code would silently read garbage into the particle arrays "
+            "so we'd rather stop here. You can:\n - Recompile the code with an "
+            "earlier or older version of HDF5.\n - Use the 'h5repack' tool to "
+            "remove the filter from the ICs (e.g. h5repack -f NONE -i in_file "
+            "-o out_file).\n",
+            props.name);
+
+      free(name);
+      free(cd_values);
+    }
+
+    H5Pclose(h_plist);
+  }
+#endif
+
   /* Create property list for collective dataset read. */
   const hid_t h_plist_id = H5Pcreate(H5P_DATASET_XFER);
   H5Pset_dxpl_mpio(h_plist_id, H5FD_MPIO_COLLECTIVE);
@@ -221,7 +295,7 @@ void readArray(hid_t grp, struct io_props props, size_t N, long long N_total,
     /* Write the first chunk */
     const size_t this_chunk = (N > max_chunk_size) ? max_chunk_size : N;
     readArray_chunk(h_data, h_plist_id, props, this_chunk, offset,
-                    internal_units, ic_units, cleanup_h, h);
+                    internal_units, ic_units, cleanup_h, cleanup_sqrt_a, h, a);
 
     /* Compute how many items are left */
     if (N > max_chunk_size) {
@@ -249,10 +323,6 @@ void readArray(hid_t grp, struct io_props props, size_t N, long long N_total,
   H5Dclose(h_data);
 }
 
-/*-----------------------------------------------------------------------------
- * Routines writing an output file
- *-----------------------------------------------------------------------------*/
-
 /**
  * @brief Prepares an array in the snapshot.
  *
@@ -549,7 +619,10 @@ void writeArray(struct engine* e, hid_t grp, char* fileName,
  * @param with_gravity Are we running with gravity ?
  * @param with_stars Are we running with stars ?
  * @param cleanup_h Are we cleaning-up h-factors from the quantities we read?
+ * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget
+ * IC velocities?
  * @param h The value of the reduced Hubble constant to use for correction.
+ * @param a The current value of the scale-factor.
  * @param mpi_rank The MPI rank of this node
  * @param mpi_size The number of MPI ranks
  * @param comm The MPI communicator
@@ -563,9 +636,9 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
                       struct spart** sparts, size_t* Ngas, size_t* Ngparts,
                       size_t* Nstars, int* periodic, int* flag_entropy,
                       int with_hydro, int with_gravity, int with_stars,
-                      int cleanup_h, double h, int mpi_rank, int mpi_size,
-                      MPI_Comm comm, MPI_Info info, int n_threads,
-                      int dry_run) {
+                      int cleanup_h, int cleanup_sqrt_a, double h, double a,
+                      int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
+                      int n_threads, int dry_run) {
 
   hid_t h_file = 0, h_grp = 0;
   /* GADGET has only cubic boxes (in cosmological mode) */
@@ -657,7 +730,7 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
   struct unit_system* ic_units =
       (struct unit_system*)malloc(sizeof(struct unit_system));
   if (ic_units == NULL) error("Unable to allocate memory for IC unit system");
-  io_read_unit_system(h_file, ic_units, mpi_rank);
+  io_read_unit_system(h_file, ic_units, internal_units, mpi_rank);
 
   /* Tell the user if a conversion will be needed */
   if (mpi_rank == 0) {
@@ -784,7 +857,8 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
     if (!dry_run)
       for (int i = 0; i < num_fields; ++i)
         readArray(h_grp, list[i], Nparticles, N_total[ptype], mpi_rank,
-                  offset[ptype], internal_units, ic_units, cleanup_h, h);
+                  offset[ptype], internal_units, ic_units, cleanup_h,
+                  cleanup_sqrt_a, h, a);
 
     /* Close particle group */
     H5Gclose(h_grp);
@@ -838,6 +912,7 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
   const struct xpart* xparts = e->s->xparts;
   const struct gpart* gparts = e->s->gparts;
   const struct spart* sparts = e->s->sparts;
+  struct swift_params* params = e->parameter_file;
   FILE* xmfFile = 0;
   int periodic = e->s->periodic;
   int numFiles = 1;
@@ -850,8 +925,12 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
 
   /* HDF5 File name */
   char fileName[FILENAME_BUFFER_SIZE];
-  snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName,
-           e->snapshot_output_count);
+  if (e->snapshot_label_delta == 1)
+    snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName,
+             e->snapshot_output_count);
+  else
+    snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%06i.hdf5", baseName,
+             e->snapshot_output_count * e->snapshot_label_delta);
 
   /* Open HDF5 file with the chosen parameters */
   hid_t h_file = H5Fcreate(fileName, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
@@ -961,7 +1040,14 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
   h_grp =
       H5Gcreate(h_file, "/Parameters", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
   if (h_grp < 0) error("Error while creating parameters group");
-  parser_write_params_to_hdf5(e->parameter_file, h_grp);
+  parser_write_params_to_hdf5(e->parameter_file, h_grp, 1);
+  H5Gclose(h_grp);
+
+  /* Print the runtime unused parameters */
+  h_grp = H5Gcreate(h_file, "/UnusedParameters", H5P_DEFAULT, H5P_DEFAULT,
+                    H5P_DEFAULT);
+  if (h_grp < 0) error("Error while creating parameters group");
+  parser_write_params_to_hdf5(e->parameter_file, h_grp, 0);
   H5Gclose(h_grp);
 
   /* Print the system of Units used in the spashot */
@@ -1013,10 +1099,19 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
         error("Particle Type %d not yet supported. Aborting", ptype);
     }
 
-    /* Prepare everything */
-    for (int i = 0; i < num_fields; ++i)
-      prepareArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i],
-                   N_total[ptype], snapshot_units);
+    /* Prepare everything that is not cancelled */
+    for (int i = 0; i < num_fields; ++i) {
+
+      /* Did the user cancel this field? */
+      char field[PARSER_MAX_LINE_SIZE];
+      sprintf(field, "SelectOutput:%s_%s", list[i].name,
+              part_type_names[ptype]);
+      int should_write = parser_get_opt_param_int(params, field, 1);
+
+      if (should_write)
+        prepareArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i],
+                     N_total[ptype], snapshot_units);
+    }
 
     /* Close particle group */
     H5Gclose(h_grp);
@@ -1068,6 +1163,7 @@ void write_output_parallel(struct engine* e, const char* baseName,
   struct gpart* dmparts = NULL;
   const struct spart* sparts = e->s->sparts;
   const struct cooling_function_data* cooling = e->cooling_func;
+  struct swift_params* params = e->parameter_file;
 
   /* Number of unassociated gparts */
   const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
@@ -1257,11 +1353,20 @@ void write_output_parallel(struct engine* e, const char* baseName,
         error("Particle Type %d not yet supported. Aborting", ptype);
     }
 
-    /* Write everything */
-    for (int i = 0; i < num_fields; ++i)
-      writeArray(e, h_grp, fileName, partTypeGroupName, list[i], Nparticles,
-                 N_total[ptype], mpi_rank, offset[ptype], internal_units,
-                 snapshot_units);
+    /* Write everything that is not cancelled */
+    for (int i = 0; i < num_fields; ++i) {
+
+      /* Did the user cancel this field? */
+      char field[PARSER_MAX_LINE_SIZE];
+      sprintf(field, "SelectOutput:%s_%s", list[i].name,
+              part_type_names[ptype]);
+      int should_write = parser_get_opt_param_int(params, field, 1);
+
+      if (should_write)
+        writeArray(e, h_grp, fileName, partTypeGroupName, list[i], Nparticles,
+                   N_total[ptype], mpi_rank, offset[ptype], internal_units,
+                   snapshot_units);
+    }
 
     /* Free temporary array */
     if (dmparts) {
diff --git a/src/parallel_io.h b/src/parallel_io.h
index 5ad3b34cdc4320d6fe3eb860615db33958ad612f..668b6f83443fe4c39ddf3269c8d2236e72588e32 100644
--- a/src/parallel_io.h
+++ b/src/parallel_io.h
@@ -39,9 +39,9 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
                       struct spart** sparts, size_t* Ngas, size_t* Ngparts,
                       size_t* Nsparts, int* periodic, int* flag_entropy,
                       int with_hydro, int with_gravity, int with_stars,
-                      int cleanup_h, double h, int mpi_rank, int mpi_size,
-                      MPI_Comm comm, MPI_Info info, int nr_threads,
-                      int dry_run);
+                      int cleanup_h, int cleanup_sqrt_a, double h, double a,
+                      int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
+                      int nr_threads, int dry_run);
 
 void write_output_parallel(struct engine* e, const char* baseName,
                            const struct unit_system* internal_units,
diff --git a/src/parser.c b/src/parser.c
index af9ef5fd6b7228dd4ad96640b59322175586862b..04015fe693fbf616e531681a3b46902812bc09e0 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -1,7 +1,7 @@
 /*******************************************************************************
  * This file is part of SWIFT.
  * Copyright (c) 2016 James Willis (james.s.willis@durham.ac.uk)
- *               2017 Peter W. Draper (p.w.draper@durham.ac.uk)
+ *               2017-2018 Peter W. Draper (p.w.draper@durham.ac.uk)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -34,6 +34,7 @@
 #include "common_io.h"
 #include "error.h"
 #include "restart.h"
+#include "tools.h"
 
 #define PARSER_COMMENT_STRING "#"
 #define PARSER_COMMENT_CHAR '#'
@@ -42,8 +43,9 @@
 #define PARSER_START_OF_FILE "---"
 #define PARSER_END_OF_FILE "..."
 
+#define CHUNK 10
+
 /* Private functions. */
-static int count_char(const char *str, char val);
 static int is_empty(const char *str);
 static int count_indentation(const char *str);
 static void parse_line(char *line, struct swift_params *params);
@@ -57,12 +59,103 @@ static void find_duplicate_section(const struct swift_params *params,
 static int lineNumber = 0;
 
 /**
- * @brief Reads an input file and stores each parameter in a structure.
+ * @brief parse a YAML list of strings returning a set of pointers to
+ *        the strings.
+ *
+ * It is assumed that the [] have been removed (also no lists in lists)
+ * words are separated by commas and the strings may or may not be quoted.
+ * So lines like:
+ *
+ *    'xyz', 'ABC', "ab'c", "de:f", "g,hi", "zzz", Hello World, again
+ *
+ * Are supported as expected.
+ *
+ * @param line the line to parse.
+ * @param result array of pointers to the strings.
+ * @return the number of strings
+ */
+static int parse_quoted_strings(const char *line, char ***result) {
+
+  char word[PARSER_MAX_LINE_SIZE];
+  int nchar = 0;
+  int nwords = 0;
+  char quote = '\0';
+
+  /* Preallocate a number of pointers. */
+  char **strings;
+  int count = CHUNK;
+  strings = (char **)malloc(count * sizeof(char *));
+
+  word[0] = '\0';
+  for (unsigned int i = 0; i < strlen(line); i++) {
+    char c = line[i];
+    if (c == '"' || c == '\'') {
+      if (c == quote) {
+        quote = '\0';
+      } else if (!quote) {
+        quote = c;
+      } else {
+        word[nchar++] = c;
+      }
+    } else if (c == ',') {
+      if (!quote) {
+
+        /* Save word. */
+        word[nchar++] = '\0';
+        if (count <= nwords) {
+          count += CHUNK;
+          strings = (char **)realloc(strings, count * sizeof(char *));
+        }
+        strings[nwords] = (char *)malloc((strlen(word) + 1) * sizeof(char));
+        strcpy(strings[nwords], trim_both(word));
+        nwords++;
+
+        /* Ready for next. */
+        nchar = 0;
+        word[0] = '\0';
+
+      } else {
+        word[nchar++] = c;
+      }
+    } else {
+      word[nchar++] = c;
+    }
+  }
+
+  /* Keep unfinished words. */
+  if (nchar > 0) {
+    word[nchar] = '\0';
+    if (count <= nwords) {
+      count += 1;
+      strings = (char **)realloc(strings, count * sizeof(char));
+    }
+    strings[nwords] = (char *)malloc((strlen(word) + 1) * sizeof(char));
+    strcpy(strings[nwords], trim_both(word));
+    nwords++;
+  }
+
+  *result = strings;
+  return nwords;
+}
+
+/**
+ * @brief Initialize the parser structure.
  *
  * @param file_name Name of file to be read
  * @param params Structure to be populated from file
  */
+void parser_init(const char *file_name, struct swift_params *params) {
+  params->paramCount = 0;
+  params->sectionCount = 0;
+  strcpy(params->fileName, file_name);
+}
 
+/**
+ * @brief Reads an input file and stores each parameter in a structure.
+ *
+ * @param file_name Name of file to be read
+ * @param params Structure to be populated from file
+ */
 void parser_read_file(const char *file_name, struct swift_params *params) {
   /* Open file for reading */
   FILE *file = fopen(file_name, "r");
@@ -71,9 +164,7 @@ void parser_read_file(const char *file_name, struct swift_params *params) {
   char line[PARSER_MAX_LINE_SIZE];
 
   /* Initialise parameter count. */
-  params->paramCount = 0;
-  params->sectionCount = 0;
-  strcpy(params->fileName, file_name);
+  parser_init(file_name, params);
 
   /* Check if parameter file exits. */
   if (file == NULL) {
@@ -106,12 +197,19 @@ void parser_set_param(struct swift_params *params, const char *namevalue) {
   /* Get the various parts. */
   char name[PARSER_MAX_LINE_SIZE];
   char value[PARSER_MAX_LINE_SIZE];
+  char section[PARSER_MAX_LINE_SIZE];
   name[0] = '\0';
   value[0] = '\0';
 
   /* Name is part until second colon. */
   const char *p1 = strchr(namevalue, ':');
   if (p1 != NULL) {
+
+    /* Section is first part until a colon. */
+    memcpy(section, namevalue, p1 - namevalue);
+    section[p1 - namevalue] = ':';
+    section[p1 - namevalue + 1] = '\0';
+
     const char *p2 = strchr(p1 + 1, ':');
     if (p2 != NULL) {
       memcpy(name, namevalue, p2 - namevalue);
@@ -136,39 +234,36 @@ void parser_set_param(struct swift_params *params, const char *namevalue) {
     if (strcmp(name, params->data[i].name) == 0) {
       message("Value of '%s' changed from '%s' to '%s'", params->data[i].name,
               params->data[i].value, value);
-      strcpy(params->data[i].value, value);
+      strcpy(params->data[i].value, trim_both(value));
       updated = 1;
     }
   }
   if (!updated) {
+    /* Is this a new section? */
+    int newsection = 1;
+    for (int i = 0; i < params->sectionCount; i++) {
+      if (strcmp(section, params->section[i].name) == 0) {
+        newsection = 0;
+        break;
+      }
+    }
+    if (newsection) {
+      strcpy(params->section[params->sectionCount].name, section);
+      params->sectionCount++;
+      if (params->sectionCount == PARSER_MAX_NO_OF_SECTIONS)
+        error("Too many sections, current maximum is %d.",
+              params->sectionCount);
+    }
+
     strcpy(params->data[params->paramCount].name, name);
     strcpy(params->data[params->paramCount].value, value);
+    params->data[params->paramCount].used = 0;
     params->paramCount++;
     if (params->paramCount == PARSER_MAX_NO_OF_PARAMS)
       error("Too many parameters, current maximum is %d.", params->paramCount);
   }
 }
 
-/**
- * @brief Counts the number of times a specific character appears in a string.
- *
- * @param str String to be checked
- * @param val Character to be counted
- *
- * @return Number of occurrences of val inside str
- */
-
-static int count_char(const char *str, char val) {
-  int count = 0;
-
-  /* Check if the line contains the character */
-  while (*str) {
-    if (*str++ == val) ++count;
-  }
-
-  return count;
-}
-
 /**
  * @brief Counts the number of white spaces that prefix a string.
  *
@@ -250,7 +345,7 @@ static void find_duplicate_section(const struct swift_params *params,
 
 static void parse_line(char *line, struct swift_params *params) {
   /* Parse line if it doesn't begin with a comment. */
-  if (*line != PARSER_COMMENT_CHAR) {
+  if (line[0] != PARSER_COMMENT_CHAR) {
     char trim_line[PARSER_MAX_LINE_SIZE];
     char tmp_str[PARSER_MAX_LINE_SIZE];
     char *token;
@@ -296,14 +391,8 @@ static void parse_value(char *line, struct swift_params *params) {
 
   char *token;
 
-  /* Check for more than one value on the same line. */
-  if (count_char(line, PARSER_VALUE_CHAR) > 1) {
-    error("Invalid line:%d '%s', only one value allowed per line.", lineNumber,
-          line);
-  }
-
   /* Check that standalone parameters have correct indentation. */
-  if (!inSection && *line == ' ') {
+  if (!inSection && line[0] == ' ') {
     error(
         "Invalid line:%d '%s', standalone parameter defined with incorrect "
         "indentation.",
@@ -311,18 +400,20 @@ static void parse_value(char *line, struct swift_params *params) {
   }
 
   /* Check that it is a parameter inside a section.*/
-  if (*line == ' ' || *line == '\t') {
+  if (line[0] == ' ' || line[0] == '\t') {
     parse_section_param(line, &isFirstParam, section, params);
-  } else { /*Else it is the start of a new section or standalone parameter. */
-    /* Take first token as the parameter name. */
-    token = strtok(line, " :\t");
-    strcpy(tmpStr, token);
+  } else {
+    /* It is the start of a new section or standalone parameter.
+     * Take first token as the parameter name. */
+    token = strtok(line, ":\t");
+    strcpy(tmpStr, trim_trailing(token));
 
     /* Take second token as the parameter value. */
-    token = strtok(NULL, " #\n");
+    token = trim_both(strtok(NULL, "#\n"));
 
-    /* If second token is NULL then the line must be a section heading. */
-    if (token == NULL) {
+    /* If second token is NULL or empty then the line must be a section
+     * heading. */
+    if (token == NULL || strlen(token) == 0) {
       strcpy(tmpSectionName, tmpStr);
       strcat(tmpSectionName, PARSER_VALUE_STRING);
 
@@ -359,6 +450,7 @@ static void parse_value(char *line, struct swift_params *params) {
        * section. */
       strcpy(params->data[params->paramCount].name, tmpStr);
       strcpy(params->data[params->paramCount].value, token);
+      params->data[params->paramCount].used = 0;
       if (params->paramCount == PARSER_MAX_NO_OF_PARAMS - 1) {
         error(
             "Maximal number of parameters in parameter file reached. Aborting "
@@ -402,11 +494,11 @@ static void parse_section_param(char *line, int *isFirstParam,
   }
 
   /* Take first token as the parameter name and trim leading white space. */
-  token = strtok(line, " :\t");
+  token = trim_both(strtok(line, ":\t"));
   strcpy(tmpStr, token);
 
   /* Take second token as the parameter value. */
-  token = strtok(NULL, " #\n");
+  token = trim_both(strtok(NULL, "#\n"));
 
   /* Prefix the parameter name with its section name and
    * copy it into the parameter structure. */
@@ -418,6 +510,7 @@ static void parse_section_param(char *line, int *isFirstParam,
 
   strcpy(params->data[params->paramCount].name, paramName);
   strcpy(params->data[params->paramCount].value, token);
+  params->data[params->paramCount].used = 0;
   if (params->paramCount == PARSER_MAX_NO_OF_PARAMS - 1) {
     error("Maximal number of parameters in parameter file reached. Aborting !");
   } else {
@@ -425,6 +518,54 @@ static void parse_section_param(char *line, int *isFirstParam,
   }
 }
 
+// Retrieve parameter value from structure. TYPE is the data type, float, int
+// etc. FMT the format required for that data type, i.e. %f, %d etc. and DESC
+// a one word description of the type, "float", "int" etc.
+#define PARSER_GET_VALUE(TYPE, FMT, DESC)                                     \
+  static int get_param_##TYPE(struct swift_params *params, const char *name,  \
+                              int required, TYPE *result) {                   \
+    char str[PARSER_MAX_LINE_SIZE];                                           \
+    for (int i = 0; i < params->paramCount; i++) {                            \
+      if (strcmp(name, params->data[i].name) == 0) {                          \
+        /* Check that exactly one number is parsed, capture junk. */          \
+        if (sscanf(params->data[i].value, " " FMT "%s ", result, str) != 1) { \
+          error("Tried parsing " DESC                                         \
+                " '%s' but found '%s' with "                                  \
+                "illegal trailing characters '%s'.",                          \
+                params->data[i].name, params->data[i].value, str);            \
+        }                                                                     \
+        /* This parameter has been used */                                    \
+        params->data[i].used = 1;                                             \
+        return 1;                                                             \
+      }                                                                       \
+    }                                                                         \
+    if (required)                                                             \
+      error("Cannot find '%s' in the structure, in file '%s'.", name,         \
+            params->fileName);                                                \
+    return 0;                                                                 \
+  }
+
+// Set a parameter to a value and save for dumping.
+#define PARSER_SAVE_VALUE(PREFIX, TYPE, FMT)                      \
+  static void save_param_##PREFIX(struct swift_params *params,    \
+                                  const char *name, TYPE value) { \
+    char str[PARSER_MAX_LINE_SIZE];                               \
+    sprintf(str, "%s: " FMT, name, value);                        \
+    parser_set_param(params, str);                                \
+    params->data[params->paramCount - 1].used = 1;                \
+  }
+
+/* Instantiations. */
+PARSER_GET_VALUE(char, "%c", "char");
+PARSER_GET_VALUE(int, "%d", "int");
+PARSER_GET_VALUE(float, "%f", "float");
+PARSER_GET_VALUE(double, "%lf", "double");
+PARSER_SAVE_VALUE(char, char, "%c");
+PARSER_SAVE_VALUE(int, int, "%d");
+PARSER_SAVE_VALUE(float, float, "%g");
+PARSER_SAVE_VALUE(double, double, "%g");
+PARSER_SAVE_VALUE(string, const char *, "%s");
+
 /**
  * @brief Retrieve integer parameter from structure.
  *
@@ -432,28 +573,10 @@ static void parse_section_param(char *line, int *isFirstParam,
  * @param name Name of the parameter to be found
  * @return Value of the parameter found
  */
-int parser_get_param_int(const struct swift_params *params, const char *name) {
-
-  char str[PARSER_MAX_LINE_SIZE];
-  int retParam = 0;
-
-  for (int i = 0; i < params->paramCount; i++) {
-    if (!strcmp(name, params->data[i].name)) {
-      /* Check that exactly one number is parsed. */
-      if (sscanf(params->data[i].value, "%d%s", &retParam, str) != 1) {
-        error(
-            "Tried parsing int '%s' but found '%s' with illegal integer "
-            "characters '%s'.",
-            params->data[i].name, params->data[i].value, str);
-      }
-
-      return retParam;
-    }
-  }
-
-  error("Cannot find '%s' in the structure, in file '%s'.", name,
-        params->fileName);
-  return 0;
+int parser_get_param_int(struct swift_params *params, const char *name) {
+  int result = 0;
+  get_param_int(params, name, 1, &result);
+  return result;
 }
 
 /**
@@ -463,29 +586,10 @@ int parser_get_param_int(const struct swift_params *params, const char *name) {
  * @param name Name of the parameter to be found
  * @return Value of the parameter found
  */
-char parser_get_param_char(const struct swift_params *params,
-                           const char *name) {
-
-  char str[PARSER_MAX_LINE_SIZE];
-  char retParam = 0;
-
-  for (int i = 0; i < params->paramCount; i++) {
-    if (!strcmp(name, params->data[i].name)) {
-      /* Check that exactly one number is parsed. */
-      if (sscanf(params->data[i].value, "%c%s", &retParam, str) != 1) {
-        error(
-            "Tried parsing char '%s' but found '%s' with illegal char "
-            "characters '%s'.",
-            params->data[i].name, params->data[i].value, str);
-      }
-
-      return retParam;
-    }
-  }
-
-  error("Cannot find '%s' in the structure, in file '%s'.", name,
-        params->fileName);
-  return 0;
+char parser_get_param_char(struct swift_params *params, const char *name) {
+  char result = 0;
+  get_param_char(params, name, 1, &result);
+  return result;
 }
 
 /**
@@ -495,29 +599,10 @@ char parser_get_param_char(const struct swift_params *params,
  * @param name Name of the parameter to be found
  * @return Value of the parameter found
  */
-float parser_get_param_float(const struct swift_params *params,
-                             const char *name) {
-
-  char str[PARSER_MAX_LINE_SIZE];
-  float retParam = 0.f;
-
-  for (int i = 0; i < params->paramCount; i++) {
-    if (!strcmp(name, params->data[i].name)) {
-      /* Check that exactly one number is parsed. */
-      if (sscanf(params->data[i].value, "%f%s", &retParam, str) != 1) {
-        error(
-            "Tried parsing float '%s' but found '%s' with illegal float "
-            "characters '%s'.",
-            params->data[i].name, params->data[i].value, str);
-      }
-
-      return retParam;
-    }
-  }
-
-  error("Cannot find '%s' in the structure, in file '%s'.", name,
-        params->fileName);
-  return 0.f;
+float parser_get_param_float(struct swift_params *params, const char *name) {
+  float result = 0;
+  get_param_float(params, name, 1, &result);
+  return result;
 }
 
 /**
@@ -527,28 +612,10 @@ float parser_get_param_float(const struct swift_params *params,
  * @param name Name of the parameter to be found
  * @return Value of the parameter found
  */
-double parser_get_param_double(const struct swift_params *params,
-                               const char *name) {
-
-  char str[PARSER_MAX_LINE_SIZE];
-  double retParam = 0.;
-
-  for (int i = 0; i < params->paramCount; i++) {
-    if (!strcmp(name, params->data[i].name)) {
-      /* Check that exactly one number is parsed. */
-      if (sscanf(params->data[i].value, "%lf%s", &retParam, str) != 1) {
-        error(
-            "Tried parsing double '%s' but found '%s' with illegal double "
-            "characters '%s'.",
-            params->data[i].name, params->data[i].value, str);
-      }
-      return retParam;
-    }
-  }
-
-  error("Cannot find '%s' in the structure, in file '%s'.", name,
-        params->fileName);
-  return 0.;
+double parser_get_param_double(struct swift_params *params, const char *name) {
+  double result = 0;
+  get_param_double(params, name, 1, &result);
+  return result;
 }
 
 /**
@@ -558,11 +625,14 @@ double parser_get_param_double(const struct swift_params *params,
  * @param name Name of the parameter to be found
  * @param retParam (return) Value of the parameter found
  */
-void parser_get_param_string(const struct swift_params *params,
-                             const char *name, char *retParam) {
+void parser_get_param_string(struct swift_params *params, const char *name,
+                             char *retParam) {
+
   for (int i = 0; i < params->paramCount; i++) {
     if (!strcmp(name, params->data[i].name)) {
       strcpy(retParam, params->data[i].value);
+      /* this parameter has been used */
+      params->data[i].used = 1;
       return;
     }
   }
@@ -578,140 +648,426 @@ void parser_get_param_string(const struct swift_params *params,
  * @param def Default value of the parameter of not found.
  * @return Value of the parameter found
  */
-int parser_get_opt_param_int(const struct swift_params *params,
-                             const char *name, int def) {
-
-  char str[PARSER_MAX_LINE_SIZE];
-  int retParam = 0;
-
-  for (int i = 0; i < params->paramCount; i++) {
-    if (!strcmp(name, params->data[i].name)) {
-      /* Check that exactly one number is parsed. */
-      if (sscanf(params->data[i].value, "%d%s", &retParam, str) != 1) {
-        error(
-            "Tried parsing int '%s' but found '%s' with illegal integer "
-            "characters '%s'.",
-            params->data[i].name, params->data[i].value, str);
-      }
+int parser_get_opt_param_int(struct swift_params *params, const char *name,
+                             int def) {
+  int result = 0;
+  if (get_param_int(params, name, 0, &result)) return result;
+  save_param_int(params, name, def);
+  return def;
+}
 
-      return retParam;
-    }
-  }
+/**
+ * @brief Retrieve optional char parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param def Default value of the parameter of not found.
+ * @return Value of the parameter found
+ */
+char parser_get_opt_param_char(struct swift_params *params, const char *name,
+                               char def) {
+  char result = 0;
+  if (get_param_char(params, name, 0, &result)) return result;
+  save_param_char(params, name, def);
+  return def;
+}
 
+/**
+ * @brief Retrieve optional float parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param def Default value of the parameter of not found.
+ * @return Value of the parameter found
+ */
+float parser_get_opt_param_float(struct swift_params *params, const char *name,
+                                 float def) {
+  float result = 0;
+  if (get_param_float(params, name, 0, &result)) return result;
+  save_param_float(params, name, def);
   return def;
 }
 
 /**
- * @brief Retrieve optional char parameter from structure.
+ * @brief Retrieve optional double parameter from structure.
  *
  * @param params Structure that holds the parameters
  * @param name Name of the parameter to be found
  * @param def Default value of the parameter of not found.
  * @return Value of the parameter found
  */
-char parser_get_opt_param_char(const struct swift_params *params,
-                               const char *name, char def) {
+double parser_get_opt_param_double(struct swift_params *params,
+                                   const char *name, double def) {
+  double result = 0;
+  if (get_param_double(params, name, 0, &result)) return result;
+  save_param_double(params, name, def);
+  return def;
+}
 
-  char str[PARSER_MAX_LINE_SIZE];
-  char retParam = 0;
+/**
+ * @brief Retrieve string parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param def Default value of the parameter of not found.
+ * @param retParam (return) Value of the parameter found
+ */
+void parser_get_opt_param_string(struct swift_params *params, const char *name,
+                                 char *retParam, const char *def) {
 
   for (int i = 0; i < params->paramCount; i++) {
     if (!strcmp(name, params->data[i].name)) {
-      /* Check that exactly one number is parsed. */
-      if (sscanf(params->data[i].value, "%c%s", &retParam, str) != 1) {
-        error(
-            "Tried parsing char '%s' but found '%s' with illegal char "
-            "characters '%s'.",
-            params->data[i].name, params->data[i].value, str);
-      }
+      strcpy(retParam, params->data[i].value);
 
-      return retParam;
+      /* this parameter has been used */
+      params->data[i].used = 1;
+      return;
     }
   }
+  save_param_string(params, name, def);
+  strcpy(retParam, def);
+}
 
-  return def;
+/* Macro defining functions that get primitive types as simple one-line YAML
+ * arrays, that is SEC: [v1,v2,v3...] format, with the extension that the []
+ * are optional. TYPE is the data type, float etc. FMT a format to parse a
+ * single value, so "%f" for a float and DESC the type description
+ * i.e. "float".
+ */
+#define PARSER_GET_ARRAY(TYPE, FMT, DESC)                             \
+  static int get_param_##TYPE##_array(struct swift_params *params,    \
+                                      const char *name, int required, \
+                                      int nval, TYPE *values) {       \
+    char str[PARSER_MAX_LINE_SIZE];                                   \
+    char cpy[PARSER_MAX_LINE_SIZE];                                   \
+                                                                      \
+    for (int i = 0; i < params->paramCount; i++) {                    \
+      if (!strcmp(name, params->data[i].name)) {                      \
+        char *cp = cpy;                                               \
+        strcpy(cp, params->data[i].value);                            \
+        cp = trim_both(cp);                                           \
+                                                                      \
+        /* Strip off [], if present. */                               \
+        if (cp[0] == '[') cp++;                                       \
+        int l = strlen(cp);                                           \
+        if (cp[l - 1] == ']') cp[l - 1] = '\0';                       \
+        cp = trim_both(cp);                                           \
+                                                                      \
+        /* Format that captures spaces and trailing junk. */          \
+        char fmt[20];                                                 \
+        sprintf(fmt, " %s%%s ", FMT);                                 \
+                                                                      \
+        /* Parse out values which should now be "v, v, v" with        \
+         * internal     whitespace variations. */                     \
+        char *p = strtok(cp, ",");                                    \
+        for (int k = 0; k < nval; k++) {                              \
+          if (p != NULL) {                                            \
+            if (sscanf(p, fmt, &values[k], str) != 1) {               \
+              error("Tried parsing " DESC                             \
+                    " '%s' but found '%s' with "                      \
+                    "illegal " DESC " characters '%s'.",              \
+                    name, p, str);                                    \
+            }                                                         \
+          } else {                                                    \
+            error(                                                    \
+                "Array '%s' with value '%s' has too few values, "     \
+                "expected %d",                                        \
+                name, params->data[i].value, nval);                   \
+          }                                                           \
+          if (k < nval - 1) p = strtok(NULL, ",");                    \
+        }                                                             \
+        params->data[i].used = 1;                                     \
+        return 1;                                                     \
+      }                                                               \
+    }                                                                 \
+    if (required)                                                     \
+      error("Cannot find '%s' in the structure, in file '%s'.", name, \
+            params->fileName);                                        \
+    return 0;                                                         \
+  }
+
+// Set values of a default parameter so they will be saved correctly.
+#define PARSER_SAVE_ARRAY(TYPE, FMT)                                           \
+  static int save_param_##TYPE##_array(                                        \
+      struct swift_params *params, const char *name, int nval, TYPE *values) { \
+    /* Save values against the parameter. */                                   \
+    char str[PARSER_MAX_LINE_SIZE];                                            \
+    int k = sprintf(str, "%s: [", name);                                       \
+    for (int i = 0; i < nval - 1; i++)                                         \
+      k += sprintf(&str[k], FMT ", ", values[i]);                              \
+    sprintf(&str[k], FMT "]", values[nval - 1]);                               \
+    parser_set_param(params, str);                                             \
+    params->data[params->paramCount - 1].used = 1;                             \
+    return 0;                                                                  \
+  }
+
+/* Instantiations. */
+PARSER_GET_ARRAY(char, "%c", "char");
+PARSER_GET_ARRAY(int, "%d", "int");
+PARSER_GET_ARRAY(float, "%f", "float");
+PARSER_GET_ARRAY(double, "%lf", "double");
+PARSER_SAVE_ARRAY(char, "%c");
+PARSER_SAVE_ARRAY(int, "%d");
+PARSER_SAVE_ARRAY(float, "%g");
+PARSER_SAVE_ARRAY(double, "%g");
+
+/**
+ * @brief Retrieve char array parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param nval number of values expected.
+ * @param values Values of the parameter found, of size at least nvals.
+ */
+void parser_get_param_char_array(struct swift_params *params, const char *name,
+                                 int nval, char *values) {
+  get_param_char_array(params, name, 1, nval, values);
 }
 
 /**
- * @brief Retrieve optional float parameter from structure.
+ * @brief Retrieve optional char array parameter from structure.
  *
  * @param params Structure that holds the parameters
  * @param name Name of the parameter to be found
- * @param def Default value of the parameter of not found.
- * @return Value of the parameter found
+ * @param nval number of values expected.
+ * @param values Values of the parameter found, of size at least nvals. If the
+ *               parameter is not found these values will be returned
+ *               unmodified, so should be set to the default values.
+ * @return whether the parameter has been found.
  */
-float parser_get_opt_param_float(const struct swift_params *params,
-                                 const char *name, float def) {
+int parser_get_opt_param_char_array(struct swift_params *params,
+                                    const char *name, int nval, char *values) {
+  if (get_param_char_array(params, name, 0, nval, values) != 1) {
+    save_param_char_array(params, name, nval, values);
+    return 0;
+  }
+  return 1;
+}
 
-  char str[PARSER_MAX_LINE_SIZE];
-  float retParam = 0.f;
+/**
+ * @brief Retrieve int array parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param nval number of values expected.
+ * @param values Values of the parameter found, of size at least nvals.
+ */
+void parser_get_param_int_array(struct swift_params *params, const char *name,
+                                int nval, int *values) {
+  get_param_int_array(params, name, 1, nval, values);
+}
 
-  for (int i = 0; i < params->paramCount; i++) {
-    if (!strcmp(name, params->data[i].name)) {
-      /* Check that exactly one number is parsed. */
-      if (sscanf(params->data[i].value, "%f%s", &retParam, str) != 1) {
-        error(
-            "Tried parsing float '%s' but found '%s' with illegal float "
-            "characters '%s'.",
-            params->data[i].name, params->data[i].value, str);
-      }
+/**
+ * @brief Retrieve optional int array parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param nval number of values expected.
+ * @param values Values of the parameter found, of size at least nvals. If the
+ *               parameter is not found these values will be returned
+ *               unmodified, so should be set to the default values.
+ * @return whether the parameter has been found.
+ */
+int parser_get_opt_param_int_array(struct swift_params *params,
+                                   const char *name, int nval, int *values) {
+  if (get_param_int_array(params, name, 0, nval, values) != 1) {
+    save_param_int_array(params, name, nval, values);
+    return 0;
+  }
+  return 1;
+}
 
-      return retParam;
-    }
+/**
+ * @brief Retrieve float array parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param nval number of values expected.
+ * @param values Values of the parameter found, of size at least nvals.
+ */
+void parser_get_param_float_array(struct swift_params *params, const char *name,
+                                  int nval, float *values) {
+  get_param_float_array(params, name, 1, nval, values);
+}
+
+/**
+ * @brief Retrieve optional float array parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param nval number of values expected.
+ * @param values Values of the parameter found, of size at least nvals. If the
+ *               parameter is not found these values will be returned
+ *               unmodified, so should be set to the default values.
+ * @return whether the parameter has been found.
+ */
+int parser_get_opt_param_float_array(struct swift_params *params,
+                                     const char *name, int nval,
+                                     float *values) {
+  if (get_param_float_array(params, name, 0, nval, values) != 1) {
+    save_param_float_array(params, name, nval, values);
+    return 0;
   }
+  return 1;
+}
 
-  return def;
+/**
+ * @brief Retrieve double array parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param nval number of values expected.
+ * @param values Values of the parameter found, of size at least nvals.
+ */
+void parser_get_param_double_array(struct swift_params *params,
+                                   const char *name, int nval, double *values) {
+  get_param_double_array(params, name, 1, nval, values);
 }
 
 /**
- * @brief Retrieve optional double parameter from structure.
+ * @brief Retrieve optional double array parameter from structure.
  *
  * @param params Structure that holds the parameters
  * @param name Name of the parameter to be found
- * @param def Default value of the parameter of not found.
- * @return Value of the parameter found
+ * @param nval number of values expected.
+ * @param values Values of the parameter found, of size at least nvals. If the
+ *               parameter is not found these values will be returned
+ *               unmodified, so should be set to the default values.
+ * @return whether the parameter has been found.
  */
-double parser_get_opt_param_double(const struct swift_params *params,
-                                   const char *name, double def) {
+int parser_get_opt_param_double_array(struct swift_params *params,
+                                      const char *name, int nval,
+                                      double *values) {
+  if (get_param_double_array(params, name, 0, nval, values) != 1) {
+    save_param_double_array(params, name, nval, values);
+    return 0;
+  }
+  return 1;
+}
 
-  char str[PARSER_MAX_LINE_SIZE];
-  double retParam = 0.;
+/**
+ * @brief Retrieve string array parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param required whether the parameter is required or not.
+ * @param nval number of values located.
+ * @param values pointer to an array of [nval] pointers to the strings.
+ *        Note this must be freed by a call to
+ *        parser_free_param_string_array when no longer required.
+ * @result whether the parameter was found or not. Note if required
+ *        an error will be thrown.
+ */
+static int get_string_array(struct swift_params *params, const char *name,
+                            int required, int *nval, char ***values) {
+
+  char cpy[PARSER_MAX_LINE_SIZE];
+  *nval = 0;
 
   for (int i = 0; i < params->paramCount; i++) {
     if (!strcmp(name, params->data[i].name)) {
-      /* Check that exactly one number is parsed. */
-      if (sscanf(params->data[i].value, "%lf%s", &retParam, str) != 1) {
-        error(
-            "Tried parsing double '%s' but found '%s' with illegal double "
-            "characters '%s'.",
-            params->data[i].name, params->data[i].value, str);
-      }
-      return retParam;
+      char *cp = cpy;
+      strcpy(cp, params->data[i].value);
+      cp = trim_both(cp);
+
+      /* Strip off [], if present. */
+      if (cp[0] == '[') cp++;
+      int l = strlen(cp);
+      if (cp[l - 1] == ']') cp[l - 1] = '\0';
+      cp = trim_both(cp);
+
+      *nval = parse_quoted_strings(cp, values);
+
+      params->data[i].used = 1;
+      return 1;
     }
   }
+  if (required)
+    error("Cannot find '%s' in the structure, in file '%s'.", name,
+          params->fileName);
+  return 0;
+}
 
-  return def;
+/**
+ * @brief Retrieve string array parameter from structure.
+ *
+ * @param params Structure that holds the parameters
+ * @param name Name of the parameter to be found
+ * @param nval number of values located.
+ * @param values pointer to an array of [nval] pointers to the strings.
+ *        Note this must be freed by a call to
+ *        parser_free_param_string_array when no longer required.
+ */
+void parser_get_param_string_array(struct swift_params *params,
+                                   const char *name, int *nval,
+                                   char ***values) {
+  get_string_array(params, name, 1, nval, values);
 }
 
 /**
- * @brief Retrieve string parameter from structure.
+ * @brief Retrieve optional string array parameter from structure.
  *
  * @param params Structure that holds the parameters
  * @param name Name of the parameter to be found
- * @param def Default value of the parameter of not found.
- * @param retParam (return) Value of the parameter found
+ * @param nval number of values located.
+ * @param values pointer to an array of [nval] pointers to the strings.
+ *        Note this must be freed by a call to
+ *        parser_free_param_string_array when no longer required.
+ * @param ndef the number of default values.
+ * @param def the default values as an array of pointers to strings.
+ *        Note copied to values if used.
+ * @result whether the parameter was found or not.
  */
-void parser_get_opt_param_string(const struct swift_params *params,
-                                 const char *name, char *retParam,
-                                 const char *def) {
-  for (int i = 0; i < params->paramCount; i++) {
-    if (!strcmp(name, params->data[i].name)) {
-      strcpy(retParam, params->data[i].value);
-      return;
-    }
+int parser_get_opt_param_string_array(struct swift_params *params,
+                                      const char *name, int *nval,
+                                      char ***values, int ndef,
+                                      const char *def[]) {
+  if (get_string_array(params, name, 0, nval, values) == 1) return 1;
+
+  /* Not found, so save the default values against the parameter. Look for
+   * single quotes in value and use if not found, otherwise use double
+   * quotes. We don't support having both in a string. */
+  char cpy[PARSER_MAX_LINE_SIZE];
+  int k = sprintf(cpy, "%s: [", name);
+  int i = 0;
+  for (i = 0; i < ndef - 1; i++) {
+    if (strchr(def[i], '\'') == 0)
+      k += sprintf(&cpy[k], "'%s', ", def[i]);
+    else
+      k += sprintf(&cpy[k], "\"%s\", ", def[i]);
   }
+  if (strchr(def[i], '\'') == 0)
+    sprintf(&cpy[k], "'%s']", def[i]);
+  else
+    sprintf(&cpy[k], "\"%s\"]", def[i]);
+  parser_set_param(params, cpy);
+
+  /* Now copy to output space. */
+  char **strings;
+  strings = (char **)malloc(ndef * sizeof(char *));
+  for (int j = 0; j < ndef; j++) {
+    strings[j] = (char *)malloc((strlen(def[j]) + 1) * sizeof(char));
+    strcpy(strings[j], def[j]);
+  }
+  *values = strings;
+  *nval = ndef;
 
-  strcpy(retParam, def);
+  params->data[params->paramCount - 1].used = 1;
+  return 0;
+}
+
+/**
+ * @brief Free string array allocated by parser_get_param_string_array.
+ *
+ * @param nval number of strings returned.
+ * @param values pointer to the returned values.
+ */
+void parser_free_param_string_array(int nval, char **values) {
+  for (int i = 0; i < nval; i++) {
+    free(values[i]);
+  }
+  free(values);
+  return;
 }
 
 /**
@@ -727,6 +1083,7 @@ void parser_print_params(const struct swift_params *params) {
   for (int i = 0; i < params->paramCount; i++) {
     printf("Parameter name: %s\n", params->data[i].name);
     printf("Parameter value: %s\n", params->data[i].value);
+    printf("Parameter used: %i\n", params->data[i].used);
   }
 }
 
@@ -736,53 +1093,96 @@ void parser_print_params(const struct swift_params *params) {
  *
  * @param params Structure that holds the parameters
  * @param file_name Name of file to be written
+ * @param write_used Write used fields or unused fields.
  */
 void parser_write_params_to_file(const struct swift_params *params,
-                                 const char *file_name) {
+                                 const char *file_name, int write_used) {
   FILE *file = fopen(file_name, "w");
-  char section[PARSER_MAX_LINE_SIZE] = {0};
   char param_name[PARSER_MAX_LINE_SIZE] = {0};
+  char section[PARSER_MAX_LINE_SIZE] = {0};
   char *token;
 
   /* Start of file identifier in YAML. */
   fprintf(file, "%s\n", PARSER_START_OF_FILE);
 
-  for (int i = 0; i < params->paramCount; i++) {
-    /* Check that the parameter name contains a section name. */
-    if (strchr(params->data[i].name, PARSER_VALUE_CHAR)) {
-      /* Copy the parameter name into a temporary string and find the section
-       * name. */
-      strcpy(param_name, params->data[i].name);
-      token = strtok(param_name, PARSER_VALUE_STRING);
-
-      /* If a new section name is found print it to the file. */
-      if (strcmp(token, section)) {
-        strcpy(section, token);
-        fprintf(file, "\n%s%c\n", section, PARSER_VALUE_CHAR);
+  /* Flags to track which parameters are written. */
+  int *written = (int *)calloc(params->paramCount, sizeof(int));
+  int nwritten = 0;
+
+  /* Loop over all sections. These are not contiguous when storing optional
+   * values. */
+  for (int k = 0; k < params->sectionCount; k++) {
+    int first = 1;
+
+    /* Locate parameters in this section. */
+    for (int i = 0; i < params->paramCount; i++) {
+      if (!written[i] && ((write_used && params->data[i].used) ||
+                          (!write_used && !params->data[i].used))) {
+
+        /* Find section part of name, if have one. */
+        if (strchr(params->data[i].name, PARSER_VALUE_CHAR)) {
+          strcpy(param_name, params->data[i].name);
+          token = strtok(param_name, PARSER_VALUE_STRING);
+          strcpy(section, token);
+          strcat(section, PARSER_VALUE_STRING);
+
+          /* If in our section name print it to the file. */
+          if (strcmp(section, params->section[k].name) == 0) {
+            if (first) {
+              fprintf(file, "\n%s\n", section);
+              first = 0;
+            }
+
+            /* Remove white space from parameter name and write it to the
+             * file. */
+            token = trim_both(strtok(NULL, "#\n"));
+            fprintf(file, "  %s%c %s\n", token, PARSER_VALUE_CHAR,
+                    params->data[i].value);
+            written[i] = 1;
+            nwritten++;
+          }
+        }
       }
+    }
+  }
 
-      /* Remove white space from parameter name and write it to the file. */
-      token = strtok(NULL, " #\n");
-
-      fprintf(file, "  %s%c %s\n", token, PARSER_VALUE_CHAR,
-              params->data[i].value);
-    } else {
-      fprintf(file, "\n%s%c %s\n", params->data[i].name, PARSER_VALUE_CHAR,
-              params->data[i].value);
+  /* Write out any parameters outside of sections. */
+  if (nwritten < params->paramCount) {
+    for (int i = 0; i < params->paramCount; i++) {
+      if (!written[i] && ((write_used && params->data[i].used) ||
+                          (!write_used && !params->data[i].used))) {
+        fprintf(file, "\n%s%c %s\n", params->data[i].name, PARSER_VALUE_CHAR,
+                params->data[i].value);
+      }
     }
   }
 
   /* End of file identifier in YAML. */
-  fprintf(file, PARSER_END_OF_FILE);
+  fprintf(file, "%s\n", PARSER_END_OF_FILE);
 
+  free(written);
   fclose(file);
 }
 
 #if defined(HAVE_HDF5)
-void parser_write_params_to_hdf5(const struct swift_params *params, hid_t grp) {
 
-  for (int i = 0; i < params->paramCount; i++)
+/**
+ * @brief Write the contents of the parameter structure to a hdf5 file
+ *
+ * @param params Structure that holds the parameters
+ * @param grp HDF5 group
+ * @param write_used Write used fields or unused fields.
+ */
+void parser_write_params_to_hdf5(const struct swift_params *params, hid_t grp,
+                                 int write_used) {
+
+  for (int i = 0; i < params->paramCount; i++) {
+    if (write_used && !params->data[i].used)
+      continue;
+    else if (!write_used && params->data[i].used)
+      continue;
     io_write_attribute_s(grp, params->data[i].name, params->data[i].value);
+  }
 }
 #endif
 
diff --git a/src/parser.h b/src/parser.h
index 58f4a53ea2114dbed29f49e2b5ccca69239b45e3..b4a8dd3d32310601f86db7af46d1bb6244c96f0a 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -39,6 +39,7 @@
 struct parameter {
   char name[PARSER_MAX_LINE_SIZE];
   char value[PARSER_MAX_LINE_SIZE];
+  int used;
 };
 
 struct section {
@@ -55,35 +56,60 @@ struct swift_params {
 };
 
 /* Public API. */
+void parser_init(const char *file_name, struct swift_params *params);
 void parser_read_file(const char *file_name, struct swift_params *params);
 void parser_print_params(const struct swift_params *params);
 void parser_write_params_to_file(const struct swift_params *params,
-                                 const char *file_name);
+                                 const char *file_name, int write_all);
 void parser_set_param(struct swift_params *params, const char *desc);
 
-char parser_get_param_char(const struct swift_params *params, const char *name);
-int parser_get_param_int(const struct swift_params *params, const char *name);
-float parser_get_param_float(const struct swift_params *params,
-                             const char *name);
-double parser_get_param_double(const struct swift_params *params,
-                               const char *name);
-void parser_get_param_string(const struct swift_params *params,
-                             const char *name, char *retParam);
+char parser_get_param_char(struct swift_params *params, const char *name);
+int parser_get_param_int(struct swift_params *params, const char *name);
+float parser_get_param_float(struct swift_params *params, const char *name);
+double parser_get_param_double(struct swift_params *params, const char *name);
+void parser_get_param_string(struct swift_params *params, const char *name,
+                             char *retParam);
 
-char parser_get_opt_param_char(const struct swift_params *params,
-                               const char *name, char def);
-int parser_get_opt_param_int(const struct swift_params *params,
-                             const char *name, int def);
-float parser_get_opt_param_float(const struct swift_params *params,
-                                 const char *name, float def);
-double parser_get_opt_param_double(const struct swift_params *params,
+char parser_get_opt_param_char(struct swift_params *params, const char *name,
+                               char def);
+int parser_get_opt_param_int(struct swift_params *params, const char *name,
+                             int def);
+float parser_get_opt_param_float(struct swift_params *params, const char *name,
+                                 float def);
+double parser_get_opt_param_double(struct swift_params *params,
                                    const char *name, double def);
-void parser_get_opt_param_string(const struct swift_params *params,
-                                 const char *name, char *retParam,
-                                 const char *def);
+void parser_get_opt_param_string(struct swift_params *params, const char *name,
+                                 char *retParam, const char *def);
+void parser_get_param_char_array(struct swift_params *params, const char *name,
+                                 int nval, char *values);
+void parser_get_param_int_array(struct swift_params *params, const char *name,
+                                int nval, int *values);
+void parser_get_param_float_array(struct swift_params *params, const char *name,
+                                  int nval, float *values);
+void parser_get_param_double_array(struct swift_params *params,
+                                   const char *name, int nval, double *values);
+
+int parser_get_opt_param_char_array(struct swift_params *params,
+                                    const char *name, int nval, char *values);
+int parser_get_opt_param_int_array(struct swift_params *params,
+                                   const char *name, int nval, int *values);
+int parser_get_opt_param_float_array(struct swift_params *params,
+                                     const char *name, int nval, float *values);
+int parser_get_opt_param_double_array(struct swift_params *params,
+                                      const char *name, int nval,
+                                      double *values);
+
+void parser_get_param_string_array(struct swift_params *params,
+                                   const char *name, int *nval, char ***values);
+int parser_get_opt_param_string_array(struct swift_params *params,
+                                      const char *name, int *nval,
+                                      char ***values, int ndef,
+                                      const char *def[]);
+void parser_free_param_string_array(int nval, char **values);
 
 #if defined(HAVE_HDF5)
-void parser_write_params_to_hdf5(const struct swift_params *params, hid_t grp);
+void parser_write_params_to_hdf5(const struct swift_params *params, hid_t grp,
+                                 int write_all);
 #endif
 
 /* Dump/restore. */
diff --git a/src/part.c b/src/part.c
index 1b696a8cbc135fd2c128b5ad705a0e6e24a2d5c8..050e10e9cdd0ab56adcd34ba3e6f2d35c274f14a 100644
--- a/src/part.c
+++ b/src/part.c
@@ -259,7 +259,7 @@ MPI_Datatype multipole_mpi_type;
 /**
  * @brief Registers MPI particle types.
  */
-void part_create_mpi_types() {
+void part_create_mpi_types(void) {
 
   /* This is not the recommended way of doing this.
      One should define the structure field by field
diff --git a/src/part.h b/src/part.h
index 3e9ab5180e7cbfb4622b4a05d396a7f785f08b0b..03ec331cb17b95b0133be568d6e857a44d1eaf73 100644
--- a/src/part.h
+++ b/src/part.h
@@ -51,17 +51,27 @@
 #elif defined(HOPKINS_PE_SPH)
 #include "./hydro/PressureEntropy/hydro_part.h"
 #define hydro_need_extra_init_loop 1
+#elif defined(HOPKINS_PU_SPH)
+#include "./hydro/PressureEnergy/hydro_part.h"
+#define hydro_need_extra_init_loop 0
 #elif defined(DEFAULT_SPH)
 #include "./hydro/Default/hydro_part.h"
 #define hydro_need_extra_init_loop 0
-#elif defined(GIZMO_SPH)
-#include "./hydro/Gizmo/hydro_part.h"
+#elif defined(GIZMO_MFV_SPH)
+#include "./hydro/GizmoMFV/hydro_part.h"
+#define hydro_need_extra_init_loop 0
+#define EXTRA_HYDRO_LOOP
+#elif defined(GIZMO_MFM_SPH)
+#include "./hydro/GizmoMFM/hydro_part.h"
 #define hydro_need_extra_init_loop 0
 #define EXTRA_HYDRO_LOOP
 #elif defined(SHADOWFAX_SPH)
 #include "./hydro/Shadowswift/hydro_part.h"
 #define hydro_need_extra_init_loop 0
 #define EXTRA_HYDRO_LOOP
+#elif defined(MINIMAL_MULTI_MAT_SPH)
+#include "./hydro/MinimalMultiMat/hydro_part.h"
+#define hydro_need_extra_init_loop 0
 #else
 #error "Invalid choice of SPH variant"
 #endif
@@ -94,7 +104,7 @@ extern MPI_Datatype gpart_mpi_type;
 extern MPI_Datatype spart_mpi_type;
 extern MPI_Datatype multipole_mpi_type;
 
-void part_create_mpi_types();
+void part_create_mpi_types(void);
 #endif
 
 #endif /* SWIFT_PART_H */
diff --git a/src/partition.c b/src/partition.c
index 8feae3b7a8dce7c78310a3b35762d31b439c69e3..f7a708a152ea5b07887a2718543c274601ef4cba 100644
--- a/src/partition.c
+++ b/src/partition.c
@@ -561,7 +561,7 @@ static void repart_edge_metis(int partweights, int bothweights, int timebins,
     struct task *t = &tasks[j];
 
     /* Skip un-interesting tasks. */
-    if (t->cost == 0) continue;
+    if (t->cost == 0.f) continue;
 
     /* Get the task weight based on costs. */
     double w = (double)t->cost;
@@ -1041,7 +1041,7 @@ void partition_initial_partition(struct partition *initial_partition,
  */
 void partition_init(struct partition *partition,
                     struct repartition *repartition,
-                    const struct swift_params *params, int nr_nodes) {
+                    struct swift_params *params, int nr_nodes) {
 
 #ifdef WITH_MPI
 
@@ -1095,12 +1095,8 @@ void partition_init(struct partition *partition,
 
   /* In case of grid, read more parameters */
   if (part_type[0] == 'g') {
-    partition->grid[0] = parser_get_opt_param_int(
-        params, "DomainDecomposition:initial_grid_x", partition->grid[0]);
-    partition->grid[1] = parser_get_opt_param_int(
-        params, "DomainDecomposition:initial_grid_y", partition->grid[1]);
-    partition->grid[2] = parser_get_opt_param_int(
-        params, "DomainDecomposition:initial_grid_z", partition->grid[2]);
+    parser_get_opt_param_int_array(params, "DomainDecomposition:initial_grid",
+                                   3, partition->grid);
   }
 
   /* Now let's check what the user wants as a repartition strategy */
diff --git a/src/partition.h b/src/partition.h
index 3ad479c6b1b343106ac736e2d9c77aa9bc93cf60..ec7d670a43537c4717090b857b6e6ba9186b8f1c 100644
--- a/src/partition.h
+++ b/src/partition.h
@@ -76,7 +76,7 @@ int partition_space_to_space(double *oldh, double *oldcdim, int *oldnodeID,
                              struct space *s);
 void partition_init(struct partition *partition,
                     struct repartition *repartition,
-                    const struct swift_params *params, int nr_nodes);
+                    struct swift_params *params, int nr_nodes);
 
 /* Dump/restore. */
 void partition_store_celllist(struct space *s, struct repartition *reparttype);
diff --git a/src/periodic.h b/src/periodic.h
index 7df36ac4c3927363305a7ef9fe3b369f58e7dd85..05cb2fe454feadf8fe75942294443a726fc94d89 100644
--- a/src/periodic.h
+++ b/src/periodic.h
@@ -49,10 +49,12 @@
  * Similarly for dx < -b.
  *
  */
-__attribute__((always_inline)) INLINE static double nearest(double dx,
-                                                            double box_size) {
-  return dx > 0.5 * box_size ? (dx - box_size)
-                             : ((dx < -0.5 * box_size) ? (dx + box_size) : dx);
+__attribute__((always_inline, const)) INLINE static double nearest(
+    const double dx, const double box_size) {
+
+  return ((dx > 0.5 * box_size)
+              ? (dx - box_size)
+              : ((dx < -0.5 * box_size) ? (dx + box_size) : dx));
 }
 
 /**
@@ -65,11 +67,12 @@ __attribute__((always_inline)) INLINE static double nearest(double dx,
  * Similarly for dx < -b.
  *
  */
-__attribute__((always_inline)) INLINE static float nearestf(float dx,
-                                                            float box_size) {
-  return dx > 0.5f * box_size
-             ? (dx - box_size)
-             : ((dx < -0.5f * box_size) ? (dx + box_size) : dx);
+__attribute__((always_inline, const)) INLINE static float nearestf(
+    const float dx, const float box_size) {
+
+  return ((dx > 0.5f * box_size)
+              ? (dx - box_size)
+              : ((dx < -0.5f * box_size) ? (dx + box_size) : dx));
 }
 
 #endif /* SWIFT_PERIODIC_H */
diff --git a/src/physical_constants.c b/src/physical_constants.c
index b1dbeaeecfbf2e056a68b7866766bb07efb5efba..3936d07f4207263a4c391715ab0a8dd9ded6fa6d 100644
--- a/src/physical_constants.c
+++ b/src/physical_constants.c
@@ -38,13 +38,12 @@
  * @param params The parsed parameter file.
  * @param internal_const The physical constants to initialize.
  */
-void phys_const_init(const struct unit_system *us,
-                     const struct swift_params *params,
+void phys_const_init(const struct unit_system *us, struct swift_params *params,
                      struct phys_const *internal_const) {
 
   /* Units are declared as {U_M, U_L, U_t, U_I, U_T} */
 
-  const float dimension_G[5] = {-1, 3, -2, 0, 0};
+  const float dimension_G[5] = {-1, 3, -2, 0, 0}; /* [g^-1 cm^3 s^-2] */
   internal_const->const_newton_G =
       const_newton_G_cgs / units_general_cgs_conversion_factor(us, dimension_G);
 
@@ -52,44 +51,49 @@ void phys_const_init(const struct unit_system *us,
   internal_const->const_newton_G = parser_get_opt_param_double(
       params, "PhysicalConstants:G", internal_const->const_newton_G);
 
-  const float dimension_c[5] = {0, 1, -1, 0, 0};
+  const float dimension_c[5] = {0, 1, -1, 0, 0}; /* [cm s^-1] */
   internal_const->const_speed_light_c =
       const_speed_light_c_cgs /
       units_general_cgs_conversion_factor(us, dimension_c);
 
-  const float dimension_h[5] = {1, -2, -1, 0, 0};
+  const float dimension_h[5] = {1, 2, -1, 0, 0}; /* [g cm^2 s^-1] */
   internal_const->const_planck_h =
       const_planck_h_cgs / units_general_cgs_conversion_factor(us, dimension_h);
   internal_const->const_planck_hbar =
       const_planck_hbar_cgs /
       units_general_cgs_conversion_factor(us, dimension_h);
 
-  const float dimension_k[5] = {1, 2, -2, 0, -1};
+  const float dimension_k[5] = {1, 2, -2, 0, -1}; /* [g cm^2 s^-2 K^-1] */
   internal_const->const_boltzmann_k =
       const_boltzmann_k_cgs /
       units_general_cgs_conversion_factor(us, dimension_k);
 
-  const float dimension_Na[5] = {0, 0, 0, 0, 0};
+  const float dimension_Na[5] = {0, 0, 0, 0, 0}; /* [ - ] */
   internal_const->const_avogadro_number =
       const_avogadro_number_cgs /
       units_general_cgs_conversion_factor(us, dimension_Na);
 
-  const float dimension_thomson[5] = {0, 2, 0, 0, 0};
+  const float dimension_thomson[5] = {0, 2, 0, 0, 0}; /* [cm^2] */
   internal_const->const_thomson_cross_section =
       const_thomson_cross_section_cgs /
       units_general_cgs_conversion_factor(us, dimension_thomson);
 
-  const float dimension_ev[5] = {1, 2, -2, 0, 0};
+  const float dimension_stefan[5] = {1, 0, -3, 0, -4}; /* [g s^-3 K^-4] */
+  internal_const->const_stefan_boltzmann =
+      const_stefan_boltzmann_cgs /
+      units_general_cgs_conversion_factor(us, dimension_stefan);
+
+  const float dimension_ev[5] = {1, 2, -2, 0, 0}; /* [g cm^2 s^-2] */
   internal_const->const_electron_volt =
       const_electron_volt_cgs /
       units_general_cgs_conversion_factor(us, dimension_ev);
 
-  const float dimension_charge[5] = {0, 0, -1, 1, 0};
+  const float dimension_charge[5] = {0, 0, 1, 1, 0}; /* [A s] */
   internal_const->const_electron_charge =
       const_electron_charge_cgs /
       units_general_cgs_conversion_factor(us, dimension_charge);
 
-  const float dimension_mass[5] = {1, 0, 0, 0, 0};
+  const float dimension_mass[5] = {1, 0, 0, 0, 0}; /* [g] */
   internal_const->const_electron_mass =
       const_electron_mass_cgs /
       units_general_cgs_conversion_factor(us, dimension_mass);
@@ -103,11 +107,11 @@ void phys_const_init(const struct unit_system *us,
       const_earth_mass_cgs /
       units_general_cgs_conversion_factor(us, dimension_mass);
 
-  const float dimension_time[5] = {0, 0, 1, 0, 0};
+  const float dimension_time[5] = {0, 0, 1, 0, 0}; /* [s] */
   internal_const->const_year =
       const_year_cgs / units_general_cgs_conversion_factor(us, dimension_time);
 
-  const float dimension_length[5] = {0, 1, 0, 0, 0};
+  const float dimension_length[5] = {0, 1, 0, 0, 0}; /* [cm] */
   internal_const->const_astronomical_unit =
       const_astronomical_unit_cgs /
       units_general_cgs_conversion_factor(us, dimension_length);
@@ -117,6 +121,11 @@ void phys_const_init(const struct unit_system *us,
   internal_const->const_light_year =
       const_light_year_cgs /
       units_general_cgs_conversion_factor(us, dimension_length);
+
+  const float dimension_temperature[5] = {0, 0, 0, 0, 1}; /* [K] */
+  internal_const->const_T_CMB_0 =
+      const_T_CMB_0_cgs /
+      units_general_cgs_conversion_factor(us, dimension_temperature);
 }
 
 /**
diff --git a/src/physical_constants.h b/src/physical_constants.h
index b0f929632ba8a55a57376975597e444a8344e4fc..16628bfd6894699608e167d4b309fa5636209219 100644
--- a/src/physical_constants.h
+++ b/src/physical_constants.h
@@ -58,6 +58,9 @@ struct phys_const {
   /*! Thomson cross-section */
   double const_thomson_cross_section;
 
+  /*! Stefan-Boltzmann constant */
+  double const_stefan_boltzmann;
+
   /*! Charge of the electron  */
   double const_electron_charge;
 
@@ -87,10 +90,12 @@ struct phys_const {
 
   /*! Mass of the Earth */
   double const_earth_mass;
+
+  /*! Temperature of the CMB at present day */
+  double const_T_CMB_0;
 };
 
-void phys_const_init(const struct unit_system* us,
-                     const struct swift_params* params,
+void phys_const_init(const struct unit_system* us, struct swift_params* params,
                      struct phys_const* internal_const);
 
 void phys_const_print(const struct phys_const* internal_const);
diff --git a/src/physical_constants_cgs.h b/src/physical_constants_cgs.h
index 1d44dae491961b60a596ec85b5ca589411516079..3eeb664ab095fddf1d3451ddd3451a28fc85bd9b 100644
--- a/src/physical_constants_cgs.h
+++ b/src/physical_constants_cgs.h
@@ -29,6 +29,9 @@
  * where all the constants are defined in the system of units specified
  * in the parameter file.
  *
+ * Of special interest if the value of G which is 0.03% different from the
+ * default value adopted by Gadget-2 (G = 6.672e-8).
+ *
  * All values are taken from C. Patrignani et al. (Particle Data Group), Chin.
  * Phys. C, 40, 100001 (2016) and 2017 update.
  * http://pdg.lbl.gov/2017/reviews/rpp2017-rev-phys-constants.pdf
@@ -41,10 +44,10 @@ const double const_newton_G_cgs = 6.67408e-8;
 /*! Speed of light in vacuum [cm s^-1] */
 const double const_speed_light_c_cgs = 2.99792458e10;
 
-/*! Planck's constant [g cm^-2 s^-1] */
+/*! Planck's constant [g cm^2 s^-1] */
 const double const_planck_h_cgs = 6.626070040e-27;
 
-/*! Planck's reduced constant [g cm^-2 s^-1] */
+/*! Planck's reduced constant [g cm^2 s^-1] */
 const double const_planck_hbar_cgs = 1.054571800e-27;
 
 /*! Boltzmann's constant [g cm^2 s^-2 K^-1] */
@@ -56,7 +59,10 @@ const double const_avogadro_number_cgs = 6.022140857e23;
 /*! Thomson cross-section [cm^2] */
 const double const_thomson_cross_section_cgs = 6.6524587158e-25;
 
-/*! Elementary charge [A s^-1] */
+/*! Stefan-Boltzmann constant [g s^-3 K^-4] */
+const double const_stefan_boltzmann_cgs = 5.670367e-5;
+
+/*! Elementary charge [A s] */
 const double const_electron_charge_cgs = 1.6021766208e-19;
 
 /*! Electron-Volt [g cm^2 s^-2] */
@@ -86,4 +92,7 @@ const double const_solar_mass_cgs = 1.98848e33;
 /*! Mass of the Earth [g] */
 const double const_earth_mass_cgs = 5.9724e27;
 
+/*! Temperature of the CMB at present day [K] */
+const double const_T_CMB_0_cgs = 2.72556;
+
 #endif /* SWIFT_PHYSICAL_CONSTANTS_CGS_H */
diff --git a/src/potential.c b/src/potential.c
index 1fda6fc8752ff626a5262d7824ea68fd3bc16d46..a313598dae36569f8de9bf15078719886805a2a3 100644
--- a/src/potential.c
+++ b/src/potential.c
@@ -35,7 +35,7 @@
  * @param s The #space we run in.
  * @param potential The external potential properties to initialize
  */
-void potential_init(const struct swift_params* parameter_file,
+void potential_init(struct swift_params* parameter_file,
                     const struct phys_const* phys_const,
                     const struct unit_system* us, const struct space* s,
                     struct external_potential* potential) {
diff --git a/src/potential.h b/src/potential.h
index 680d4e235fdf7a7666901f34a82f62feda4ae9bb..814b83c69180631db21e392704c0279808a6f03e 100644
--- a/src/potential.h
+++ b/src/potential.h
@@ -47,7 +47,7 @@
 #endif
 
 /* Now, some generic functions, defined in the source file */
-void potential_init(const struct swift_params* parameter_file,
+void potential_init(struct swift_params* parameter_file,
                     const struct phys_const* phys_const,
                     const struct unit_system* us, const struct space* s,
                     struct external_potential* potential);
diff --git a/src/potential/disc_patch/potential.h b/src/potential/disc_patch/potential.h
index ab229d009c692db727e8f2341c3c49813f74f2b8..40c747314994cfdc4a38679d747e3351e4fbd4d1 100644
--- a/src/potential/disc_patch/potential.h
+++ b/src/potential/disc_patch/potential.h
@@ -269,9 +269,9 @@ external_gravity_get_potential_energy(
  * @param potential The external potential properties to initialize
  */
 static INLINE void potential_init_backend(
-    const struct swift_params* parameter_file,
-    const struct phys_const* phys_const, const struct unit_system* us,
-    const struct space* s, struct external_potential* potential) {
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
 
   potential->surface_density = parser_get_param_double(
       parameter_file, "DiscPatchPotential:surface_density");
diff --git a/src/potential/isothermal/potential.h b/src/potential/isothermal/potential.h
index c974618d7b581884f871863bb83200b8cecee7a5..b5f8d7c39738bfe1895c73e6e59ae1279c0f74fa 100644
--- a/src/potential/isothermal/potential.h
+++ b/src/potential/isothermal/potential.h
@@ -42,7 +42,7 @@
 struct external_potential {
 
   /*! Position of the centre of potential */
-  double x, y, z;
+  double x[3];
 
   /*! Rotation velocity */
   double vrot;
@@ -73,9 +73,9 @@ __attribute__((always_inline)) INLINE static float external_gravity_timestep(
     const struct phys_const* restrict phys_const,
     const struct gpart* restrict g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
 
   const float r2_plus_epsilon2_inv =
       1.f / (dx * dx + dy * dy + dz * dz + potential->epsilon2);
@@ -115,9 +115,9 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
     double time, const struct external_potential* potential,
     const struct phys_const* const phys_const, struct gpart* g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
   const float r2_plus_epsilon2_inv =
       1.f / (dx * dx + dy * dy + dz * dz + potential->epsilon2);
 
@@ -144,9 +144,9 @@ external_gravity_get_potential_energy(
     double time, const struct external_potential* potential,
     const struct phys_const* const phys_const, const struct gpart* g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
 
   return -0.5f * potential->vrot * potential->vrot *
          logf(dx * dx + dy * dy + dz * dz + potential->epsilon2);
@@ -162,19 +162,16 @@ external_gravity_get_potential_energy(
  * @param potential The external potential properties to initialize
  */
 static INLINE void potential_init_backend(
-    const struct swift_params* parameter_file,
-    const struct phys_const* phys_const, const struct unit_system* us,
-    const struct space* s, struct external_potential* potential) {
-
-  potential->x =
-      s->dim[0] / 2. +
-      parser_get_param_double(parameter_file, "IsothermalPotential:position_x");
-  potential->y =
-      s->dim[1] / 2. +
-      parser_get_param_double(parameter_file, "IsothermalPotential:position_y");
-  potential->z =
-      s->dim[2] / 2. +
-      parser_get_param_double(parameter_file, "IsothermalPotential:position_z");
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
+
+  parser_get_param_double_array(parameter_file, "IsothermalPotential:position",
+                                3, potential->x);
+  potential->x[0] += s->dim[0] / 2.;
+  potential->x[1] += s->dim[1] / 2.;
+  potential->x[2] += s->dim[2] / 2.;
+
   potential->vrot =
       parser_get_param_double(parameter_file, "IsothermalPotential:vrot");
   potential->timestep_mult = parser_get_param_float(
@@ -198,7 +195,7 @@ static INLINE void potential_print_backend(
       "External potential is 'Isothermal' with properties are (x,y,z) = (%e, "
       "%e, %e), vrot = %e "
       "timestep multiplier = %e, epsilon = %e",
-      potential->x, potential->y, potential->z, potential->vrot,
+      potential->x[0], potential->x[1], potential->x[2], potential->vrot,
       potential->timestep_mult, sqrtf(potential->epsilon2));
 }
 
diff --git a/src/potential/none/potential.h b/src/potential/none/potential.h
index a8550cad702891ff211539c95c42eca57418c464..2303f2531d059660a063a92be999405f05e9e6aa 100644
--- a/src/potential/none/potential.h
+++ b/src/potential/none/potential.h
@@ -98,9 +98,9 @@ external_gravity_get_potential_energy(
  * @param potential The external potential properties to initialize
  */
 static INLINE void potential_init_backend(
-    const struct swift_params* parameter_file,
-    const struct phys_const* phys_const, const struct unit_system* us,
-    const struct space* s, struct external_potential* potential) {}
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {}
 
 /**
  * @brief Prints the properties of the external potential to stdout.
diff --git a/src/potential/point_mass/potential.h b/src/potential/point_mass/potential.h
index adea9d912056fd07e134fb98a7603030e897ec7a..f9d56a1ff165f2331c91ea828b5ffe0e0db76c2f 100644
--- a/src/potential/point_mass/potential.h
+++ b/src/potential/point_mass/potential.h
@@ -41,7 +41,7 @@
 struct external_potential {
 
   /*! Position of the point mass */
-  double x, y, z;
+  double x[3];
 
   /*! Mass */
   double mass;
@@ -66,15 +66,15 @@ __attribute__((always_inline)) INLINE static float external_gravity_timestep(
     const struct gpart* restrict g) {
 
   const float G_newton = phys_const->const_newton_G;
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
   const float rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz);
   const float rinv2 = rinv * rinv;
   const float rinv3 = rinv2 * rinv;
-  const float drdv = (g->x[0] - potential->x) * (g->v_full[0]) +
-                     (g->x[1] - potential->y) * (g->v_full[1]) +
-                     (g->x[2] - potential->z) * (g->v_full[2]);
+  const float drdv = (g->x[0] - potential->x[0]) * (g->v_full[0]) +
+                     (g->x[1] - potential->x[1]) * (g->v_full[1]) +
+                     (g->x[2] - potential->x[2]) * (g->v_full[2]);
   const float dota_x = G_newton * potential->mass * rinv3 *
                        (-g->v_full[0] + 3.f * rinv2 * drdv * dx);
   const float dota_y = G_newton * potential->mass * rinv3 *
@@ -109,9 +109,9 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
     double time, const struct external_potential* restrict potential,
     const struct phys_const* restrict phys_const, struct gpart* restrict g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
   const float rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz);
   const float rinv3 = rinv * rinv * rinv;
 
@@ -134,9 +134,9 @@ external_gravity_get_potential_energy(
     double time, const struct external_potential* potential,
     const struct phys_const* const phys_const, const struct gpart* g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
   const float rinv = 1. / sqrtf(dx * dx + dy * dy + dz * dz);
   return -phys_const->const_newton_G * potential->mass * rinv;
 }
@@ -152,16 +152,12 @@ external_gravity_get_potential_energy(
  * @param potential The external potential properties to initialize
  */
 static INLINE void potential_init_backend(
-    const struct swift_params* parameter_file,
-    const struct phys_const* phys_const, const struct unit_system* us,
-    const struct space* s, struct external_potential* potential) {
-
-  potential->x =
-      parser_get_param_double(parameter_file, "PointMassPotential:position_x");
-  potential->y =
-      parser_get_param_double(parameter_file, "PointMassPotential:position_y");
-  potential->z =
-      parser_get_param_double(parameter_file, "PointMassPotential:position_z");
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
+
+  parser_get_param_double_array(parameter_file, "PointMassPotential:position",
+                                3, potential->x);
   potential->mass =
       parser_get_param_double(parameter_file, "PointMassPotential:mass");
   potential->timestep_mult = parser_get_param_float(
@@ -179,7 +175,7 @@ static INLINE void potential_print_backend(
   message(
       "External potential is 'Point mass' with properties (x,y,z) = (%e, %e, "
       "%e), M = %e timestep multiplier = %e.",
-      potential->x, potential->y, potential->z, potential->mass,
+      potential->x[0], potential->x[1], potential->x[2], potential->mass,
       potential->timestep_mult);
 }
 
diff --git a/src/potential/point_mass_ring/potential.h b/src/potential/point_mass_ring/potential.h
index ebf047ea7c1f946536300f976713893e66295c59..b384c3df6771c9f3342f5aed6514e75c77bf2c17 100644
--- a/src/potential/point_mass_ring/potential.h
+++ b/src/potential/point_mass_ring/potential.h
@@ -41,7 +41,7 @@
 struct external_potential {
 
   /*! Position of the point mass */
-  double x, y, z;
+  double x[3];
 
   /*! Mass */
   double mass;
@@ -68,16 +68,16 @@ __attribute__((always_inline)) INLINE static float external_gravity_timestep(
     const struct gpart* restrict g) {
 
   const float G_newton = phys_const->const_newton_G;
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
   const float r = sqrtf(dx * dx + dy * dy + dz * dz);
   const float rinv = 1.f / r;
   const float rinv2 = rinv * rinv;
   const float rinv3 = rinv2 * rinv;
-  const float drdv = (g->x[0] - potential->x) * (g->v_full[0]) +
-                     (g->x[1] - potential->y) * (g->v_full[1]) +
-                     (g->x[2] - potential->z) * (g->v_full[2]);
+  const float drdv = (g->x[0] - potential->x[0]) * (g->v_full[0]) +
+                     (g->x[1] - potential->x[1]) * (g->v_full[1]) +
+                     (g->x[2] - potential->x[2]) * (g->v_full[2]);
   float factor;
 
   if (r < 0.175) {
@@ -129,9 +129,9 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
     double time, const struct external_potential* restrict potential,
     const struct phys_const* restrict phys_const, struct gpart* restrict g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
   const float r = sqrtf(dx * dx + dy * dy + dz * dz);
   const float rinv = 1.f / r;
   const float rinv2 = rinv * rinv;
@@ -174,9 +174,9 @@ external_gravity_get_potential_energy(
     double time, const struct external_potential* potential,
     const struct phys_const* const phys_const, const struct gpart* g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
   const float rinv = 1. / sqrtf(dx * dx + dy * dy + dz * dz);
   return -phys_const->const_newton_G * potential->mass * rinv;
 }
@@ -192,16 +192,12 @@ external_gravity_get_potential_energy(
  * @param potential The external potential properties to initialize
  */
 static INLINE void potential_init_backend(
-    const struct swift_params* parameter_file,
-    const struct phys_const* phys_const, const struct unit_system* us,
-    const struct space* s, struct external_potential* potential) {
-
-  potential->x =
-      parser_get_param_double(parameter_file, "PointMassPotential:position_x");
-  potential->y =
-      parser_get_param_double(parameter_file, "PointMassPotential:position_y");
-  potential->z =
-      parser_get_param_double(parameter_file, "PointMassPotential:position_z");
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
+
+  parser_get_param_double_array(parameter_file, "PointMassPotential:position",
+                                3, potential->x);
   potential->mass =
       parser_get_param_double(parameter_file, "PointMassPotential:mass");
   potential->timestep_mult = parser_get_param_float(
@@ -219,7 +215,7 @@ static INLINE void potential_print_backend(
   message(
       "External potential is 'Point mass' with properties (x,y,z) = (%e, %e, "
       "%e), M = %e timestep multiplier = %e.",
-      potential->x, potential->y, potential->z, potential->mass,
+      potential->x[0], potential->x[1], potential->x[2], potential->mass,
       potential->timestep_mult);
 }
 
diff --git a/src/potential/point_mass_softened/potential.h b/src/potential/point_mass_softened/potential.h
index 83a79ea3cddbff37fdd70d58d70afcaf46f7bc0e..0e35e7bb9870c7954b47316a3cc30bb68cde5fc4 100644
--- a/src/potential/point_mass_softened/potential.h
+++ b/src/potential/point_mass_softened/potential.h
@@ -50,7 +50,7 @@
 struct external_potential {
 
   /*! Position of the point mass */
-  double x, y, z;
+  double x[3];
 
   /*! Mass */
   double mass;
@@ -77,9 +77,9 @@ __attribute__((always_inline)) INLINE static float external_gravity_timestep(
     const struct phys_const* restrict phys_const,
     const struct gpart* restrict g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
 
   const float softening2 = potential->softening * potential->softening;
   const float r2 = dx * dx + dy * dy + dz * dz;
@@ -133,9 +133,9 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
     double time, const struct external_potential* restrict potential,
     const struct phys_const* restrict phys_const, struct gpart* restrict g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
   const float rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz +
                                  potential->softening * potential->softening);
   const float rinv3 = rinv * rinv * rinv;
@@ -159,9 +159,9 @@ external_gravity_get_potential_energy(
     double time, const struct external_potential* potential,
     const struct phys_const* const phys_const, const struct gpart* g) {
 
-  const float dx = g->x[0] - potential->x;
-  const float dy = g->x[1] - potential->y;
-  const float dz = g->x[2] - potential->z;
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
   const float rinv = 1. / sqrtf(dx * dx + dy * dy + dz * dz +
                                 potential->softening * potential->softening);
 
@@ -179,16 +179,12 @@ external_gravity_get_potential_energy(
  * @param potential The external potential properties to initialize
  */
 static INLINE void potential_init_backend(
-    const struct swift_params* parameter_file,
-    const struct phys_const* phys_const, const struct unit_system* us,
-    const struct space* s, struct external_potential* potential) {
-
-  potential->x =
-      parser_get_param_double(parameter_file, "PointMassPotential:position_x");
-  potential->y =
-      parser_get_param_double(parameter_file, "PointMassPotential:position_y");
-  potential->z =
-      parser_get_param_double(parameter_file, "PointMassPotential:position_z");
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
+
+  parser_get_param_double_array(parameter_file, "PointMassPotential:position",
+                                3, potential->x);
   potential->mass =
       parser_get_param_double(parameter_file, "PointMassPotential:mass");
   potential->timestep_mult = parser_get_param_float(
@@ -208,7 +204,7 @@ static INLINE void potential_print_backend(
   message(
       "External potential is 'Point mass' with properties (x,y,z) = (%e, %e, "
       "%e), M = %e timestep multiplier = %e, softening = %e.",
-      potential->x, potential->y, potential->z, potential->mass,
+      potential->x[0], potential->x[1], potential->x[2], potential->mass,
       potential->timestep_mult, potential->softening);
 }
 
diff --git a/src/potential/sine_wave/potential.h b/src/potential/sine_wave/potential.h
index 1a4ee8aae8238c5db4c99eacb9e96bd967bcc7c4..8a1786baaf9ed0b0683ea16fdefee997ecb4eceb 100644
--- a/src/potential/sine_wave/potential.h
+++ b/src/potential/sine_wave/potential.h
@@ -117,9 +117,9 @@ external_gravity_get_potential_energy(
  * @param potential The external potential properties to initialize
  */
 static INLINE void potential_init_backend(
-    const struct swift_params* parameter_file,
-    const struct phys_const* phys_const, const struct unit_system* us,
-    const struct space* s, struct external_potential* potential) {
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
 
   potential->amplitude =
       parser_get_param_double(parameter_file, "SineWavePotential:amplitude");
diff --git a/src/profiler.c b/src/profiler.c
index 1dd1a41cc336942d17790e96c8f883d65e54a51f..58fd279d312d3c752d65ccaceab803ace66fddac 100644
--- a/src/profiler.c
+++ b/src/profiler.c
@@ -146,8 +146,8 @@ void profiler_write_all_timing_info_headers(const struct engine *e,
 void profiler_write_timing_info(const struct engine *e, ticks time,
                                 FILE *file) {
 
-  fprintf(file, "  %6d %14e %14e %10zu %10zu %10zu %21.3f\n", e->step, e->time,
-          e->time_step, e->updates, e->g_updates, e->s_updates,
+  fprintf(file, "  %6d %14e %14e %10lld %10lld %10lld %21.3f\n", e->step,
+          e->time, e->time_step, e->updates, e->g_updates, e->s_updates,
           clocks_from_ticks(time));
   fflush(file);
 }
diff --git a/src/queue.c b/src/queue.c
index b22313d71d733ae1a6b3419d492356f9d914f61f..3a9919163a4fb9d146c055e58859a175858c17eb 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -259,7 +259,7 @@ struct task *queue_gettask(struct queue *q, const struct task *prev,
     int k = ind;
     if (k < qcount) {
       qtid[k] = qtid[qcount];
-      int w = qtasks[qtid[k]].weight;
+      const float w = qtasks[qtid[k]].weight;
       while (k > 0 && w > qtasks[qtid[(k - 1) / 2]].weight) {
         int temp = q->tid[k];
         q->tid[k] = q->tid[(k - 1) / 2];
diff --git a/src/restart.c b/src/restart.c
index 6344e10e2480134959e18a76a2b6d2244531baec..c412c8477d9f93e7c085e13c9e3fe72cd0cab9df 100644
--- a/src/restart.c
+++ b/src/restart.c
@@ -220,7 +220,10 @@ void restart_read_blocks(void *ptr, size_t size, size_t nblocks, FILE *stream,
             errstr, head.len, nblocks * size);
 
     /* Return label, if required. */
-    if (label != NULL) strncpy(label, head.label, LABLEN);
+    if (label != NULL) {
+      head.label[LABLEN] = '\0';
+      strncpy(label, head.label, LABLEN + 1);
+    }
 
     nread = fread(ptr, size, nblocks, stream);
     if (nread != nblocks)
diff --git a/src/riemann/riemann_exact.h b/src/riemann/riemann_exact.h
index 0c6c1c86273b42f521102e6e91d642d3dff30b27..9a671559c12a9df6d1e11016adc619c6043c83a9 100644
--- a/src/riemann/riemann_exact.h
+++ b/src/riemann/riemann_exact.h
@@ -485,6 +485,87 @@ __attribute__((always_inline)) INLINE static void riemann_solver_solve(
   Whalf[3] += vhalf * n_unit[2];
 }
 
+/**
+ * @brief Solve the Riemann problem between the given left and right state and
+ * return the velocity and pressure in the middle state
+ *
+ * Based on chapter 4 in Toro
+ *
+ * @param WL Left state.
+ * @param vL Left state velocity.
+ * @param WR Right state.
+ * @param vR Right state velocity.
+ * @param vM Middle state velocity.
+ * @param PM Middle state pressure.
+ */
+__attribute__((always_inline)) INLINE static void
+riemann_solver_solve_middle_state(const float* WL, const float vL,
+                                  const float* WR, const float vR, float* vM,
+                                  float* PM) {
+
+  /* sound speeds */
+  float aL, aR;
+  /* variables used for finding pstar */
+  float p, pguess, fp, fpguess;
+
+  /* calculate sound speeds */
+  aL = sqrtf(hydro_gamma * WL[4] / WL[0]);
+  aR = sqrtf(hydro_gamma * WR[4] / WR[0]);
+
+  /* vacuum */
+  /* check vacuum (generation) condition */
+  if (riemann_is_vacuum(WL, WR, vL, vR, aL, aR)) {
+    *vM = 0.0f;
+    *PM = 0.0f;
+    return;
+  }
+
+  /* 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.0f;
+  /* 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;
+  }
+
+  *PM = p;
+  /* calculate the velocity in the intermediate state */
+  *vM =
+      0.5f * (vL + vR) + 0.5f * (riemann_fb(p, WR, aR) - riemann_fb(p, WL, aL));
+}
+
 __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
     const float* Wi, const float* Wj, const float* n_unit, const float* vij,
     float* totflux) {
@@ -543,4 +624,43 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
 #endif
 }
 
+__attribute__((always_inline)) INLINE static void
+riemann_solve_for_middle_state_flux(const float* Wi, const float* Wj,
+                                    const float* n_unit, const float* vij,
+                                    float* totflux) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  riemann_check_input(Wi, Wj, n_unit, vij);
+#endif
+
+  /* vacuum? */
+  if (Wi[0] == 0.0f || Wj[0] == 0.0f) {
+    totflux[0] = 0.0f;
+    totflux[1] = 0.0f;
+    totflux[2] = 0.0f;
+    totflux[3] = 0.0f;
+    totflux[4] = 0.0f;
+    return;
+  }
+
+  const float vL = Wi[1] * n_unit[0] + Wi[2] * n_unit[1] + Wi[3] * n_unit[2];
+  const float vR = Wj[1] * n_unit[0] + Wj[2] * n_unit[1] + Wj[3] * n_unit[2];
+
+  float vM, PM;
+  riemann_solver_solve_middle_state(Wi, vL, Wj, vR, &vM, &PM);
+
+  const float vface =
+      vij[0] * n_unit[0] + vij[1] * n_unit[1] + vij[2] * n_unit[2];
+
+  totflux[0] = 0.0f;
+  totflux[1] = PM * n_unit[0];
+  totflux[2] = PM * n_unit[1];
+  totflux[3] = PM * n_unit[2];
+  totflux[4] = (vM + vface) * PM;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  riemann_check_output(Wi, Wj, n_unit, vij, totflux);
+#endif
+}
+
 #endif /* SWIFT_RIEMANN_EXACT_H */
diff --git a/src/riemann/riemann_hllc.h b/src/riemann/riemann_hllc.h
index 26fcd627fcf11769baf18955af53ed6e948ba513..68a92111d8e075faff5f93ab9c3d8a1ebc496cb0 100644
--- a/src/riemann/riemann_hllc.h
+++ b/src/riemann/riemann_hllc.h
@@ -185,4 +185,77 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
 #endif
 }
 
+__attribute__((always_inline)) INLINE static void
+riemann_solve_for_middle_state_flux(const float *WL, const float *WR,
+                                    const float *n, const float *vij,
+                                    float *totflux) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  riemann_check_input(WL, WR, n, vij);
+#endif
+
+  /* Handle pure vacuum */
+  if (!WL[0] && !WR[0]) {
+    totflux[0] = 0.f;
+    totflux[1] = 0.f;
+    totflux[2] = 0.f;
+    totflux[3] = 0.f;
+    totflux[4] = 0.f;
+    return;
+  }
+
+  /* STEP 0: obtain velocity in interface frame */
+  const float uL = WL[1] * n[0] + WL[2] * n[1] + WL[3] * n[2];
+  const float uR = WR[1] * n[0] + WR[2] * n[1] + WR[3] * n[2];
+  const float aL = sqrtf(hydro_gamma * WL[4] / WL[0]);
+  const float aR = sqrtf(hydro_gamma * WR[4] / WR[0]);
+
+  /* Handle vacuum: vacuum does not require iteration and is always exact */
+  if (riemann_is_vacuum(WL, WR, uL, uR, aL, aR)) {
+    totflux[0] = 0.f;
+    totflux[1] = 0.f;
+    totflux[2] = 0.f;
+    totflux[3] = 0.f;
+    totflux[4] = 0.f;
+    return;
+  }
+
+  /* STEP 1: pressure estimate */
+  const float rhobar = 0.5f * (WL[0] + WR[0]);
+  const float abar = 0.5f * (aL + aR);
+  const float pPVRS = 0.5f * (WL[4] + WR[4]) - 0.5f * (uR - uL) * rhobar * abar;
+  const float pstar = max(0.f, pPVRS);
+
+  /* STEP 2: wave speed estimates
+     all these speeds are along the interface normal, since uL and uR are */
+  float qL = 1.f;
+  if (pstar > WL[4] && WL[4] > 0.f) {
+    qL = sqrtf(1.f +
+               0.5f * hydro_gamma_plus_one * hydro_one_over_gamma *
+                   (pstar / WL[4] - 1.f));
+  }
+  float qR = 1.f;
+  if (pstar > WR[4] && WR[4] > 0.f) {
+    qR = sqrtf(1.f +
+               0.5f * hydro_gamma_plus_one * hydro_one_over_gamma *
+                   (pstar / WR[4] - 1.f));
+  }
+  const float SL = uL - aL * qL;
+  const float SR = uR + aR * qR;
+  const float Sstar =
+      (WR[4] - WL[4] + WL[0] * uL * (SL - uL) - WR[0] * uR * (SR - uR)) /
+      (WL[0] * (SL - uL) - WR[0] * (SR - uR));
+
+  totflux[0] = 0.0f;
+  totflux[1] = pstar * n[0];
+  totflux[2] = pstar * n[1];
+  totflux[3] = pstar * n[2];
+  const float vface = vij[0] * n[0] + vij[1] * n[1] + vij[2] * n[2];
+  totflux[4] = pstar * (Sstar + vface);
+
+#ifdef SWIFT_DEBUG_CHECKS
+  riemann_check_output(WL, WR, n, vij, totflux);
+#endif
+}
+
 #endif /* SWIFT_RIEMANN_HLLC_H */
diff --git a/src/riemann/riemann_trrs.h b/src/riemann/riemann_trrs.h
index 597f6f8edd9690dbdd34a3c9885ae228c2af734c..c19ff70f62a9018f4d121f8b689a0a13c371d86a 100644
--- a/src/riemann/riemann_trrs.h
+++ b/src/riemann/riemann_trrs.h
@@ -219,4 +219,64 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
 #endif
 }
 
+__attribute__((always_inline)) INLINE static void
+riemann_solve_for_middle_state_flux(const float* Wi, const float* Wj,
+                                    const float* n_unit, const float* vij,
+                                    float* totflux) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  riemann_check_input(Wi, Wj, n_unit, vij);
+#endif
+
+  if (Wi[0] == 0.0f || Wj[0] == 0.0f) {
+    totflux[0] = 0.0f;
+    totflux[1] = 0.0f;
+    totflux[2] = 0.0f;
+    totflux[3] = 0.0f;
+    totflux[4] = 0.0f;
+    return;
+  }
+
+  /* calculate the velocities along the interface normal */
+  const float vL = Wi[1] * n_unit[0] + Wi[2] * n_unit[1] + Wi[3] * n_unit[2];
+  const float vR = Wj[1] * n_unit[0] + Wj[2] * n_unit[1] + Wj[3] * n_unit[2];
+
+  /* calculate the sound speeds */
+  const float aL = sqrtf(hydro_gamma * Wi[4] / Wi[0]);
+  const float aR = sqrtf(hydro_gamma * Wj[4] / Wj[0]);
+
+  if (riemann_is_vacuum(Wi, Wj, vL, vR, aL, aR)) {
+    totflux[0] = 0.0f;
+    totflux[1] = 0.0f;
+    totflux[2] = 0.0f;
+    totflux[3] = 0.0f;
+    totflux[4] = 0.0f;
+    return;
+  }
+
+  /* calculate the velocity and pressure in the intermediate state */
+  const float PLR = pow_gamma_minus_one_over_two_gamma(Wi[4] / Wj[4]);
+  const float ustar = (PLR * vL / aL + vR / aR +
+                       hydro_two_over_gamma_minus_one * (PLR - 1.0f)) /
+                      (PLR / aL + 1.0f / aR);
+  const float pstar =
+      0.5f *
+      (Wi[4] * pow_two_gamma_over_gamma_minus_one(
+                   1.0f + hydro_gamma_minus_one_over_two / aL * (vL - ustar)) +
+       Wj[4] * pow_two_gamma_over_gamma_minus_one(
+                   1.0f + hydro_gamma_minus_one_over_two / aR * (ustar - vR)));
+
+  totflux[0] = 0.0f;
+  totflux[1] = pstar * n_unit[0];
+  totflux[2] = pstar * n_unit[1];
+  totflux[3] = pstar * n_unit[2];
+  const float vface =
+      vij[0] * n_unit[0] + vij[1] * n_unit[1] + vij[2] * n_unit[2];
+  totflux[4] = pstar * (ustar + vface);
+
+#ifdef SWIFT_DEBUG_CHECKS
+  riemann_check_output(Wi, Wj, n_unit, vij, totflux);
+#endif
+}
+
 #endif /* SWIFT_RIEMANN_TRRS_H */
diff --git a/src/runner.c b/src/runner.c
index 51801a779481a8ce0500d8c5f3f0b20d7ed79faf..1590e478e95e5c285f535c7d45fa9c43e4566617 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -54,12 +54,12 @@
 #include "hydro_properties.h"
 #include "kick.h"
 #include "minmax.h"
-#include "runner_doiact_fft.h"
 #include "runner_doiact_vec.h"
 #include "scheduler.h"
 #include "sort_part.h"
 #include "sourceterms.h"
 #include "space.h"
+#include "space_getsid.h"
 #include "stars.h"
 #include "task.h"
 #include "timers.h"
@@ -94,7 +94,6 @@
 #undef FUNCTION_TASK_LOOP
 
 /* Import the gravity loop functions. */
-#include "runner_doiact_fft.h"
 #include "runner_doiact_grav.h"
 
 /**
@@ -176,6 +175,41 @@ void runner_do_grav_external(struct runner *r, struct cell *c, int timer) {
   if (timer) TIMER_TOC(timer_dograv_external);
 }
 
+/**
+ * @brief Calculate gravity accelerations from the periodic mesh
+ *
+ * @param r runner task
+ * @param c cell
+ * @param timer 1 if the time is to be recorded.
+ */
+void runner_do_grav_mesh(struct runner *r, struct cell *c, int timer) {
+
+  struct gpart *restrict gparts = c->gparts;
+  const int gcount = c->gcount;
+  const struct engine *e = r->e;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (!e->s->periodic) error("Calling mesh forces in non-periodic mode.");
+#endif
+
+  TIMER_TIC;
+
+  /* Anything to do here? */
+  if (!cell_is_active_gravity(c, e)) return;
+
+  /* Recurse? */
+  if (c->split) {
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL) runner_do_grav_mesh(r, c->progeny[k], 0);
+  } else {
+
+    /* Get the forces from the gravity mesh */
+    pm_mesh_interpolate_forces(e->mesh, e, gparts, gcount);
+  }
+
+  if (timer) TIMER_TOC(timer_dograv_mesh);
+}
+
 /**
  * @brief Calculate change in thermal state of particles induced
  * by radiative cooling and heating.
@@ -605,8 +639,10 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) {
 #ifdef EXTRA_HYDRO_LOOP
 
   struct part *restrict parts = c->parts;
+  struct xpart *restrict xparts = c->xparts;
   const int count = c->count;
   const struct engine *e = r->e;
+  const struct cosmology *cosmo = e->cosmology;
 
   TIMER_TIC;
 
@@ -624,11 +660,23 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) {
 
       /* Get a direct pointer on the part. */
       struct part *restrict p = &parts[i];
+      struct xpart *restrict xp = &xparts[i];
 
       if (part_is_active(p, e)) {
 
-        /* Get ready for a force calculation */
+        /* Finish the gradient calculation */
         hydro_end_gradient(p);
+
+        /* As of here, particle force variables will be set. */
+
+        /* Compute variables required for the force loop */
+        hydro_prepare_force(p, xp, cosmo);
+
+        /* The particle force values are now set.  Do _NOT_
+           try to read any particle density variables! */
+
+        /* Prepare the particle for the force loop over neighbours */
+        hydro_reset_acceleration(p);
       }
     }
   }
@@ -709,9 +757,13 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
         const float h_old_dim = pow_dimension(h_old);
         const float h_old_dim_minus_one = pow_dimension_minus_one(h_old);
         float h_new;
+        int has_no_neighbours = 0;
 
         if (p->density.wcount == 0.f) { /* No neighbours case */
 
+          /* Flag that there were no neighbours */
+          has_no_neighbours = 1;
+
           /* Double h and try again */
           h_new = 2.f * h_old;
         } else {
@@ -728,7 +780,8 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
               p->density.wcount_dh * h_old_dim +
               hydro_dimension * p->density.wcount * h_old_dim_minus_one;
 
-          h_new = h_old - f / f_prime;
+          /* Avoid floating point exception from f_prime = 0 */
+          h_new = h_old - f / (f_prime + FLT_MIN);
 
 #ifdef SWIFT_DEBUG_CHECKS
           if ((f > 0.f && h_new > h_old) || (f < 0.f && h_new < h_old))
@@ -767,13 +820,30 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
             p->h = hydro_h_max;
 
             /* Do some damage control if no neighbours at all were found */
-            if (p->density.wcount == kernel_root * kernel_norm)
+            if (has_no_neighbours) {
               hydro_part_has_no_neighbours(p, xp, cosmo);
+              chemistry_part_has_no_neighbours(p, xp, chemistry, cosmo);
+            }
           }
         }
 
-        /* We now have a particle whose smoothing length has converged */
+/* We now have a particle whose smoothing length has converged */
+
+#ifdef EXTRA_HYDRO_LOOP
+
+        /* As of here, particle gradient variables will be set. */
+        /* The force variables are set in the extra ghost. */
+
+        /* Compute variables required for the gradient loop */
+        hydro_prepare_gradient(p, xp, cosmo);
+
+        /* The particle gradient values are now set.  Do _NOT_
+           try to read any particle density variables! */
 
+        /* Prepare the particle for the gradient loop over neighbours */
+        hydro_reset_gradient(p);
+
+#else
         /* As of here, particle force variables will be set. */
 
         /* Compute variables required for the force loop */
@@ -784,6 +854,8 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
         /* Prepare the particle for the force loop over neighbours */
         hydro_reset_acceleration(p);
+
+#endif /* EXTRA_HYDRO_LOOP */
       }
 
       /* We now need to treat the particles whose smoothing length had not
@@ -841,14 +913,9 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
       }
     }
 
-#ifdef SWIFT_DEBUG_CHECKS
     if (count) {
       error("Smoothing length failed to converge on %i particles.", count);
     }
-#else
-    if (count)
-      error("Smoothing length failed to converge on %i particles.", count);
-#endif
 
     /* Be clean */
     free(pid);
@@ -1583,6 +1650,7 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
 void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
 
   const struct engine *e = r->e;
+  const struct space *s = e->s;
   const struct cosmology *cosmo = e->cosmology;
   const int count = c->count;
   const int gcount = c->gcount;
@@ -1590,7 +1658,12 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
   struct part *restrict parts = c->parts;
   struct gpart *restrict gparts = c->gparts;
   struct spart *restrict sparts = c->sparts;
-  const double const_G = e->physical_constants->const_newton_G;
+  const int periodic = s->periodic;
+  const int with_cosmology = e->policy & engine_policy_cosmology;
+  const float Omega_m = e->cosmology->Omega_m;
+  const float H0 = e->cosmology->H0;
+  const float G_newton = e->physical_constants->const_newton_G;
+  const float rho_crit0 = 3.f * H0 * H0 / (8.f * M_PI * G_newton);
 
   TIMER_TIC;
 
@@ -1625,16 +1698,34 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
       if (gpart_is_active(gp, e)) {
 
         /* Finish the force calculation */
-        gravity_end_force(gp, const_G);
+        gravity_end_force(gp, G_newton);
+
+        /* Apply periodic BC contribution to the potential */
+        if (with_cosmology && periodic) {
+          const float mass = gravity_get_mass(gp);
+          const float mass2 = mass * mass;
+
+          /* This correction term matches the one used in Gadget-2 */
+          /* The numerical constant is taken from Hernquist, Bouchet & Suto 1991
+           */
+          gp->potential -= 2.8372975f * cbrtf(mass2 * Omega_m * rho_crit0);
+        }
 
 #ifdef SWIFT_NO_GRAVITY_BELOW_ID
+
+        /* Get the ID of the gpart */
+        long long id = 0;
+        if (gp->type == swift_type_gas)
+          id = e->s->parts[-gp->id_or_neg_offset].id;
+        else if (gp->type == swift_type_star)
+          id = e->s->sparts[-gp->id_or_neg_offset].id;
+        else if (gp->type == swift_type_black_hole)
+          error("Unexisting type");
+        else
+          id = gp->id_or_neg_offset;
+
         /* Cancel gravity forces of these particles */
-        if ((gp->type == swift_type_dark_matter &&
-             gp->id_or_neg_offset < SWIFT_NO_GRAVITY_BELOW_ID) ||
-            (gp->type == swift_type_gas &&
-             parts[-gp->id_or_neg_offset].id < SWIFT_NO_GRAVITY_BELOW_ID) ||
-            (gp->type == swift_type_star &&
-             sparts[-gp->id_or_neg_offset].id < SWIFT_NO_GRAVITY_BELOW_ID)) {
+        if (id < SWIFT_NO_GRAVITY_BELOW_ID) {
 
           /* Don't move ! */
           gp->a_grav[0] = 0.f;
@@ -1651,14 +1742,27 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
 
           /* Check that this gpart has interacted with all the other
            * particles (via direct or multipoles) in the box */
-          if (gp->num_interacted != e->total_nr_gparts)
+          if (gp->num_interacted != e->total_nr_gparts) {
+
+            /* Get the ID of the gpart */
+            long long my_id = 0;
+            if (gp->type == swift_type_gas)
+              my_id = e->s->parts[-gp->id_or_neg_offset].id;
+            else if (gp->type == swift_type_star)
+              my_id = e->s->sparts[-gp->id_or_neg_offset].id;
+            else if (gp->type == swift_type_black_hole)
+              error("Unexisting type");
+            else
+              my_id = gp->id_or_neg_offset;
+
             error(
                 "g-particle (id=%lld, type=%s) did not interact "
-                "gravitationally "
-                "with all other gparts gp->num_interacted=%lld, "
-                "total_gparts=%lld (local num_gparts=%zd)",
-                gp->id_or_neg_offset, part_type_names[gp->type],
-                gp->num_interacted, e->total_nr_gparts, e->s->nr_gparts);
+                "gravitationally with all other gparts "
+                "gp->num_interacted=%lld, total_gparts=%lld (local "
+                "num_gparts=%zd)",
+                my_id, part_type_names[gp->type], gp->num_interacted,
+                e->total_nr_gparts, e->s->nr_gparts);
+          }
         }
 #endif
       }
@@ -1994,7 +2098,7 @@ void *runner_main(void *data) {
           else if (t->subtype == task_subtype_force)
             runner_doself2_branch_force(r, ci);
           else if (t->subtype == task_subtype_grav)
-            runner_doself_grav(r, ci, 1);
+            runner_doself_recursive_grav(r, ci, 1);
           else if (t->subtype == task_subtype_external_grav)
             runner_do_grav_external(r, ci, 1);
           else
@@ -2011,7 +2115,7 @@ void *runner_main(void *data) {
           else if (t->subtype == task_subtype_force)
             runner_dopair2_branch_force(r, ci, cj);
           else if (t->subtype == task_subtype_grav)
-            runner_dopair_grav(r, ci, cj, 1);
+            runner_dopair_recursive_grav(r, ci, cj, 1);
           else
             error("Unknown/invalid task subtype (%d).", t->subtype);
           break;
@@ -2109,8 +2213,8 @@ void *runner_main(void *data) {
         case task_type_grav_down:
           runner_do_grav_down(r, t->ci, 1);
           break;
-        case task_type_grav_top_level:
-          runner_do_grav_fft(r, 1);
+        case task_type_grav_mesh:
+          runner_do_grav_mesh(r, t->ci, 1);
           break;
         case task_type_grav_long_range:
           runner_do_grav_long_range(r, t->ci, 1);
diff --git a/src/runner_doiact_fft.c b/src/runner_doiact_fft.c
deleted file mode 100644
index 97283542f43a139afbff6073dde7a77cbcf094e2..0000000000000000000000000000000000000000
--- a/src/runner_doiact_fft.c
+++ /dev/null
@@ -1,601 +0,0 @@
-/*******************************************************************************
- * This file is part of SWIFT.
- * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- ******************************************************************************/
-
-/* Config parameters. */
-#include "../config.h"
-
-#ifdef HAVE_FFTW
-#include <fftw3.h>
-#endif
-
-/* This object's header. */
-#include "runner_doiact_fft.h"
-
-/* Local includes. */
-#include "debug.h"
-#include "engine.h"
-#include "error.h"
-#include "kernel_long_gravity.h"
-#include "part.h"
-#include "runner.h"
-#include "space.h"
-#include "timers.h"
-
-#ifdef HAVE_FFTW
-
-/**
- * @brief Returns 1D index of a 3D NxNxN array using row-major style.
- *
- * Wraps around in the corresponding dimension if any of the 3 indices is >= N
- * or < 0.
- *
- * @param i Index along x.
- * @param j Index along y.
- * @param k Index along z.
- * @param N Size of the array along one axis.
- */
-__attribute__((always_inline)) INLINE static int row_major_id(int i, int j,
-                                                              int k, int N) {
-  return (((i + N) % N) * N * N + ((j + N) % N) * N + ((k + N) % N));
-}
-
-/**
- * @brief Interpolate values from a the mesh using CIC.
- *
- * @param mesh The mesh to read from.
- * @param i The index of the cell along x
- * @param j The index of the cell along y
- * @param k The index of the cell along z
- * @param tx First CIC coefficient along x
- * @param ty First CIC coefficient along y
- * @param tz First CIC coefficient along z
- * @param dx Second CIC coefficient along x
- * @param dy Second CIC coefficient along y
- * @param dz Second CIC coefficient along z
- */
-__attribute__((always_inline)) INLINE static double CIC_get(
-    double mesh[6][6][6], int i, int j, int k, double tx, double ty, double tz,
-    double dx, double dy, double dz) {
-
-  double temp;
-  temp = mesh[i + 0][j + 0][k + 0] * tx * ty * tz;
-  temp += mesh[i + 0][j + 0][k + 1] * tx * ty * dz;
-  temp += mesh[i + 0][j + 1][k + 0] * tx * dy * tz;
-  temp += mesh[i + 0][j + 1][k + 1] * tx * dy * dz;
-  temp += mesh[i + 1][j + 0][k + 0] * dx * ty * tz;
-  temp += mesh[i + 1][j + 0][k + 1] * dx * ty * dz;
-  temp += mesh[i + 1][j + 1][k + 0] * dx * dy * tz;
-  temp += mesh[i + 1][j + 1][k + 1] * dx * dy * dz;
-
-  return temp;
-}
-
-/**
- * @brief Assigns a given multipole to a density mesh using the CIC method.
- *
- * @param m The #multipole.
- * @param rho The density mesh.
- * @param N the size of the mesh along one axis.
- * @param fac The width of a mesh cell.
- * @param dim The dimensions of the simulation box.
- */
-__attribute__((always_inline)) INLINE static void multipole_to_mesh_CIC(
-    const struct gravity_tensors* m, double* rho, int N, double fac,
-    const double dim[3]) {
-
-  /* Box wrap the multipole's position */
-  const double CoM_x = box_wrap(m->CoM[0], 0., dim[0]);
-  const double CoM_y = box_wrap(m->CoM[1], 0., dim[1]);
-  const double CoM_z = box_wrap(m->CoM[2], 0., dim[2]);
-
-  int i = (int)(fac * CoM_x);
-  if (i >= N) i = N - 1;
-  const double dx = fac * CoM_x - i;
-  const double tx = 1. - dx;
-
-  int j = (int)(fac * CoM_y);
-  if (j >= N) j = N - 1;
-  const double dy = fac * CoM_y - j;
-  const double ty = 1. - dy;
-
-  int k = (int)(fac * CoM_z);
-  if (k >= N) k = N - 1;
-  const double dz = fac * CoM_z - k;
-  const double tz = 1. - dz;
-
-#ifdef SWIFT_DEBUG_CHECKS
-  if (i < 0 || i >= N) error("Invalid multipole position in x");
-  if (j < 0 || j >= N) error("Invalid multipole position in y");
-  if (k < 0 || k >= N) error("Invalid multipole position in z");
-#endif
-
-  const double mass = m->m_pole.M_000;
-
-  /* CIC ! */
-  rho[row_major_id(i + 0, j + 0, k + 0, N)] += mass * tx * ty * tz;
-  rho[row_major_id(i + 0, j + 0, k + 1, N)] += mass * tx * ty * dz;
-  rho[row_major_id(i + 0, j + 1, k + 0, N)] += mass * tx * dy * tz;
-  rho[row_major_id(i + 0, j + 1, k + 1, N)] += mass * tx * dy * dz;
-  rho[row_major_id(i + 1, j + 0, k + 0, N)] += mass * dx * ty * tz;
-  rho[row_major_id(i + 1, j + 0, k + 1, N)] += mass * dx * ty * dz;
-  rho[row_major_id(i + 1, j + 1, k + 0, N)] += mass * dx * dy * tz;
-  rho[row_major_id(i + 1, j + 1, k + 1, N)] += mass * dx * dy * dz;
-}
-/**
- * @brief Computes the potential on a multipole from a given mesh using the CIC
- * method.
- *
- * @param m The #multipole.
- * @param pot The potential mesh.
- * @param N the size of the mesh along one axis.
- * @param fac width of a mesh cell.
- * @param dim The dimensions of the simulation box.
- */
-__attribute__((always_inline)) INLINE static void mesh_to_multipole_CIC(
-    struct gravity_tensors* m, const double* pot, int N, double fac,
-    const double dim[3]) {
-
-  /* Box wrap the multipole's position */
-  const double CoM_x = box_wrap(m->CoM[0], 0., dim[0]);
-  const double CoM_y = box_wrap(m->CoM[1], 0., dim[1]);
-  const double CoM_z = box_wrap(m->CoM[2], 0., dim[2]);
-
-  int i = (int)(fac * CoM_x);
-  if (i >= N) i = N - 1;
-  const double dx = fac * CoM_x - i;
-  const double tx = 1. - dx;
-
-  int j = (int)(fac * CoM_y);
-  if (j >= N) j = N - 1;
-  const double dy = fac * CoM_y - j;
-  const double ty = 1. - dy;
-
-  int k = (int)(fac * CoM_z);
-  if (k >= N) k = N - 1;
-  const double dz = fac * CoM_z - k;
-  const double tz = 1. - dz;
-
-#ifdef SWIFT_DEBUG_CHECKS
-  if (i < 0 || i >= N) error("Invalid multipole position in x");
-  if (j < 0 || j >= N) error("Invalid multipole position in y");
-  if (k < 0 || k >= N) error("Invalid multipole position in z");
-#endif
-
-  /* First, copy the necessary part of the mesh for stencil operations */
-  /* This includes box-wrapping in all 3 dimensions. */
-  double phi[6][6][6];
-  for (int iii = -2; iii <= 3; ++iii) {
-    for (int jjj = -2; jjj <= 3; ++jjj) {
-      for (int kkk = -2; kkk <= 3; ++kkk) {
-        phi[iii + 2][jjj + 2][kkk + 2] =
-            pot[row_major_id(i + iii, j + jjj, k + kkk, N)];
-      }
-    }
-  }
-
-  /* Indices of (i,j,k) in the local copy of the mesh */
-  const int ii = 2, jj = 2, kk = 2;
-
-  /* Some local accumulators */
-  double F_000 = 0.;
-  double F_100 = 0., F_010 = 0., F_001 = 0.;
-  double F_200 = 0., F_020 = 0., F_002 = 0.;
-  double F_110 = 0., F_101 = 0., F_011 = 0.;
-  double F_300 = 0., F_030 = 0., F_003 = 0.;
-
-  /* Simple CIC for the potential itself */
-  F_000 -= CIC_get(phi, ii, jj, kk, tx, ty, tz, dx, dy, dz);
-
-  /* ---- */
-
-  /* 5-point stencil along each axis for the 1st derivatives */
-  F_100 -= (1. / 12.) * CIC_get(phi, ii + 2, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_100 += (2. / 3.) * CIC_get(phi, ii + 1, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_100 -= (2. / 3.) * CIC_get(phi, ii - 1, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_100 += (1. / 12.) * CIC_get(phi, ii - 2, jj, kk, tx, ty, tz, dx, dy, dz);
-
-  F_010 -= (1. / 12.) * CIC_get(phi, ii, jj + 2, kk, tx, ty, tz, dx, dy, dz);
-  F_010 += (2. / 3.) * CIC_get(phi, ii, jj + 1, kk, tx, ty, tz, dx, dy, dz);
-  F_010 -= (2. / 3.) * CIC_get(phi, ii, jj - 1, kk, tx, ty, tz, dx, dy, dz);
-  F_010 += (1. / 12.) * CIC_get(phi, ii, jj - 2, kk, tx, ty, tz, dx, dy, dz);
-
-  F_001 -= (1. / 12.) * CIC_get(phi, ii, jj, kk + 2, tx, ty, tz, dx, dy, dz);
-  F_001 += (2. / 3.) * CIC_get(phi, ii, jj, kk + 1, tx, ty, tz, dx, dy, dz);
-  F_001 -= (2. / 3.) * CIC_get(phi, ii, jj, kk - 1, tx, ty, tz, dx, dy, dz);
-  F_001 += (1. / 12.) * CIC_get(phi, ii, jj, kk - 2, tx, ty, tz, dx, dy, dz);
-
-  /* ---- */
-
-  /* 5-point stencil along each axis for the 2nd derivatives (diagonal) */
-  F_200 -= (1. / 12.) * CIC_get(phi, ii + 2, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_200 += (4. / 3.) * CIC_get(phi, ii + 1, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_200 -= (5. / 2.) * CIC_get(phi, ii + 0, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_200 += (4. / 3.) * CIC_get(phi, ii - 1, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_200 -= (1. / 12.) * CIC_get(phi, ii - 2, jj, kk, tx, ty, tz, dx, dy, dz);
-
-  F_020 -= (1. / 12.) * CIC_get(phi, ii, jj + 2, kk, tx, ty, tz, dx, dy, dz);
-  F_020 += (4. / 3.) * CIC_get(phi, ii, jj + 1, kk, tx, ty, tz, dx, dy, dz);
-  F_020 -= (5. / 2.) * CIC_get(phi, ii, jj + 0, kk, tx, ty, tz, dx, dy, dz);
-  F_020 += (4. / 3.) * CIC_get(phi, ii, jj - 1, kk, tx, ty, tz, dx, dy, dz);
-  F_020 -= (1. / 12.) * CIC_get(phi, ii, jj - 2, kk, tx, ty, tz, dx, dy, dz);
-
-  F_002 -= (1. / 12.) * CIC_get(phi, ii, jj, kk + 2, tx, ty, tz, dx, dy, dz);
-  F_002 += (4. / 3.) * CIC_get(phi, ii, jj, kk + 1, tx, ty, tz, dx, dy, dz);
-  F_002 -= (5. / 2.) * CIC_get(phi, ii, jj, kk + 0, tx, ty, tz, dx, dy, dz);
-  F_002 += (4. / 3.) * CIC_get(phi, ii, jj, kk - 1, tx, ty, tz, dx, dy, dz);
-  F_002 -= (1. / 12.) * CIC_get(phi, ii, jj, kk - 2, tx, ty, tz, dx, dy, dz);
-
-  /* Regular stencil for the 2nd derivatives (non-diagonal) */
-  F_110 += (1. / 4.) * CIC_get(phi, ii + 1, jj + 1, kk, tx, ty, tz, dx, dy, dz);
-  F_110 -= (1. / 4.) * CIC_get(phi, ii + 1, jj - 1, kk, tx, ty, tz, dx, dy, dz);
-  F_110 -= (1. / 4.) * CIC_get(phi, ii - 1, jj + 1, kk, tx, ty, tz, dx, dy, dz);
-  F_110 += (1. / 4.) * CIC_get(phi, ii - 1, jj - 1, kk, tx, ty, tz, dx, dy, dz);
-
-  F_101 += (1. / 4.) * CIC_get(phi, ii + 1, jj, kk + 1, tx, ty, tz, dx, dy, dz);
-  F_101 -= (1. / 4.) * CIC_get(phi, ii + 1, jj, kk - 1, tx, ty, tz, dx, dy, dz);
-  F_101 -= (1. / 4.) * CIC_get(phi, ii - 1, jj, kk + 1, tx, ty, tz, dx, dy, dz);
-  F_101 += (1. / 4.) * CIC_get(phi, ii - 1, jj, kk - 1, tx, ty, tz, dx, dy, dz);
-
-  F_011 += (1. / 4.) * CIC_get(phi, ii, jj + 1, kk + 1, tx, ty, tz, dx, dy, dz);
-  F_011 -= (1. / 4.) * CIC_get(phi, ii, jj + 1, kk - 1, tx, ty, tz, dx, dy, dz);
-  F_011 -= (1. / 4.) * CIC_get(phi, ii, jj - 1, kk + 1, tx, ty, tz, dx, dy, dz);
-  F_011 += (1. / 4.) * CIC_get(phi, ii, jj - 1, kk - 1, tx, ty, tz, dx, dy, dz);
-
-  /* ---- */
-
-  /* 5-point stencil along each axis for the 3rd derivatives (diagonal) */
-  F_300 -= (1. / 2.) * CIC_get(phi, ii + 2, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_300 += 1. * CIC_get(phi, ii + 1, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_300 -= 1. * CIC_get(phi, ii - 1, jj, kk, tx, ty, tz, dx, dy, dz);
-  F_300 += (1. / 2.) * CIC_get(phi, ii - 2, jj, kk, tx, ty, tz, dx, dy, dz);
-
-  F_030 -= (1. / 2.) * CIC_get(phi, ii, jj + 2, kk, tx, ty, tz, dx, dy, dz);
-  F_030 += (2. / 3.) * CIC_get(phi, ii, jj + 1, kk, tx, ty, tz, dx, dy, dz);
-  F_030 -= (2. / 3.) * CIC_get(phi, ii, jj - 1, kk, tx, ty, tz, dx, dy, dz);
-  F_030 += (1. / 2.) * CIC_get(phi, ii, jj - 2, kk, tx, ty, tz, dx, dy, dz);
-
-  F_003 -= (1. / 2.) * CIC_get(phi, ii, jj, kk + 2, tx, ty, tz, dx, dy, dz);
-  F_003 += 1. * CIC_get(phi, ii, jj, kk + 1, tx, ty, tz, dx, dy, dz);
-  F_003 -= 1. * CIC_get(phi, ii, jj, kk - 1, tx, ty, tz, dx, dy, dz);
-  F_003 += (1. / 2.) * CIC_get(phi, ii, jj, kk - 2, tx, ty, tz, dx, dy, dz);
-
-  /* Store things back */
-  m->pot.F_000 += F_000;
-  m->pot.F_100 -= F_100 * fac;
-  m->pot.F_010 -= F_010 * fac;
-  m->pot.F_001 -= F_001 * fac;
-  m->pot.F_200 += F_200 * fac * fac;
-  m->pot.F_020 += F_020 * fac * fac;
-  m->pot.F_002 += F_002 * fac * fac;
-  m->pot.F_110 -= F_110 * fac * fac;
-  m->pot.F_011 -= F_011 * fac * fac;
-  m->pot.F_101 -= F_101 * fac * fac;
-  m->pot.F_300 += F_300 * fac * fac * fac;
-  m->pot.F_030 += F_030 * fac * fac * fac;
-  m->pot.F_003 += F_003 * fac * fac * fac;
-
-  m->pot.interacted = 1;
-}
-
-#ifdef SWIFT_GRAVITY_FORCE_CHECKS
-
-/**
- * @brief Computes the potential on a gpart from a given mesh using the CIC
- * method.
- *
- * Debugging routine.
- *
- * @param gp The #gpart.
- * @param pot The potential mesh.
- * @param N the size of the mesh along one axis.
- * @param fac width of a mesh cell.
- * @param dim The dimensions of the simulation box.
- */
-void mesh_to_gparts_CIC(struct gpart* gp, const double* pot, int N, double fac,
-                        const double dim[3]) {
-
-  /* Box wrap the multipole's position */
-  const double pos_x = box_wrap(gp->x[0], 0., dim[0]);
-  const double pos_y = box_wrap(gp->x[1], 0., dim[1]);
-  const double pos_z = box_wrap(gp->x[2], 0., dim[2]);
-
-  int i = (int)(fac * pos_x);
-  if (i >= N) i = N - 1;
-  const double dx = fac * pos_x - i;
-  const double tx = 1. - dx;
-
-  int j = (int)(fac * pos_y);
-  if (j >= N) j = N - 1;
-  const double dy = fac * pos_y - j;
-  const double ty = 1. - dy;
-
-  int k = (int)(fac * pos_z);
-  if (k >= N) k = N - 1;
-  const double dz = fac * pos_z - k;
-  const double tz = 1. - dz;
-
-#ifdef SWIFT_DEBUG_CHECKS
-  if (i < 0 || i >= N) error("Invalid multipole position in x");
-  if (j < 0 || j >= N) error("Invalid multipole position in y");
-  if (k < 0 || k >= N) error("Invalid multipole position in z");
-#endif
-
-  if (gp->a_grav_PM[0] != 0. || gp->potential_PM != 0.)
-    error("Particle with non-initalised stuff");
-
-  /* First, copy the necessary part of the mesh for stencil operations */
-  /* This includes box-wrapping in all 3 dimensions. */
-  double phi[6][6][6];
-  for (int iii = -2; iii <= 3; ++iii) {
-    for (int jjj = -2; jjj <= 3; ++jjj) {
-      for (int kkk = -2; kkk <= 3; ++kkk) {
-        phi[iii + 2][jjj + 2][kkk + 2] =
-            pot[row_major_id(i + iii, j + jjj, k + kkk, N)];
-      }
-    }
-  }
-
-  /* Some local accumulators */
-  double p = 0.;
-  double a[3] = {0.};
-
-  /* Indices of (i,j,k) in the local copy of the mesh */
-  const int ii = 2, jj = 2, kk = 2;
-
-  /* Simple CIC for the potential itself */
-  p += CIC_get(phi, ii, jj, kk, tx, ty, tz, dx, dy, dz);
-
-  /* ---- */
-
-  /* 5-point stencil along each axis for the accelerations */
-  a[0] += (1. / 12.) * CIC_get(phi, ii + 2, jj, kk, tx, ty, tz, dx, dy, dz);
-  a[0] -= (2. / 3.) * CIC_get(phi, ii + 1, jj, kk, tx, ty, tz, dx, dy, dz);
-  a[0] += (2. / 3.) * CIC_get(phi, ii - 1, jj, kk, tx, ty, tz, dx, dy, dz);
-  a[0] -= (1. / 12.) * CIC_get(phi, ii - 2, jj, kk, tx, ty, tz, dx, dy, dz);
-
-  a[1] += (1. / 12.) * CIC_get(phi, ii, jj + 2, kk, tx, ty, tz, dx, dy, dz);
-  a[1] -= (2. / 3.) * CIC_get(phi, ii, jj + 1, kk, tx, ty, tz, dx, dy, dz);
-  a[1] += (2. / 3.) * CIC_get(phi, ii, jj - 1, kk, tx, ty, tz, dx, dy, dz);
-  a[1] -= (1. / 12.) * CIC_get(phi, ii, jj - 2, kk, tx, ty, tz, dx, dy, dz);
-
-  a[2] += (1. / 12.) * CIC_get(phi, ii, jj, kk + 2, tx, ty, tz, dx, dy, dz);
-  a[2] -= (2. / 3.) * CIC_get(phi, ii, jj, kk + 1, tx, ty, tz, dx, dy, dz);
-  a[2] += (2. / 3.) * CIC_get(phi, ii, jj, kk - 1, tx, ty, tz, dx, dy, dz);
-  a[2] -= (1. / 12.) * CIC_get(phi, ii, jj, kk - 2, tx, ty, tz, dx, dy, dz);
-
-  /* ---- */
-
-  /* Store things back */
-  gp->potential_PM = p;
-  gp->a_grav_PM[0] = fac * a[0];
-  gp->a_grav_PM[1] = fac * a[1];
-  gp->a_grav_PM[2] = fac * a[2];
-}
-#endif
-
-/**
- * @brief Dump a real array of size NxNxN to stdout.
- *
- * Debugging routine.
- *
- * @param array The array to dump.
- * @param N The side-length of the array to dump
- */
-void print_array(double* array, int N) {
-
-  for (int k = N - 1; k >= 0; --k) {
-    printf("--- z = %d ---------\n", k);
-    for (int j = N - 1; j >= 0; --j) {
-      for (int i = 0; i < N; ++i) {
-        printf("%f ", array[i * N * N + j * N + k]);
-      }
-      printf("\n");
-    }
-  }
-}
-
-/**
- * @brief Dump a complex array of size NxNxN to stdout.
- *
- * Debugging routine.
- *
- * @param array The array to dump.
- * @param N The side-length of the array to dump
- */
-void print_carray(fftw_complex* array, int N) {
-
-  for (int k = N - 1; k >= 0; --k) {
-    printf("--- z = %d ---------\n", k);
-    for (int j = N - 1; j >= 0; --j) {
-      for (int i = 0; i < N; ++i) {
-        printf("(%f %f) ", array[i * N * N + j * N + k][0],
-               array[i * N * N + j * N + k][1]);
-      }
-      printf("\n");
-    }
-  }
-}
-
-#endif /* HAVE_FFTW */
-
-/**
- * @brief Computes the potential on the top multipoles using a Fourier transform
- *
- * @param r The #runner task
- * @param timer Are we timing this ?
- */
-void runner_do_grav_fft(struct runner* r, int timer) {
-
-#ifdef HAVE_FFTW
-
-  const struct engine* e = r->e;
-  const struct space* s = e->s;
-  const double a_smooth = e->gravity_properties->a_smooth;
-  const double box_size = s->dim[0];
-  const int cdim[3] = {s->cdim[0], s->cdim[1], s->cdim[2]};
-  const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
-
-  TIMER_TIC;
-
-  if (cdim[0] != cdim[1] || cdim[0] != cdim[2]) error("Non-square mesh");
-
-  /* Some useful constants */
-  const int N = cdim[0];
-  const int N_half = N / 2;
-  const double cell_fac = N / box_size;
-
-  /* Recover the list of top-level multipoles */
-  const int nr_cells = s->nr_cells;
-  struct gravity_tensors* restrict multipoles = s->multipoles_top;
-
-#ifdef SWIFT_DEBUG_CHECKS
-  const struct cell* cells = s->cells_top;
-  const integertime_t ti_current = e->ti_current;
-
-  /* Make sure everything has been drifted to the current point */
-  for (int i = 0; i < nr_cells; ++i)
-    if (cells[i].ti_old_multipole != ti_current)
-      error("Top-level multipole %d not drifted", i);
-#endif
-
-  /* Allocates some memory for the density mesh */
-  double* restrict rho = (double*)fftw_malloc(sizeof(double) * N * N * N);
-  if (rho == NULL) error("Error allocating memory for density mesh");
-
-  /* Allocates some memory for the mesh in Fourier space */
-  fftw_complex* restrict frho =
-      (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N * N * (N_half + 1));
-  if (frho == NULL)
-    error("Error allocating memory for transform of density mesh");
-
-  /* Prepare the FFT library */
-  fftw_plan forward_plan = fftw_plan_dft_r2c_3d(
-      N, N, N, rho, frho, FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
-  fftw_plan inverse_plan = fftw_plan_dft_c2r_3d(
-      N, N, N, frho, rho, FFTW_ESTIMATE | FFTW_DESTROY_INPUT);
-
-  /* Do a CIC mesh assignment of the multipoles */
-  bzero(rho, N * N * N * sizeof(double));
-  for (int i = 0; i < nr_cells; ++i)
-    multipole_to_mesh_CIC(&multipoles[i], rho, N, cell_fac, dim);
-
-  /* print_array(rho, N); */
-
-  /* Fourier transform to go to magic-land */
-  fftw_execute(forward_plan);
-
-  /* frho now contains the Fourier transform of the density field */
-  /* frho contains NxNx(N/2+1) complex numbers */
-
-  /* Some common factors */
-  const double green_fac = -1. / (M_PI * box_size);
-  const double a_smooth2 =
-      4. * M_PI * M_PI * a_smooth * a_smooth / ((double)(N * N));
-  const double k_fac = M_PI / (double)N;
-
-  /* Now de-convolve the CIC kernel and apply the Green function */
-  for (int i = 0; i < N; ++i) {
-
-    /* kx component of vector in Fourier space and 1/sinc(kx) */
-    const int kx = (i > N_half ? i - N : i);
-    const double kx_d = (double)kx;
-    const double fx = k_fac * kx_d;
-    const double sinc_kx_inv = (kx != 0) ? fx / sin(fx) : 1.;
-
-    for (int j = 0; j < N; ++j) {
-
-      /* ky component of vector in Fourier space and 1/sinc(ky) */
-      const int ky = (j > N_half ? j - N : j);
-      const double ky_d = (double)ky;
-      const double fy = k_fac * ky_d;
-      const double sinc_ky_inv = (ky != 0) ? fy / sin(fy) : 1.;
-
-      for (int k = 0; k < N_half + 1; ++k) {
-
-        /* kz component of vector in Fourier space and 1/sinc(kz) */
-        const int kz = (k > N_half ? k - N : k);
-        const double kz_d = (double)kz;
-        const double fz = k_fac * kz_d;
-        const double sinc_kz_inv = (kz != 0) ? fz / (sin(fz) + FLT_MIN) : 1.;
-
-        /* Norm of vector in Fourier space */
-        const double k2 = (kx_d * kx_d + ky_d * ky_d + kz_d * kz_d);
-
-        /* Avoid FPEs... */
-        if (k2 == 0.) continue;
-
-        /* Green function */
-        double W;
-        fourier_kernel_long_grav_eval(k2 * a_smooth2, &W);
-        const double green_cor = green_fac * W / (k2 + FLT_MIN);
-
-        /* Deconvolution of CIC */
-        const double CIC_cor = sinc_kx_inv * sinc_ky_inv * sinc_kz_inv;
-        const double CIC_cor2 = CIC_cor * CIC_cor;
-        const double CIC_cor4 = CIC_cor2 * CIC_cor2;
-
-        /* Combined correction */
-        const double total_cor = green_cor * CIC_cor4;
-
-        /* Apply to the mesh */
-        const int index = N * (N_half + 1) * i + (N_half + 1) * j + k;
-        frho[index][0] *= total_cor;
-        frho[index][1] *= total_cor;
-      }
-    }
-  }
-
-  /* Correct singularity at (0,0,0) */
-  frho[0][0] = 0.;
-  frho[0][1] = 0.;
-
-  /* Fourier transform to come back from magic-land */
-  fftw_execute(inverse_plan);
-
-  /* rho now contains the potential */
-  /* Let's create an alias to avoid confusion */
-  /* This array is now again NxNxN real numbers */
-  double* potential = rho;
-
-  /* message("\n\n\n POTENTIAL"); */
-  /* print_array(potential, N); */
-
-  /* Get the potential from the mesh to the gravity tensors using CIC */
-  for (int i = 0; i < nr_cells; ++i)
-    mesh_to_multipole_CIC(&multipoles[i], potential, N, cell_fac, dim);
-
-#ifdef SWIFT_GRAVITY_FORCE_CHECKS
-  /* Get the potential from the mesh to the gparts using CIC */
-  for (size_t i = 0; i < s->nr_gparts; ++i)
-    mesh_to_gparts_CIC(&s->gparts[i], potential, N, cell_fac, dim);
-#endif
-
-  /* Clean-up the mess */
-  fftw_destroy_plan(forward_plan);
-  fftw_destroy_plan(inverse_plan);
-  fftw_free(rho);
-  fftw_free(frho);
-
-  /* Time the whole thing */
-  if (timer) TIMER_TOC(timer_dograv_top_level);
-
-#else
-  error("No FFTW library found. Cannot compute periodic long-range forces.");
-#endif
-}
diff --git a/src/runner_doiact_fft.h b/src/runner_doiact_fft.h
deleted file mode 100644
index e9836311e71803952969b9c9316e8c81676d2dd8..0000000000000000000000000000000000000000
--- a/src/runner_doiact_fft.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*******************************************************************************
- * This file is part of SWIFT.
- * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- ******************************************************************************/
-#ifndef SWIFT_RUNNER_DOIACT_FFT_H
-#define SWIFT_RUNNER_DOIACT_FFT_H
-
-struct runner;
-
-void runner_do_grav_fft(struct runner *r, int timer);
-
-#endif /* SWIFT_RUNNER_DOIACT_FFT_H */
diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h
index 9e03e6989bd9b6275d03b046c707fb11d06a0fa3..05c523657b22b8e23ef44bde32ed6b42f6246db2 100644
--- a/src/runner_doiact_grav.h
+++ b/src/runner_doiact_grav.h
@@ -24,8 +24,10 @@
 #include "active.h"
 #include "cell.h"
 #include "gravity.h"
+#include "gravity_cache.h"
 #include "inline.h"
 #include "part.h"
+#include "space_getsid.h"
 #include "timers.h"
 
 /**
@@ -42,10 +44,6 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
   /* Some constants */
   const struct engine *e = r->e;
 
-  /* Cell properties */
-  struct gpart *gparts = c->gparts;
-  const int gcount = c->gcount;
-
   TIMER_TIC;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -54,7 +52,9 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
     error("c->field tensor not initialised");
 #endif
 
-  if (c->split) { /* Node case */
+  if (c->split) {
+
+    /* Node case */
 
     /* Add the field-tensor to all the 8 progenitors */
     for (int k = 0; k < 8; ++k) {
@@ -69,11 +69,11 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
         if (cp->multipole->pot.ti_init != e->ti_current)
           error("cp->field tensor not initialised");
 #endif
-        struct grav_tensor shifted_tensor;
-
         /* If the tensor received any contribution, push it down */
         if (c->multipole->pot.interacted) {
 
+          struct grav_tensor shifted_tensor;
+
           /* Shift the field tensor */
           gravity_L2L(&shifted_tensor, &c->multipole->pot, cp->multipole->CoM,
                       c->multipole->CoM);
@@ -87,13 +87,22 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
       }
     }
 
-  } else { /* Leaf case */
+  } else {
+
+    /* Leaf case */
 
     /* We can abort early if no interactions via multipole happened */
     if (!c->multipole->pot.interacted) return;
 
     if (!cell_are_gpart_drifted(c, e)) error("Un-drifted gparts");
 
+    /* Cell properties */
+    struct gpart *gparts = c->gparts;
+    const int gcount = c->gcount;
+    const struct grav_tensor *pot = &c->multipole->pot;
+    const double CoM[3] = {c->multipole->CoM[0], c->multipole->CoM[1],
+                           c->multipole->CoM[2]};
+
     /* Apply accelerations to the particles */
     for (int i = 0; i < gcount; ++i) {
 
@@ -110,9 +119,8 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
         if (c->multipole->pot.ti_init != e->ti_current)
           error("c->field tensor not initialised");
 #endif
-
         /* Apply the kernel */
-        gravity_L2P(&c->multipole->pot, c->multipole->CoM, gp);
+        gravity_L2P(pot, CoM, gp);
       }
     }
   }
@@ -121,68 +129,32 @@ static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
 }
 
 /**
- * @brief Computes the interaction of the field tensor in a cell with the
- * multipole of another cell.
+ * @brief Compute the non-truncated gravity interactions between all particles
+ * of a cell and the particles of the other cell.
  *
- * @param r The #runner.
- * @param ci The #cell with field tensor to interact.
- * @param cj The #cell with the multipole.
+ * The calculation is performed non-symmetrically using the pre-filled
+ * #gravity_cache structures. The loop over the j cache should auto-vectorize.
+ *
+ * @param ci_cache #gravity_cache contaning the particles to be updated.
+ * @param cj_cache #gravity_cache contaning the source particles.
+ * @param gcount_i The number of particles in the cell i.
+ * @param gcount_padded_j The number of particles in the cell j padded to the
+ * vector length.
+ * @param periodic Is the calculation using periodic BCs ?
+ * @param dim The size of the simulation volume.
+ *
+ * @param e The #engine (for debugging checks only).
+ * @param gparts_i The #gpart in cell i (for debugging checks only).
+ * @param gparts_j The #gpart in cell j (for debugging checks only).
+ * @param gcount_j The number of particles in the cell j (for debugging checks
+ * only).
  */
-static INLINE void runner_dopair_grav_mm(const struct runner *r,
-                                         struct cell *restrict ci,
-                                         struct cell *restrict cj) {
-
-  /* Some constants */
-  const struct engine *e = r->e;
-  const struct space *s = e->s;
-  const int periodic = s->periodic;
-  const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
-  const struct gravity_props *props = e->gravity_properties;
-  // const float a_smooth = e->gravity_properties->a_smooth;
-  // const float rlr_inv = 1. / (a_smooth * ci->super->width[0]);
-
-  TIMER_TIC;
-
-  /* Anything to do here? */
-  if (!cell_is_active_gravity(ci, e) || ci->nodeID != engine_rank) return;
-
-  /* Short-cut to the multipole */
-  const struct multipole *multi_j = &cj->multipole->m_pole;
-
-#ifdef SWIFT_DEBUG_CHECKS
-  if (ci == cj) error("Interacting a cell with itself using M2L");
-
-  if (multi_j->num_gpart == 0)
-    error("Multipole does not seem to have been set.");
-
-  if (ci->multipole->pot.ti_init != e->ti_current)
-    error("ci->grav tensor not initialised.");
-#endif
-
-  /* Do we need to drift the multipole ? */
-  if (cj->ti_old_multipole != e->ti_current)
-    error(
-        "Undrifted multipole cj->ti_old_multipole=%lld cj->nodeID=%d "
-        "ci->nodeID=%d "
-        "e->ti_current=%lld",
-        cj->ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current);
-
-  /* Let's interact at this level */
-  gravity_M2L(&ci->multipole->pot, multi_j, ci->multipole->CoM,
-              cj->multipole->CoM, props, periodic, dim);
-
-  TIMER_TOC(timer_dopair_grav_mm);
-}
-
-static INLINE void runner_dopair_grav_pp_full(const struct engine *e,
-                                              struct gravity_cache *ci_cache,
-                                              struct gravity_cache *cj_cache,
-                                              int gcount_i, int gcount_j,
-                                              int gcount_padded_j,
-                                              struct gpart *restrict gparts_i,
-                                              struct gpart *restrict gparts_j) {
-
-  TIMER_TIC;
+static INLINE void runner_dopair_grav_pp_full(
+    struct gravity_cache *restrict ci_cache,
+    struct gravity_cache *restrict cj_cache, const int gcount_i,
+    const int gcount_j, const int gcount_padded_j, const int periodic,
+    const float dim[3], const struct engine *restrict e,
+    struct gpart *restrict gparts_i, const struct gpart *restrict gparts_j) {
 
   /* Loop over all particles in ci... */
   for (int pid = 0; pid < gcount_i; pid++) {
@@ -195,7 +167,7 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e,
 
 #ifdef SWIFT_DEBUG_CHECKS
     if (!gpart_is_active(&gparts_i[pid], e))
-      error("Active particle went through the cache");
+      error("Inactive particle went through the cache");
 #endif
 
     const float x_i = ci_cache->x[pid];
@@ -227,10 +199,18 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e,
       const float z_j = cj_cache->z[pjd];
       const float mass_j = cj_cache->m[pjd];
 
-      /* Compute the pairwise (square) distance. */
-      const float dx = x_i - x_j;
-      const float dy = y_i - y_j;
-      const float dz = z_i - z_j;
+      /* Compute the pairwise distance. */
+      float dx = x_j - x_i;
+      float dy = y_j - y_i;
+      float dz = z_j - z_i;
+
+      /* Correct for periodic BCs */
+      if (periodic) {
+        dx = nearestf(dx, dim[0]);
+        dy = nearestf(dy, dim[1]);
+        dz = nearestf(dz, dim[2]);
+      }
+
       const float r2 = dx * dx + dy * dy + dz * dz;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -250,9 +230,9 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e,
                                &pot_ij);
 
       /* Store it back */
-      a_x -= f_ij * dx;
-      a_y -= f_ij * dy;
-      a_z -= f_ij * dz;
+      a_x += f_ij * dx;
+      a_y += f_ij * dy;
+      a_z += f_ij * dz;
       pot += pot_ij;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -262,22 +242,47 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e,
     }
 
     /* Store everything back in cache */
-    ci_cache->a_x[pid] = a_x;
-    ci_cache->a_y[pid] = a_y;
-    ci_cache->a_z[pid] = a_z;
-    ci_cache->pot[pid] = pot;
+    ci_cache->a_x[pid] += a_x;
+    ci_cache->a_y[pid] += a_y;
+    ci_cache->a_z[pid] += a_z;
+    ci_cache->pot[pid] += pot;
   }
-
-  TIMER_TOC(timer_dopair_grav_pp);
 }
 
+/**
+ * @brief Compute the truncated gravity interactions between all particles
+ * of a cell and the particles of the other cell.
+ *
+ * The calculation is performed non-symmetrically using the pre-filled
+ * #gravity_cache structures. The loop over the j cache should auto-vectorize.
+ *
+ * This function only makes sense in periodic BCs.
+ *
+ * @param ci_cache #gravity_cache contaning the particles to be updated.
+ * @param cj_cache #gravity_cache contaning the source particles.
+ * @param gcount_i The number of particles in the cell i.
+ * @param gcount_padded_j The number of particles in the cell j padded to the
+ * vector length.
+ * @param dim The size of the simulation volume.
+ * @param r_s_inv The inverse of the gravity-mesh smoothing-scale.
+ *
+ * @param e The #engine (for debugging checks only).
+ * @param gparts_i The #gpart in cell i (for debugging checks only).
+ * @param gparts_j The #gpart in cell j (for debugging checks only).
+ * @param gcount_j The number of particles in the cell j (for debugging checks
+ * only).
+ */
 static INLINE void runner_dopair_grav_pp_truncated(
-    const struct engine *e, const float rlr_inv, struct gravity_cache *ci_cache,
-    struct gravity_cache *cj_cache, int gcount_i, int gcount_j,
-    int gcount_padded_j, struct gpart *restrict gparts_i,
-    struct gpart *restrict gparts_j) {
+    struct gravity_cache *restrict ci_cache,
+    struct gravity_cache *restrict cj_cache, const int gcount_i,
+    const int gcount_j, const int gcount_padded_j, const float dim[3],
+    const float r_s_inv, const struct engine *restrict e,
+    struct gpart *restrict gparts_i, const struct gpart *restrict gparts_j) {
 
-  TIMER_TIC;
+#ifdef SWIFT_DEBUG_CHECKS
+  if (!e->s->periodic)
+    error("Calling truncated PP function in non-periodic setup.");
+#endif
 
   /* Loop over all particles in ci... */
   for (int pid = 0; pid < gcount_i; pid++) {
@@ -290,7 +295,7 @@ static INLINE void runner_dopair_grav_pp_truncated(
 
 #ifdef SWIFT_DEBUG_CHECKS
     if (!gpart_is_active(&gparts_i[pid], e))
-      error("Active particle went through the cache");
+      error("Inactive particle went through the cache");
 #endif
 
     const float x_i = ci_cache->x[pid];
@@ -322,10 +327,16 @@ static INLINE void runner_dopair_grav_pp_truncated(
       const float z_j = cj_cache->z[pjd];
       const float mass_j = cj_cache->m[pjd];
 
-      /* Compute the pairwise (square) distance. */
-      const float dx = x_i - x_j;
-      const float dy = y_i - y_j;
-      const float dz = z_i - z_j;
+      /* Compute the pairwise distance. */
+      float dx = x_j - x_i;
+      float dy = y_j - y_i;
+      float dz = z_j - z_i;
+
+      /* Correct for periodic BCs */
+      dx = nearestf(dx, dim[0]);
+      dy = nearestf(dy, dim[1]);
+      dz = nearestf(dz, dim[2]);
+
       const float r2 = dx * dx + dy * dy + dz * dz;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -342,12 +353,12 @@ static INLINE void runner_dopair_grav_pp_truncated(
       /* Interact! */
       float f_ij, pot_ij;
       runner_iact_grav_pp_truncated(r2, h2_i, h_inv_i, h_inv3_i, mass_j,
-                                    rlr_inv, &f_ij, &pot_ij);
+                                    r_s_inv, &f_ij, &pot_ij);
 
       /* Store it back */
-      a_x -= f_ij * dx;
-      a_y -= f_ij * dy;
-      a_z -= f_ij * dz;
+      a_x += f_ij * dx;
+      a_y += f_ij * dy;
+      a_z += f_ij * dz;
       pot += pot_ij;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -357,22 +368,161 @@ static INLINE void runner_dopair_grav_pp_truncated(
     }
 
     /* Store everything back in cache */
-    ci_cache->a_x[pid] = a_x;
-    ci_cache->a_y[pid] = a_y;
-    ci_cache->a_z[pid] = a_z;
-    ci_cache->pot[pid] = pot;
+    ci_cache->a_x[pid] += a_x;
+    ci_cache->a_y[pid] += a_y;
+    ci_cache->a_z[pid] += a_z;
+    ci_cache->pot[pid] += pot;
   }
+}
 
-  TIMER_TOC(timer_dopair_grav_pp);
+/**
+ * @brief Compute the gravity interactions between all particles
+ * of a cell and the multipole of the other cell.
+ *
+ * The calculation is performedusing the pre-filled
+ * #gravity_cache structure. The loop over the i cache should auto-vectorize.
+ *
+ * @param ci_cache #gravity_cache contaning the particles to be updated.
+ * @param gcount_padded_i The number of particles in the cell i padded to the
+ * vector length.
+ * @param CoM_j Position of the #multipole in #cell j.
+ * @param multi_j The #multipole in #cell j.
+ * @param periodic Is the calculation using periodic BCs ?
+ * @param dim The size of the simulation volume.
+ *
+ * @param e The #engine (for debugging checks only).
+ * @param gparts_i The #gpart in cell i (for debugging checks only).
+ * @param gcount_i The number of particles in the cell i (for debugging checks
+ * only).
+ * @param cj The #cell j (for debugging checks only).
+ */
+static INLINE void runner_dopair_grav_pm_full(
+    struct gravity_cache *ci_cache, const int gcount_padded_i,
+    const float CoM_j[3], const struct multipole *restrict multi_j,
+    const int periodic, const float dim[3], const struct engine *restrict e,
+    struct gpart *restrict gparts_i, const int gcount_i,
+    const struct cell *restrict cj) {
+
+  /* Make the compiler understand we are in happy vectorization land */
+  swift_declare_aligned_ptr(float, x, ci_cache->x, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, y, ci_cache->y, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, z, ci_cache->z, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, epsilon, ci_cache->epsilon,
+                            SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, a_x, ci_cache->a_x, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, a_y, ci_cache->a_y, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, a_z, ci_cache->a_z, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(float, pot, ci_cache->pot, SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(int, active, ci_cache->active,
+                            SWIFT_CACHE_ALIGNMENT);
+  swift_declare_aligned_ptr(int, use_mpole, ci_cache->use_mpole,
+                            SWIFT_CACHE_ALIGNMENT);
+  swift_assume_size(gcount_padded_i, VEC_SIZE);
+
+  /* Loop over all particles in ci... */
+  for (int pid = 0; pid < gcount_padded_i; pid++) {
+
+    /* Skip inactive particles */
+    if (!active[pid]) continue;
+
+    /* Skip particle that cannot use the multipole */
+    if (!use_mpole[pid]) continue;
+
+#ifdef SWIFT_DEBUG_CHECKS
+    if (pid < gcount_i && !gpart_is_active(&gparts_i[pid], e))
+      error("Active particle went through the cache");
+
+    if (pid >= gcount_i) error("Adding forces to padded particle");
+#endif
+
+    const float x_i = x[pid];
+    const float y_i = y[pid];
+    const float z_i = z[pid];
+
+    /* Some powers of the softening length */
+    const float h_i = epsilon[pid];
+    const float h_inv_i = 1.f / h_i;
+
+    /* Distance to the Multipole */
+    float dx = CoM_j[0] - x_i;
+    float dy = CoM_j[1] - y_i;
+    float dz = CoM_j[2] - z_i;
+
+    /* Apply periodic BCs? */
+    if (periodic) {
+      dx = nearestf(dx, dim[0]);
+      dy = nearestf(dy, dim[1]);
+      dz = nearestf(dz, dim[2]);
+    }
+
+    const float r2 = dx * dx + dy * dy + dz * dz;
+
+#ifdef SWIFT_DEBUG_CHECKSa
+    const float r_max_j = cj->multipole->r_max;
+    const float r_max2 = r_max_j * r_max_j;
+    const float theta_crit2 = e->gravity_properties->theta_crit2;
+
+    /* 1.01 to avoid FP rounding false-positives */
+    if (!gravity_M2P_accept(r_max2, theta_crit2 * 1.01, r2))
+      error(
+          "use_mpole[i] set when M2P accept fails CoM=[%e %e %e] pos=[%e %e "
+          "%e], rmax=%e",
+          CoM_j[0], CoM_j[1], CoM_j[2], x_i, y_i, z_i, r_max_j);
+#endif
+
+    /* Interact! */
+    float f_x, f_y, f_z, pot_ij;
+    runner_iact_grav_pm_full(dx, dy, dz, r2, h_i, h_inv_i, multi_j, &f_x, &f_y,
+                             &f_z, &pot_ij);
+
+    /* Store it back */
+    a_x[pid] += f_x;
+    a_y[pid] += f_y;
+    a_z[pid] += f_z;
+    pot[pid] += pot_ij;
+
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Update the interaction counter */
+    if (pid < gcount_i)
+      gparts_i[pid].num_interacted += cj->multipole->m_pole.num_gpart;
+#endif
+  }
 }
 
-static INLINE void runner_dopair_grav_pm(
-    const struct engine *restrict e, struct gravity_cache *ci_cache,
-    int gcount_i, int gcount_padded_i, struct gpart *restrict gparts_i,
+/**
+ * @brief Compute the gravity interactions between all particles
+ * of a cell and the multipole of the other cell.
+ *
+ * The calculation is performedusing the pre-filled
+ * #gravity_cache structure. The loop over the i cache should auto-vectorize.
+ *
+ * This function only makes sense in periodic BCs.
+ *
+ * @param ci_cache #gravity_cache contaning the particles to be updated.
+ * @param gcount_padded_i The number of particles in the cell i padded to the
+ * vector length.
+ * @param CoM_j Position of the #multipole in #cell j.
+ * @param multi_j The #multipole in #cell j.
+ * @param dim The size of the simulation volume.
+ * @param r_s_inv The inverse of the gravity-mesh smoothing-scale.
+ *
+ * @param e The #engine (for debugging checks only).
+ * @param gparts_i The #gpart in cell i (for debugging checks only).
+ * @param gcount_i The number of particles in the cell i (for debugging checks
+ * only).
+ * @param cj The #cell j (for debugging checks only).
+ */
+static INLINE void runner_dopair_grav_pm_truncated(
+    struct gravity_cache *ci_cache, const int gcount_padded_i,
     const float CoM_j[3], const struct multipole *restrict multi_j,
-    struct cell *restrict cj) {
+    const float dim[3], const float r_s_inv, const struct engine *restrict e,
+    struct gpart *restrict gparts_i, const int gcount_i,
+    const struct cell *restrict cj) {
 
-  TIMER_TIC;
+#ifdef SWIFT_DEBUG_CHECKS
+  if (!e->s->periodic)
+    error("Calling truncated PP function in non-periodic setup.");
+#endif
 
   /* Make the compiler understand we are in happy vectorization land */
   swift_declare_aligned_ptr(float, x, ci_cache->x, SWIFT_CACHE_ALIGNMENT);
@@ -402,6 +552,8 @@ static INLINE void runner_dopair_grav_pm(
 #ifdef SWIFT_DEBUG_CHECKS
     if (pid < gcount_i && !gpart_is_active(&gparts_i[pid], e))
       error("Active particle went through the cache");
+
+    if (pid >= gcount_i) error("Adding forces to padded particle");
 #endif
 
     const float x_i = x[pid];
@@ -413,21 +565,40 @@ static INLINE void runner_dopair_grav_pm(
     const float h_inv_i = 1.f / h_i;
 
     /* Distance to the Multipole */
-    const float dx = x_i - CoM_j[0];
-    const float dy = y_i - CoM_j[1];
-    const float dz = z_i - CoM_j[2];
+    float dx = CoM_j[0] - x_i;
+    float dy = CoM_j[1] - y_i;
+    float dz = CoM_j[2] - z_i;
+
+    /* Apply periodic BCs */
+    dx = nearestf(dx, dim[0]);
+    dy = nearestf(dy, dim[1]);
+    dz = nearestf(dz, dim[2]);
+
     const float r2 = dx * dx + dy * dy + dz * dz;
 
+#ifdef SWIFT_DEBUG_CHECKS
+    const float r_max_j = cj->multipole->r_max;
+    const float r_max2 = r_max_j * r_max_j;
+    const float theta_crit2 = e->gravity_properties->theta_crit2;
+
+    /* 1.01 to avoid FP rounding false-positives */
+    if (!gravity_M2P_accept(r_max2, theta_crit2 * 1.01, r2))
+      error(
+          "use_mpole[i] set when M2P accept fails CoM=[%e %e %e] pos=[%e %e "
+          "%e], rmax=%e",
+          CoM_j[0], CoM_j[1], CoM_j[2], x_i, y_i, z_i, r_max_j);
+#endif
+
     /* Interact! */
     float f_x, f_y, f_z, pot_ij;
-    runner_iact_grav_pm(dx, dy, dz, r2, h_i, h_inv_i, multi_j, &f_x, &f_y, &f_z,
-                        &pot_ij);
+    runner_iact_grav_pm_truncated(dx, dy, dz, r2, h_i, h_inv_i, r_s_inv,
+                                  multi_j, &f_x, &f_y, &f_z, &pot_ij);
 
     /* Store it back */
-    a_x[pid] = f_x;
-    a_y[pid] = f_y;
-    a_z[pid] = f_z;
-    pot[pid] = pot_ij;
+    a_x[pid] += f_x;
+    a_y[pid] += f_y;
+    a_z[pid] += f_z;
+    pot[pid] += pot_ij;
 
 #ifdef SWIFT_DEBUG_CHECKS
     /* Update the interaction counter */
@@ -435,72 +606,66 @@ static INLINE void runner_dopair_grav_pm(
       gparts_i[pid].num_interacted += cj->multipole->m_pole.num_gpart;
 #endif
   }
-
-  TIMER_TOC(timer_dopair_grav_pm);
 }
 
 /**
  * @brief Computes the interaction of all the particles in a cell with all the
- * particles of another cell (switching function between full and truncated).
+ * particles of another cell.
+ *
+ * This function switches between the full potential and the truncated one
+ * depending on needs. It will also use the M2P (multipole) interaction
+ * for the subset of particles in either cell for which the distance criterion
+ * is valid.
+ *
+ * This function starts by constructing the require #gravity_cache for both
+ * cells and then call the specialised functions doing the actual work on
+ * the caches. It then write the data back to the particles.
  *
  * @param r The #runner.
  * @param ci The first #cell.
  * @param cj The other #cell.
+ * @param symmetric Are we updating both cells (1) or just ci (0) ?
+ * @param allow_mpole Are we allowing the use of P2M interactions ?
  */
 static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
-                                         struct cell *cj) {
+                                         struct cell *cj, const int symmetric,
+                                         const int allow_mpole) {
 
+  /* Recover some useful constants */
   const struct engine *e = r->e;
+  const int periodic = e->mesh->periodic;
+  const float dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]};
+  const float r_s_inv = e->mesh->r_s_inv;
+  const double min_trunc = e->mesh->r_cut_min;
 
   TIMER_TIC;
 
+  /* Record activity status */
+  const int ci_active = cell_is_active_gravity(ci, e);
+  const int cj_active = cell_is_active_gravity(cj, e);
+
   /* Anything to do here? */
-  if (!cell_is_active_gravity(ci, e) && !cell_is_active_gravity(cj, e)) return;
+  if (!ci_active && !cj_active) return;
+  if (!ci_active && !symmetric) return;
 
   /* Check that we are not doing something stupid */
   if (ci->split || cj->split) error("Running P-P on splitable cells");
 
-  /* Let's start by drifting things */
+  /* Let's start by checking things are drifted */
   if (!cell_are_gpart_drifted(ci, e)) error("Un-drifted gparts");
   if (!cell_are_gpart_drifted(cj, e)) error("Un-drifted gparts");
-
-  /* Recover some useful constants */
-  struct space *s = e->s;
-  const int periodic = s->periodic;
-  const double cell_width = s->width[0];
-  const double a_smooth = e->gravity_properties->a_smooth;
-  const double r_cut_min = e->gravity_properties->r_cut_min;
-  const double rlr = cell_width * a_smooth;
-  const double min_trunc = rlr * r_cut_min;
-  const float rlr_inv = 1. / rlr;
-
-  /* Caches to play with */
-  struct gravity_cache *const ci_cache = &r->ci_gravity_cache;
-  struct gravity_cache *const cj_cache = &r->cj_gravity_cache;
-
-  /* Get the distance vector between the pairs, wrapping. */
-  double cell_shift[3];
-  space_getsid(s, &ci, &cj, cell_shift);
-
-  /* Record activity status */
-  const int ci_active = cell_is_active_gravity(ci, e);
-  const int cj_active = cell_is_active_gravity(cj, e);
-
-  /* Do we need to drift the multipoles ? */
   if (cj_active && ci->ti_old_multipole != e->ti_current)
     error("Un-drifted multipole");
   if (ci_active && cj->ti_old_multipole != e->ti_current)
     error("Un-drifted multipole");
 
-  /* Centre of the cell pair */
-  const double loc[3] = {ci->loc[0],   // + 0. * ci->width[0],
-                         ci->loc[1],   // + 0. * ci->width[1],
-                         ci->loc[2]};  // + 0. * ci->width[2]};
+  /* Caches to play with */
+  struct gravity_cache *const ci_cache = &r->ci_gravity_cache;
+  struct gravity_cache *const cj_cache = &r->cj_gravity_cache;
 
   /* Shift to apply to the particles in each cell */
-  const double shift_i[3] = {loc[0] + cell_shift[0], loc[1] + cell_shift[1],
-                             loc[2] + cell_shift[2]};
-  const double shift_j[3] = {loc[0], loc[1], loc[2]};
+  const double shift_i[3] = {0., 0., 0.};
+  const double shift_j[3] = {0., 0., 0.};
 
   /* Recover the multipole info and shift the CoM locations */
   const float rmax_i = ci->multipole->r_max;
@@ -532,12 +697,12 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
 #endif
 
   /* Fill the caches */
-  gravity_cache_populate(e->max_active_bin, ci_cache, ci->gparts, gcount_i,
-                         gcount_padded_i, shift_i, CoM_j, rmax2_j, ci,
-                         e->gravity_properties);
-  gravity_cache_populate(e->max_active_bin, cj_cache, cj->gparts, gcount_j,
-                         gcount_padded_j, shift_j, CoM_i, rmax2_i, cj,
-                         e->gravity_properties);
+  gravity_cache_populate(e->max_active_bin, allow_mpole, periodic, dim,
+                         ci_cache, ci->gparts, gcount_i, gcount_padded_i,
+                         shift_i, CoM_j, rmax2_j, ci, e->gravity_properties);
+  gravity_cache_populate(e->max_active_bin, allow_mpole, periodic, dim,
+                         cj_cache, cj->gparts, gcount_j, gcount_padded_j,
+                         shift_j, CoM_i, rmax2_i, cj, e->gravity_properties);
 
   /* Can we use the Newtonian version or do we need the truncated one ? */
   if (!periodic) {
@@ -548,21 +713,26 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
     if (ci_active) {
 
       /* First the P2P */
-      runner_dopair_grav_pp_full(e, ci_cache, cj_cache, gcount_i, gcount_j,
-                                 gcount_padded_j, ci->gparts, cj->gparts);
+      runner_dopair_grav_pp_full(ci_cache, cj_cache, gcount_i, gcount_j,
+                                 gcount_padded_j, periodic, dim, e, ci->gparts,
+                                 cj->gparts);
 
       /* Then the M2P */
-      runner_dopair_grav_pm(e, ci_cache, gcount_i, gcount_padded_i, ci->gparts,
-                            CoM_j, multi_j, cj);
+      if (allow_mpole)
+        runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j,
+                                   periodic, dim, e, ci->gparts, gcount_i, cj);
     }
-    if (cj_active) {
+    if (cj_active && symmetric) {
 
       /* First the P2P */
-      runner_dopair_grav_pp_full(e, cj_cache, ci_cache, gcount_j, gcount_i,
-                                 gcount_padded_i, cj->gparts, ci->gparts);
+      runner_dopair_grav_pp_full(cj_cache, ci_cache, gcount_j, gcount_i,
+                                 gcount_padded_i, periodic, dim, e, cj->gparts,
+                                 ci->gparts);
+
       /* Then the M2P */
-      runner_dopair_grav_pm(e, cj_cache, gcount_j, gcount_padded_j, cj->gparts,
-                            CoM_i, multi_i, ci);
+      if (allow_mpole)
+        runner_dopair_grav_pm_full(cj_cache, gcount_padded_j, CoM_i, multi_i,
+                                   periodic, dim, e, cj->gparts, gcount_j, ci);
     }
 
   } else { /* Periodic BC */
@@ -584,24 +754,28 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
       if (ci_active) {
 
         /* First the (truncated) P2P */
-        runner_dopair_grav_pp_truncated(e, rlr_inv, ci_cache, cj_cache,
-                                        gcount_i, gcount_j, gcount_padded_j,
+        runner_dopair_grav_pp_truncated(ci_cache, cj_cache, gcount_i, gcount_j,
+                                        gcount_padded_j, dim, r_s_inv, e,
                                         ci->gparts, cj->gparts);
 
         /* Then the M2P */
-        runner_dopair_grav_pm(e, ci_cache, gcount_i, gcount_padded_i,
-                              ci->gparts, CoM_j, multi_j, cj);
+        if (allow_mpole)
+          runner_dopair_grav_pm_truncated(ci_cache, gcount_padded_i, CoM_j,
+                                          multi_j, dim, r_s_inv, e, ci->gparts,
+                                          gcount_i, cj);
       }
-      if (cj_active) {
+      if (cj_active && symmetric) {
 
         /* First the (truncated) P2P */
-        runner_dopair_grav_pp_truncated(e, rlr_inv, cj_cache, ci_cache,
-                                        gcount_j, gcount_i, gcount_padded_i,
+        runner_dopair_grav_pp_truncated(cj_cache, ci_cache, gcount_j, gcount_i,
+                                        gcount_padded_i, dim, r_s_inv, e,
                                         cj->gparts, ci->gparts);
 
         /* Then the M2P */
-        runner_dopair_grav_pm(e, cj_cache, gcount_j, gcount_padded_j,
-                              cj->gparts, CoM_i, multi_i, ci);
+        if (allow_mpole)
+          runner_dopair_grav_pm_truncated(cj_cache, gcount_padded_j, CoM_i,
+                                          multi_i, dim, r_s_inv, e, cj->gparts,
+                                          gcount_j, ci);
       }
 
     } else {
@@ -612,73 +786,58 @@ static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
       if (ci_active) {
 
         /* First the (Newtonian) P2P */
-        runner_dopair_grav_pp_full(e, ci_cache, cj_cache, gcount_i, gcount_j,
-                                   gcount_padded_j, ci->gparts, cj->gparts);
+        runner_dopair_grav_pp_full(ci_cache, cj_cache, gcount_i, gcount_j,
+                                   gcount_padded_j, periodic, dim, e,
+                                   ci->gparts, cj->gparts);
 
         /* Then the M2P */
-        runner_dopair_grav_pm(e, ci_cache, gcount_i, gcount_padded_i,
-                              ci->gparts, CoM_j, multi_j, cj);
+        if (allow_mpole)
+          runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j,
+                                     periodic, dim, e, ci->gparts, gcount_i,
+                                     cj);
       }
-      if (cj_active) {
+      if (cj_active && symmetric) {
 
         /* First the (Newtonian) P2P */
-        runner_dopair_grav_pp_full(e, cj_cache, ci_cache, gcount_j, gcount_i,
-                                   gcount_padded_i, cj->gparts, ci->gparts);
+        runner_dopair_grav_pp_full(cj_cache, ci_cache, gcount_j, gcount_i,
+                                   gcount_padded_i, periodic, dim, e,
+                                   cj->gparts, ci->gparts);
 
         /* Then the M2P */
-        runner_dopair_grav_pm(e, cj_cache, gcount_j, gcount_padded_j,
-                              cj->gparts, CoM_i, multi_i, ci);
+        if (allow_mpole)
+          runner_dopair_grav_pm_full(cj_cache, gcount_padded_j, CoM_i, multi_i,
+                                     periodic, dim, e, cj->gparts, gcount_j,
+                                     ci);
       }
     }
   }
 
   /* Write back to the particles */
   if (ci_active) gravity_cache_write_back(ci_cache, ci->gparts, gcount_i);
-  if (cj_active) gravity_cache_write_back(cj_cache, cj->gparts, gcount_j);
+  if (cj_active && symmetric)
+    gravity_cache_write_back(cj_cache, cj->gparts, gcount_j);
 
-  TIMER_TOC(timer_dopair_grav_branch);
+  TIMER_TOC(timer_dopair_grav_pp);
 }
 
 /**
- * @brief Computes the interaction of all the particles in a cell using the
- * full Newtonian potential.
+ * @brief Compute the non-truncated gravity interactions between all particles
+ * of a cell and the particles of the other cell.
  *
- * @param r The #runner.
- * @param c The #cell.
+ * The calculation is performed non-symmetrically using the pre-filled
+ * #gravity_cache structures. The loop over the j cache should auto-vectorize.
+ *
+ * @param ci_cache #gravity_cache contaning the particles to be updated.
+ * @param gcount The number of particles in the cell.
+ * @param gcount_padded The number of particles in the cell padded to the
+ * vector length.
  *
- * @todo Use a local cache for the particles.
+ * @param e The #engine (for debugging checks only).
+ * @param gparts The #gpart in the cell (for debugging checks only).
  */
-static INLINE void runner_doself_grav_pp_full(struct runner *r,
-                                              struct cell *c) {
-
-  /* Some constants */
-  const struct engine *const e = r->e;
-  struct gravity_cache *const ci_cache = &r->ci_gravity_cache;
-
-  /* Cell properties */
-  const int gcount = c->gcount;
-  struct gpart *restrict gparts = c->gparts;
-  const int c_active = cell_is_active_gravity(c, e);
-  const double loc[3] = {c->loc[0] + 0.5 * c->width[0],
-                         c->loc[1] + 0.5 * c->width[1],
-                         c->loc[2] + 0.5 * c->width[2]};
-
-  /* Anything to do here ?*/
-  if (!c_active) return;
-
-#ifdef SWIFT_DEBUG_CHECKS
-  /* Check that we fit in cache */
-  if (gcount > ci_cache->count)
-    error("Not enough space in the cache! gcount=%d", gcount);
-#endif
-
-  /* Computed the padded counts */
-  const int gcount_padded = gcount - (gcount % VEC_SIZE) + VEC_SIZE;
-
-  gravity_cache_populate_no_mpole(e->max_active_bin, ci_cache, gparts, gcount,
-                                  gcount_padded, loc, c, e->gravity_properties);
-
-  /* Ok... Here we go ! */
+static INLINE void runner_doself_grav_pp_full(
+    struct gravity_cache *restrict ci_cache, const int gcount,
+    const int gcount_padded, const struct engine *e, struct gpart *gparts) {
 
   /* Loop over all particles in ci... */
   for (int pid = 0; pid < gcount; pid++) {
@@ -719,9 +878,10 @@ static INLINE void runner_doself_grav_pp_full(struct runner *r,
       const float mass_j = ci_cache->m[pjd];
 
       /* Compute the pairwise (square) distance. */
-      const float dx = x_i - x_j;
-      const float dy = y_i - y_j;
-      const float dz = z_i - z_j;
+      /* Note: no need for periodic wrapping inside a cell */
+      const float dx = x_j - x_i;
+      const float dy = y_j - y_i;
+      const float dz = z_j - z_i;
       const float r2 = dx * dx + dy * dy + dz * dz;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -741,9 +901,9 @@ static INLINE void runner_doself_grav_pp_full(struct runner *r,
                                &pot_ij);
 
       /* Store it back */
-      a_x -= f_ij * dx;
-      a_y -= f_ij * dy;
-      a_z -= f_ij * dz;
+      a_x += f_ij * dx;
+      a_y += f_ij * dy;
+      a_z += f_ij * dz;
       pot += pot_ij;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -753,64 +913,41 @@ static INLINE void runner_doself_grav_pp_full(struct runner *r,
     }
 
     /* Store everything back in cache */
-    ci_cache->a_x[pid] = a_x;
-    ci_cache->a_y[pid] = a_y;
-    ci_cache->a_z[pid] = a_z;
-    ci_cache->pot[pid] = pot;
+    ci_cache->a_x[pid] += a_x;
+    ci_cache->a_y[pid] += a_y;
+    ci_cache->a_z[pid] += a_z;
+    ci_cache->pot[pid] += pot;
   }
-
-  /* Write back to the particles */
-  gravity_cache_write_back(ci_cache, gparts, gcount);
 }
 
 /**
- * @brief Computes the interaction of all the particles in a cell using the
- * truncated Newtonian potential.
+ * @brief Compute the truncated gravity interactions between all particles
+ * of a cell and the particles of the other cell.
  *
- * @param r The #runner.
- * @param c The #cell.
+ * The calculation is performed non-symmetrically using the pre-filled
+ * #gravity_cache structures. The loop over the j cache should auto-vectorize.
+ *
+ * This function only makes sense in periodic BCs.
  *
- * @todo Use a local cache for the particles.
+ * @param ci_cache #gravity_cache contaning the particles to be updated.
+ * @param gcount The number of particles in the cell.
+ * @param gcount_padded The number of particles in the cell padded to the
+ * vector length.
+ * @param r_s_inv The inverse of the gravity-mesh smoothing-scale.
+ *
+ * @param e The #engine (for debugging checks only).
+ * @param gparts The #gpart in the cell (for debugging checks only).
  */
-static INLINE void runner_doself_grav_pp_truncated(struct runner *r,
-                                                   struct cell *c) {
-
-  /* Some constants */
-  const struct engine *const e = r->e;
-  const struct space *s = e->s;
-  const double cell_width = s->width[0];
-  const double a_smooth = e->gravity_properties->a_smooth;
-  const double rlr = cell_width * a_smooth;
-  const float rlr_inv = 1. / rlr;
-
-  /* Caches to play with */
-  struct gravity_cache *const ci_cache = &r->ci_gravity_cache;
-
-  /* Cell properties */
-  const int gcount = c->gcount;
-  struct gpart *restrict gparts = c->gparts;
-  const int c_active = cell_is_active_gravity(c, e);
-  const double loc[3] = {c->loc[0] + 0.5 * c->width[0],
-                         c->loc[1] + 0.5 * c->width[1],
-                         c->loc[2] + 0.5 * c->width[2]};
-
-  /* Anything to do here ?*/
-  if (!c_active) return;
+static INLINE void runner_doself_grav_pp_truncated(
+    struct gravity_cache *restrict ci_cache, const int gcount,
+    const int gcount_padded, const float r_s_inv, const struct engine *e,
+    struct gpart *gparts) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  /* Check that we fit in cache */
-  if (gcount > ci_cache->count)
-    error("Not enough space in the caches! gcount=%d", gcount);
+  if (!e->s->periodic)
+    error("Calling truncated PP function in non-periodic setup.");
 #endif
 
-  /* Computed the padded counts */
-  const int gcount_padded = gcount - (gcount % VEC_SIZE) + VEC_SIZE;
-
-  gravity_cache_populate_no_mpole(e->max_active_bin, ci_cache, gparts, gcount,
-                                  gcount_padded, loc, c, e->gravity_properties);
-
-  /* Ok... Here we go ! */
-
   /* Loop over all particles in ci... */
   for (int pid = 0; pid < gcount; pid++) {
 
@@ -850,9 +987,11 @@ static INLINE void runner_doself_grav_pp_truncated(struct runner *r,
       const float mass_j = ci_cache->m[pjd];
 
       /* Compute the pairwise (square) distance. */
-      const float dx = x_i - x_j;
-      const float dy = y_i - y_j;
-      const float dz = z_i - z_j;
+      /* Note: no need for periodic wrapping inside a cell */
+      const float dx = x_j - x_i;
+      const float dy = y_j - y_i;
+      const float dz = z_j - z_i;
+
       const float r2 = dx * dx + dy * dy + dz * dz;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -869,12 +1008,21 @@ static INLINE void runner_doself_grav_pp_truncated(struct runner *r,
       /* Interact! */
       float f_ij, pot_ij;
       runner_iact_grav_pp_truncated(r2, h2_i, h_inv_i, h_inv3_i, mass_j,
-                                    rlr_inv, &f_ij, &pot_ij);
+                                    r_s_inv, &f_ij, &pot_ij);
+
+      /* if (e->s->parts[-gparts[pid].id_or_neg_offset].id == ICHECK) { */
+      /*   if (pjd < gcount) */
+      /*     message("Interacting with particle ID= %lld f_ij=%e", */
+      /*             e->s->parts[-gparts[pjd].id_or_neg_offset].id, f_ij); */
+      /*   // else */
+      /*   //  message("Interacting with particle ID= %lld (padded) f_ij=%e", */
+      /*   //  e->s->parts[-gparts[pjd].id_or_neg_offset].id, f_ij); */
+      /* } */
 
       /* Store it back */
-      a_x -= f_ij * dx;
-      a_y -= f_ij * dy;
-      a_z -= f_ij * dz;
+      a_x += f_ij * dx;
+      a_y += f_ij * dy;
+      a_z += f_ij * dz;
       pot += pot_ij;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -884,33 +1032,34 @@ static INLINE void runner_doself_grav_pp_truncated(struct runner *r,
     }
 
     /* Store everything back in cache */
-    ci_cache->a_x[pid] = a_x;
-    ci_cache->a_y[pid] = a_y;
-    ci_cache->a_z[pid] = a_z;
-    ci_cache->pot[pid] = pot;
+    ci_cache->a_x[pid] += a_x;
+    ci_cache->a_y[pid] += a_y;
+    ci_cache->a_z[pid] += a_z;
+    ci_cache->pot[pid] += pot;
   }
-
-  /* Write back to the particles */
-  gravity_cache_write_back(ci_cache, gparts, gcount);
 }
 
 /**
- * @brief Computes the interaction of all the particles in a cell directly
- * (Switching function between truncated and full)
+ * @brief Computes the interaction of all the particles in a cell with all the
+ * other ones.
+ *
+ * This function switches between the full potential and the truncated one
+ * depending on needs.
+ *
+ * This function starts by constructing the require #gravity_cache for the
+ * cell and then call the specialised functions doing the actual work on
+ * the cache. It then write the data back to the particles.
  *
  * @param r The #runner.
  * @param c The #cell.
  */
 static INLINE void runner_doself_grav_pp(struct runner *r, struct cell *c) {
 
-  /* Some properties of the space */
+  /* Recover some useful constants */
   const struct engine *e = r->e;
-  const struct space *s = e->s;
-  const int periodic = s->periodic;
-  const double cell_width = s->width[0];
-  const double a_smooth = e->gravity_properties->a_smooth;
-  const double r_cut_min = e->gravity_properties->r_cut_min;
-  const double min_trunc = cell_width * r_cut_min * a_smooth;
+  const int periodic = e->mesh->periodic;
+  const float r_s_inv = e->mesh->r_s_inv;
+  const double min_trunc = e->mesh->r_cut_min;
 
   TIMER_TIC;
 
@@ -927,49 +1076,218 @@ static INLINE void runner_doself_grav_pp(struct runner *r, struct cell *c) {
   /* Do we need to start by drifting things ? */
   if (!cell_are_gpart_drifted(c, e)) error("Un-drifted gparts");
 
+  /* Start by constructing a cache for the particles */
+  struct gravity_cache *const ci_cache = &r->ci_gravity_cache;
+
+  /* Shift to apply to the particles in the cell */
+  const double loc[3] = {c->loc[0] + 0.5 * c->width[0],
+                         c->loc[1] + 0.5 * c->width[1],
+                         c->loc[2] + 0.5 * c->width[2]};
+
+  /* Computed the padded counts */
+  const int gcount = c->gcount;
+  const int gcount_padded = gcount - (gcount % VEC_SIZE) + VEC_SIZE;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Check that we fit in cache */
+  if (gcount > ci_cache->count)
+    error("Not enough space in the cache! gcount=%d", gcount);
+#endif
+
+  /* Fill the cache */
+  gravity_cache_populate_no_mpole(e->max_active_bin, ci_cache, c->gparts,
+                                  gcount, gcount_padded, loc, c,
+                                  e->gravity_properties);
+
   /* Can we use the Newtonian version or do we need the truncated one ? */
   if (!periodic) {
-    runner_doself_grav_pp_full(r, c);
+
+    /* Not periodic -> Can always use Newtonian potential */
+    runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e, c->gparts);
+
   } else {
 
     /* Get the maximal distance between any two particles */
     const double max_r = 2. * c->multipole->r_max;
 
     /* Do we need to use the truncated interactions ? */
-    if (max_r > min_trunc)
-      runner_doself_grav_pp_truncated(r, c);
-    else
-      runner_doself_grav_pp_full(r, c);
+    if (max_r > min_trunc) {
+
+      /* Periodic but far-away cells must use the truncated potential */
+      runner_doself_grav_pp_truncated(ci_cache, gcount, gcount_padded, r_s_inv,
+                                      e, c->gparts);
+
+    } else {
+
+      /* Periodic but close-by cells can use the full Newtonian potential */
+      runner_doself_grav_pp_full(ci_cache, gcount, gcount_padded, e, c->gparts);
+    }
   }
 
+  /* Write back to the particles */
+  gravity_cache_write_back(ci_cache, c->gparts, gcount);
+
   TIMER_TOC(timer_doself_grav_pp);
 }
 
+/**
+ * @brief Computes the interaction of the field tensor in a cell with the
+ * multipole of another cell.
+ *
+ * @param r The #runner.
+ * @param ci The #cell with field tensor to interact.
+ * @param cj The #cell with the multipole.
+ */
+static INLINE void runner_dopair_grav_mm(struct runner *r,
+                                         struct cell *restrict ci,
+                                         struct cell *restrict cj) {
+
+  /* Some constants */
+  const struct engine *e = r->e;
+  const struct gravity_props *props = e->gravity_properties;
+  const int periodic = e->mesh->periodic;
+  const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]};
+  const float r_s_inv = e->mesh->r_s_inv;
+
+  TIMER_TIC;
+
+  /* Anything to do here? */
+  if (!cell_is_active_gravity(ci, e) || ci->nodeID != engine_rank) return;
+
+  /* Short-cut to the multipole */
+  const struct multipole *multi_j = &cj->multipole->m_pole;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (ci == cj) error("Interacting a cell with itself using M2L");
+
+  if (multi_j->num_gpart == 0)
+    error("Multipole does not seem to have been set.");
+
+  if (ci->multipole->pot.ti_init != e->ti_current)
+    error("ci->grav tensor not initialised.");
+#endif
+
+  /* Do we need to drift the multipole ? */
+  if (cj->ti_old_multipole != e->ti_current)
+    error(
+        "Undrifted multipole cj->ti_old_multipole=%lld cj->nodeID=%d "
+        "ci->nodeID=%d e->ti_current=%lld",
+        cj->ti_old_multipole, cj->nodeID, ci->nodeID, e->ti_current);
+
+  /* Let's interact at this level */
+  gravity_M2L(&ci->multipole->pot, multi_j, ci->multipole->CoM,
+              cj->multipole->CoM, props, periodic, dim, r_s_inv);
+
+  TIMER_TOC(timer_dopair_grav_mm);
+}
+
+static INLINE void runner_dopair_recursive_grav_pm(struct runner *r,
+                                                   struct cell *ci,
+                                                   const struct cell *cj) {
+  /* Some constants */
+  const struct engine *e = r->e;
+  const int periodic = e->mesh->periodic;
+  const float dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]};
+  const float r_s_inv = e->mesh->r_s_inv;
+
+  /* Anything to do here? */
+  if (!(cell_is_active_gravity(ci, e) && ci->nodeID == e->nodeID)) return;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Early abort? */
+  if (ci->gcount == 0 || cj->gcount == 0)
+    error("Doing pair gravity on an empty cell !");
+
+  /* Sanity check */
+  if (ci == cj) error("Pair interaction between a cell and itself.");
+
+  if (cj->ti_old_multipole != e->ti_current)
+    error("cj->multipole not drifted.");
+#endif
+
+  /* Can we recurse further? */
+  if (ci->split) {
+
+    /* Loop over ci's children */
+    for (int k = 0; k < 8; k++) {
+      if (ci->progeny[k] != NULL)
+        runner_dopair_recursive_grav_pm(r, ci->progeny[k], cj);
+    }
+
+    /* Ok, let's do the interaction here */
+  } else {
+
+    /* Start by constructing particle caches */
+
+    /* Cache to play with */
+    struct gravity_cache *const ci_cache = &r->ci_gravity_cache;
+
+    /* Computed the padded counts */
+    const int gcount_i = ci->gcount;
+    const int gcount_padded_i = gcount_i - (gcount_i % VEC_SIZE) + VEC_SIZE;
+
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Check that we fit in cache */
+    if (gcount_i > ci_cache->count)
+      error("Not enough space in the cache! gcount_i=%d", gcount_i);
+#endif
+
+    /* Recover the multipole info and the CoM locations */
+    const struct multipole *multi_j = &cj->multipole->m_pole;
+    const float r_max = cj->multipole->r_max;
+    const float CoM_j[3] = {(float)(cj->multipole->CoM[0]),
+                            (float)(cj->multipole->CoM[1]),
+                            (float)(cj->multipole->CoM[2])};
+
+    /* Fill the cache */
+    gravity_cache_populate_all_mpole(
+        e->max_active_bin, periodic, dim, ci_cache, ci->gparts, gcount_i,
+        gcount_padded_i, ci, CoM_j, r_max * r_max, e->gravity_properties);
+
+    /* Can we use the Newtonian version or do we need the truncated one ? */
+    if (!periodic) {
+
+      runner_dopair_grav_pm_full(ci_cache, gcount_padded_i, CoM_j, multi_j,
+                                 periodic, dim, e, ci->gparts, gcount_i, cj);
+
+    } else {
+
+      runner_dopair_grav_pm_truncated(ci_cache, gcount_padded_i, CoM_j, multi_j,
+                                      dim, r_s_inv, e, ci->gparts, gcount_i,
+                                      cj);
+    }
+
+    /* Write back to the particles */
+    gravity_cache_write_back(ci_cache, ci->gparts, gcount_i);
+  }
+}
+
 /**
  * @brief Computes the interaction of all the particles in a cell with all the
  * particles of another cell.
  *
+ * This function will try to recurse as far down the tree as possible and only
+ * default to direct summation if there is no better option.
+ *
+ * If using periodic BCs, we will abort the recursion if th distance between the
+ * cells is larger than the set threshold.
+ *
  * @param r The #runner.
  * @param ci The first #cell.
  * @param cj The other #cell.
  * @param gettimer Are we timing this ?
- *
- * @todo Use a local cache for the particles.
  */
-static INLINE void runner_dopair_grav(struct runner *r, struct cell *ci,
-                                      struct cell *cj, int gettimer) {
+static INLINE void runner_dopair_recursive_grav(struct runner *r,
+                                                struct cell *ci,
+                                                struct cell *cj, int gettimer) {
 
   /* Some constants */
   const struct engine *e = r->e;
-  const struct space *s = e->s;
   const int nodeID = e->nodeID;
-  const int periodic = s->periodic;
-  const double cell_width = s->width[0];
-  const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
-  const struct gravity_props *props = e->gravity_properties;
-  const double theta_crit2 = props->theta_crit2;
-  const double max_distance = props->a_smooth * props->r_cut_max * cell_width;
-  const double max_distance2 = max_distance * max_distance;
+  const int periodic = e->mesh->periodic;
+  const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]};
+  const double theta_crit2 = e->gravity_properties->theta_crit2;
+  const double max_distance = e->mesh->r_cut_max;
 
   /* Anything to do here? */
   if (!((cell_is_active_gravity(ci, e) && ci->nodeID == nodeID) ||
@@ -1013,8 +1331,11 @@ static INLINE void runner_dopair_grav(struct runner *r, struct cell *ci,
   }
   const double r2 = dx * dx + dy * dy + dz * dz;
 
+  /* Minimal distance between any 2 particles in the two cells */
+  const double r_lr_check = sqrt(r2) - (multi_i->r_max + multi_j->r_max);
+
   /* Are we beyond the distance where the truncated forces are 0? */
-  if (periodic && r2 > max_distance2) {
+  if (periodic && r_lr_check > max_distance) {
 
 #ifdef SWIFT_DEBUG_CHECKS
     /* Need to account for the interactions we missed */
@@ -1035,13 +1356,16 @@ static INLINE void runner_dopair_grav(struct runner *r, struct cell *ci,
     /* MATTHIEU: make a symmetric M-M interaction function ! */
     runner_dopair_grav_mm(r, ci, cj);
     runner_dopair_grav_mm(r, cj, ci);
-  }
-  /* We have two leaves. Go P-P. */
-  else if (!ci->split && !cj->split) {
-    runner_dopair_grav_pp(r, ci, cj);
-  }
-  /* Alright, we'll have to split and recurse. */
-  else {
+
+  } else if (!ci->split && !cj->split) {
+
+    /* We have two leaves. Go P-P. */
+    runner_dopair_grav_pp(r, ci, cj, /*symmetric*/ 1, /*allow_mpoles*/ 1);
+
+  } else {
+
+    /* Alright, we'll have to split and recurse. */
+    /* We know at least one of ci and cj is splittable */
 
     const double ri_max = multi_i->r_max;
     const double rj_max = multi_j->r_max;
@@ -1055,20 +1379,19 @@ static INLINE void runner_dopair_grav(struct runner *r, struct cell *ci,
         /* Loop over ci's children */
         for (int k = 0; k < 8; k++) {
           if (ci->progeny[k] != NULL)
-            runner_dopair_grav(r, ci->progeny[k], cj, 0);
+            runner_dopair_recursive_grav(r, ci->progeny[k], cj, 0);
         }
 
-      } else if (cj->split) {
+      } else {
+        /* cj is split */
+
         /* MATTHIEU: This could maybe be replaced by P-M interactions ?  */
 
         /* Loop over cj's children */
         for (int k = 0; k < 8; k++) {
           if (cj->progeny[k] != NULL)
-            runner_dopair_grav(r, ci, cj->progeny[k], 0);
+            runner_dopair_recursive_grav(r, ci, cj->progeny[k], 0);
         }
-
-      } else {
-        error("Fundamental error in the logic");
       }
     } else {
 
@@ -1078,20 +1401,19 @@ static INLINE void runner_dopair_grav(struct runner *r, struct cell *ci,
         /* Loop over cj's children */
         for (int k = 0; k < 8; k++) {
           if (cj->progeny[k] != NULL)
-            runner_dopair_grav(r, ci, cj->progeny[k], 0);
+            runner_dopair_recursive_grav(r, ci, cj->progeny[k], 0);
         }
 
-      } else if (ci->split) {
+      } else {
+        /* ci is split */
+
         /* MATTHIEU: This could maybe be replaced by P-M interactions ?  */
 
         /* Loop over ci's children */
         for (int k = 0; k < 8; k++) {
           if (ci->progeny[k] != NULL)
-            runner_dopair_grav(r, ci->progeny[k], cj, 0);
+            runner_dopair_recursive_grav(r, ci->progeny[k], cj, 0);
         }
-
-      } else {
-        error("Fundamental error in the logic");
       }
     }
   }
@@ -1100,16 +1422,17 @@ static INLINE void runner_dopair_grav(struct runner *r, struct cell *ci,
 }
 
 /**
- * @brief Computes the interaction of all the particles in a cell
+ * @brief Computes the interaction of all the particles in a cell.
+ *
+ * This function will try to recurse as far down the tree as possible and only
+ * default to direct summation if there is no better option.
  *
  * @param r The #runner.
  * @param c The first #cell.
  * @param gettimer Are we timing this ?
- *
- * @todo Use a local cache for the particles.
  */
-static INLINE void runner_doself_grav(struct runner *r, struct cell *c,
-                                      int gettimer) {
+static INLINE void runner_doself_recursive_grav(struct runner *r,
+                                                struct cell *c, int gettimer) {
 
   /* Some constants */
   const struct engine *e = r->e;
@@ -1131,12 +1454,12 @@ static INLINE void runner_doself_grav(struct runner *r, struct cell *c,
     for (int j = 0; j < 8; j++) {
       if (c->progeny[j] != NULL) {
 
-        runner_doself_grav(r, c->progeny[j], 0);
+        runner_doself_recursive_grav(r, c->progeny[j], 0);
 
         for (int k = j + 1; k < 8; k++) {
           if (c->progeny[k] != NULL) {
 
-            runner_dopair_grav(r, c->progeny[j], c->progeny[k], 0);
+            runner_dopair_recursive_grav(r, c->progeny[j], c->progeny[k], 0);
           }
         }
       }
@@ -1165,20 +1488,16 @@ static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci,
 
   /* Some constants */
   const struct engine *e = r->e;
-  const struct space *s = e->s;
-  const struct gravity_props *props = e->gravity_properties;
-  const int periodic = s->periodic;
-  const double cell_width = s->width[0];
-  const double dim[3] = {s->dim[0], s->dim[1], s->dim[2]};
-  const double theta_crit2 = props->theta_crit2;
-  const double max_distance = props->a_smooth * props->r_cut_max * cell_width;
-  const double max_distance2 = max_distance * max_distance;
+  const int periodic = e->mesh->periodic;
+  const double dim[3] = {e->mesh->dim[0], e->mesh->dim[1], e->mesh->dim[2]};
+  const double theta_crit2 = e->gravity_properties->theta_crit2;
+  const double max_distance = e->mesh->r_cut_max;
 
   TIMER_TIC;
 
   /* Recover the list of top-level cells */
-  struct cell *cells = s->cells_top;
-  const int nr_cells = s->nr_cells;
+  struct cell *cells = e->s->cells_top;
+  const int nr_cells = e->s->nr_cells;
 
   /* Anything to do here? */
   if (!cell_is_active_gravity(ci, e)) return;
@@ -1192,11 +1511,13 @@ static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci,
 
   /* Recover the local multipole */
   struct gravity_tensors *const multi_i = ci->multipole;
-  const double CoM_i[3] = {multi_i->CoM[0], multi_i->CoM[1], multi_i->CoM[2]};
   const double CoM_rebuild_i[3] = {multi_i->CoM_rebuild[0],
                                    multi_i->CoM_rebuild[1],
                                    multi_i->CoM_rebuild[2]};
 
+  /* Flag that contributions will be recieved */
+  multi_i->pot.interacted = 1;
+
   /* Loop over all the top-level cells and go for a M-M interaction if
    * well-separated */
   for (int n = 0; n < nr_cells; ++n) {
@@ -1224,43 +1545,27 @@ static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci,
     }
     const double r2_rebuild = dx_r * dx_r + dy_r * dy_r + dz_r * dz_r;
 
-    /* Are we in charge of this cell pair? */
-    if (gravity_M2L_accept(multi_i->r_max_rebuild, multi_j->r_max_rebuild,
-                           theta_crit2, r2_rebuild)) {
-
-      /* Let's compute the current distance between the cell pair*/
-      double dx = CoM_i[0] - multi_j->CoM[0];
-      double dy = CoM_i[1] - multi_j->CoM[1];
-      double dz = CoM_i[2] - multi_j->CoM[2];
+    const double max_radius =
+        sqrt(r2_rebuild) - (multi_i->r_max_rebuild + multi_j->r_max_rebuild);
 
-      /* Apply BC */
-      if (periodic) {
-        dx = nearest(dx, dim[0]);
-        dy = nearest(dy, dim[1]);
-        dz = nearest(dz, dim[2]);
-      }
-      const double r2 = dx * dx + dy * dy + dz * dz;
-
-      /* Are we beyond the distance where the truncated forces are 0 ?*/
-      if (periodic && r2 > max_distance2) {
+    /* Are we beyond the distance where the truncated forces are 0 ?*/
+    if (periodic && max_radius > max_distance) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-        /* Need to account for the interactions we missed */
-        multi_i->pot.num_interacted += multi_j->m_pole.num_gpart;
+      /* Need to account for the interactions we missed */
+      multi_i->pot.num_interacted += multi_j->m_pole.num_gpart;
 #endif
-        continue;
-      }
+      continue;
+    }
 
-      /* Check the multipole acceptance criterion */
-      if (gravity_M2L_accept(multi_i->r_max, multi_j->r_max, theta_crit2, r2)) {
+    /* Are we in charge of this cell pair? */
+    if (gravity_M2L_accept(multi_i->r_max_rebuild, multi_j->r_max_rebuild,
+                           theta_crit2, r2_rebuild)) {
+
+      /* Call the PM interaction fucntion on the active sub-cells of ci */
+      runner_dopair_grav_mm(r, ci, cj);
+      // runner_dopair_recursive_grav_pm(r, ci, cj);
 
-        /* Go for a (non-symmetric) M-M calculation */
-        runner_dopair_grav_mm(r, ci, cj);
-      } else {
-        /* Alright, we have to take charge of that pair in a different way. */
-        // MATTHIEU: We should actually open the tree-node here and recurse.
-        runner_dopair_grav_mm(r, ci, cj);
-      }
     } /* We are in charge of this pair */
   }   /* Loop over top-level cells */
 
diff --git a/src/scheduler.c b/src/scheduler.c
index 151304293749a29abe9bd9680d8f8f81bc845884..da38255b79431cc3c181c4e435204a33111a7257 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -48,6 +48,7 @@
 #include "queue.h"
 #include "sort_part.h"
 #include "space.h"
+#include "space_getsid.h"
 #include "task.h"
 #include "timers.h"
 #include "version.h"
@@ -262,7 +263,7 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose) {
           if (type == task_type_self + k && subtype == task_subtype_grav)
             gravity_cluster[k] = 1;
         }
-        if (type == task_type_grav_top_level) gravity_cluster[2] = 1;
+        if (type == task_type_grav_mesh) gravity_cluster[2] = 1;
         if (type == task_type_grav_long_range) gravity_cluster[3] = 1;
       }
     }
@@ -303,7 +304,7 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose) {
       fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k],
               subtaskID_names[task_subtype_grav]);
   if (gravity_cluster[2])
-    fprintf(f, "\t\t %s;\n", taskID_names[task_type_grav_top_level]);
+    fprintf(f, "\t\t %s;\n", taskID_names[task_type_grav_mesh]);
   if (gravity_cluster[3])
     fprintf(f, "\t\t %s;\n", taskID_names[task_type_grav_long_range]);
   fprintf(f, "\t};\n");
@@ -900,9 +901,7 @@ void scheduler_splittasks_mapper(void *map_data, int num_elements,
       scheduler_splittask_gravity(t, s);
     } else if (t->subtype == task_subtype_grav) {
       scheduler_splittask_gravity(t, s);
-    } else if (t->type == task_type_grav_top_level ||
-               t->type == task_type_grav_ghost_in ||
-               t->type == task_type_grav_ghost_out) {
+    } else if (t->type == task_type_grav_mesh) {
       /* For future use */
     } else {
       error("Unexpected task sub-type");
@@ -1186,103 +1185,105 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
   /* Run through the tasks backwards and set their weights. */
   for (int k = nr_tasks - 1; k >= 0; k--) {
     struct task *t = &tasks[tid[k]];
-    t->weight = 0;
+    t->weight = 0.f;
     for (int j = 0; j < t->nr_unlock_tasks; j++)
       if (t->unlock_tasks[j]->weight > t->weight)
         t->weight = t->unlock_tasks[j]->weight;
-    int cost = 0;
+    float cost = 0.f;
+
+    const float count_i = (t->ci != NULL) ? t->ci->count : 0.f;
+    const float count_j = (t->cj != NULL) ? t->cj->count : 0.f;
+    const float gcount_i = (t->ci != NULL) ? t->ci->gcount : 0.f;
+    const float gcount_j = (t->cj != NULL) ? t->cj->gcount : 0.f;
+
     switch (t->type) {
       case task_type_sort:
-        cost = wscale * intrinsics_popcount(t->flags) * t->ci->count *
+        cost = wscale * intrinsics_popcount(t->flags) * count_i *
                (sizeof(int) * 8 - intrinsics_clz(t->ci->count));
         break;
 
       case task_type_self:
         if (t->subtype == task_subtype_grav)
-          cost = 1.f * (wscale * t->ci->gcount) * t->ci->gcount;
+          cost = 1.f * (wscale * gcount_i) * gcount_i;
         else if (t->subtype == task_subtype_external_grav)
-          cost = 1.f * wscale * t->ci->gcount;
+          cost = 1.f * wscale * gcount_i;
         else
-          cost = 1.f * (wscale * t->ci->count) * t->ci->count;
+          cost = 1.f * (wscale * count_i) * count_i;
         break;
 
       case task_type_pair:
         if (t->subtype == task_subtype_grav) {
           if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID)
-            cost = 3.f * (wscale * t->ci->gcount) * t->cj->gcount;
+            cost = 3.f * (wscale * gcount_i) * gcount_j;
           else
-            cost = 2.f * (wscale * t->ci->gcount) * t->cj->gcount;
+            cost = 2.f * (wscale * gcount_i) * gcount_j;
         } else {
           if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID)
-            cost = 3.f * (wscale * t->ci->count) * t->cj->count *
-                   sid_scale[t->flags];
+            cost = 3.f * (wscale * count_i) * count_j * sid_scale[t->flags];
           else
-            cost = 2.f * (wscale * t->ci->count) * t->cj->count *
-                   sid_scale[t->flags];
+            cost = 2.f * (wscale * count_i) * count_j * sid_scale[t->flags];
         }
         break;
 
       case task_type_sub_pair:
         if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) {
           if (t->flags < 0)
-            cost = 3.f * (wscale * t->ci->count) * t->cj->count;
+            cost = 3.f * (wscale * count_i) * count_j;
           else
-            cost = 3.f * (wscale * t->ci->count) * t->cj->count *
-                   sid_scale[t->flags];
+            cost = 3.f * (wscale * count_i) * count_j * sid_scale[t->flags];
         } else {
           if (t->flags < 0)
-            cost = 2.f * (wscale * t->ci->count) * t->cj->count;
+            cost = 2.f * (wscale * count_i) * count_j;
           else
-            cost = 2.f * (wscale * t->ci->count) * t->cj->count *
-                   sid_scale[t->flags];
+            cost = 2.f * (wscale * count_i) * count_j * sid_scale[t->flags];
         }
         break;
 
       case task_type_sub_self:
-        cost = 1.f * (wscale * t->ci->count) * t->ci->count;
+        cost = 1.f * (wscale * count_i) * count_i;
         break;
       case task_type_ghost:
-        if (t->ci == t->ci->super_hydro) cost = wscale * t->ci->count;
+        if (t->ci == t->ci->super_hydro) cost = wscale * count_i;
         break;
       case task_type_extra_ghost:
-        if (t->ci == t->ci->super_hydro) cost = wscale * t->ci->count;
+        if (t->ci == t->ci->super_hydro) cost = wscale * count_i;
         break;
       case task_type_drift_part:
-        cost = wscale * t->ci->count;
+        cost = wscale * count_i;
         break;
       case task_type_drift_gpart:
-        cost = wscale * t->ci->gcount;
+        cost = wscale * gcount_i;
         break;
       case task_type_init_grav:
-        cost = wscale * t->ci->gcount;
+        cost = wscale * gcount_i;
         break;
       case task_type_grav_down:
-        cost = wscale * t->ci->gcount;
+        cost = wscale * gcount_i;
         break;
       case task_type_grav_long_range:
-        cost = wscale * t->ci->gcount;
+        cost = wscale * gcount_i;
         break;
       case task_type_end_force:
-        cost = wscale * t->ci->count + wscale * t->ci->gcount;
+        cost = wscale * count_i + wscale * gcount_i;
         break;
       case task_type_kick1:
-        cost = wscale * t->ci->count + wscale * t->ci->gcount;
+        cost = wscale * count_i + wscale * gcount_i;
         break;
       case task_type_kick2:
-        cost = wscale * t->ci->count + wscale * t->ci->gcount;
+        cost = wscale * count_i + wscale * gcount_i;
         break;
       case task_type_timestep:
-        cost = wscale * t->ci->count + wscale * t->ci->gcount;
+        cost = wscale * count_i + wscale * gcount_i;
         break;
       case task_type_send:
-        if (t->ci->count < 1e5)
-          cost = 10.f * (wscale * t->ci->count) * t->ci->count;
+        if (count_i < 1e5)
+          cost = 10.f * (wscale * count_i) * count_i;
         else
           cost = 2e9;
         break;
       case task_type_recv:
-        if (t->ci->count < 1e5)
-          cost = 5.f * (wscale * t->ci->count) * t->ci->count;
+        if (count_i < 1e5)
+          cost = 5.f * (wscale * count_i) * count_i;
         else
           cost = 1e9;
         break;
@@ -1290,6 +1291,7 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
         cost = 0;
         break;
     }
+
 #if defined(WITH_MPI) && defined(HAVE_METIS)
     t->cost = cost;
 #endif
diff --git a/src/serial_io.c b/src/serial_io.c
index ab0853794863adf809c35c19a9f56a6a153b839c..dafa75ab0baacb1b5ddeee34020c9773893bced7 100644
--- a/src/serial_io.c
+++ b/src/serial_io.c
@@ -49,14 +49,11 @@
 #include "io_properties.h"
 #include "kernel_hydro.h"
 #include "part.h"
+#include "part_type.h"
 #include "stars_io.h"
 #include "units.h"
 #include "xmf.h"
 
-/*-----------------------------------------------------------------------------
- * Routines reading an IC file
- *-----------------------------------------------------------------------------*/
-
 /**
  * @brief Reads a data array from a given HDF5 group.
  *
@@ -68,7 +65,10 @@
  * @param internal_units The #unit_system used internally
  * @param ic_units The #unit_system used in the ICs
  * @param cleanup_h Are we removing h-factors from the ICs?
+ * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget
+ * IC velocities?
  * @param h The value of the reduced Hubble constant to use for cleaning.
+ * @param a The current value of the scale-factor.
  *
  * @todo A better version using HDF5 hyper-slabs to read the file directly into
  * the part array will be written once the structures have been stabilized.
@@ -76,7 +76,8 @@
 void readArray(hid_t grp, const struct io_props props, size_t N,
                long long N_total, long long offset,
                const struct unit_system* internal_units,
-               const struct unit_system* ic_units, int cleanup_h, double h) {
+               const struct unit_system* ic_units, int cleanup_h,
+               int cleanup_sqrt_a, double h, double a) {
 
   const size_t typeSize = io_sizeof_type(props.type);
   const size_t copySize = typeSize * props.dimension;
@@ -158,20 +159,35 @@ void readArray(hid_t grp, const struct io_props props, size_t N,
   /* Clean-up h if necessary */
   const float h_factor_exp = units_h_factor(internal_units, props.units);
   if (cleanup_h && h_factor_exp != 0.f && exist != 0) {
-    const double h_factor = pow(h, h_factor_exp);
 
     /* message("Multipltying '%s' by h^%f=%f", props.name, h_factor_exp,
      * h_factor); */
 
     if (io_is_double_precision(props.type)) {
       double* temp_d = (double*)temp;
+      const double h_factor = pow(h, h_factor_exp);
       for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= h_factor;
     } else {
       float* temp_f = (float*)temp;
+      const float h_factor = pow(h, h_factor_exp);
       for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= h_factor;
     }
   }
 
+  /* Clean-up a if necessary */
+  if (cleanup_sqrt_a && a != 1. && (strcmp(props.name, "Velocities") == 0)) {
+
+    if (io_is_double_precision(props.type)) {
+      double* temp_d = (double*)temp;
+      const double vel_factor = sqrt(a);
+      for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= vel_factor;
+    } else {
+      float* temp_f = (float*)temp;
+      const float vel_factor = sqrt(a);
+      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= vel_factor;
+    }
+  }
+
   /* Copy temporary buffer to particle data */
   char* temp_c = temp;
   for (size_t i = 0; i < N; ++i)
@@ -184,10 +200,6 @@ void readArray(hid_t grp, const struct io_props props, size_t N,
   H5Dclose(h_data);
 }
 
-/*-----------------------------------------------------------------------------
- * Routines writing an output file
- *-----------------------------------------------------------------------------*/
-
 void prepareArray(const struct engine* e, hid_t grp, char* fileName,
                   FILE* xmfFile, char* partTypeGroupName,
                   const struct io_props props, unsigned long long N_total,
@@ -233,6 +245,11 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName,
     error("Error while setting chunk size (%llu, %llu) for field '%s'.",
           chunk_shape[0], chunk_shape[1], props.name);
 
+  /* Impose check-sum to verify data corruption */
+  h_err = H5Pset_fletcher32(h_prop);
+  if (h_err < 0)
+    error("Error while setting checksum options for field '%s'.", props.name);
+
   /* Impose data compression */
   if (e->snapshot_compression > 0) {
     h_err = H5Pset_shuffle(h_prop);
@@ -387,7 +404,10 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
  * @param with_gravity Are we reading/creating #gpart arrays ?
  * @param with_stars Are we reading star particles ?
  * @param cleanup_h Are we cleaning-up h-factors from the quantities we read?
+ * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget
+ * IC velocities?
  * @param h The value of the reduced Hubble constant to use for correction.
+ * @param a The current value of the scale-factor.
  * @param mpi_rank The MPI rank of this node
  * @param mpi_size The number of MPI ranks
  * @param comm The MPI communicator
@@ -408,8 +428,9 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
                     struct spart** sparts, size_t* Ngas, size_t* Ngparts,
                     size_t* Nstars, int* periodic, int* flag_entropy,
                     int with_hydro, int with_gravity, int with_stars,
-                    int cleanup_h, double h, int mpi_rank, int mpi_size,
-                    MPI_Comm comm, MPI_Info info, int n_threads, int dry_run) {
+                    int cleanup_h, int cleanup_sqrt_a, double h, double a,
+                    int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
+                    int n_threads, int dry_run) {
 
   hid_t h_file = 0, h_grp = 0;
   /* GADGET has only cubic boxes (in cosmological mode) */
@@ -497,7 +518,7 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
 
     /* Read the unit system used in the ICs */
     if (ic_units == NULL) error("Unable to allocate memory for IC unit system");
-    io_read_unit_system(h_file, ic_units, mpi_rank);
+    io_read_unit_system(h_file, ic_units, internal_units, mpi_rank);
 
     if (units_are_equal(ic_units, internal_units)) {
 
@@ -650,7 +671,8 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
         if (!dry_run)
           for (int i = 0; i < num_fields; ++i)
             readArray(h_grp, list[i], Nparticles, N_total[ptype], offset[ptype],
-                      internal_units, ic_units, cleanup_h, h);
+                      internal_units, ic_units, cleanup_h, cleanup_sqrt_a, h,
+                      a);
 
         /* Close particle group */
         H5Gclose(h_grp);
@@ -727,6 +749,7 @@ void write_output_serial(struct engine* e, const char* baseName,
   struct gpart* dmparts = NULL;
   const struct spart* sparts = e->s->sparts;
   const struct cooling_function_data* cooling = e->cooling_func;
+  struct swift_params* params = e->parameter_file;
   FILE* xmfFile = 0;
 
   /* Number of unassociated gparts */
@@ -734,8 +757,12 @@ void write_output_serial(struct engine* e, const char* baseName,
 
   /* File name */
   char fileName[FILENAME_BUFFER_SIZE];
-  snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName,
-           e->snapshot_output_count);
+  if (e->snapshot_label_delta == 1)
+    snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName,
+             e->snapshot_output_count);
+  else
+    snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%06i.hdf5", baseName,
+             e->snapshot_output_count * e->snapshot_label_delta);
 
   /* Compute offset in the file and total number of particles */
   size_t N[swift_type_count] = {Ngas, Ndm, 0, 0, Nstars, 0};
@@ -868,7 +895,14 @@ void write_output_serial(struct engine* e, const char* baseName,
     h_grp =
         H5Gcreate(h_file, "/Parameters", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
     if (h_grp < 0) error("Error while creating parameters group");
-    parser_write_params_to_hdf5(e->parameter_file, h_grp);
+    parser_write_params_to_hdf5(e->parameter_file, h_grp, 1);
+    H5Gclose(h_grp);
+
+    /* Print the runtime unused parameters */
+    h_grp = H5Gcreate(h_file, "/UnusedParameters", H5P_DEFAULT, H5P_DEFAULT,
+                      H5P_DEFAULT);
+    if (h_grp < 0) error("Error while creating parameters group");
+    parser_write_params_to_hdf5(e->parameter_file, h_grp, 0);
     H5Gclose(h_grp);
 
     /* Print the system of Units used in the spashot */
@@ -1001,11 +1035,20 @@ void write_output_serial(struct engine* e, const char* baseName,
             error("Particle Type %d not yet supported. Aborting", ptype);
         }
 
-        /* Write everything */
-        for (int i = 0; i < num_fields; ++i)
-          writeArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i],
-                     Nparticles, N_total[ptype], mpi_rank, offset[ptype],
-                     internal_units, snapshot_units);
+        /* Write everything that is not cancelled */
+        for (int i = 0; i < num_fields; ++i) {
+
+          /* Did the user cancel this field? */
+          char field[PARSER_MAX_LINE_SIZE];
+          sprintf(field, "SelectOutput:%s_%s", list[i].name,
+                  part_type_names[ptype]);
+          int should_write = parser_get_opt_param_int(params, field, 1);
+
+          if (should_write)
+            writeArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i],
+                       Nparticles, N_total[ptype], mpi_rank, offset[ptype],
+                       internal_units, snapshot_units);
+        }
 
         /* Free temporary array */
         if (dmparts) {
diff --git a/src/serial_io.h b/src/serial_io.h
index cad428916f400bc9b144dcf4c23f9d38c75c1e9d..6644e34bb32bcbd63250f25502563155eda0a293 100644
--- a/src/serial_io.h
+++ b/src/serial_io.h
@@ -39,8 +39,9 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
                     struct spart** sparts, size_t* Ngas, size_t* Ngparts,
                     size_t* Nstars, int* periodic, int* flag_entropy,
                     int with_hydro, int with_gravity, int with_stars,
-                    int cleanup_h, double h, int mpi_rank, int mpi_size,
-                    MPI_Comm comm, MPI_Info info, int nr_threads, int dry_run);
+                    int cleanup_h, int cleanup_sqrt_a, double h, double a,
+                    int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
+                    int nr_threads, int dry_run);
 
 void write_output_serial(struct engine* e, const char* baseName,
                          const struct unit_system* internal_units,
diff --git a/src/sign.h b/src/sign.h
index 04ecdb3a99c5baf35e40d6f8286d25102d22e5fd..a92875d5d6c5f319eb6019adb20aa34af86d620b 100644
--- a/src/sign.h
+++ b/src/sign.h
@@ -25,7 +25,7 @@
  * @param x The number of interest.
  * @return >0 if positive, 0 if negative.
  */
-__attribute__((always_inline)) INLINE static int signf(float x) {
+__attribute__((always_inline, const)) INLINE static int signf(float x) {
 #ifdef __GNUC__
   return !signbit(x);
 #else
@@ -39,7 +39,8 @@ __attribute__((always_inline)) INLINE static int signf(float x) {
  * @param x The first number
  * @param y The second number
  */
-__attribute__((always_inline)) INLINE static int same_signf(float x, float y) {
+__attribute__((always_inline, const)) INLINE static int same_signf(float x,
+                                                                   float y) {
   return signf(x) == signf(y);
 }
 
diff --git a/src/single_io.c b/src/single_io.c
index f5b7d331875175d35ed048ed605c9468f68e89a2..a0f02878b52c89beca94d15c09ef7d456ce0a4eb 100644
--- a/src/single_io.c
+++ b/src/single_io.c
@@ -48,14 +48,11 @@
 #include "io_properties.h"
 #include "kernel_hydro.h"
 #include "part.h"
+#include "part_type.h"
 #include "stars_io.h"
 #include "units.h"
 #include "xmf.h"
 
-/*-----------------------------------------------------------------------------
- * Routines reading an IC file
- *-----------------------------------------------------------------------------*/
-
 /**
  * @brief Reads a data array from a given HDF5 group.
  *
@@ -65,44 +62,48 @@
  * @param internal_units The #unit_system used internally
  * @param ic_units The #unit_system used in the ICs
  * @param cleanup_h Are we removing h-factors from the ICs?
- * @param The value of the reduced Hubble constant.
+ * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget
+ * IC velocities?
+ * @param h The value of the reduced Hubble constant.
+ * @param a The current value of the scale-factor.
  *
  * @todo A better version using HDF5 hyper-slabs to read the file directly into
  * the part array will be written once the structures have been stabilized.
  */
-void readArray(hid_t h_grp, const struct io_props prop, size_t N,
+void readArray(hid_t h_grp, const struct io_props props, size_t N,
                const struct unit_system* internal_units,
-               const struct unit_system* ic_units, int cleanup_h, double h) {
+               const struct unit_system* ic_units, int cleanup_h,
+               int cleanup_sqrt_a, double h, double a) {
 
-  const size_t typeSize = io_sizeof_type(prop.type);
-  const size_t copySize = typeSize * prop.dimension;
-  const size_t num_elements = N * prop.dimension;
+  const size_t typeSize = io_sizeof_type(props.type);
+  const size_t copySize = typeSize * props.dimension;
+  const size_t num_elements = N * props.dimension;
 
   /* Check whether the dataspace exists or not */
-  const htri_t exist = H5Lexists(h_grp, prop.name, 0);
+  const htri_t exist = H5Lexists(h_grp, props.name, 0);
   if (exist < 0) {
-    error("Error while checking the existence of data set '%s'.", prop.name);
+    error("Error while checking the existence of data set '%s'.", props.name);
   } else if (exist == 0) {
-    if (prop.importance == COMPULSORY) {
-      error("Compulsory data set '%s' not present in the file.", prop.name);
+    if (props.importance == COMPULSORY) {
+      error("Compulsory data set '%s' not present in the file.", props.name);
     } else {
       /* message("Optional data set '%s' not present. Zeroing this particle
-       * prop...", name);	   */
+       * props...", name);	   */
 
       for (size_t i = 0; i < N; ++i)
-        memset(prop.field + i * prop.partSize, 0, copySize);
+        memset(props.field + i * props.partSize, 0, copySize);
 
       return;
     }
   }
 
   /* message("Reading %s '%s' array...", */
-  /*         prop.importance == COMPULSORY ? "compulsory" : "optional  ", */
-  /*         prop.name); */
+  /*         props.importance == COMPULSORY ? "compulsory" : "optional  ", */
+  /*         props.name); */
 
   /* Open data space */
-  const hid_t h_data = H5Dopen(h_grp, prop.name, H5P_DEFAULT);
-  if (h_data < 0) error("Error while opening data space '%s'.", prop.name);
+  const hid_t h_data = H5Dopen(h_grp, props.name, H5P_DEFAULT);
+  if (h_data < 0) error("Error while opening data space '%s'.", props.name);
 
   /* Allocate temporary buffer */
   void* temp = malloc(num_elements * typeSize);
@@ -111,18 +112,18 @@ void readArray(hid_t h_grp, const struct io_props prop, size_t N,
   /* Read HDF5 dataspace in temporary buffer */
   /* Dirty version that happens to work for vectors but should be improved */
   /* Using HDF5 dataspaces would be better */
-  const hid_t h_err = H5Dread(h_data, io_hdf5_type(prop.type), H5S_ALL, H5S_ALL,
-                              H5P_DEFAULT, temp);
-  if (h_err < 0) error("Error while reading data array '%s'.", prop.name);
+  const hid_t h_err = H5Dread(h_data, io_hdf5_type(props.type), H5S_ALL,
+                              H5S_ALL, H5P_DEFAULT, temp);
+  if (h_err < 0) error("Error while reading data array '%s'.", props.name);
 
   /* Unit conversion if necessary */
   const double unit_factor =
-      units_conversion_factor(ic_units, internal_units, prop.units);
+      units_conversion_factor(ic_units, internal_units, props.units);
   if (unit_factor != 1. && exist != 0) {
 
     /* message("Converting ! factor=%e", factor); */
 
-    if (io_is_double_precision(prop.type)) {
+    if (io_is_double_precision(props.type)) {
       double* temp_d = (double*)temp;
       for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= unit_factor;
     } else {
@@ -132,36 +133,47 @@ void readArray(hid_t h_grp, const struct io_props prop, size_t N,
   }
 
   /* Clean-up h if necessary */
-  const float h_factor_exp = units_h_factor(internal_units, prop.units);
+  const float h_factor_exp = units_h_factor(internal_units, props.units);
   if (cleanup_h && h_factor_exp != 0.f && exist != 0) {
-    const double h_factor = pow(h, h_factor_exp);
 
-    /* message("Multipltying '%s' by h^%f=%f", prop.name, h_factor_exp,
+    /* message("Multipltying '%s' by h^%f=%f", props.name, h_factor_exp,
      * h_factor); */
 
-    if (io_is_double_precision(prop.type)) {
+    if (io_is_double_precision(props.type)) {
       double* temp_d = (double*)temp;
+      const double h_factor = pow(h, h_factor_exp);
       for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= h_factor;
     } else {
       float* temp_f = (float*)temp;
+      const float h_factor = pow(h, h_factor_exp);
       for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= h_factor;
     }
   }
 
+  /* Clean-up a if necessary */
+  if (cleanup_sqrt_a && a != 1. && (strcmp(props.name, "Velocities") == 0)) {
+
+    if (io_is_double_precision(props.type)) {
+      double* temp_d = (double*)temp;
+      const double vel_factor = sqrt(a);
+      for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= vel_factor;
+    } else {
+      float* temp_f = (float*)temp;
+      const float vel_factor = sqrt(a);
+      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= vel_factor;
+    }
+  }
+
   /* Copy temporary buffer to particle data */
   char* temp_c = (char*)temp;
   for (size_t i = 0; i < N; ++i)
-    memcpy(prop.field + i * prop.partSize, &temp_c[i * copySize], copySize);
+    memcpy(props.field + i * props.partSize, &temp_c[i * copySize], copySize);
 
   /* Free and close everything */
   free(temp);
   H5Dclose(h_data);
 }
 
-/*-----------------------------------------------------------------------------
- * Routines writing an output file
- *-----------------------------------------------------------------------------*/
-
 /**
  * @brief Writes a data array in given HDF5 group.
  *
@@ -239,6 +251,11 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
     error("Error while setting chunk size (%llu, %llu) for field '%s'.",
           chunk_shape[0], chunk_shape[1], props.name);
 
+  /* Impose check-sum to verify data corruption */
+  h_err = H5Pset_fletcher32(h_prop);
+  if (h_err < 0)
+    error("Error while setting checksum options for field '%s'.", props.name);
+
   /* Impose data compression */
   if (e->snapshot_compression > 0) {
     h_err = H5Pset_shuffle(h_prop);
@@ -303,7 +320,10 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
  * @param with_gravity Are we reading/creating #gpart arrays ?
  * @param with_stars Are we reading star particles ?
  * @param cleanup_h Are we cleaning-up h-factors from the quantities we read?
+ * @param cleanup_sqrt_a Are we cleaning-up the sqrt(a) factors in the Gadget
+ * IC velocities?
  * @param h The value of the reduced Hubble constant to use for correction.
+ * @param a The current value of the scale-factor.
  * @prarm n_threads The number of threads to use for the temporary threadpool.
  * @param dry_run If 1, don't read the particle. Only allocates the arrays.
  *
@@ -313,21 +333,22 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
  *
  * @warning Can not read snapshot distributed over more than 1 file !!!
  * @todo Read snapshots distributed in more than one file.
- *
  */
-void read_ic_single(char* fileName, const struct unit_system* internal_units,
-                    double dim[3], struct part** parts, struct gpart** gparts,
+void read_ic_single(const char* fileName,
+                    const struct unit_system* internal_units, double dim[3],
+                    struct part** parts, struct gpart** gparts,
                     struct spart** sparts, size_t* Ngas, size_t* Ngparts,
                     size_t* Nstars, int* periodic, int* flag_entropy,
                     int with_hydro, int with_gravity, int with_stars,
-                    int cleanup_h, double h, int n_threads, int dry_run) {
+                    int cleanup_h, int cleanup_sqrt_a, double h, double a,
+                    int n_threads, int dry_run) {
 
   hid_t h_file = 0, h_grp = 0;
   /* GADGET has only cubic boxes (in cosmological mode) */
   double boxSize[3] = {0.0, -1.0, -1.0};
   /* GADGET has 6 particle types. We only keep the type 0 & 1 for now...*/
-  int numParticles[swift_type_count] = {0};
-  int numParticles_highWord[swift_type_count] = {0};
+  long long numParticles[swift_type_count] = {0};
+  long long numParticles_highWord[swift_type_count] = {0};
   size_t N[swift_type_count] = {0};
   int dimension = 3; /* Assume 3D if nothing is specified */
   size_t Ndm = 0;
@@ -367,13 +388,12 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units,
   io_read_attribute(h_grp, "Flag_Entropy_ICs", INT, flag_entropy_temp);
   *flag_entropy = flag_entropy_temp[0];
   io_read_attribute(h_grp, "BoxSize", DOUBLE, boxSize);
-  io_read_attribute(h_grp, "NumPart_Total", UINT, numParticles);
-  io_read_attribute(h_grp, "NumPart_Total_HighWord", UINT,
+  io_read_attribute(h_grp, "NumPart_Total", LONGLONG, numParticles);
+  io_read_attribute(h_grp, "NumPart_Total_HighWord", LONGLONG,
                     numParticles_highWord);
 
   for (int ptype = 0; ptype < swift_type_count; ++ptype)
-    N[ptype] = ((long long)numParticles[ptype]) +
-               ((long long)numParticles_highWord[ptype] << 32);
+    N[ptype] = (numParticles[ptype]) + (numParticles_highWord[ptype] << 32);
 
   /* Get the box size if not cubic */
   dim[0] = boxSize[0];
@@ -403,7 +423,7 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units,
   struct unit_system* ic_units =
       (struct unit_system*)malloc(sizeof(struct unit_system));
   if (ic_units == NULL) error("Unable to allocate memory for IC unit system");
-  io_read_unit_system(h_file, ic_units, 0);
+  io_read_unit_system(h_file, ic_units, internal_units, 0);
 
   /* Tell the user if a conversion will be needed */
   if (units_are_equal(ic_units, internal_units)) {
@@ -526,7 +546,7 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units,
     if (!dry_run)
       for (int i = 0; i < num_fields; ++i)
         readArray(h_grp, list[i], Nparticles, internal_units, ic_units,
-                  cleanup_h, h);
+                  cleanup_h, cleanup_sqrt_a, h, a);
 
     /* Close particle group */
     H5Gclose(h_grp);
@@ -593,6 +613,7 @@ void write_output_single(struct engine* e, const char* baseName,
   struct gpart* dmparts = NULL;
   const struct spart* sparts = e->s->sparts;
   const struct cooling_function_data* cooling = e->cooling_func;
+  struct swift_params* params = e->parameter_file;
 
   /* Number of unassociated gparts */
   const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
@@ -602,8 +623,12 @@ void write_output_single(struct engine* e, const char* baseName,
 
   /* File name */
   char fileName[FILENAME_BUFFER_SIZE];
-  snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName,
-           e->snapshot_output_count);
+  if (e->snapshot_label_delta == 1)
+    snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%04i.hdf5", baseName,
+             e->snapshot_output_count);
+  else
+    snprintf(fileName, FILENAME_BUFFER_SIZE, "%s_%06i.hdf5", baseName,
+             e->snapshot_output_count * e->snapshot_label_delta);
 
   /* First time, we need to create the XMF file */
   if (e->snapshot_output_count == 0) xmf_create_file(baseName);
@@ -720,7 +745,14 @@ void write_output_single(struct engine* e, const char* baseName,
   h_grp =
       H5Gcreate(h_file, "/Parameters", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
   if (h_grp < 0) error("Error while creating parameters group");
-  parser_write_params_to_hdf5(e->parameter_file, h_grp);
+  parser_write_params_to_hdf5(e->parameter_file, h_grp, 1);
+  H5Gclose(h_grp);
+
+  /* Print the runtime unused parameters */
+  h_grp = H5Gcreate(h_file, "/UnusedParameters", H5P_DEFAULT, H5P_DEFAULT,
+                    H5P_DEFAULT);
+  if (h_grp < 0) error("Error while creating parameters group");
+  parser_write_params_to_hdf5(e->parameter_file, h_grp, 0);
   H5Gclose(h_grp);
 
   /* Print the system of Units used in the spashot */
@@ -819,10 +851,19 @@ void write_output_single(struct engine* e, const char* baseName,
         error("Particle Type %d not yet supported. Aborting", ptype);
     }
 
-    /* Write everything */
-    for (int i = 0; i < num_fields; ++i)
-      writeArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i], N,
-                 internal_units, snapshot_units);
+    /* Write everything that is not cancelled */
+    for (int i = 0; i < num_fields; ++i) {
+
+      /* Did the user cancel this field? */
+      char field[PARSER_MAX_LINE_SIZE];
+      sprintf(field, "SelectOutput:%s_%s", list[i].name,
+              part_type_names[ptype]);
+      int should_write = parser_get_opt_param_int(params, field, 1);
+
+      if (should_write)
+        writeArray(e, h_grp, fileName, xmfFile, partTypeGroupName, list[i], N,
+                   internal_units, snapshot_units);
+    }
 
     /* Free temporary array */
     if (dmparts) {
diff --git a/src/single_io.h b/src/single_io.h
index 26b849716e3e018d9a10c5c5c513ad26c7ccb274..a0ce8370dfa1009f28e7c399b3f1db345c23de49 100644
--- a/src/single_io.h
+++ b/src/single_io.h
@@ -29,12 +29,14 @@
 #include "part.h"
 #include "units.h"
 
-void read_ic_single(char* fileName, const struct unit_system* internal_units,
-                    double dim[3], struct part** parts, struct gpart** gparts,
+void read_ic_single(const char* fileName,
+                    const struct unit_system* internal_units, double dim[3],
+                    struct part** parts, struct gpart** gparts,
                     struct spart** sparts, size_t* Ngas, size_t* Ndm,
                     size_t* Nstars, int* periodic, int* flag_entropy,
                     int with_hydro, int with_gravity, int with_stars,
-                    int cleanup_h, double h, int nr_threads, int dry_run);
+                    int cleanup_h, int cleanup_sqrt_a, double h, double a,
+                    int nr_threads, int dry_run);
 
 void write_output_single(struct engine* e, const char* baseName,
                          const struct unit_system* internal_units,
diff --git a/src/sourceterms.c b/src/sourceterms.c
index 994658740a50a764edd3988ef7d6b78e00546f8f..993045e61503e4e78b855816921bc057706b76d1 100644
--- a/src/sourceterms.c
+++ b/src/sourceterms.c
@@ -36,7 +36,7 @@
  * @param us The current internal system of units
  * @param source the structure that has all the source term properties
  */
-void sourceterms_init(const struct swift_params *parameter_file,
+void sourceterms_init(struct swift_params *parameter_file,
                       struct unit_system *us, struct sourceterms *source) {
 #ifdef SOURCETERMS_SN_FEEDBACK
   supernova_init(parameter_file, us, source);
diff --git a/src/sourceterms.h b/src/sourceterms.h
index a5d0c3c727d70d50fb3388d5e5e3cc3d6362276f..407d2f19362531a3fd3537889593c484319919b5 100644
--- a/src/sourceterms.h
+++ b/src/sourceterms.h
@@ -41,7 +41,7 @@ struct sourceterms {
 #include "sourceterms/sn_feedback/sn_feedback.h"
 #endif
 
-void sourceterms_init(const struct swift_params* parameter_file,
+void sourceterms_init(struct swift_params* parameter_file,
                       struct unit_system* us, struct sourceterms* source);
 void sourceterms_print(struct sourceterms* source);
 
diff --git a/src/sourceterms/sn_feedback/sn_feedback.h b/src/sourceterms/sn_feedback/sn_feedback.h
index f2f224ce871ebb768c318aef42a690861dd974df..411673c37e82ff89d906425d1cadaa135c46a38d 100644
--- a/src/sourceterms/sn_feedback/sn_feedback.h
+++ b/src/sourceterms/sn_feedback/sn_feedback.h
@@ -171,7 +171,7 @@ __attribute__((always_inline)) INLINE static void supernova_feedback_apply(
  */
 
 __attribute__((always_inline)) INLINE static void supernova_init(
-    const struct swift_params* parameter_file, struct unit_system* us,
+    struct swift_params* parameter_file, struct unit_system* us,
     struct sourceterms* source) {
   source->supernova.time = parser_get_param_double(parameter_file, "SN:time");
   source->supernova.energy =
diff --git a/src/space.c b/src/space.c
index 9dc6037820063847d47624bad1f3389f5e113975..e78c72725a7e6f2561940ccfc83cc52e94ebb7aa 100644
--- a/src/space.c
+++ b/src/space.c
@@ -55,7 +55,6 @@
 #include "minmax.h"
 #include "multipole.h"
 #include "restart.h"
-#include "runner.h"
 #include "sort_part.h"
 #include "stars.h"
 #include "threadpool.h"
@@ -105,52 +104,6 @@ struct index_data {
   int *cell_counts;
 };
 
-/**
- * @brief Get the shift-id of the given pair of cells, swapping them
- *      if need be.
- *
- * @param s The space
- * @param ci Pointer to first #cell.
- * @param cj Pointer second #cell.
- * @param shift Vector from ci to cj.
- *
- * @return The shift ID and set shift, may or may not swap ci and cj.
- */
-int space_getsid(struct space *s, struct cell **ci, struct cell **cj,
-                 double *shift) {
-
-  /* Get the relative distance between the pairs, wrapping. */
-  const int periodic = s->periodic;
-  double dx[3];
-  for (int k = 0; k < 3; k++) {
-    dx[k] = (*cj)->loc[k] - (*ci)->loc[k];
-    if (periodic && dx[k] < -s->dim[k] / 2)
-      shift[k] = s->dim[k];
-    else if (periodic && dx[k] > s->dim[k] / 2)
-      shift[k] = -s->dim[k];
-    else
-      shift[k] = 0.0;
-    dx[k] += shift[k];
-  }
-
-  /* Get the sorting index. */
-  int sid = 0;
-  for (int k = 0; k < 3; k++)
-    sid = 3 * sid + ((dx[k] < 0.0) ? 0 : ((dx[k] > 0.0) ? 2 : 1));
-
-  /* Switch the cells around? */
-  if (runner_flip[sid]) {
-    struct cell *temp = *ci;
-    *ci = *cj;
-    *cj = temp;
-    for (int k = 0; k < 3; k++) shift[k] = -shift[k];
-  }
-  sid = sortlistID[sid];
-
-  /* Return the sort ID. */
-  return sid;
-}
-
 /**
  * @brief Recursively dismantle a cell tree.
  *
@@ -234,8 +187,6 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->drift_gpart = NULL;
     c->cooling = NULL;
     c->sourceterms = NULL;
-    c->grav_ghost_in = NULL;
-    c->grav_ghost_out = NULL;
     c->grav_long_range = NULL;
     c->grav_down = NULL;
     c->super = c;
@@ -342,20 +293,6 @@ void space_regrid(struct space *s, int verbose) {
         " - particles with velocities so large that they move by more than two "
         "box sizes per time-step.\n");
 
-  /* Check if we have enough cells for periodic gravity. */
-  if (s->gravity && s->periodic && (cdim[0] < 8 || cdim[1] < 8 || cdim[2] < 8))
-    error(
-        "Must have at least 8 cells in each spatial dimension when periodic "
-        "gravity is switched on.\nThis error is often caused by any of the "
-        "followings:\n"
-        " - too few particles to generate a sensible grid,\n"
-        " - the initial value of 'Scheduler:max_top_level_cells' is too "
-        "small,\n"
-        " - the (minimal) time-step is too large leading to particles with "
-        "predicted smoothing lengths too large for the box size,\n"
-        " - particles with velocities so large that they move by more than two "
-        "box sizes per time-step.\n");
-
 /* In MPI-Land, changing the top-level cell size requires that the
  * global partition is recomputed and the particles redistributed.
  * Be prepared to do that. */
@@ -1087,6 +1024,10 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts,
   if (cell_counts == NULL)
     error("Failed to allocate temporary cell count buffer.");
 
+  /* Init the local collectors */
+  float min_mass = FLT_MAX;
+  float sum_vel_norm = 0.f;
+
   /* Loop over the parts. */
   for (int k = 0; k < nr_parts; k++) {
 
@@ -1105,8 +1046,6 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts,
     /* Get its cell index */
     const int index =
         cell_getid(cdim, pos_x * ih_x, pos_y * ih_y, pos_z * ih_z);
-    ind[k] = index;
-    cell_counts[index]++;
 
 #ifdef SWIFT_DEBUG_CHECKS
     if (index < 0 || index >= cdim[0] * cdim[1] * cdim[2])
@@ -1119,6 +1058,15 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts,
             pos_z);
 #endif
 
+    ind[k] = index;
+    cell_counts[index]++;
+
+    /* Compute minimal mass */
+    min_mass = min(min_mass, hydro_get_mass(p));
+
+    /* Compute sum of velocity norm */
+    sum_vel_norm += p->v[0] * p->v[0] + p->v[1] * p->v[1] + p->v[2] * p->v[2];
+
     /* Update the position */
     p->x[0] = pos_x;
     p->x[1] = pos_y;
@@ -1129,6 +1077,10 @@ void space_parts_get_cell_index_mapper(void *map_data, int nr_parts,
   for (int k = 0; k < s->nr_cells; k++)
     if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]);
   free(cell_counts);
+
+  /* Write back the minimal part mass and velocity sum */
+  atomic_min_f(&s->min_part_mass, min_mass);
+  atomic_add_f(&s->sum_part_vel_norm, sum_vel_norm);
 }
 
 /**
@@ -1161,6 +1113,10 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts,
   if (cell_counts == NULL)
     error("Failed to allocate temporary cell count buffer.");
 
+  /* Init the local collectors */
+  float min_mass = FLT_MAX;
+  float sum_vel_norm = 0.f;
+
   for (int k = 0; k < nr_gparts; k++) {
 
     /* Get the particle */
@@ -1178,8 +1134,6 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts,
     /* Get its cell index */
     const int index =
         cell_getid(cdim, pos_x * ih_x, pos_y * ih_y, pos_z * ih_z);
-    ind[k] = index;
-    cell_counts[index]++;
 
 #ifdef SWIFT_DEBUG_CHECKS
     if (index < 0 || index >= cdim[0] * cdim[1] * cdim[2])
@@ -1192,6 +1146,17 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts,
             pos_z);
 #endif
 
+    ind[k] = index;
+    cell_counts[index]++;
+
+    /* Compute minimal mass */
+    if (gp->type == swift_type_dark_matter) {
+      min_mass = min(min_mass, gp->mass);
+      sum_vel_norm += gp->v_full[0] * gp->v_full[0] +
+                      gp->v_full[1] * gp->v_full[1] +
+                      gp->v_full[2] * gp->v_full[2];
+    }
+
     /* Update the position */
     gp->x[0] = pos_x;
     gp->x[1] = pos_y;
@@ -1202,6 +1167,10 @@ void space_gparts_get_cell_index_mapper(void *map_data, int nr_gparts,
   for (int k = 0; k < s->nr_cells; k++)
     if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]);
   free(cell_counts);
+
+  /* Write back the minimal part mass and velocity sum */
+  atomic_min_f(&s->min_gpart_mass, min_mass);
+  atomic_add_f(&s->sum_gpart_vel_norm, sum_vel_norm);
 }
 
 /**
@@ -1234,6 +1203,10 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
   if (cell_counts == NULL)
     error("Failed to allocate temporary cell count buffer.");
 
+  /* Init the local collectors */
+  float min_mass = FLT_MAX;
+  float sum_vel_norm = 0.f;
+
   for (int k = 0; k < nr_sparts; k++) {
 
     /* Get the particle */
@@ -1251,8 +1224,6 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
     /* Get its cell index */
     const int index =
         cell_getid(cdim, pos_x * ih_x, pos_y * ih_y, pos_z * ih_z);
-    ind[k] = index;
-    cell_counts[index]++;
 
 #ifdef SWIFT_DEBUG_CHECKS
     if (index < 0 || index >= cdim[0] * cdim[1] * cdim[2])
@@ -1265,6 +1236,16 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
             pos_z);
 #endif
 
+    ind[k] = index;
+    cell_counts[index]++;
+
+    /* Compute minimal mass */
+    min_mass = min(min_mass, sp->mass);
+
+    /* Compute sum of velocity norm */
+    sum_vel_norm +=
+        sp->v[0] * sp->v[0] + sp->v[1] * sp->v[1] + sp->v[2] * sp->v[2];
+
     /* Update the position */
     sp->x[0] = pos_x;
     sp->x[1] = pos_y;
@@ -1275,6 +1256,10 @@ void space_sparts_get_cell_index_mapper(void *map_data, int nr_sparts,
   for (int k = 0; k < s->nr_cells; k++)
     if (cell_counts[k]) atomic_add(&data->cell_counts[k], cell_counts[k]);
   free(cell_counts);
+
+  /* Write back the minimal part mass and velocity sum */
+  atomic_min_f(&s->min_spart_mass, min_mass);
+  atomic_add_f(&s->sum_spart_vel_norm, sum_vel_norm);
 }
 
 /**
@@ -1291,6 +1276,10 @@ void space_parts_get_cell_index(struct space *s, int *ind, int *cell_counts,
 
   const ticks tic = getticks();
 
+  /* Re-set the counters */
+  s->min_part_mass = FLT_MAX;
+  s->sum_part_vel_norm = 0.f;
+
   /* Pack the extra information */
   struct index_data data;
   data.s = s;
@@ -1320,6 +1309,10 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
 
   const ticks tic = getticks();
 
+  /* Re-set the counters */
+  s->min_gpart_mass = FLT_MAX;
+  s->sum_gpart_vel_norm = 0.f;
+
   /* Pack the extra information */
   struct index_data data;
   data.s = s;
@@ -1349,6 +1342,10 @@ void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
 
   const ticks tic = getticks();
 
+  /* Re-set the counters */
+  s->min_spart_mass = FLT_MAX;
+  s->sum_spart_vel_norm = 0.f;
+
   /* Pack the extra information */
   struct index_data data;
   data.s = s;
@@ -1378,9 +1375,11 @@ void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
 void space_parts_sort(struct part *parts, struct xpart *xparts, int *ind,
                       int *counts, int num_bins, ptrdiff_t parts_offset) {
   /* Create the offsets array. */
-  size_t *offsets = (size_t *)malloc(sizeof(size_t) * (num_bins + 1));
-  if (offsets == NULL)
+  size_t *offsets = NULL;
+  if (posix_memalign((void **)&offsets, SWIFT_STRUCT_ALIGNMENT,
+                     sizeof(size_t) * (num_bins + 1)) != 0)
     error("Failed to allocate temporary cell offsets array.");
+
   offsets[0] = 0;
   for (int k = 1; k <= num_bins; k++) {
     offsets[k] = offsets[k - 1] + counts[k - 1];
@@ -1438,9 +1437,11 @@ void space_parts_sort(struct part *parts, struct xpart *xparts, int *ind,
 void space_sparts_sort(struct spart *sparts, int *ind, int *counts,
                        int num_bins, ptrdiff_t sparts_offset) {
   /* Create the offsets array. */
-  size_t *offsets = (size_t *)malloc(sizeof(size_t) * (num_bins + 1));
-  if (offsets == NULL)
+  size_t *offsets = NULL;
+  if (posix_memalign((void **)&offsets, SWIFT_STRUCT_ALIGNMENT,
+                     sizeof(size_t) * (num_bins + 1)) != 0)
     error("Failed to allocate temporary cell offsets array.");
+
   offsets[0] = 0;
   for (int k = 1; k <= num_bins; k++) {
     offsets[k] = offsets[k - 1] + counts[k - 1];
@@ -1496,9 +1497,11 @@ void space_gparts_sort(struct gpart *gparts, struct part *parts,
                        struct spart *sparts, int *ind, int *counts,
                        int num_bins) {
   /* Create the offsets array. */
-  size_t *offsets = (size_t *)malloc(sizeof(size_t) * (num_bins + 1));
-  if (offsets == NULL)
+  size_t *offsets = NULL;
+  if (posix_memalign((void **)&offsets, SWIFT_STRUCT_ALIGNMENT,
+                     sizeof(size_t) * (num_bins + 1)) != 0)
     error("Failed to allocate temporary cell offsets array.");
+
   offsets[0] = 0;
   for (int k = 1; k <= num_bins; k++) {
     offsets[k] = offsets[k - 1] + counts[k - 1];
@@ -1728,6 +1731,7 @@ void space_split_recursive(struct space *s, struct cell *c,
   struct spart *sparts = c->sparts;
   struct xpart *xparts = c->xparts;
   struct engine *e = s->e;
+  const integertime_t ti_current = e->ti_current;
 
   /* If the buff is NULL, allocate it, and remember to free it. */
   const int allocate_buffer = (buff == NULL && gbuff == NULL && sbuff == NULL);
@@ -1818,43 +1822,51 @@ void space_split_recursive(struct space *s, struct cell *c,
 #endif
     }
 
-    /* Split the cell data. */
+    /* Split the cell's partcle data. */
     cell_split(c, c->parts - s->parts, c->sparts - s->sparts, buff, sbuff,
                gbuff);
 
-    /* Remove any progeny with zero parts. */
+    /* Buffers for the progenitors */
     struct cell_buff *progeny_buff = buff, *progeny_gbuff = gbuff,
                      *progeny_sbuff = sbuff;
+
     for (int k = 0; k < 8; k++) {
-      if (c->progeny[k]->count == 0 && c->progeny[k]->gcount == 0 &&
-          c->progeny[k]->scount == 0) {
-        space_recycle(s, c->progeny[k]);
+
+      /* Get the progenitor */
+      struct cell *cp = c->progeny[k];
+
+      /* Remove any progeny with zero particles. */
+      if (cp->count == 0 && cp->gcount == 0 && cp->scount == 0) {
+
+        space_recycle(s, cp);
         c->progeny[k] = NULL;
+
       } else {
-        space_split_recursive(s, c->progeny[k], progeny_buff, progeny_sbuff,
+
+        /* Recurse */
+        space_split_recursive(s, cp, progeny_buff, progeny_sbuff,
                               progeny_gbuff);
-        progeny_buff += c->progeny[k]->count;
-        progeny_gbuff += c->progeny[k]->gcount;
-        progeny_sbuff += c->progeny[k]->scount;
-        h_max = max(h_max, c->progeny[k]->h_max);
-        ti_hydro_end_min =
-            min(ti_hydro_end_min, c->progeny[k]->ti_hydro_end_min);
-        ti_hydro_end_max =
-            max(ti_hydro_end_max, c->progeny[k]->ti_hydro_end_max);
-        ti_hydro_beg_max =
-            max(ti_hydro_beg_max, c->progeny[k]->ti_hydro_beg_max);
-        ti_gravity_end_min =
-            min(ti_gravity_end_min, c->progeny[k]->ti_gravity_end_min);
-        ti_gravity_end_max =
-            max(ti_gravity_end_max, c->progeny[k]->ti_gravity_end_max);
-        ti_gravity_beg_max =
-            max(ti_gravity_beg_max, c->progeny[k]->ti_gravity_beg_max);
-        if (c->progeny[k]->maxdepth > maxdepth)
-          maxdepth = c->progeny[k]->maxdepth;
+
+        /* Update the pointers in the buffers */
+        progeny_buff += cp->count;
+        progeny_gbuff += cp->gcount;
+        progeny_sbuff += cp->scount;
+
+        /* Update the cell-wide properties */
+        h_max = max(h_max, cp->h_max);
+        ti_hydro_end_min = min(ti_hydro_end_min, cp->ti_hydro_end_min);
+        ti_hydro_end_max = max(ti_hydro_end_max, cp->ti_hydro_end_max);
+        ti_hydro_beg_max = max(ti_hydro_beg_max, cp->ti_hydro_beg_max);
+        ti_gravity_end_min = min(ti_gravity_end_min, cp->ti_gravity_end_min);
+        ti_gravity_end_max = max(ti_gravity_end_max, cp->ti_gravity_end_max);
+        ti_gravity_beg_max = max(ti_gravity_beg_max, cp->ti_gravity_beg_max);
+
+        /* Increase the depth */
+        if (cp->maxdepth > maxdepth) maxdepth = cp->maxdepth;
       }
     }
 
-    /* Deal with multipole */
+    /* Deal with the multipole */
     if (s->gravity) {
 
       /* Reset everything */
@@ -1910,14 +1922,17 @@ void space_split_recursive(struct space *s, struct cell *c,
 
       /* Take minimum of both limits */
       c->multipole->r_max = min(r_max, sqrt(dx * dx + dy * dy + dz * dz));
+
+      /* Store the value at rebuild time */
       c->multipole->r_max_rebuild = c->multipole->r_max;
       c->multipole->CoM_rebuild[0] = c->multipole->CoM[0];
       c->multipole->CoM_rebuild[1] = c->multipole->CoM[1];
       c->multipole->CoM_rebuild[2] = c->multipole->CoM[2];
+
     } /* Deal with gravity */
-  }
+  }   /* Split or let it be? */
 
-  /* Otherwise, collect the data for this cell. */
+  /* Otherwise, collect the data from the particles this cell. */
   else {
 
     /* Clear the progeny. */
@@ -1938,12 +1953,14 @@ void space_split_recursive(struct space *s, struct cell *c,
       hydro_time_bin_max = max(hydro_time_bin_max, parts[k].time_bin);
       h_max = max(h_max, parts[k].h);
     }
-    /* parts: Reset x_diff */
+
+    /* xparts: Reset x_diff */
     for (int k = 0; k < count; k++) {
       xparts[k].x_diff[0] = 0.f;
       xparts[k].x_diff[1] = 0.f;
       xparts[k].x_diff[2] = 0.f;
     }
+
     /* gparts: Get dt_min/dt_max, reset x_diff. */
     for (int k = 0; k < gcount; k++) {
 #ifdef SWIFT_DEBUG_CHECKS
@@ -1956,6 +1973,7 @@ void space_split_recursive(struct space *s, struct cell *c,
       gparts[k].x_diff[1] = 0.f;
       gparts[k].x_diff[2] = 0.f;
     }
+
     /* sparts: Get dt_min/dt_max */
     for (int k = 0; k < scount; k++) {
 #ifdef SWIFT_DEBUG_CHECKS
@@ -1967,32 +1985,26 @@ void space_split_recursive(struct space *s, struct cell *c,
     }
 
     /* Convert into integer times */
-    ti_hydro_end_min = get_integer_time_end(e->ti_current, hydro_time_bin_min);
-    ti_hydro_end_max = get_integer_time_end(e->ti_current, hydro_time_bin_max);
+    ti_hydro_end_min = get_integer_time_end(ti_current, hydro_time_bin_min);
+    ti_hydro_end_max = get_integer_time_end(ti_current, hydro_time_bin_max);
     ti_hydro_beg_max =
-        get_integer_time_begin(e->ti_current + 1, hydro_time_bin_max);
-    ti_gravity_end_min =
-        get_integer_time_end(e->ti_current, gravity_time_bin_min);
-    ti_gravity_end_max =
-        get_integer_time_end(e->ti_current, gravity_time_bin_max);
+        get_integer_time_begin(ti_current + 1, hydro_time_bin_max);
+    ti_gravity_end_min = get_integer_time_end(ti_current, gravity_time_bin_min);
+    ti_gravity_end_max = get_integer_time_end(ti_current, gravity_time_bin_max);
     ti_gravity_beg_max =
-        get_integer_time_begin(e->ti_current + 1, gravity_time_bin_max);
+        get_integer_time_begin(ti_current + 1, gravity_time_bin_max);
 
     /* Construct the multipole and the centre of mass*/
     if (s->gravity) {
       if (gcount > 0) {
+
         gravity_P2M(c->multipole, c->gparts, c->gcount);
-        const double dx = c->multipole->CoM[0] > c->loc[0] + c->width[0] / 2.
-                              ? c->multipole->CoM[0] - c->loc[0]
-                              : c->loc[0] + c->width[0] - c->multipole->CoM[0];
-        const double dy = c->multipole->CoM[1] > c->loc[1] + c->width[1] / 2.
-                              ? c->multipole->CoM[1] - c->loc[1]
-                              : c->loc[1] + c->width[1] - c->multipole->CoM[1];
-        const double dz = c->multipole->CoM[2] > c->loc[2] + c->width[2] / 2.
-                              ? c->multipole->CoM[2] - c->loc[2]
-                              : c->loc[2] + c->width[2] - c->multipole->CoM[2];
-        c->multipole->r_max = sqrt(dx * dx + dy * dy + dz * dz);
+
       } else {
+
+        /* No gparts in that leaf cell */
+
+        /* Set the values to something sensible */
         gravity_multipole_init(&c->multipole->m_pole);
         if (c->nodeID == engine_rank) {
           c->multipole->CoM[0] = c->loc[0] + c->width[0] / 2.;
@@ -2001,6 +2013,8 @@ void space_split_recursive(struct space *s, struct cell *c,
           c->multipole->r_max = 0.;
         }
       }
+
+      /* Store the value at rebuild time */
       c->multipole->r_max_rebuild = c->multipole->r_max;
       c->multipole->CoM_rebuild[0] = c->multipole->CoM[0];
       c->multipole->CoM_rebuild[1] = c->multipole->CoM[1];
@@ -2331,7 +2345,7 @@ void space_first_init_parts_mapper(void *restrict map_data, int count,
   const struct cosmology *cosmo = s->e->cosmology;
   const struct phys_const *phys_const = s->e->physical_constants;
   const struct unit_system *us = s->e->internal_units;
-  const float a_factor_vel = cosmo->a * cosmo->a;
+  const float a_factor_vel = cosmo->a;
 
   const struct hydro_props *hydro_props = s->e->hydro_properties;
   const float u_init = hydro_props->initial_internal_energy;
@@ -2340,8 +2354,8 @@ void space_first_init_parts_mapper(void *restrict map_data, int count,
   const struct chemistry_global_data *chemistry = e->chemistry;
   const struct cooling_function_data *cool_func = e->cooling_func;
 
+  /* Convert velocities to internal units */
   for (int k = 0; k < count; k++) {
-    /* Convert velocities to internal units */
     p[k].v[0] *= a_factor_vel;
     p[k].v[1] *= a_factor_vel;
     p[k].v[2] *= a_factor_vel;
@@ -2355,6 +2369,10 @@ void space_first_init_parts_mapper(void *restrict map_data, int count,
     p[k].x[1] = p[k].x[2] = 0.f;
     p[k].v[1] = p[k].v[2] = 0.f;
 #endif
+  }
+
+  /* Initialise the rest */
+  for (int k = 0; k < count; k++) {
 
     hydro_first_init_part(&p[k], &xp[k]);
 
@@ -2406,11 +2424,11 @@ void space_first_init_gparts_mapper(void *restrict map_data, int count,
   const struct space *restrict s = (struct space *)extra_data;
 
   const struct cosmology *cosmo = s->e->cosmology;
-  const float a_factor_vel = cosmo->a * cosmo->a;
+  const float a_factor_vel = cosmo->a;
   const struct gravity_props *grav_props = s->e->gravity_properties;
 
+  /* Convert velocities to internal units */
   for (int k = 0; k < count; k++) {
-    /* Convert velocities to internal units */
     gp[k].v_full[0] *= a_factor_vel;
     gp[k].v_full[1] *= a_factor_vel;
     gp[k].v_full[2] *= a_factor_vel;
@@ -2424,6 +2442,10 @@ void space_first_init_gparts_mapper(void *restrict map_data, int count,
     gp[k].x[1] = gp[k].x[2] = 0.f;
     gp[k].v_full[1] = gp[k].v_full[2] = 0.f;
 #endif
+  }
+
+  /* Initialise the rest */
+  for (int k = 0; k < count; k++) {
 
     gravity_first_init_gpart(&gp[k], grav_props);
 
@@ -2463,10 +2485,11 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count,
 #endif
 
   const struct cosmology *cosmo = s->e->cosmology;
-  const float a_factor_vel = cosmo->a * cosmo->a;
+  const float a_factor_vel = cosmo->a;
 
+  /* Convert velocities to internal units */
   for (int k = 0; k < count; k++) {
-    /* Convert velocities to internal units */
+
     sp[k].v[0] *= a_factor_vel;
     sp[k].v[1] *= a_factor_vel;
     sp[k].v[2] *= a_factor_vel;
@@ -2480,6 +2503,10 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count,
     sp[k].x[1] = sp[k].x[2] = 0.f;
     sp[k].v[1] = sp[k].v[2] = 0.f;
 #endif
+  }
+
+  /* Initialise the rest */
+  for (int k = 0; k < count; k++) {
 
     star_first_init_spart(&sp[k]);
 
@@ -2569,6 +2596,7 @@ void space_convert_quantities_mapper(void *restrict map_data, int count,
   struct part *restrict parts = (struct part *)map_data;
   const ptrdiff_t index = parts - s->parts;
   struct xpart *restrict xparts = s->xparts + index;
+
   for (int k = 0; k < count; k++)
     hydro_convert_quantities(&parts[k], &xparts[k], cosmo);
 }
@@ -2618,7 +2646,7 @@ void space_convert_quantities(struct space *s, int verbose) {
  * parts with a cutoff below half the cell width are then split
  * recursively.
  */
-void space_init(struct space *s, const struct swift_params *params,
+void space_init(struct space *s, struct swift_params *params,
                 const struct cosmology *cosmo, double dim[3],
                 struct part *parts, struct gpart *gparts, struct spart *sparts,
                 size_t Npart, size_t Ngpart, size_t Nspart, int periodic,
@@ -2643,6 +2671,12 @@ void space_init(struct space *s, const struct swift_params *params,
   s->nr_sparts = Nspart;
   s->size_sparts = Nspart;
   s->sparts = sparts;
+  s->min_part_mass = FLT_MAX;
+  s->min_gpart_mass = FLT_MAX;
+  s->min_spart_mass = FLT_MAX;
+  s->sum_part_vel_norm = 0.f;
+  s->sum_gpart_vel_norm = 0.f;
+  s->sum_spart_vel_norm = 0.f;
   s->nr_queues = 1; /* Temporary value until engine construction */
 
   /* Are we generating gas from the DM-only ICs? */
@@ -2729,12 +2763,8 @@ void space_init(struct space *s, const struct swift_params *params,
 
   /* Apply shift */
   double shift[3] = {0.0, 0.0, 0.0};
-  shift[0] =
-      parser_get_opt_param_double(params, "InitialConditions:shift_x", 0.0);
-  shift[1] =
-      parser_get_opt_param_double(params, "InitialConditions:shift_y", 0.0);
-  shift[2] =
-      parser_get_opt_param_double(params, "InitialConditions:shift_z", 0.0);
+  parser_get_opt_param_double_array(params, "InitialConditions:shift", 3,
+                                    shift);
   if ((shift[0] != 0. || shift[1] != 0. || shift[2] != 0.) && !dry_run) {
     message("Shifting particles by [%e %e %e]", shift[0], shift[1], shift[2]);
     for (size_t k = 0; k < Npart; k++) {
@@ -3079,7 +3109,7 @@ void space_check_cosmology(struct space *s, const struct cosmology *cosmo,
     if (fabs(Omega_m - cosmo->Omega_m) > 1e-3)
       error(
           "The matter content of the simulation does not match the cosmology "
-          "in the parameter file comso.Omega_m=%e Omega_m=%e",
+          "in the parameter file cosmo.Omega_m=%e Omega_m=%e",
           cosmo->Omega_m, Omega_m);
   }
 }
diff --git a/src/space.h b/src/space.h
index e8ad1929fa3902221ac24de790547e7e9aa5163d..67fc34e55bb1a0de3e42f243542bfdb6492d1bab 100644
--- a/src/space.h
+++ b/src/space.h
@@ -146,6 +146,24 @@ struct space {
   /*! The top-level FFT task */
   struct task *grav_top_level;
 
+  /*! Minimal mass of all the #part */
+  float min_part_mass;
+
+  /*! Minimal mass of all the dark-matter #gpart */
+  float min_gpart_mass;
+
+  /*! Minimal mass of all the #spart */
+  float min_spart_mass;
+
+  /*! Sum of the norm of the velocity of all the #part */
+  float sum_part_vel_norm;
+
+  /*! Sum of the norm of the velocity of all the dark-matter #gpart */
+  float sum_gpart_vel_norm;
+
+  /*! Sum of the norm of the velocity of all the #spart */
+  float sum_spart_vel_norm;
+
   /*! General-purpose lock for this space. */
   swift_lock_type lock;
 
@@ -182,9 +200,7 @@ void space_gparts_sort(struct gpart *gparts, struct part *parts,
 void space_sparts_sort(struct spart *sparts, int *ind, int *counts,
                        int num_bins, ptrdiff_t sparts_offset);
 void space_getcells(struct space *s, int nr_cells, struct cell **cells);
-int space_getsid(struct space *s, struct cell **ci, struct cell **cj,
-                 double *shift);
-void space_init(struct space *s, const struct swift_params *params,
+void space_init(struct space *s, struct swift_params *params,
                 const struct cosmology *cosmo, double dim[3],
                 struct part *parts, struct gpart *gparts, struct spart *sparts,
                 size_t Npart, size_t Ngpart, size_t Nspart, int periodic,
@@ -218,9 +234,9 @@ void space_gparts_get_cell_index(struct space *s, int *gind, int *cell_counts,
 void space_sparts_get_cell_index(struct space *s, int *sind, int *cell_counts,
                                  struct cell *cells, int verbose);
 void space_synchronize_particle_positions(struct space *s);
-void space_do_parts_sort();
-void space_do_gparts_sort();
-void space_do_sparts_sort();
+void space_do_parts_sort(void);
+void space_do_gparts_sort(void);
+void space_do_sparts_sort(void);
 void space_first_init_parts(struct space *s, int verbose);
 void space_first_init_gparts(struct space *s, int verbose);
 void space_first_init_sparts(struct space *s, int verbose);
diff --git a/src/space_getsid.h b/src/space_getsid.h
new file mode 100644
index 0000000000000000000000000000000000000000..8f364c5197402b737da3035a94a3ea59d70e6345
--- /dev/null
+++ b/src/space_getsid.h
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_SPACE_GETSID_H
+#define SWIFT_SPACE_GETSID_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <stddef.h>
+
+/* Includes. */
+#include "cell.h"
+#include "runner.h"
+#include "space.h"
+
+/**
+ * @brief Get the shift-id of the given pair of cells, swapping them
+ *      if need be.
+ *
+ * WARNING: This function may swap the cells ci and cj.
+ *
+ * @param s The space
+ * @param ci Pointer to first #cell.
+ * @param cj Pointer second #cell.
+ * @param shift Vector from ci to cj.
+ *
+ * @return The shift ID and set shift, may or may not swap ci and cj.
+ */
+__attribute__((always_inline)) INLINE static int space_getsid(struct space *s,
+                                                              struct cell **ci,
+                                                              struct cell **cj,
+                                                              double *shift) {
+
+  /* Get the relative distance between the pairs, wrapping. */
+  const int periodic = s->periodic;
+  double dx[3];
+  for (int k = 0; k < 3; k++) {
+    dx[k] = (*cj)->loc[k] - (*ci)->loc[k];
+    if (periodic && dx[k] < -s->dim[k] / 2)
+      shift[k] = s->dim[k];
+    else if (periodic && dx[k] > s->dim[k] / 2)
+      shift[k] = -s->dim[k];
+    else
+      shift[k] = 0.0;
+    dx[k] += shift[k];
+  }
+
+  /* Get the sorting index. */
+  int sid = 0;
+  for (int k = 0; k < 3; k++)
+    sid = 3 * sid + ((dx[k] < 0.0) ? 0 : ((dx[k] > 0.0) ? 2 : 1));
+
+  /* Switch the cells around? */
+  if (runner_flip[sid]) {
+    struct cell *temp = *ci;
+    *ci = *cj;
+    *cj = temp;
+    for (int k = 0; k < 3; k++) shift[k] = -shift[k];
+  }
+  sid = sortlistID[sid];
+
+  /* Return the sort ID. */
+  return sid;
+}
+
+#endif /* SWIFT_SPACE_GETSID_H */
diff --git a/src/stars/Default/star_io.h b/src/stars/Default/star_io.h
index c3dc31096383533e1e15fa65615d2c9aac0f43e3..7ad29f0a935c002b1337c2a75d6f987c05c9bb43 100644
--- a/src/stars/Default/star_io.h
+++ b/src/stars/Default/star_io.h
@@ -28,8 +28,8 @@
  * @param list The list of i/o properties to read.
  * @param num_fields The number of i/o fields to read.
  */
-void star_read_particles(struct spart* sparts, struct io_props* list,
-                         int* num_fields) {
+INLINE static void star_read_particles(struct spart* sparts,
+                                       struct io_props* list, int* num_fields) {
 
   /* Say how much we want to read */
   *num_fields = 4;
@@ -52,8 +52,9 @@ void star_read_particles(struct spart* sparts, struct io_props* list,
  * @param list The list of i/o properties to write.
  * @param num_fields The number of i/o fields to write.
  */
-void star_write_particles(const struct spart* sparts, struct io_props* list,
-                          int* num_fields) {
+INLINE static void star_write_particles(const struct spart* sparts,
+                                        struct io_props* list,
+                                        int* num_fields) {
 
   /* Say how much we want to read */
   *num_fields = 4;
diff --git a/src/statistics.c b/src/statistics.c
index 8d8afae0d1441927e001d7d58a3ee5ff0399f5ed..bdca6cfb4ef84bb64aa4776bfc600b0727e0d606 100644
--- a/src/statistics.c
+++ b/src/statistics.c
@@ -107,6 +107,8 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) {
   const struct space *s = data->s;
   const struct engine *e = s->e;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
+  const int with_ext_grav = (e->policy & engine_policy_external_gravity);
+  const int with_self_grav = (e->policy & engine_policy_self_gravity);
   const integertime_t ti_current = e->ti_current;
   const double time_base = e->time_base;
   const double time = e->time;
@@ -190,11 +192,11 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) {
                    a_inv2; /* 1/2 m a^2 \dot{r}^2 */
     stats.E_int += m * u_inter;
     stats.E_rad += cooling_get_radiated_energy(xp);
-    if (gp != NULL) {
+    if (gp != NULL && with_self_grav)
       stats.E_pot_self += 0.5f * m * gravity_get_physical_potential(gp, cosmo);
+    if (gp != NULL && with_ext_grav)
       stats.E_pot_ext += m * external_gravity_get_potential_energy(
                                  time, potential, phys_const, gp);
-    }
 
     /* Collect entropy */
     stats.entropy += m * entropy;
@@ -220,6 +222,8 @@ void stats_collect_gpart_mapper(void *map_data, int nr_gparts,
   const struct space *s = data->s;
   const struct engine *e = s->e;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
+  const int with_ext_grav = (e->policy & engine_policy_external_gravity);
+  const int with_self_grav = (e->policy & engine_policy_self_gravity);
   const integertime_t ti_current = e->ti_current;
   const double time_base = e->time_base;
   const double time = e->time;
@@ -292,9 +296,11 @@ void stats_collect_gpart_mapper(void *map_data, int nr_gparts,
     /* Collect energies. */
     stats.E_kin += 0.5f * m * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]) *
                    a_inv2; /* 1/2 m a^2 \dot{r}^2 */
-    stats.E_pot_self += 0.5f * m * gravity_get_physical_potential(gp, cosmo);
-    stats.E_pot_ext += m * external_gravity_get_potential_energy(
-                               time, potential, phys_const, gp);
+    if (with_self_grav)
+      stats.E_pot_self += 0.5f * m * gravity_get_physical_potential(gp, cosmo);
+    if (with_ext_grav)
+      stats.E_pot_ext += m * external_gravity_get_potential_energy(
+                                 time, potential, phys_const, gp);
   }
 
   /* Now write back to memory */
@@ -390,7 +396,7 @@ void stats_add_MPI(void *in, void *inout, int *len, MPI_Datatype *datatype) {
 /**
  * @brief Registers MPI #statistics type and reduction function.
  */
-void stats_create_MPI_type() {
+void stats_create_MPI_type(void) {
 
   /* This is not the recommended way of doing this.
      One should define the structure field by field
diff --git a/src/statistics.h b/src/statistics.h
index e8cddda1e855d156070b8a000cb15b515f127740..adc9f5b6a24a093419b7dd644404a68ef736a685 100644
--- a/src/statistics.h
+++ b/src/statistics.h
@@ -77,7 +77,7 @@ extern MPI_Datatype statistics_mpi_type;
 extern MPI_Op statistics_mpi_reduce_op;
 
 void stats_add_MPI(void* in, void* out, int* len, MPI_Datatype* datatype);
-void stats_create_MPI_type();
+void stats_create_MPI_type(void);
 #endif
 
 #endif /* SWIFT_STATISTICS_H */
diff --git a/src/swift.h b/src/swift.h
index 7691720942f32d29d3269ccdd7adbe8db32280bf..58634443e2ed5de629123ab22a9f7560e8652107 100644
--- a/src/swift.h
+++ b/src/swift.h
@@ -29,8 +29,10 @@
 #include "cell.h"
 #include "chemistry.h"
 #include "clocks.h"
+#include "common_io.h"
 #include "const.h"
 #include "cooling.h"
+#include "cooling_struct.h"
 #include "cosmology.h"
 #include "cycle.h"
 #include "debug.h"
@@ -45,6 +47,7 @@
 #include "lock.h"
 #include "logger.h"
 #include "map.h"
+#include "mesh_gravity.h"
 #include "multipole.h"
 #include "parallel_io.h"
 #include "parser.h"
diff --git a/src/task.c b/src/task.c
index 4fa41fe409ba8559e57455f64e87448287bb2fe7..fad903b3de818504a9c3dcda478e7a1dd1dd86d0 100644
--- a/src/task.c
+++ b/src/task.c
@@ -48,15 +48,11 @@
 
 /* Task type names. */
 const char *taskID_names[task_type_count] = {
-    "none",          "sort",           "self",
-    "pair",          "sub_self",       "sub_pair",
-    "init_grav",     "ghost_in",       "ghost",
-    "ghost_out",     "extra_ghost",    "drift_part",
-    "drift_gpart",   "end_force",      "kick1",
-    "kick2",         "timestep",       "send",
-    "recv",          "grav_top_level", "grav_long_range",
-    "grav_ghost_in", "grav_ghost_out", "grav_mm",
-    "grav_down",     "cooling",        "sourceterms"};
+    "none",        "sort",       "self",        "pair",      "sub_self",
+    "sub_pair",    "init_grav",  "ghost_in",    "ghost",     "ghost_out",
+    "extra_ghost", "drift_part", "drift_gpart", "end_force", "kick1",
+    "kick2",       "timestep",   "send",        "recv",      "grav_long_range",
+    "grav_mm",     "grav_down",  "grav_mesh",   "cooling",   "sourceterms"};
 
 /* Sub-task type names. */
 const char *subtaskID_names[task_subtype_count] = {
@@ -171,14 +167,14 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
       break;
 
     case task_type_init_grav:
-    case task_type_grav_top_level:
-    case task_type_grav_long_range:
     case task_type_grav_mm:
       return task_action_multipole;
       break;
 
     case task_type_drift_gpart:
     case task_type_grav_down:
+    case task_type_grav_mesh:
+    case task_type_grav_long_range:
       return task_action_gpart;
       break;
 
@@ -289,6 +285,7 @@ void task_unlock(struct task *t) {
       break;
 
     case task_type_drift_gpart:
+    case task_type_grav_mesh:
       cell_gunlocktree(ci);
       break;
 
@@ -320,8 +317,8 @@ void task_unlock(struct task *t) {
       cell_munlocktree(ci);
       break;
 
-    case task_type_grav_long_range:
     case task_type_grav_mm:
+    case task_type_grav_long_range:
       cell_munlocktree(ci);
       break;
 
@@ -384,6 +381,7 @@ int task_lock(struct task *t) {
       break;
 
     case task_type_drift_gpart:
+    case task_type_grav_mesh:
       if (ci->ghold) return 0;
       if (cell_glocktree(ci) != 0) return 0;
       break;
diff --git a/src/task.h b/src/task.h
index 73dc272a763162bfcc8a10774c8c05f1b9a401f6..a353ff76a8419cf728b50999aa86e6deb9660ef3 100644
--- a/src/task.h
+++ b/src/task.h
@@ -58,12 +58,10 @@ enum task_types {
   task_type_timestep,
   task_type_send,
   task_type_recv,
-  task_type_grav_top_level,
   task_type_grav_long_range,
-  task_type_grav_ghost_in,
-  task_type_grav_ghost_out,
   task_type_grav_mm,
   task_type_grav_down,
+  task_type_grav_mesh,
   task_type_cooling,
   task_type_sourceterms,
   task_type_count
@@ -138,11 +136,11 @@ struct task {
   int rank;
 
   /*! Weight of the task */
-  int weight;
+  float weight;
 
 #if defined(WITH_MPI) && defined(HAVE_METIS)
   /*! Individual cost estimate for this task. */
-  int cost;
+  float cost;
 #endif
 
   /*! Number of tasks unlocked by this one */
diff --git a/src/timeline.h b/src/timeline.h
index 66b2c9456fb9e2e9f8b36272a9bbf3b7764523fc..4078a904c3e9205b8ea6ae7090534bd3d3d0784f 100644
--- a/src/timeline.h
+++ b/src/timeline.h
@@ -51,7 +51,8 @@ typedef char timebin_t;
  *
  * @param bin The time bin of interest.
  */
-static INLINE integertime_t get_integer_timestep(timebin_t bin) {
+__attribute__((const)) static INLINE integertime_t
+get_integer_timestep(timebin_t bin) {
 
   if (bin <= 0) return 0;
   return 1LL << (bin + 1);
@@ -62,7 +63,8 @@ static INLINE integertime_t get_integer_timestep(timebin_t bin) {
  *
  * Assumes that integertime_t maps to an unsigned long long.
  */
-static INLINE timebin_t get_time_bin(integertime_t time_step) {
+__attribute__((const)) static INLINE timebin_t
+get_time_bin(integertime_t time_step) {
 
   /* ((int) log_2(time_step)) - 1 */
   return (timebin_t)(62 - intrinsics_clzll(time_step));
@@ -74,7 +76,8 @@ static INLINE timebin_t get_time_bin(integertime_t time_step) {
  * @param bin The time bin of interest.
  * @param timeBase the minimal time-step size of the simulation.
  */
-static INLINE double get_timestep(timebin_t bin, double timeBase) {
+__attribute__((const)) static INLINE double get_timestep(timebin_t bin,
+                                                         double timeBase) {
 
   return get_integer_timestep(bin) * timeBase;
 }
@@ -86,8 +89,8 @@ static INLINE double get_timestep(timebin_t bin, double timeBase) {
  * @param ti_current The current time on the integer time line.
  * @param bin The time bin of interest.
  */
-static INLINE integertime_t get_integer_time_begin(integertime_t ti_current,
-                                                   timebin_t bin) {
+__attribute__((const)) static INLINE integertime_t
+get_integer_time_begin(integertime_t ti_current, timebin_t bin) {
 
   const integertime_t dti = get_integer_timestep(bin);
   if (dti == 0)
@@ -103,8 +106,8 @@ static INLINE integertime_t get_integer_time_begin(integertime_t ti_current,
  * @param ti_current The current time on the integer time line.
  * @param bin The time bin of interest.
  */
-static INLINE integertime_t get_integer_time_end(integertime_t ti_current,
-                                                 timebin_t bin) {
+__attribute__((const)) static INLINE integertime_t
+get_integer_time_end(integertime_t ti_current, timebin_t bin) {
 
   const integertime_t dti = get_integer_timestep(bin);
   if (dti == 0)
@@ -118,7 +121,8 @@ static INLINE integertime_t get_integer_time_end(integertime_t ti_current,
  *
  * @param time The current point on the time line.
  */
-static INLINE timebin_t get_max_active_bin(integertime_t time) {
+__attribute__((const)) static INLINE timebin_t
+get_max_active_bin(integertime_t time) {
 
   if (time == 0) return num_time_bins;
 
@@ -134,8 +138,8 @@ static INLINE timebin_t get_max_active_bin(integertime_t time) {
  * @param ti_current The current point on the time line.
  * @param ti_old The last synchronisation point on the time line.
  */
-static INLINE timebin_t get_min_active_bin(integertime_t ti_current,
-                                           integertime_t ti_old) {
+__attribute__((const)) static INLINE timebin_t
+get_min_active_bin(integertime_t ti_current, integertime_t ti_old) {
 
   const timebin_t min_bin = get_max_active_bin(ti_current - ti_old);
   return (ti_old > 0) ? min_bin : (min_bin - 1);
diff --git a/src/timers.c b/src/timers.c
index fec111dd939528bd0648609d8a1f5f83e595ec02..e3fbfdb01249e98e46d2c60d45bd98adb0a34241 100644
--- a/src/timers.c
+++ b/src/timers.c
@@ -54,12 +54,11 @@ const char* timers_names[timer_count] = {
     "dopair_density",
     "dopair_gradient",
     "dopair_force",
-    "dopair_grav_branch",
     "dopair_grav_mm",
-    "dopair_grav_pm",
     "dopair_grav_pp",
     "dograv_external",
     "dograv_down",
+    "dograv_mesh",
     "dograv_top_level",
     "dograv_long_range",
     "dosource",
@@ -110,7 +109,7 @@ void timers_reset(unsigned long long mask) {
  * @brief Re-set all the timers.
  *
  */
-void timers_reset_all() { timers_reset(timers_mask_all); }
+void timers_reset_all(void) { timers_reset(timers_mask_all); }
 
 /**
  * @brief Outputs all the timers to the timers dump file.
@@ -145,4 +144,4 @@ void timers_open_file(int rank) {
 /**
  * @brief Close the file containing the timer info.
  */
-void timers_close_file() { fclose(timers_file); }
+void timers_close_file(void) { fclose(timers_file); }
diff --git a/src/timers.h b/src/timers.h
index 38ede8251eb5d640282e728e17d9330956a1cba8..91d26c1c0d781f725b4c55a7ed3b6cfe956651df 100644
--- a/src/timers.h
+++ b/src/timers.h
@@ -55,12 +55,11 @@ enum {
   timer_dopair_density,
   timer_dopair_gradient,
   timer_dopair_force,
-  timer_dopair_grav_branch,
   timer_dopair_grav_mm,
-  timer_dopair_grav_pm,
   timer_dopair_grav_pp,
   timer_dograv_external,
   timer_dograv_down,
+  timer_dograv_mesh,
   timer_dograv_top_level,
   timer_dograv_long_range,
   timer_dosource,
@@ -119,10 +118,10 @@ INLINE static ticks timers_toc(unsigned int t, ticks tic) {
 #endif
 
 /* Function prototypes. */
-void timers_reset_all();
+void timers_reset_all(void);
 void timers_reset(unsigned long long mask);
 void timers_open_file(int rank);
-void timers_close_file();
+void timers_close_file(void);
 void timers_print(int step);
 
 #endif /* SWIFT_TIMERS_H */
diff --git a/src/timestep.h b/src/timestep.h
index 958a4c4c1c23d5a5332cf7e4eb7d56f84b0c2077..d065df4c444cb880a74688be97245c285a391817 100644
--- a/src/timestep.h
+++ b/src/timestep.h
@@ -151,7 +151,7 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep(
 
   new_dt = min(new_dt, dt_h_change);
 
-  /* Apply the maximal displacement constraint (FLT_MAX  if non-cosmological)*/
+  /* Apply the maximal displacement constraint (FLT_MAX if non-cosmological)*/
   new_dt = min(new_dt, e->dt_max_RMS_displacement);
 
   /* Apply cosmology correction (This is 1 if non-cosmological) */
@@ -159,6 +159,7 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep(
 
   /* Limit timestep within the allowed range */
   new_dt = min(new_dt, e->dt_max);
+
   if (new_dt < e->dt_min)
     error("part (id=%lld) wants a time-step (%e) below dt_min (%e)", p->id,
           new_dt, e->dt_min);
diff --git a/src/tools.c b/src/tools.c
index 8b8b7fdf37d91547f328a6b49d69b3c5a491aed1..9c0df6012737872eef8d97521b3a7532ceb42720 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -23,6 +23,7 @@
 #include "../config.h"
 
 /* Some standard headers. */
+#include <ctype.h>
 #include <math.h>
 #include <stddef.h>
 #include <stdio.h>
@@ -728,3 +729,46 @@ long get_maxrss() {
   getrusage(RUSAGE_SELF, &usage);
   return usage.ru_maxrss;
 }
+
+/**
+ * @brief trim leading white space from a string.
+ *
+ * Returns pointer to first character.
+ *
+ * @param s the string.
+ * @result the result.
+ */
+char *trim_leading(char *s) {
+  if (s == NULL || strlen(s) < 2) return s;
+  while (isspace(*s)) s++;
+  return s;
+}
+
+/**
+ * @brief trim trailing white space from a string.
+ *
+ * Modifies the string by adding a NULL to the end.
+ *
+ * @param s the string.
+ * @result the result.
+ */
+char *trim_trailing(char *s) {
+  if (s == NULL || strlen(s) < 2) return s;
+  char *end = s + strlen(s) - 1;
+  while (isspace(*end)) end--;
+  *(end + 1) = '\0';
+  return s;
+}
+
+/**
+ * @brief trim leading and trailing white space from a string.
+ *
+ * Can modify the string by adding a NULL to the end.
+ *
+ * @param s the string.
+ * @result the result.
+ */
+char *trim_both(char *s) {
+  if (s == NULL || strlen(s) < 2) return s;
+  return trim_trailing(trim_leading(s));
+}
diff --git a/src/tools.h b/src/tools.h
index bb141101a3bf6fad38a83a15ea7f6bb5de86e9f8..25d024679174eabbe89908c0254651e4bbc69e15 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -52,6 +52,10 @@ int compare_values(double a, double b, double threshold, double *absDiff,
                    double *absSum, double *relDiff);
 int compare_particles(struct part a, struct part b, double threshold);
 
-long get_maxrss();
+long get_maxrss(void);
+
+char *trim_leading(char *s);
+char *trim_trailing(char *s);
+char *trim_both(char *s);
 
 #endif /* SWIFT_TOOL_H */
diff --git a/src/units.c b/src/units.c
index 4b632e735b7c6e1c12afe8aebc16aa44abc5b597..6152d7f42f9a409bea9d057862c04d0ae9fb6a75 100644
--- a/src/units.c
+++ b/src/units.c
@@ -28,11 +28,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-/* MPI headers. */
-#ifdef WITH_MPI
-#include <mpi.h>
-#endif
-
 /* This object's header. */
 #include "units.h"
 
@@ -55,6 +50,25 @@ void units_init_cgs(struct unit_system* us) {
   us->UnitTemperature_in_cgs = 1.;
 }
 
+/**
+ * @brief Initialise the unit_system with values for the base units.
+ *
+ * @param us The #unit_system to initialise.
+ * @param U_M_in_cgs The mass unit in [g].
+ * @param U_L_in_cgs The length unit in [cm].
+ * @param U_t_in_cgs The time unit in [s].
+ * @param U_C_in_cgs The current unit in [A].
+ * @param U_T_in_cgs The temperature unit in [K].
+ */
+void units_init(struct unit_system* us, double U_M_in_cgs, double U_L_in_cgs,
+                double U_t_in_cgs, double U_C_in_cgs, double U_T_in_cgs) {
+  us->UnitMass_in_cgs = U_M_in_cgs;
+  us->UnitLength_in_cgs = U_L_in_cgs;
+  us->UnitTime_in_cgs = U_t_in_cgs;
+  us->UnitCurrent_in_cgs = U_C_in_cgs;
+  us->UnitTemperature_in_cgs = U_T_in_cgs;
+}
+
 /**
  * @brief Initialises the unit_system structure with the constants given in
  * the parameter file.
@@ -63,8 +77,8 @@ void units_init_cgs(struct unit_system* us) {
  * @param params The parsed parameter file.
  * @param category The section of the parameter file to read from.
  */
-void units_init(struct unit_system* us, const struct swift_params* params,
-                const char* category) {
+void units_init_from_params(struct unit_system* us, struct swift_params* params,
+                            const char* category) {
 
   char buffer[200];
   sprintf(buffer, "%s:UnitMass_in_cgs", category);
@@ -89,9 +103,8 @@ void units_init(struct unit_system* us, const struct swift_params* params,
  * @param category The section of the parameter file to read from.
  * @param def The default unit system to copy from if required.
  */
-void units_init_default(struct unit_system* us,
-                        const struct swift_params* params, const char* category,
-                        const struct unit_system* def) {
+void units_init_default(struct unit_system* us, struct swift_params* params,
+                        const char* category, const struct unit_system* def) {
 
   if (!def) error("Default unit_system not allocated");
 
@@ -115,6 +128,21 @@ void units_init_default(struct unit_system* us,
       parser_get_opt_param_double(params, buffer, def->UnitTemperature_in_cgs);
 }
 
+/**
+ * @brief Copy the content of a #unit_system to another one.
+ *
+ * @param dest The destination of the copy.
+ * @param src The source of the copy.
+ */
+void units_copy(struct unit_system* dest, const struct unit_system* src) {
+
+  dest->UnitMass_in_cgs = src->UnitMass_in_cgs;
+  dest->UnitLength_in_cgs = src->UnitLength_in_cgs;
+  dest->UnitTime_in_cgs = src->UnitTime_in_cgs;
+  dest->UnitCurrent_in_cgs = src->UnitCurrent_in_cgs;
+  dest->UnitTemperature_in_cgs = src->UnitTemperature_in_cgs;
+}
+
 /**
  * @brief Returns the base unit conversion factor for a given unit system
  * @param us The unit_system used
@@ -457,7 +485,7 @@ float units_general_a_factor(const struct unit_system* us,
 void units_general_cgs_conversion_string(char* buffer,
                                          const struct unit_system* us,
                                          const float baseUnitsExponants[5]) {
-  char temp[14];
+  char temp[20];
   const double a_exp = units_general_a_factor(us, baseUnitsExponants);
   const double h_exp = units_general_h_factor(us, baseUnitsExponants);
 
@@ -490,7 +518,7 @@ void units_general_cgs_conversion_string(char* buffer,
     sprintf(temp, "h^%d ", (int)h_exp);
   else
     sprintf(temp, "h^%7.4f ", h_exp);
-  strncat(buffer, temp, 12);
+  strcat(buffer, temp);
 
   /* Add conversion units */
   for (int i = 0; i < 5; ++i)
@@ -508,11 +536,11 @@ void units_general_cgs_conversion_string(char* buffer,
         sprintf(temp, "%s^%7.4f ",
                 units_get_base_unit_internal_symbol((enum base_units)i),
                 baseUnitsExponants[i]);
-      strncat(buffer, temp, 12);
+      strcat(buffer, temp);
     }
 
   /* Add CGS units */
-  strncat(buffer, " [ ", 3);
+  strcat(buffer, " [ ");
 
   for (int i = 0; i < 5; ++i) {
     if (baseUnitsExponants[i] != 0) {
@@ -529,11 +557,11 @@ void units_general_cgs_conversion_string(char* buffer,
         sprintf(temp, "%s^%7.4f ",
                 units_get_base_unit_cgs_symbol((enum base_units)i),
                 baseUnitsExponants[i]);
-      strncat(buffer, temp, 12);
+      strcat(buffer, temp);
     }
   }
 
-  strncat(buffer, "]", 2);
+  strcat(buffer, "]");
 }
 
 /**
diff --git a/src/units.h b/src/units.h
index 87c44cc6eb4934980027a60642dd135f03029f7c..da2c209815b07d1d5597a598ee4a61f3132e39db 100644
--- a/src/units.h
+++ b/src/units.h
@@ -28,7 +28,7 @@
 /**
  * @brief The unit system used internally.
  *
- * This structure contains the conversion factors to the 7 cgs base units to the
+ * This structure contains the conversion factors to the 5 cgs base units to the
  * internal units. It is used everytime a conversion is performed or an i/o
  * function is called.
  **/
@@ -96,12 +96,14 @@ enum unit_conversion_factor {
 };
 
 void units_init_cgs(struct unit_system*);
-void units_init(struct unit_system*, const struct swift_params*,
-                const char* category);
-void units_init_default(struct unit_system* us,
-                        const struct swift_params* params, const char* category,
-                        const struct unit_system* def);
-
+void units_init(struct unit_system* us, double U_M_in_cgs, double U_L_in_cgs,
+                double U_t_in_cgs, double U_C_in_cgs, double U_T_in_cgs);
+void units_init_from_params(struct unit_system*, struct swift_params*,
+                            const char* category);
+void units_init_default(struct unit_system* us, struct swift_params* params,
+                        const char* category, const struct unit_system* def);
+
+void units_copy(struct unit_system* dest, const struct unit_system* src);
 int units_are_equal(const struct unit_system* a, const struct unit_system* b);
 
 /* Base units */
diff --git a/src/utilities.h b/src/utilities.h
new file mode 100644
index 0000000000000000000000000000000000000000..28faf6b1e59159af4ee4a7b87054ab6a36478c9c
--- /dev/null
+++ b/src/utilities.h
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#ifndef SWIFT_UTILITIES_H
+#define SWIFT_UTILITIES_H
+
+/**
+ * @brief Search for a value in a monotonically increasing array to find the
+ *      index such that array[index] < value < array[index + 1]
+ *
+ * @param x The value to find
+ * @param array The array to search
+ * @param n The length of the array
+ *
+ * Return -1 and n for x below and above the array edge values respectively.
+ */
+INLINE static int find_value_in_monot_incr_array(const float x,
+                                                 const float *array,
+                                                 const int n) {
+
+  int index_mid, index_low = 0, index_high = n;
+
+  // Until array[index_low] < x < array[index_high=index_low+1]
+  while (index_high - index_low > 1) {
+    index_mid = (index_high + index_low) / 2;  // Middle index
+
+    // Replace the low or high index with the middle
+    if (array[index_mid] <= x)
+      index_low = index_mid;
+    else
+      index_high = index_mid;
+  }
+
+  // Set index with the found index_low or an error value if outside the array
+  if (x < array[0])
+    return -1;
+  else if (array[n - 1] <= x)
+    return n;
+  else
+    return index_low;
+}
+
+#endif /* SWIFT_UTILITIES_H */
diff --git a/src/vector.h b/src/vector.h
index 9048e273759ae0c0978c8ddbf26a810d4761f464..a1ecddc6ed68ef659759665f15f25aa7e32dc908 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -493,7 +493,7 @@ __attribute__((always_inline)) INLINE vector vector_set1(const float x) {
  * @return temp set #vector.
  * @return A #vector filled with zeros.
  */
-__attribute__((always_inline)) INLINE vector vector_setzero() {
+__attribute__((always_inline)) INLINE vector vector_setzero(void) {
 
   vector temp;
   temp.v = vec_setzero();
diff --git a/src/version.c b/src/version.c
index 54749721de96bde010f56965152c536b08672230..69f70b9aec3549c061c162f2ce183f8fafcc2e9f 100644
--- a/src/version.c
+++ b/src/version.c
@@ -146,7 +146,7 @@ const char *configuration_options(void) {
   static int initialised = 0;
   static const char *config = SWIFT_CONFIG_FLAGS;
   if (!initialised) {
-    snprintf(buf, 1024, "'%s'", config);
+    snprintf(buf, 1024, "'%.1021s'", config);
     initialised = 1;
   }
   return buf;
@@ -162,7 +162,7 @@ const char *compilation_cflags(void) {
   static int initialised = 0;
   static const char *cflags = SWIFT_CFLAGS;
   if (!initialised) {
-    snprintf(buf, 1024, "'%s'", cflags);
+    snprintf(buf, 1024, "'%.1021s'", cflags);
     initialised = 1;
   }
   return buf;
@@ -272,12 +272,12 @@ const char *mpi_version(void) {
 #else
   /* Use autoconf guessed value. */
   static char lib_version[60] = {0};
-  snprintf(lib_version, 60, "%s", SWIFT_MPI_LIBRARY);
+  snprintf(lib_version, 60, "%.60s", SWIFT_MPI_LIBRARY);
 #endif
 
   /* Numeric version. */
   MPI_Get_version(&std_version, &std_subversion);
-  snprintf(version, 80, "%s (MPI std v%i.%i)", lib_version, std_version,
+  snprintf(version, 80, "%.60s (MPI std v%i.%i)", lib_version, std_version,
            std_subversion);
 #else
   sprintf(version, "Code was not compiled with MPI support");
@@ -345,7 +345,7 @@ const char *libgsl_version(void) {
 
   static char version[256] = {0};
 #if defined(HAVE_LIBGSL)
-  sprintf(version, "%s", gsl_version);
+  sprintf(version, "%.255s", gsl_version);
 #else
   sprintf(version, "Unknown version");
 #endif
@@ -368,6 +368,26 @@ const char *thread_barrier_version(void) {
   return version;
 }
 
+/**
+ * @brief return the allocator library used in SWIFT.
+ *
+ * @result description of the allocation library
+ */
+const char *allocator_version(void) {
+
+  static char version[256] = {0};
+#if defined(HAVE_TBBMALLOC)
+  sprintf(version, "TBB malloc");
+#elif defined(HAVE_TCMALLOC)
+  sprintf(version, "tc-malloc");
+#elif defined(HAVE_JEMALLOC)
+  sprintf(version, "je-malloc");
+#else
+  sprintf(version, "Compiler version (probably glibc)");
+#endif
+  return version;
+}
+
 /**
  * @brief Prints a greeting message to the standard output containing code
  * version and revision number
diff --git a/src/version.h b/src/version.h
index 3163f242c50e56c64cc709b13dfe926f93672a00..44119b6a3bbdf57c3f0195bae5ff329d05c61fd5 100644
--- a/src/version.h
+++ b/src/version.h
@@ -36,6 +36,7 @@ const char* hdf5_version(void);
 const char* fftw3_version(void);
 const char* libgsl_version(void);
 const char* thread_barrier_version(void);
+const char* allocator_version(void);
 void greetings(void);
 
 #endif /* SWIFT_VERSION_H */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 643d89aaf05e31b6ea1c304c4fac6c28f7e89d03..ad7dcf92d6e4133e80bdd077447841626e59d722 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,33 +1,34 @@
 # This file is part of SWIFT.
 # Copyright (c) 2015 matthieu.schaller@durham.ac.uk.
-# 
+#
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation, either version 3 of the License, or
 # (at your option) any later version.
-# 
+#
 # 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 General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # Add the source directory and the non-standard paths to the included library headers to CFLAGS
 AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS)
 
-AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS)
+AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS)
 
 # List of programs and scripts to run in the test suite
 TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetry \
         testActivePair.sh test27cells.sh test27cellsPerturbed.sh  \
         testParser.sh testSPHStep test125cells.sh test125cellsPerturbed.sh testFFT \
-        testAdiabaticIndex testRiemannExact testRiemannTRRS testRiemannHLLC \
+        testAdiabaticIndex \
         testMatrixInversion testThreadpool testDump testLogger testInteractions.sh \
         testVoronoi1D testVoronoi2D testVoronoi3D testGravityDerivatives \
 	testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \
-	testPotentialPair 
+	testPotentialPair testEOS testUtilities testSelectOutput.sh \
+    testCbrt
 
 # List of test programs to compile
 check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \
@@ -37,7 +38,8 @@ check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \
                  testAdiabaticIndex testRiemannExact testRiemannTRRS \
                  testRiemannHLLC testMatrixInversion testDump testLogger \
 		 testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \
-		 testGravityDerivatives testPotentialSelf testPotentialPair
+		 testGravityDerivatives testPotentialSelf testPotentialPair testEOS testUtilities \
+		 testSelectOutput testCbrt
 
 # Rebuild tests when SWIFT is updated.
 $(check_PROGRAMS): ../src/.libs/libswiftsim.a
@@ -49,8 +51,13 @@ testMaths_SOURCES = testMaths.c
 
 testReading_SOURCES = testReading.c
 
+testSelectOutput_SOURCES = testSelectOutput.c
+
 testSymmetry_SOURCES = testSymmetry.c
 
+# Added because of issues using memcmp on clang 4.x
+testSymmetry_CFLAGS = $(AM_CFLAGS) -fno-builtin-memcmp
+
 testTimeIntegration_SOURCES = testTimeIntegration.c
 
 testSPHStep_SOURCES = testSPHStep.c
@@ -105,6 +112,10 @@ testPotentialSelf_SOURCES = testPotentialSelf.c
 
 testPotentialPair_SOURCES = testPotentialPair.c
 
+testEOS_SOURCES = testEOS.c
+
+testUtilities_SOURCES = testUtilities.c
+
 # Files necessary for distribution
 EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \
 	     test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \
@@ -112,4 +123,5 @@ EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \
 	     difffloat.py tolerance_125_normal.dat tolerance_125_perturbed.dat \
              tolerance_27_normal.dat tolerance_27_perturbed.dat tolerance_27_perturbed_h.dat tolerance_27_perturbed_h2.dat \
 	     tolerance_testInteractions.dat tolerance_pair_active.dat tolerance_pair_force_active.dat \
-	     fft_params.yml tolerance_periodic_BC_normal.dat tolerance_periodic_BC_perturbed.dat
+	     fft_params.yml tolerance_periodic_BC_normal.dat tolerance_periodic_BC_perturbed.dat \
+	     testEOS.sh testEOS_plot.sh testSelectOutput.sh selectOutput.yml
diff --git a/tests/difffloat.py b/tests/difffloat.py
index ddcf7bcb29758afa3429dea8bcf50e1c5c0477dc..55295309e1309ab35542a50f7dd5b15c86c1508f 100644
--- a/tests/difffloat.py
+++ b/tests/difffloat.py
@@ -57,7 +57,7 @@ if fileTol != "":
 
 
 if shape(data1) != shape(data2):
-    print "Non-matching array sizes in the files", file1, "and", file2, "."
+    print("Non-matching array sizes in the files", file1, "and", file2, ".")
     sys.exit(1)
 
 n_lines = shape(data1)[0]
@@ -65,18 +65,18 @@ n_columns = shape(data1)[1]
 
 if fileTol != "":
     if n_linesTol != 3:
-        print "Incorrect number of lines in tolerance file '%s'."%fileTol
+        print("Incorrect number of lines in tolerance file '%s'."%fileTol)
     if n_columnsTol != n_columns:
-        print "Incorrect number of columns in tolerance file '%s'."%fileTol
+        print("Incorrect number of columns in tolerance file '%s'."%fileTol)
 
 if fileTol == "":
-    print "Absolute difference tolerance:", abs_tol
-    print "Relative difference tolerance:", rel_tol
+    print("Absolute difference tolerance:", abs_tol)
+    print("Relative difference tolerance:", rel_tol)
     absTol = ones(n_columns) * abs_tol
     relTol = ones(n_columns) * rel_tol
     limTol = zeros(n_columns)
 else:
-    print "Tolerances read from file"
+    print("Tolerances read from file")
     absTol = dataTol[0,:]
     relTol = dataTol[1,:]
     limTol = dataTol[2,:]
@@ -85,10 +85,10 @@ n_lines_to_check = 0
 if number_to_check > 0:
     n_lines_to_check = number_to_check**3
     n_lines_to_check = min(n_lines_to_check, n_lines)
-    print "Checking the first %d particles."%n_lines_to_check
+    print("Checking the first %d particles."%n_lines_to_check)
 else:
     n_lines_to_check = n_lines
-    print "Checking all particles in the file."
+    print("Checking all particles in the file.")
 
 error = False
 for i in range(n_lines_to_check):
@@ -103,26 +103,26 @@ for i in range(n_lines_to_check):
             rel_diff = 0.
 
         if( abs_diff > 1.1*absTol[j]):
-            print "Absolute difference larger than tolerance (%e) for particle %d, column %s:"%(absTol[j], data1[i,0], part_props[j])
-            print "%10s:           a = %e"%("File 1", data1[i,j])
-            print "%10s:           b = %e"%("File 2", data2[i,j])
-            print "%10s:       |a-b| = %e"%("Difference", abs_diff)
-            print ""
+            print("Absolute difference larger than tolerance (%e) for particle %d, column %s:"%(absTol[j], data1[i,0], part_props[j]))
+            print("%10s:           a = %e"%("File 1", data1[i,j]))
+            print("%10s:           b = %e"%("File 2", data2[i,j]))
+            print("%10s:       |a-b| = %e"%("Difference", abs_diff))
+            print("")
             error = True
 
         if abs(data1[i,j]) + abs(data2[i,j]) < limTol[j] : continue
 
         if( rel_diff > 1.1*relTol[j]):
-            print "Relative difference larger than tolerance (%e) for particle %d, column %s:"%(relTol[j], data1[i,0], part_props[j])
-            print "%10s:           a = %e"%("File 1", data1[i,j])
-            print "%10s:           b = %e"%("File 2", data2[i,j])
-            print "%10s: |a-b|/|a+b| = %e"%("Difference", rel_diff)
-            print ""
+            print("Relative difference larger than tolerance (%e) for particle %d, column %s:"%(relTol[j], data1[i,0], part_props[j]))
+            print("%10s:           a = %e"%("File 1", data1[i,j]))
+            print("%10s:           b = %e"%("File 2", data2[i,j]))
+            print("%10s: |a-b|/|a+b| = %e"%("Difference", rel_diff))
+            print("")
             error = True
 
 
 if error:
     exit(1)
 else:
-    print "No differences found"
+    print("No differences found")
     exit(0)
diff --git a/tests/fft_params.yml b/tests/fft_params.yml
index 6938e3658148874e58f50ab768a5e1fbc41d9573..ff32d83d4811162141f8723739bb0c93c992d81b 100644
--- a/tests/fft_params.yml
+++ b/tests/fft_params.yml
@@ -7,5 +7,5 @@ Gravity:
   theta:                  0.7        # Opening angle (Multipole acceptance criterion)
   comoving_softening:     0.00001    # Comoving softening length (in internal units).
   max_physical_softening: 0.00001    # Physical softening length (in internal units).
-  a_smooth:               0.
+  a_smooth:               0.00001
   r_cut:                  0.
diff --git a/tests/selectOutput.yml b/tests/selectOutput.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1778935146b19992e25efcb320d8cc523c6472a5
--- /dev/null
+++ b/tests/selectOutput.yml
@@ -0,0 +1,12 @@
+SelectOutput:
+  # Particle Type 0
+  Coordinates_Gas: 1   # check if written when specified
+  Velocities_Gas:  0   # check if not written when specified
+  Masses_Gas:     -5   # check warning if not 0 or 1 and if written
+  Pot_Gas:         1   # check warning if wrong name
+  # Density_Gas:   1   # check if written when not specified
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
diff --git a/tests/test125cells.c b/tests/test125cells.c
index ddebce8cabda227e96e04996214104354703c185..ddce3176d463f2ef754bf82b364858085e317e4b 100644
--- a/tests/test125cells.c
+++ b/tests/test125cells.c
@@ -118,9 +118,11 @@ void set_energy_state(struct part *part, enum pressure_field press, float size,
   part->entropy = pressure / pow_gamma(density);
 #elif defined(DEFAULT_SPH)
   part->u = pressure / (hydro_gamma_minus_one * density);
-#elif defined(MINIMAL_SPH)
+#elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH)
   part->u = pressure / (hydro_gamma_minus_one * density);
-#elif defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#elif defined(MINIMAL_MULTI_MAT_SPH)
+  part->u = pressure / (hydro_gamma_minus_one * density);
+#elif defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
   part->primitives.P = pressure;
 #else
   error("Need to define pressure here !");
@@ -220,9 +222,9 @@ void reset_particles(struct cell *c, struct hydro_space *hs,
 
     hydro_init_part(p, hs);
 
-#if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
     float volume = p->conserved.mass / density;
-#if defined(GIZMO_SPH)
+#if defined(GIZMO_MFV_SPH)
     p->geometry.volume = volume;
 #else
     p->cell.volume = volume;
@@ -266,7 +268,7 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
 
   const size_t count = n * n * n;
   const double volume = size * size * size;
-  struct cell *cell = malloc(sizeof(struct cell));
+  struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
 
   if (posix_memalign((void **)&cell->parts, part_align,
@@ -298,7 +300,7 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
         part->h = size * h / (float)n;
         h_max = fmax(h_max, part->h);
 
-#if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
         part->conserved.mass = density * volume / count;
 #else
         part->mass = density * volume / count;
@@ -312,7 +314,7 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
         part->id = ++(*partId);
         part->time_bin = 1;
 
-#if defined(GIZMO_SPH)
+#if defined(GIZMO_MFV_SPH)
         part->geometry.volume = part->conserved.mass / density;
         part->primitives.rho = density;
         part->primitives.v[0] = part->v[0];
@@ -403,7 +405,9 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
             main_cell->parts[pid].v[0], main_cell->parts[pid].v[1],
             main_cell->parts[pid].v[2], main_cell->parts[pid].h,
             hydro_get_comoving_density(&main_cell->parts[pid]),
-#if defined(MINIMAL_SPH) || defined(SHADOWFAX_SPH)
+#if defined(MINIMAL_SPH) || defined(MINIMAL_MULTI_MAT_SPH) || \
+    defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH) ||       \
+    defined(HOPKINS_PU_SPH)
             0.f,
 #else
             main_cell->parts[pid].density.div_v,
@@ -420,7 +424,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
 #elif defined(DEFAULT_SPH)
             main_cell->parts[pid].force.v_sig, 0.f,
             main_cell->parts[pid].force.u_dt
-#elif defined(MINIMAL_SPH)
+#elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH)
             main_cell->parts[pid].force.v_sig, 0.f, main_cell->parts[pid].u_dt
 #else
             0.f, 0.f, 0.f
@@ -633,8 +637,8 @@ int main(int argc, char *argv[]) {
   main_cell = cells[62];
 
   /* Construct the real solution */
-  struct solution_part *solution =
-      malloc(main_cell->count * sizeof(struct solution_part));
+  struct solution_part *solution = (struct solution_part *)malloc(
+      main_cell->count * sizeof(struct solution_part));
   get_solution(main_cell, solution, rho, vel, press, size);
 
   ticks timings[27];
@@ -660,6 +664,7 @@ int main(int argc, char *argv[]) {
       runner_do_sort(&runner, cells[j], 0x1FFF, 0, 0);
 
 /* Do the density calculation */
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
 /* Initialise the particle cache. */
 #ifdef WITH_VECTORIZATION
@@ -703,10 +708,13 @@ int main(int argc, char *argv[]) {
     for (int j = 0; j < 27; ++j)
       runner_doself1_density(&runner, inner_cells[j]);
 
+#endif
+
     /* Ghost to finish everything on the central cells */
     for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0);
 
 /* Do the force calculation */
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
 #ifdef WITH_VECTORIZATION
     /* Initialise the cache. */
@@ -742,6 +750,7 @@ int main(int argc, char *argv[]) {
     DOSELF2(&runner, main_cell);
 
     timings[26] += getticks() - self_tic;
+#endif
 
     /* Finally, give a gentle kick */
     runner_do_end_force(&runner, main_cell, 0);
@@ -750,7 +759,7 @@ int main(int argc, char *argv[]) {
 
     /* Dump if necessary */
     if (n == 0) {
-      sprintf(outputFileName, "swift_dopair_125_%s.dat",
+      sprintf(outputFileName, "swift_dopair_125_%.150s.dat",
               outputFileNameExtension);
       dump_particle_fields(outputFileName, main_cell, solution, 0);
     }
@@ -787,17 +796,18 @@ int main(int argc, char *argv[]) {
 
   const ticks tic = getticks();
 
-  /* Kick the central cell */
-  // runner_do_kick1(&runner, main_cell, 0);
+/* Kick the central cell */
+// runner_do_kick1(&runner, main_cell, 0);
 
-  /* And drift it */
-  // runner_do_drift_particles(&runner, main_cell, 0);
+/* And drift it */
+// runner_do_drift_particles(&runner, main_cell, 0);
 
-  /* Initialise the particles */
-  // for (int j = 0; j < 125; ++j) runner_do_drift_particles(&runner, cells[j],
-  // 0);
+/* Initialise the particles */
+// for (int j = 0; j < 125; ++j) runner_do_drift_particles(&runner, cells[j],
+// 0);
 
-  /* Do the density calculation */
+/* Do the density calculation */
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
   /* Run all the pairs (only once !)*/
   for (int i = 0; i < 5; i++) {
@@ -832,10 +842,13 @@ int main(int argc, char *argv[]) {
   /* And now the self-interaction for the central cells*/
   for (int j = 0; j < 27; ++j) self_all_density(&runner, inner_cells[j]);
 
+#endif
+
   /* Ghost to finish everything on the central cells */
   for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0);
 
-  /* Do the force calculation */
+/* Do the force calculation */
+#if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
 
   /* Do the pairs (for the central 27 cells) */
   for (int i = 1; i < 4; i++) {
@@ -852,6 +865,8 @@ int main(int argc, char *argv[]) {
   /* And now the self-interaction for the main cell */
   self_all_force(&runner, main_cell);
 
+#endif
+
   /* Finally, give a gentle kick */
   runner_do_end_force(&runner, main_cell, 0);
   // runner_do_kick2(&runner, main_cell, 0);
@@ -861,7 +876,8 @@ int main(int argc, char *argv[]) {
   /* Output timing */
   message("Brute force calculation took : %15lli ticks.", toc - tic);
 
-  sprintf(outputFileName, "brute_force_125_%s.dat", outputFileNameExtension);
+  sprintf(outputFileName, "brute_force_125_%.150s.dat",
+          outputFileNameExtension);
   dump_particle_fields(outputFileName, main_cell, solution, 0);
 
   /* Clean things to make the sanitizer happy ... */
diff --git a/tests/test27cells.c b/tests/test27cells.c
index cc3a617fb52deed2848e60c776b27675caf3288f..f62c169486250ba940c22f21ba1556cca4060c1a 100644
--- a/tests/test27cells.c
+++ b/tests/test27cells.c
@@ -98,7 +98,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
   const size_t count = n * n * n;
   const double volume = size * size * size;
   float h_max = 0.f;
-  struct cell *cell = malloc(sizeof(struct cell));
+  struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
 
   if (posix_memalign((void **)&cell->parts, part_align,
@@ -150,7 +150,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
         h_max = fmaxf(h_max, part->h);
         part->id = ++(*partId);
 
-#if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
         part->conserved.mass = density * volume / count;
 
 #ifdef SHADOWFAX_SPH
@@ -217,8 +217,20 @@ 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) {
+#ifdef SHADOWFAX_SPH
+  struct hydro_space hs;
+  hs.anchor[0] = 0.;
+  hs.anchor[1] = 0.;
+  hs.anchor[2] = 0.;
+  hs.side[0] = 1.;
+  hs.side[1] = 1.;
+  hs.side[2] = 1.;
+  struct hydro_space *hspointer = &hs;
+#else
+  struct hydro_space *hspointer = NULL;
+#endif
   for (int pid = 0; pid < c->count; pid++) {
-    hydro_init_part(&c->parts[pid], NULL);
+    hydro_init_part(&c->parts[pid], hspointer);
   }
 }
 
@@ -261,14 +273,17 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
             main_cell->parts[pid].v[0], main_cell->parts[pid].v[1],
             main_cell->parts[pid].v[2],
             hydro_get_comoving_density(&main_cell->parts[pid]),
-#if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
             0.f,
+#elif defined(HOPKINS_PU_SPH)
+            main_cell->parts[pid].density.pressure_bar_dh,
 #else
             main_cell->parts[pid].density.rho_dh,
 #endif
             main_cell->parts[pid].density.wcount,
             main_cell->parts[pid].density.wcount_dh,
-#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH)
+#if defined(GADGET2_SPH) || defined(DEFAULT_SPH) || defined(HOPKINS_PE_SPH) || \
+    defined(HOPKINS_PU_SPH)
             main_cell->parts[pid].density.div_v,
             main_cell->parts[pid].density.rot_v[0],
             main_cell->parts[pid].density.rot_v[1],
@@ -298,7 +313,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell,
               cj->parts[pjd].id, cj->parts[pjd].x[0], cj->parts[pjd].x[1],
               cj->parts[pjd].x[2], cj->parts[pjd].v[0], cj->parts[pjd].v[1],
               cj->parts[pjd].v[2], hydro_get_comoving_density(&cj->parts[pjd]),
-#if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
               0.f,
 #else
               main_cell->parts[pjd].density.rho_dh,
@@ -499,7 +514,7 @@ int main(int argc, char *argv[]) {
 #if defined(TEST_DOSELF_SUBSET) || defined(TEST_DOPAIR_SUBSET)
     int *pid = NULL;
     int count = 0;
-    if ((pid = malloc(sizeof(int) * main_cell->count)) == NULL)
+    if ((pid = (int *)malloc(sizeof(int) * main_cell->count)) == NULL)
       error("Can't allocate memory for pid.");
     for (int k = 0; k < main_cell->count; k++)
       if (part_is_active(&main_cell->parts[k], &engine)) {
@@ -543,7 +558,7 @@ int main(int argc, char *argv[]) {
 
     /* Dump if necessary */
     if (i % 50 == 0) {
-      sprintf(outputFileName, "swift_dopair_27_%s.dat",
+      sprintf(outputFileName, "swift_dopair_27_%.150s.dat",
               outputFileNameExtension);
       dump_particle_fields(outputFileName, main_cell, cells);
     }
@@ -586,7 +601,7 @@ int main(int argc, char *argv[]) {
   end_calculation(main_cell, &cosmo);
 
   /* Dump */
-  sprintf(outputFileName, "brute_force_27_%s.dat", outputFileNameExtension);
+  sprintf(outputFileName, "brute_force_27_%.150s.dat", outputFileNameExtension);
   dump_particle_fields(outputFileName, main_cell, cells);
 
   /* Output timing */
diff --git a/tests/testActivePair.c b/tests/testActivePair.c
index 038d40a44afe49a12034cd9c408eba30cd026055..6889a18887894af0a9434f786df21dbf842e87e5 100644
--- a/tests/testActivePair.c
+++ b/tests/testActivePair.c
@@ -59,7 +59,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
   const size_t count = n * n * n;
   const double volume = size * size * size;
   float h_max = 0.f;
-  struct cell *cell = malloc(sizeof(struct cell));
+  struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
 
   if (posix_memalign((void **)&cell->parts, part_align,
@@ -94,7 +94,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
         part->id = ++(*partId);
 
 /* Set the mass */
-#if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
         part->conserved.mass = density * volume / count;
 
 #ifdef SHADOWFAX_SPH
@@ -105,12 +105,12 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
 
 #else
         part->mass = density * volume / count;
-#endif /* GIZMO_SPH */
+#endif /* GIZMO_MFV_SPH */
 
 /* Set the thermodynamic variable */
 #if defined(GADGET2_SPH)
         part->entropy = 1.f;
-#elif defined(MINIMAL_SPH)
+#elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH)
         part->u = 1.f;
 #elif defined(HOPKINS_PE_SPH)
         part->entropy = 1.f;
@@ -194,13 +194,13 @@ void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo) {
     p->density.rot_v[1] = 0.f;
     p->density.rot_v[2] = 0.f;
     p->density.div_v = 0.f;
-#endif
+#endif /* GADGET-2 */
 #ifdef MINIMAL_SPH
     p->rho = 1.f;
     p->density.rho_dh = 0.f;
     p->density.wcount = 48.f / (kernel_norm * pow_dimension(p->h));
     p->density.wcount_dh = 0.f;
-#endif
+#endif /* MINIMAL */
 #ifdef HOPKINS_PE_SPH
     p->rho = 1.f;
     p->rho_bar = 1.f;
@@ -208,7 +208,15 @@ void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo) {
     p->density.pressure_dh = 0.f;
     p->density.wcount = 48.f / (kernel_norm * pow_dimension(p->h));
     p->density.wcount_dh = 0.f;
-#endif
+#endif /* PRESSURE-ENTROPY */
+#ifdef HOPKINS_PU_SPH
+    p->rho = 1.f;
+    p->pressure_bar = 0.6666666;
+    p->density.rho_dh = 0.f;
+    p->density.pressure_bar_dh = 0.f;
+    p->density.wcount = 48.f / (kernel_norm * pow_dimension(p->h));
+    p->density.wcount_dh = 0.f;
+#endif /* PRESSURE-ENERGY */
 
     /* And prepare for a round of force tasks. */
     hydro_prepare_force(p, xp, cosmo);
@@ -570,8 +578,9 @@ int main(int argc, char *argv[]) {
   runner->e = &engine;
 
   /* Create output file names. */
-  sprintf(swiftOutputFileName, "swift_dopair_%s.dat", outputFileNameExtension);
-  sprintf(bruteForceOutputFileName, "brute_force_pair_%s.dat",
+  sprintf(swiftOutputFileName, "swift_dopair_%.150s.dat",
+          outputFileNameExtension);
+  sprintf(bruteForceOutputFileName, "brute_force_pair_%.150s.dat",
           outputFileNameExtension);
 
   /* Delete files if they already exist. */
@@ -624,9 +633,9 @@ int main(int argc, char *argv[]) {
   finalise = &end_calculation_force;
 
   /* Create new output file names. */
-  sprintf(swiftOutputFileName, "swift_dopair2_force_%s.dat",
+  sprintf(swiftOutputFileName, "swift_dopair2_force_%.150s.dat",
           outputFileNameExtension);
-  sprintf(bruteForceOutputFileName, "brute_force_dopair2_%s.dat",
+  sprintf(bruteForceOutputFileName, "brute_force_dopair2_%.150s.dat",
           outputFileNameExtension);
 
   /* Delete files if they already exist. */
diff --git a/tests/testAdiabaticIndex.c b/tests/testAdiabaticIndex.c
index 64a60fd2aa1f85a9a28fa312922f5fd68daa62d7..60ecefa264f48bed2d4df205766dc392a1a03d0f 100644
--- a/tests/testAdiabaticIndex.c
+++ b/tests/testAdiabaticIndex.c
@@ -16,7 +16,6 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-
 #include "../config.h"
 
 #include <fenv.h>
@@ -42,7 +41,7 @@ void check_value(float a, float b, const char* s) {
  * @brief Check that the pre-defined adiabatic index constants contain correct
  * values
  */
-void check_constants() {
+void check_constants(void) {
   float val;
 
   val = 0.5 * (hydro_gamma + 1.0f) / hydro_gamma;
@@ -115,7 +114,7 @@ void check_functions(float x) {
 /**
  * @brief Check adiabatic index constants and power functions
  */
-int main() {
+int main(int argc, char* argv[]) {
 
   /* Initialize CPU frequency, this also starts time. */
   unsigned long long cpufreq = 0;
diff --git a/tests/testCbrt.c b/tests/testCbrt.c
new file mode 100644
index 0000000000000000000000000000000000000000..b608f9a00d619570c298f4123038f930584a245c
--- /dev/null
+++ b/tests/testCbrt.c
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2016 Pedro Gonnet (pedro.gonnet@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Standard includes. */
+#include <fenv.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Local includes. */
+#include "cbrt.h"
+#include "clocks.h"
+#include "error.h"
+
+int main(int argc, char *argv[]) {
+
+  /* Initialize CPU frequency, this also starts time. */
+  unsigned long long cpufreq = 0;
+  clocks_set_cpufreq(cpufreq);
+
+  /* Choke on FP-exceptions */
+  feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+
+  /* Some constants for this test. */
+  const int num_vals = 200000000;
+  const float range_min = -1e6f;
+  const float range_max = 1e6f;
+  const float err_rel_tol = 1e-7;
+  message("executing %i runs of each command.", num_vals);
+
+  /* Create and fill an array of floats. */
+  float *data = (float *)malloc(sizeof(float) * num_vals);
+  for (int k = 0; k < num_vals; k++) {
+    data[k] = (float)rand() / RAND_MAX;
+    data[k] = (1.0f - data[k]) * range_min + data[k] * range_max;
+    if (data[k] == 0.f) k--; /* Skip 0 to avoid spurious mistakes */
+  }
+
+  /* First run just checks for correctness. */
+  for (int k = 0; k < num_vals; k++) {
+    const double exact = cbrt(data[k]);  // computed in double just to be sure.
+    const float ours = 1.0f / icbrtf(data[k]);
+    const float err_abs = fabsf(exact - ours);
+    const float err_rel = 0.5f * fabsf(exact - ours) / (exact + ours);
+
+    if (err_rel > err_rel_tol && data[k] != 0.f)
+      error(
+          "failed for x = %.8e, exact = %.8e, ours = %.8e, err = %.3e, rel = "
+          "%.3e",
+          data[k], exact, ours, err_abs, err_rel);
+  }
+
+  /* Second run to check the speed of the inverse cube root. */
+  double acc_exact = 0.0f;
+  ticks tic_exact = getticks();
+  for (int k = 0; k < num_vals; k++) {
+    acc_exact += 1.0f / cbrtf(data[k]);
+  }
+  message("1.0f / cbrtf took %9.3f %s (acc = %18.11e).",
+          clocks_from_ticks(getticks() - tic_exact), clocks_getunit(),
+          acc_exact);
+
+  double acc_ours = 0.0;
+  ticks tic_ours = getticks();
+  for (int k = 0; k < num_vals; k++) {
+    acc_ours += icbrtf(data[k]);
+  }
+  message("icbrtf       took %9.3f %s (acc = %18.11e).",
+          clocks_from_ticks(getticks() - tic_ours), clocks_getunit(), acc_ours);
+
+  /* Third run to check the speed of the cube root. */
+  acc_exact = 0.0f;
+  tic_exact = getticks();
+  for (int k = 0; k < num_vals; k++) {
+    acc_exact += cbrtf(data[k]);
+  }
+  message("cbrtf        took %9.3f %s (acc = %18.11e).",
+          clocks_from_ticks(getticks() - tic_exact), clocks_getunit(),
+          acc_exact);
+
+  acc_ours = 0.0f;
+  tic_ours = getticks();
+  for (int k = 0; k < num_vals; k++) {
+    const float temp = icbrtf(data[k]);
+    acc_ours += data[k] * temp * temp;
+  }
+  message("x * icbrtf^2 took %9.3f %s (acc = %18.11e).",
+          clocks_from_ticks(getticks() - tic_ours), clocks_getunit(), acc_ours);
+
+  /* Fourth run to check the speed of (.)^(2/3). */
+  acc_exact = 0.0f;
+  tic_exact = getticks();
+  for (int k = 0; k < num_vals; k++) {
+    const float temp = cbrtf(data[k]);
+    acc_exact += temp * temp;
+  }
+  message("cbrtf^2      took %9.3f %s (acc = %18.11e).",
+          clocks_from_ticks(getticks() - tic_exact), clocks_getunit(),
+          acc_exact);
+
+  acc_ours = 0.0f;
+  tic_ours = getticks();
+  for (int k = 0; k < num_vals; k++) {
+    acc_ours += data[k] * icbrtf(data[k]);
+  }
+  message("x * icbrtf   took %9.3f %s (acc = %18.11e).",
+          clocks_from_ticks(getticks() - tic_ours), clocks_getunit(), acc_ours);
+
+  return 0;
+}
diff --git a/tests/testDump.c b/tests/testDump.c
index d4a3b3c1bacdc8071a32fce6d5f1f746530e589c..f47a44256536d6ac1d9676c844f7081a6daa5ca4 100644
--- a/tests/testDump.c
+++ b/tests/testDump.c
@@ -38,7 +38,7 @@
 void dump_mapper(void *map_data, int num_elements, void *extra_data) {
   struct dump *d = (struct dump *)extra_data;
   size_t offset;
-  char *out_string = dump_get(d, 7, &offset);
+  char *out_string = (char *)dump_get(d, 7, &offset);
   char out_buff[8];
   /* modulo due to bug in gcc, should be removed */
   snprintf(out_buff, 8, "%06zi\n", (offset / 7) % 1000000);
@@ -99,6 +99,8 @@ int main(int argc, char *argv[]) {
 
 #else
 
+#include <stdio.h>
+
 int main(int argc, char *argv[]) {
   printf("No posix_fallocate, not testing anything.\n");
   return 0;
diff --git a/tests/testEOS.c b/tests/testEOS.c
new file mode 100644
index 0000000000000000000000000000000000000000..595dd0726a0a4a1606390cd38eb06c71399acb78
--- /dev/null
+++ b/tests/testEOS.c
@@ -0,0 +1,278 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
+ *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <fenv.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Conditional headers. */
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
+/* Local headers. */
+#include "equation_of_state.h"
+#include "swift.h"
+
+/* Engine policy flags. */
+#ifndef ENGINE_POLICY
+#define ENGINE_POLICY engine_policy_none
+#endif
+
+/**
+ * @brief Write a list of densities, energies, and resulting pressures to file
+ *  from an equation of state.
+ *
+ *                      WORK IN PROGRESS
+ *
+ * So far only does the Hubbard & MacFarlane (1980) equations of state.
+ *
+ * Usage:
+ *      $  ./testEOS  (mat_id)  (do_output)
+ *
+ * Sys args (optional):
+ *      mat_id | int | Material ID, see equation_of_state.h for the options.
+ *          Default: 201 (= id_HM80_ice).
+ *
+ *      do_output | int | Set 1 to write the output file of rho, u, P values,
+ *          set 0 for no output. Default: 0.
+ *
+ * Output text file contains:
+ *  header
+ *  num_rho num_u   mat_id                      # Header values
+ *  rho_0   rho_1   rho_2   ...   rho_num_rho   # Array of densities, rho
+ *  u_0     u_1     u_2     ...   u_num_u       # Array of energies, u
+ *  P_0_0   P_0_1   ...     P_0_num_u           # Array of pressures, P(rho, u)
+ *  P_1_0   ...     ...     P_1_num_u
+ *  ...     ...     ...     ...
+ *  P_num_rho_0     ...     P_num_rho_num_u
+ *
+ * Note that the values tested extend beyond the range that most EOS are
+ * designed for (e.g. outside table limits), to help test the EOS in case of
+ * unexpected particle behaviour.
+ *
+ */
+
+#ifdef EOS_PLANETARY
+int main(int argc, char *argv[]) {
+  float rho, log_rho, log_u, P;
+  struct unit_system us;
+  const struct phys_const *phys_const = 0;  // Unused placeholder
+  struct swift_params *params = 0;          // Unused placeholder
+  const float J_kg_to_erg_g = 1e4;          // Convert J/kg to erg/g
+  char filename[64];
+  // Output table params
+  const int num_rho = 100, num_u = 100;
+  float log_rho_min = logf(1e-4), log_rho_max = logf(30.f),
+        log_u_min = logf(1e4), log_u_max = logf(1e10),
+        log_rho_step = (log_rho_max - log_rho_min) / (num_rho - 1.f),
+        log_u_step = (log_u_max - log_u_min) / (num_u - 1.f);
+  float A1_rho[num_rho], A1_u[num_u];
+  // Sys args
+  int mat_id, do_output;
+  // Default sys args
+  const int mat_id_def = eos_planetary_id_HM80_ice;
+  const int do_output_def = 0;
+
+  // Check the number of system arguments and use defaults if not provided
+  switch (argc) {
+    case 1:
+      // Default both
+      mat_id = mat_id_def;
+      do_output = do_output_def;
+      break;
+
+    case 2:
+      // Read mat_id, default do_output
+      mat_id = atoi(argv[1]);
+      do_output = do_output_def;
+      break;
+
+    case 3:
+      // Read both
+      mat_id = atoi(argv[1]);
+      do_output = atoi(argv[2]);
+      break;
+
+    default:
+      error("Invalid number of system arguments!\n");
+      mat_id = mat_id_def;  // Ignored, just here to keep the compiler happy
+      do_output = do_output_def;
+  };
+
+  /* Greeting message */
+  printf("This is %s\n", package_description());
+
+  // Check material ID
+  // Material base type
+  switch ((int)(mat_id / eos_planetary_type_factor)) {
+    // Tillotson
+    case eos_planetary_type_Til:
+      switch (mat_id) {
+        case eos_planetary_id_Til_iron:
+          printf("  Tillotson iron \n");
+          break;
+
+        case eos_planetary_id_Til_granite:
+          printf("  Tillotson granite \n");
+          break;
+
+        case eos_planetary_id_Til_water:
+          printf("  Tillotson water \n");
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d \n", mat_id);
+      };
+      break;
+
+    // Hubbard & MacFarlane (1980)
+    case eos_planetary_type_HM80:
+      switch (mat_id) {
+        case eos_planetary_id_HM80_HHe:
+          printf("  Hubbard & MacFarlane (1980) hydrogen-helium atmosphere \n");
+          break;
+
+        case eos_planetary_id_HM80_ice:
+          printf("  Hubbard & MacFarlane (1980) ice mix \n");
+          break;
+
+        case eos_planetary_id_HM80_rock:
+          printf("  Hubbard & MacFarlane (1980) rock mix \n");
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d \n", mat_id);
+      };
+      break;
+
+    // ANEOS
+    case eos_planetary_type_ANEOS:
+      switch (mat_id) {
+        case eos_planetary_id_ANEOS_iron:
+          printf("  ANEOS iron \n");
+          break;
+
+        case eos_planetary_id_MANEOS_forsterite:
+          printf("  MANEOS forsterite \n");
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d \n", mat_id);
+      };
+      break;
+
+    // SESAME
+    case eos_planetary_type_SESAME:
+      switch (mat_id) {
+        case eos_planetary_id_SESAME_iron:
+          printf("  SESAME iron \n");
+          break;
+
+        default:
+          error("Unknown material ID! mat_id = %d \n", mat_id);
+      };
+      break;
+
+    default:
+      error("Unknown material type! mat_id = %d \n", mat_id);
+  }
+
+  // Convert to internal units (Earth masses and radii)
+  units_init(&us, 5.9724e27, 6.3710e8, 1.f, 1.f, 1.f);
+  log_rho_min -= logf(units_cgs_conversion_factor(&us, UNIT_CONV_DENSITY));
+  log_rho_max -= logf(units_cgs_conversion_factor(&us, UNIT_CONV_DENSITY));
+  log_u_min += logf(J_kg_to_erg_g / units_cgs_conversion_factor(
+                                        &us, UNIT_CONV_ENERGY_PER_UNIT_MASS));
+  log_u_max += logf(J_kg_to_erg_g / units_cgs_conversion_factor(
+                                        &us, UNIT_CONV_ENERGY_PER_UNIT_MASS));
+
+  // Initialise the EOS materials
+  eos_init(&eos, phys_const, &us, params);
+
+  // Output file
+  sprintf(filename, "testEOS_rho_u_P_%d.txt", mat_id);
+  FILE *f = fopen(filename, "w");
+  if (f == NULL) {
+    printf("Could not open output file!\n");
+    exit(EXIT_FAILURE);
+  }
+
+  if (do_output == 1) {
+    fprintf(f, "Density  Sp.Int.Energy  mat_id \n");
+    fprintf(f, "%d      %d            %d \n", num_rho, num_u, mat_id);
+  }
+
+  // Densities
+  log_rho = log_rho_min;
+  for (int i = 0; i < num_rho; i++) {
+    A1_rho[i] = exp(log_rho);
+    log_rho += log_rho_step;
+
+    if (do_output == 1)
+      fprintf(f, "%.6e ",
+              A1_rho[i] * units_cgs_conversion_factor(&us, UNIT_CONV_DENSITY));
+  }
+  if (do_output == 1) fprintf(f, "\n");
+
+  // Sp. int. energies
+  log_u = log_u_min;
+  for (int i = 0; i < num_u; i++) {
+    A1_u[i] = exp(log_u);
+    log_u += log_u_step;
+
+    if (do_output == 1)
+      fprintf(f, "%.6e ", A1_u[i] * units_cgs_conversion_factor(
+                                        &us, UNIT_CONV_ENERGY_PER_UNIT_MASS));
+  }
+  if (do_output == 1) fprintf(f, "\n");
+
+  // Pressures
+  for (int i = 0; i < num_rho; i++) {
+    rho = A1_rho[i];
+
+    for (int j = 0; j < num_u; j++) {
+      P = gas_pressure_from_internal_energy(rho, A1_u[j], mat_id);
+
+      if (do_output == 1)
+        fprintf(f, "%.6e ",
+                P * units_cgs_conversion_factor(&us, UNIT_CONV_PRESSURE));
+    }
+
+    if (do_output == 1) fprintf(f, "\n");
+  }
+  fclose(f);
+
+  return 0;
+}
+#else
+int main(int argc, char *argv[]) { return 0; }
+#endif
diff --git a/tests/testEOS.py b/tests/testEOS.py
new file mode 100644
index 0000000000000000000000000000000000000000..363bab200b58c65fa24cc033af4b8d3c04b7b503
--- /dev/null
+++ b/tests/testEOS.py
@@ -0,0 +1,176 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ #               2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk)
+ #
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ #
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ #
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ #
+ ##############################################################################
+"""
+Plot the output of testEOS to show how the equation of state pressure varies
+with density and specific internal energy.
+
+Usage:
+    python  testEOS.py  (mat_id)
+
+    Sys args (optional):
+        mat_id | int | Material ID, see equation_of_state.h for the options.
+            Default: 201 (= HM80_ice).
+
+Text file contains:
+    header
+    num_rho  num_u  mat_id                      # Header info
+    rho_0   rho_1   rho_2   ...   rho_num_rho   # Array of densities, rho
+    u_0     u_1     u_2     ...   u_num_u       # Array of energies, u
+    P_0_0   P_0_1   ...     P_0_num_u           # Array of pressures, P(rho, u)
+    P_1_0   ...     ...     P_1_num_u
+    ...     ...     ...     ...
+    P_num_rho_0     ...     P_num_rho_num_u
+
+Note that the values tested extend beyond the range that most EOS are
+designed for (e.g. outside table limits), to help test the EOS in case of
+unexpected particle behaviour.
+"""
+
+# ========
+# Modules and constants
+# ========
+from __future__ import division
+import numpy as np
+import matplotlib
+matplotlib.use("Agg")
+import matplotlib.pyplot as plt
+import sys
+
+# Material types (copied from src/equation_of_state/planetary/equation_of_state.h)
+type_factor = 100
+Di_type = {
+    'Til'       : 1,
+    'HM80'      : 2,
+    'ANEOS'     : 3,
+    'SESAME'    : 4,
+}
+Di_material = {
+    # Tillotson
+    'Til_iron'      : Di_type['Til']*type_factor,
+    'Til_granite'   : Di_type['Til']*type_factor + 1,
+    'Til_water'     : Di_type['Til']*type_factor + 2,
+    # Hubbard & MacFarlane (1980) Uranus/Neptune
+    'HM80_HHe'      : Di_type['HM80']*type_factor,      # Hydrogen-helium atmosphere
+    'HM80_ice'      : Di_type['HM80']*type_factor + 1,  # H20-CH4-NH3 ice mix
+    'HM80_rock'     : Di_type['HM80']*type_factor + 2,  # SiO2-MgO-FeS-FeO rock mix
+    # ANEOS
+    'ANEOS_iron'        : Di_type['ANEOS']*type_factor,
+    'MANEOS_forsterite' : Di_type['ANEOS']*type_factor + 1,
+    # SESAME
+    'SESAME_iron'   : Di_type['SESAME']*type_factor,
+}
+# Invert so the mat_id are the keys
+Di_mat_id = {mat_id : mat for mat, mat_id in Di_material.iteritems()}
+
+# Unit conversion
+Ba_to_Mbar = 1e-12
+erg_g_to_J_kg = 1e-4
+
+if __name__ == '__main__':
+    # Sys args
+    try:
+        mat_id = int(sys.argv[1])
+    except IndexError:
+        mat_id = Di_material['HM80_ice']
+
+    # Check the material
+    try:
+        mat = Di_mat_id[mat_id]
+        print mat
+        sys.stdout.flush()
+    except KeyError:
+        print "Error: unknown material ID! mat_id = %d" % mat_id
+        print "Materials:"
+        for mat_id, mat in sorted(Di_mat_id.iteritems()):
+            print "  %s%s%d" % (mat, (20 - len("%s" % mat))*" ", mat_id)
+
+    filename = "testEOS_rho_u_P_%d.txt" % mat_id
+
+    # Load the header info and density and energy arrays
+    with open(filename) as f:
+        f.readline()
+        num_rho, num_u, mat_id = np.array(f.readline().split(), dtype=int)
+        A1_rho = np.array(f.readline().split(), dtype=float)
+        A1_u = np.array(f.readline().split(), dtype=float)
+
+    # Load pressure array
+    A2_P = np.loadtxt(filename, skiprows=4)
+
+    # Convert pressures from cgs Barye to Mbar
+    A2_P *= Ba_to_Mbar
+    # Convert energies from cgs to SI
+    A1_u *= erg_g_to_J_kg
+
+    # Check that the numbers are right
+    assert A1_rho.shape == (num_rho,)
+    assert A1_u.shape == (num_u,)
+    assert A2_P.shape == (num_rho, num_u)
+
+    # Plot
+    plt.figure(figsize=(7, 7))
+    ax = plt.gca()
+
+    # P(rho) at fixed u
+    num_u_fix = 9
+    A1_idx = np.floor(np.linspace(0, num_u - 1, num_u_fix)).astype(int)
+    A1_colour = matplotlib.cm.rainbow(np.linspace(0, 1, num_u_fix))
+
+    for i, idx in enumerate(A1_idx):
+        plt.plot(A1_rho, A2_P[:, idx], c=A1_colour[i],
+                 label=r"%.2e" % A1_u[idx])
+
+    plt.legend(title="Sp. Int. Energy (J kg$^{-1}$)")
+    plt.xscale('log')
+    plt.yscale('log')
+    plt.xlabel(r"Density (g cm$^{-3}$)")
+    plt.ylabel(r"Pressure (Mbar)")
+    plt.title(mat)
+    plt.tight_layout()
+
+    plt.savefig("testEOS_%d.png" % mat_id)
+    plt.close()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/testEOS.sh b/tests/testEOS.sh
new file mode 100755
index 0000000000000000000000000000000000000000..411ac746be186bfe5758e03c2a852e081daefd10
--- /dev/null
+++ b/tests/testEOS.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+echo ""
+
+rm -f testEOS_rho_u_P_*.txt
+
+echo "Running testEOS for each planetary material"
+
+A1_mat_id=(
+    100
+    101
+    102
+    200
+    201
+    202
+)
+
+for mat_id in "${A1_mat_id[@]}"
+do
+    ./testEOS "$mat_id" 1
+done
+
+exit $?
diff --git a/tests/testEOS_plot.sh b/tests/testEOS_plot.sh
new file mode 100755
index 0000000000000000000000000000000000000000..39108c5e19d8f4474de508e205951a1fd0aebcc9
--- /dev/null
+++ b/tests/testEOS_plot.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+echo ""
+
+echo "Plotting testEOS output for each planetary material"
+
+A1_mat_id=(
+    100
+    101
+    102
+    200
+    201
+    202
+)
+
+for mat_id in "${A1_mat_id[@]}"
+do
+    python ./testEOS.py "$mat_id"
+done
+
+exit $?
diff --git a/tests/testFFT.c b/tests/testFFT.c
index 7b67181ebd3e29bffbf564d00f702e6c15669fab..1654120b6ca6f059d2be07834ef336be877a86a3 100644
--- a/tests/testFFT.c
+++ b/tests/testFFT.c
@@ -1,6 +1,6 @@
 /*******************************************************************************
  * This file is part of SWIFT.
- * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ * Copyright (C) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk).
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -18,33 +18,48 @@
  ******************************************************************************/
 
 /* Some standard headers. */
-
 #include "../config.h"
 
-#ifndef HAVE_FFTW
+// MATTHIEU fix this test
+#if 1 || !defined(HAVE_FFTW)
 
-int main() { return 0; }
+int main(int argc, char *argv[]) { return 0; }
 
 #else
 
 /* Some standard headers. */
+#include <fenv.h>
 #include <stdlib.h>
 #include <string.h>
 
 /* Includes. */
+#include "runner_doiact_fft.h"
 #include "swift.h"
 
-int main() {
+__attribute__((always_inline)) INLINE static int row_major_id(int i, int j,
+                                                              int k, int N) {
+  return (((i + N) % N) * N * N + ((j + N) % N) * N + ((k + N) % N));
+}
+
+int is_close(double x, double y, double abs_err) {
+  return (abs(x - y) < abs_err);
+}
 
+int main(int argc, char *argv[]) {
   /* Initialize CPU frequency, this also starts time. */
   unsigned long long cpufreq = 0;
   clocks_set_cpufreq(cpufreq);
 
+/* Choke on FP-exceptions */
+#ifdef HAVE_FE_ENABLE_EXCEPT
+  feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+#endif
+
   /* Make one particle */
   int nr_gparts = 1;
   struct gpart *gparts = NULL;
-  if (posix_memalign((void **)&gparts, 64, nr_gparts * sizeof(struct gpart)) !=
-      0)
+  if (posix_memalign((void **)&gparts, gpart_align,
+                     nr_gparts * sizeof(struct gpart)) != 0)
     error("Impossible to allocate memory for gparts.");
   bzero(gparts, nr_gparts * sizeof(struct gpart));
 
@@ -54,7 +69,8 @@ int main() {
   gparts[0].mass = 1.f;
 
   /* Read the parameter file */
-  struct swift_params *params = malloc(sizeof(struct swift_params));
+  struct swift_params *params =
+      (struct swift_params *)malloc(sizeof(struct swift_params));
   parser_read_file("fft_params.yml", params);
 
   struct cosmology cosmo;
@@ -68,7 +84,7 @@ int main() {
   struct space space;
   double dim[3] = {1., 1., 1.};
   space_init(&space, params, &cosmo, dim, NULL, gparts, NULL, 0, nr_gparts, 0,
-             1, 1, 0, 1, 0, 0);
+             1, 1, 0, 1, 1, 0);
 
   struct engine engine;
   engine.s = &space;
@@ -81,6 +97,7 @@ int main() {
   engine.nr_threads = 1;
   engine.nodeID = 0;
   engine_rank = 0;
+  engine.verbose = 1;
 
   struct runner runner;
   runner.e = &engine;
@@ -88,34 +105,361 @@ int main() {
   /* Initialize the threadpool. */
   threadpool_init(&engine.threadpool, engine.nr_threads);
 
-  space_rebuild(&space, 0);
+  /* Construct the space and all the multipoles. */
+  space_rebuild(&space, 1);
+
+/* Initialise the Ewald correction table */
+#ifdef SWIFT_GRAVITY_FORCE_CHECKS
+  gravity_exact_force_ewald_init(dim[0]);
+#endif
 
   /* Run the FFT task */
   runner_do_grav_fft(&runner, 1);
 
   /* Now check that we got the right answer */
   int nr_cells = space.nr_cells;
-  double *r = malloc(nr_cells * sizeof(double));
-  double *pot = malloc(nr_cells * sizeof(double));
-  double *pot_exact = malloc(nr_cells * sizeof(double));
+  double *r = (double *)malloc(nr_cells * sizeof(double));
+  double *m = (double *)malloc(nr_cells * sizeof(double));
+  double *pot = (double *)malloc(nr_cells * sizeof(double));
+  double *pot_exact = (double *)malloc(nr_cells * sizeof(double));
 
-  // FILE *file = fopen("potential.dat", "w");
+  FILE *file = fopen("potential.dat", "w");
   for (int i = 0; i < nr_cells; ++i) {
     pot[i] = space.multipoles_top[i].pot.F_000;
+    m[i] = space.multipoles_top[i].m_pole.M_000;
     double dx =
         nearest(space.multipoles_top[i].CoM[0] - gparts[0].x[0], dim[0]);
     double dy =
         nearest(space.multipoles_top[i].CoM[1] - gparts[0].x[1], dim[1]);
     double dz =
         nearest(space.multipoles_top[i].CoM[2] - gparts[0].x[2], dim[2]);
+
+    /* Distance */
     r[i] = sqrt(dx * dx + dy * dy + dz * dz);
-    if (r[i] > 0) pot_exact[i] = -1. / r[i];
-    // fprintf(file, "%e %e %e\n", r[i], pot[i], pot_exact[i]);
+
+    /* Potential with correction */
+    if (r[i] > 0) pot_exact[i] = 1. / r[i];
+
+#ifdef SWIFT_GRAVITY_FORCE_CHECKS
+    /* Get Ewald periodic correction */
+    double f_corr[3], pot_corr;
+    gravity_exact_force_ewald_evaluate(dx, dy, dz, f_corr, &pot_corr);
+    pot_exact[i] -= pot_corr;
+#endif
+
+    fprintf(file, "%e %e %e %e\n", r[i], m[i], pot[i], pot_exact[i]);
+  }
+  fclose(file);
+
+  /* Let's now check the interpolation functions */
+  int cdim[3] = {space.cdim[0], space.cdim[1], space.cdim[2]};
+
+  /* Constant function --> Derivatives must be 0 */
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        pot[row_major_id(i, j, k, cdim[0])] = 1.;
+      }
+    }
+  }
+  for (int i = 0; i < nr_cells; ++i)
+    gravity_field_tensors_init(&space.multipoles_top[i].pot, engine.ti_current);
+  for (int i = 0; i < nr_cells; ++i)
+    mesh_to_multipole_CIC(&space.multipoles_top[i], pot, cdim[0], cdim[0], dim);
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        const struct grav_tensor *f =
+            &space.multipoles_top[row_major_id(i, j, k, cdim[0])].pot;
+
+        if (!is_close(f->F_000, -1., 1e-10))
+          error("Invalid value for (%d %d %d) F_000 (%e)", i, j, k, f->F_000);
+        if (!is_close(f->F_100, 0., 1e-10))
+          error("Invalid value for (%d %d %d) F_100 (%e)", i, j, k, f->F_100);
+        if (!is_close(f->F_010, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_010 (%e)", i, j, k, f->F_010);
+        if (!is_close(f->F_001, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_001 (%e)", i, j, k, f->F_001);
+        if (!is_close(f->F_200, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_200 (%e)", i, j, k, f->F_200);
+        if (!is_close(f->F_020, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_020 (%e)", i, j, k, f->F_020);
+        if (!is_close(f->F_002, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_002 (%e)", i, j, k, f->F_002);
+        if (!is_close(f->F_011, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_011 (%e)", i, j, k, f->F_011);
+        if (!is_close(f->F_101, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_101 (%e)", i, j, k, f->F_101);
+        if (!is_close(f->F_110, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_110 (%e)", i, j, k, f->F_110);
+      }
+    }
+  }
+
+  /* Linear function in x --> Derivatives must be 1 */
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        pot[row_major_id(i, j, k, cdim[0])] = i / ((double)cdim[0]);
+      }
+    }
+  }
+  for (int i = 0; i < nr_cells; ++i)
+    gravity_field_tensors_init(&space.multipoles_top[i].pot, engine.ti_current);
+  for (int i = 0; i < nr_cells; ++i)
+    mesh_to_multipole_CIC(&space.multipoles_top[i], pot, cdim[0], cdim[0], dim);
+  for (int i = 2; i < cdim[0] - 3; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        const struct grav_tensor *f =
+            &space.multipoles_top[row_major_id(i, j, k, cdim[0])].pot;
+
+        if (!is_close(f->F_000, -i / ((double)cdim[0]), 1e-10))
+          error("Invalid value for (%d %d %d) F_000 (%e)", i, j, k, f->F_000);
+        if (!is_close(f->F_100, -1., 1e-10))
+          error("Invalid value for (%d %d %d) F_100 (%e)", i, j, k, f->F_100);
+        if (!is_close(f->F_010, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_010 (%e)", i, j, k, f->F_010);
+        if (!is_close(f->F_001, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_001 (%e)", i, j, k, f->F_001);
+        if (!is_close(f->F_200, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_200 (%e)", i, j, k, f->F_200);
+        if (!is_close(f->F_020, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_020 (%e)", i, j, k, f->F_020);
+        if (!is_close(f->F_002, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_002 (%e)", i, j, k, f->F_002);
+        if (!is_close(f->F_011, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_011 (%e)", i, j, k, f->F_011);
+        if (!is_close(f->F_101, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_101 (%e)", i, j, k, f->F_101);
+        if (!is_close(f->F_110, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_110 (%e)", i, j, k, f->F_110);
+      }
+    }
+  }
+
+  /* Linear function in y --> Derivatives must be 1 */
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        pot[row_major_id(i, j, k, cdim[0])] = j / ((double)cdim[0]);
+      }
+    }
+  }
+  for (int i = 0; i < nr_cells; ++i)
+    gravity_field_tensors_init(&space.multipoles_top[i].pot, engine.ti_current);
+  for (int i = 0; i < nr_cells; ++i)
+    mesh_to_multipole_CIC(&space.multipoles_top[i], pot, cdim[0], cdim[0], dim);
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 2; j < cdim[1] - 3; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        const struct grav_tensor *f =
+            &space.multipoles_top[row_major_id(i, j, k, cdim[0])].pot;
+
+        if (!is_close(f->F_000, -j / ((double)cdim[0]), 1e-10))
+          error("Invalid value for (%d %d %d) F_000 (%e)", i, j, k, f->F_000);
+        if (!is_close(f->F_100, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_100 (%e)", i, j, k, f->F_100);
+        if (!is_close(f->F_010, -1., 1e-10))
+          error("Invalid value for (%d %d %d) F_010 (%e)", i, j, k, f->F_010);
+        if (!is_close(f->F_001, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_001 (%e)", i, j, k, f->F_001);
+        if (!is_close(f->F_200, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_200 (%e)", i, j, k, f->F_200);
+        if (!is_close(f->F_020, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_020 (%e)", i, j, k, f->F_020);
+        if (!is_close(f->F_002, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_002 (%e)", i, j, k, f->F_002);
+        if (!is_close(f->F_011, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_011 (%e)", i, j, k, f->F_011);
+        if (!is_close(f->F_101, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_101 (%e)", i, j, k, f->F_101);
+        if (!is_close(f->F_110, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_110 (%e)", i, j, k, f->F_110);
+      }
+    }
+  }
+
+  /* Linear function in z --> Derivatives must be 1 */
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        pot[row_major_id(i, j, k, cdim[0])] = k / ((double)cdim[0]);
+      }
+    }
+  }
+  for (int i = 0; i < nr_cells; ++i)
+    gravity_field_tensors_init(&space.multipoles_top[i].pot, engine.ti_current);
+  for (int i = 0; i < nr_cells; ++i)
+    mesh_to_multipole_CIC(&space.multipoles_top[i], pot, cdim[0], cdim[0], dim);
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 2; k < cdim[2] - 3; ++k) {
+        const struct grav_tensor *f =
+            &space.multipoles_top[row_major_id(i, j, k, cdim[0])].pot;
+
+        if (!is_close(f->F_000, -k / ((double)cdim[0]), 1e-10))
+          error("Invalid value for (%d %d %d) F_000 (%e)", i, j, k, f->F_000);
+        if (!is_close(f->F_100, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_100 (%e)", i, j, k, f->F_100);
+        if (!is_close(f->F_010, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_010 (%e)", i, j, k, f->F_010);
+        if (!is_close(f->F_001, -1., 1e-10))
+          error("Invalid value for (%d %d %d) F_001 (%e)", i, j, k, f->F_001);
+        if (!is_close(f->F_200, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_200 (%e)", i, j, k, f->F_200);
+        if (!is_close(f->F_020, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_020 (%e)", i, j, k, f->F_020);
+        if (!is_close(f->F_002, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_002 (%e)", i, j, k, f->F_002);
+        if (!is_close(f->F_011, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_011 (%e)", i, j, k, f->F_011);
+        if (!is_close(f->F_101, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_101 (%e)", i, j, k, f->F_101);
+        if (!is_close(f->F_110, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_110 (%e)", i, j, k, f->F_110);
+      }
+    }
+  }
+
+  /* Quadratic function in x --> Derivatives must be 2 */
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        pot[row_major_id(i, j, k, cdim[0])] =
+            i * i / ((double)cdim[0] * cdim[0]);
+      }
+    }
+  }
+  for (int i = 0; i < nr_cells; ++i)
+    gravity_field_tensors_init(&space.multipoles_top[i].pot, engine.ti_current);
+  for (int i = 0; i < nr_cells; ++i)
+    mesh_to_multipole_CIC(&space.multipoles_top[i], pot, cdim[0], cdim[0], dim);
+  for (int i = 2; i < cdim[0] - 3; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        const struct grav_tensor *f =
+            &space.multipoles_top[row_major_id(i, j, k, cdim[0])].pot;
+        const double val = i / ((double)cdim[0]);
+        const double val2 = val * val;
+
+        if (!is_close(f->F_000, -val2, 1e-10))
+          error("Invalid value for (%d %d %d) F_000 (%e)", i, j, k, f->F_000);
+        if (!is_close(f->F_100, -2 * val, 1e-10))
+          error("Invalid value for (%d %d %d) F_100 (%e)", i, j, k, f->F_100);
+        if (!is_close(f->F_010, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_010 (%e)", i, j, k, f->F_010);
+        if (!is_close(f->F_001, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_001 (%e)", i, j, k, f->F_001);
+        if (!is_close(f->F_200, 2.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_200 (%e)", i, j, k, f->F_200);
+        if (!is_close(f->F_020, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_020 (%e)", i, j, k, f->F_020);
+        if (!is_close(f->F_002, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_002 (%e)", i, j, k, f->F_002);
+        if (!is_close(f->F_011, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_011 (%e)", i, j, k, f->F_011);
+        if (!is_close(f->F_101, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_101 (%e)", i, j, k, f->F_101);
+        if (!is_close(f->F_110, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_110 (%e)", i, j, k, f->F_110);
+      }
+    }
+  }
+
+  /* Quadratic function in y --> Derivatives must be 2 */
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        pot[row_major_id(i, j, k, cdim[0])] =
+            j * j / ((double)cdim[0] * cdim[0]);
+      }
+    }
+  }
+  for (int i = 0; i < nr_cells; ++i)
+    gravity_field_tensors_init(&space.multipoles_top[i].pot, engine.ti_current);
+  for (int i = 0; i < nr_cells; ++i)
+    mesh_to_multipole_CIC(&space.multipoles_top[i], pot, cdim[0], cdim[0], dim);
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 2; j < cdim[1] - 3; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        const struct grav_tensor *f =
+            &space.multipoles_top[row_major_id(i, j, k, cdim[0])].pot;
+        const double val = j / ((double)cdim[0]);
+        const double val2 = val * val;
+
+        if (!is_close(f->F_000, -val2, 1e-10))
+          error("Invalid value for (%d %d %d) F_000 (%e)", i, j, k, f->F_000);
+        if (!is_close(f->F_100, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_100 (%e)", i, j, k, f->F_100);
+        if (!is_close(f->F_010, -2 * val, 1e-10))
+          error("Invalid value for (%d %d %d) F_010 (%e)", i, j, k, f->F_010);
+        if (!is_close(f->F_001, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_001 (%e)", i, j, k, f->F_001);
+        if (!is_close(f->F_200, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_200 (%e)", i, j, k, f->F_200);
+        if (!is_close(f->F_020, 2.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_020 (%e)", i, j, k, f->F_020);
+        if (!is_close(f->F_002, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_002 (%e)", i, j, k, f->F_002);
+        if (!is_close(f->F_011, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_011 (%e)", i, j, k, f->F_011);
+        if (!is_close(f->F_101, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_101 (%e)", i, j, k, f->F_101);
+        if (!is_close(f->F_110, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_110 (%e)", i, j, k, f->F_110);
+      }
+    }
+  }
+
+  /* Quadratic function in z --> Derivatives must be 2 */
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 0; k < cdim[2]; ++k) {
+        pot[row_major_id(i, j, k, cdim[0])] =
+            k * k / ((double)cdim[0] * cdim[0]);
+      }
+    }
+  }
+  for (int i = 0; i < nr_cells; ++i)
+    gravity_field_tensors_init(&space.multipoles_top[i].pot, engine.ti_current);
+  for (int i = 0; i < nr_cells; ++i)
+    mesh_to_multipole_CIC(&space.multipoles_top[i], pot, cdim[0], cdim[0], dim);
+  for (int i = 0; i < cdim[0]; ++i) {
+    for (int j = 0; j < cdim[1]; ++j) {
+      for (int k = 2; k < cdim[2] - 3; ++k) {
+        const struct grav_tensor *f =
+            &space.multipoles_top[row_major_id(i, j, k, cdim[0])].pot;
+        const double val = k / ((double)cdim[0]);
+        const double val2 = val * val;
+
+        if (!is_close(f->F_000, -val2, 1e-10))
+          error("Invalid value for (%d %d %d) F_000 (%e)", i, j, k, f->F_000);
+        if (!is_close(f->F_100, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_100 (%e)", i, j, k, f->F_100);
+        if (!is_close(f->F_010, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_010 (%e)", i, j, k, f->F_010);
+        if (!is_close(f->F_001, -2 * val, 1e-10))
+          error("Invalid value for (%d %d %d) F_001 (%e)", i, j, k, f->F_001);
+        if (!is_close(f->F_200, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_200 (%e)", i, j, k, f->F_200);
+        if (!is_close(f->F_020, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_020 (%e)", i, j, k, f->F_020);
+        if (!is_close(f->F_002, 2.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_002 (%e)", i, j, k, f->F_002);
+        if (!is_close(f->F_011, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_011 (%e)", i, j, k, f->F_011);
+        if (!is_close(f->F_101, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_101 (%e)", i, j, k, f->F_101);
+        if (!is_close(f->F_110, 0.f, 1e-10))
+          error("Invalid value for (%d %d %d) F_110 (%e)", i, j, k, f->F_110);
+      }
+    }
   }
-  // fclose(file);
 
   /* Clean up */
   free(r);
+  free(m);
   free(pot);
   free(pot_exact);
   free(params);
diff --git a/tests/testGravityDerivatives.c b/tests/testGravityDerivatives.c
index 1e58dcc49a9fe277ddbc6982b71cfd741992e3b3..184d66db623f34963dc91915c12fc58fbaa4ec4d 100644
--- a/tests/testGravityDerivatives.c
+++ b/tests/testGravityDerivatives.c
@@ -40,10 +40,7 @@
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_000(double r_x, double r_y, double r_z, double r_inv) {
-
-  return r_inv;
-}
+double D_000(double r_x, double r_y, double r_z, double r_inv) { return r_inv; }
 
 /*************************/
 /* 1st order derivatives */
@@ -57,7 +54,7 @@ INLINE static double D_000(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_100(double r_x, double r_y, double r_z, double r_inv) {
+double D_100(double r_x, double r_y, double r_z, double r_inv) {
 
   return -r_x * r_inv * r_inv * r_inv;
 }
@@ -70,7 +67,7 @@ INLINE static double D_100(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_010(double r_x, double r_y, double r_z, double r_inv) {
+double D_010(double r_x, double r_y, double r_z, double r_inv) {
 
   return -r_y * r_inv * r_inv * r_inv;
 }
@@ -83,7 +80,7 @@ INLINE static double D_010(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_001(double r_x, double r_y, double r_z, double r_inv) {
+double D_001(double r_x, double r_y, double r_z, double r_inv) {
 
   return -r_z * r_inv * r_inv * r_inv;
 }
@@ -100,7 +97,7 @@ INLINE static double D_001(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_200(double r_x, double r_y, double r_z, double r_inv) {
+double D_200(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv3 = r_inv * r_inv2;
   const double r_inv5 = r_inv3 * r_inv2;
@@ -115,7 +112,7 @@ INLINE static double D_200(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_020(double r_x, double r_y, double r_z, double r_inv) {
+double D_020(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv3 = r_inv * r_inv2;
   const double r_inv5 = r_inv3 * r_inv2;
@@ -130,7 +127,7 @@ INLINE static double D_020(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_002(double r_x, double r_y, double r_z, double r_inv) {
+double D_002(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv3 = r_inv * r_inv2;
   const double r_inv5 = r_inv3 * r_inv2;
@@ -146,7 +143,7 @@ INLINE static double D_002(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_110(double r_x, double r_y, double r_z, double r_inv) {
+double D_110(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   return 3. * r_x * r_y * r_inv5;
@@ -161,7 +158,7 @@ INLINE static double D_110(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_101(double r_x, double r_y, double r_z, double r_inv) {
+double D_101(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   return 3. * r_x * r_z * r_inv5;
@@ -176,7 +173,7 @@ INLINE static double D_101(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_011(double r_x, double r_y, double r_z, double r_inv) {
+double D_011(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   return 3. * r_y * r_z * r_inv5;
@@ -194,7 +191,7 @@ INLINE static double D_011(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_300(double r_x, double r_y, double r_z, double r_inv) {
+double D_300(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   const double r_inv7 = r_inv5 * r_inv2;
@@ -209,7 +206,7 @@ INLINE static double D_300(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_030(double r_x, double r_y, double r_z, double r_inv) {
+double D_030(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   const double r_inv7 = r_inv5 * r_inv2;
@@ -224,7 +221,7 @@ INLINE static double D_030(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_003(double r_x, double r_y, double r_z, double r_inv) {
+double D_003(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   const double r_inv7 = r_inv5 * r_inv2;
@@ -240,7 +237,7 @@ INLINE static double D_003(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_210(double r_x, double r_y, double r_z, double r_inv) {
+double D_210(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   const double r_inv7 = r_inv5 * r_inv2;
@@ -256,7 +253,7 @@ INLINE static double D_210(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_201(double r_x, double r_y, double r_z, double r_inv) {
+double D_201(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   const double r_inv7 = r_inv5 * r_inv2;
@@ -272,7 +269,7 @@ INLINE static double D_201(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_120(double r_x, double r_y, double r_z, double r_inv) {
+double D_120(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   const double r_inv7 = r_inv5 * r_inv2;
@@ -288,7 +285,7 @@ INLINE static double D_120(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_021(double r_x, double r_y, double r_z, double r_inv) {
+double D_021(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   const double r_inv7 = r_inv5 * r_inv2;
@@ -304,7 +301,7 @@ INLINE static double D_021(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_102(double r_x, double r_y, double r_z, double r_inv) {
+double D_102(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   const double r_inv7 = r_inv5 * r_inv2;
@@ -320,7 +317,7 @@ INLINE static double D_102(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_012(double r_x, double r_y, double r_z, double r_inv) {
+double D_012(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv2 = r_inv * r_inv;
   const double r_inv5 = r_inv2 * r_inv2 * r_inv;
   const double r_inv7 = r_inv5 * r_inv2;
@@ -336,7 +333,7 @@ INLINE static double D_012(double r_x, double r_y, double r_z, double r_inv) {
  * @param r_z z-coordinate of the distance vector (\f$ r_z \f$).
  * @param r_inv Inverse of the norm of the distance vector (\f$ |r|^{-1} \f$)
  */
-INLINE static double D_111(double r_x, double r_y, double r_z, double r_inv) {
+double D_111(double r_x, double r_y, double r_z, double r_inv) {
   const double r_inv3 = r_inv * r_inv * r_inv;
   const double r_inv7 = r_inv3 * r_inv3 * r_inv;
   return -15. * r_x * r_y * r_z * r_inv7;
@@ -351,7 +348,7 @@ INLINE static double D_111(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_004(double r_x, double r_y, double r_z, double r_inv) {
+double D_004(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_z * r_z * r_z * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * 6.0 *
@@ -366,7 +363,7 @@ INLINE static double D_004(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_013(double r_x, double r_y, double r_z, double r_inv) {
+double D_013(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_y * r_z * r_z * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * 3.0 *
@@ -380,7 +377,7 @@ INLINE static double D_013(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_022(double r_x, double r_y, double r_z, double r_inv) {
+double D_022(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_y * r_y * r_z * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -397,7 +394,7 @@ INLINE static double D_022(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_031(double r_x, double r_y, double r_z, double r_inv) {
+double D_031(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_y * r_y * r_y * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * 3.0 *
@@ -410,7 +407,7 @@ INLINE static double D_031(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_040(double r_x, double r_y, double r_z, double r_inv) {
+double D_040(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_y * r_y * r_y * r_y) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * 6.0 *
@@ -425,7 +422,7 @@ INLINE static double D_040(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_103(double r_x, double r_y, double r_z, double r_inv) {
+double D_103(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_z * r_z * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * 3.0 *
@@ -439,7 +436,7 @@ INLINE static double D_103(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_112(double r_x, double r_y, double r_z, double r_inv) {
+double D_112(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_y * r_z * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -453,7 +450,7 @@ INLINE static double D_112(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_121(double r_x, double r_y, double r_z, double r_inv) {
+double D_121(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_y * r_y * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -467,7 +464,7 @@ INLINE static double D_121(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_130(double r_x, double r_y, double r_z, double r_inv) {
+double D_130(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_y * r_y * r_y) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * 3.0 *
@@ -481,7 +478,7 @@ INLINE static double D_130(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_202(double r_x, double r_y, double r_z, double r_inv) {
+double D_202(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_x * r_z * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -498,7 +495,7 @@ INLINE static double D_202(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_211(double r_x, double r_y, double r_z, double r_inv) {
+double D_211(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_x * r_y * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -512,7 +509,7 @@ INLINE static double D_211(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_220(double r_x, double r_y, double r_z, double r_inv) {
+double D_220(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_x * r_y * r_y) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -529,7 +526,7 @@ INLINE static double D_220(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_301(double r_x, double r_y, double r_z, double r_inv) {
+double D_301(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_x * r_x * r_z) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * 3.0 *
@@ -543,7 +540,7 @@ INLINE static double D_301(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_310(double r_x, double r_y, double r_z, double r_inv) {
+double D_310(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_x * r_x * r_y) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * 3.0 *
@@ -556,7 +553,7 @@ INLINE static double D_310(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_400(double r_x, double r_y, double r_z, double r_inv) {
+double D_400(double r_x, double r_y, double r_z, double r_inv) {
   return +105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * (r_x * r_x * r_x * r_x) -
          15. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * 6.0 *
@@ -574,7 +571,7 @@ INLINE static double D_400(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_005(double r_x, double r_y, double r_z, double r_inv) {
+double D_005(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_z * r_z * r_z * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -590,7 +587,7 @@ INLINE static double D_005(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_014(double r_x, double r_y, double r_z, double r_inv) {
+double D_014(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_y * r_z * r_z * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -606,7 +603,7 @@ INLINE static double D_014(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_023(double r_x, double r_y, double r_z, double r_inv) {
+double D_023(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_y * r_y * r_z * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -624,7 +621,7 @@ INLINE static double D_023(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_032(double r_x, double r_y, double r_z, double r_inv) {
+double D_032(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_y * r_y * r_y * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -642,7 +639,7 @@ INLINE static double D_032(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_041(double r_x, double r_y, double r_z, double r_inv) {
+double D_041(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_y * r_y * r_y * r_y * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -657,7 +654,7 @@ INLINE static double D_041(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_050(double r_x, double r_y, double r_z, double r_inv) {
+double D_050(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_y * r_y * r_y * r_y * r_y) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -673,7 +670,7 @@ INLINE static double D_050(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_104(double r_x, double r_y, double r_z, double r_inv) {
+double D_104(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_z * r_z * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -689,7 +686,7 @@ INLINE static double D_104(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_113(double r_x, double r_y, double r_z, double r_inv) {
+double D_113(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_y * r_z * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -703,7 +700,7 @@ INLINE static double D_113(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_122(double r_x, double r_y, double r_z, double r_inv) {
+double D_122(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_y * r_y * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -720,7 +717,7 @@ INLINE static double D_122(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_131(double r_x, double r_y, double r_z, double r_inv) {
+double D_131(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_y * r_y * r_y * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -734,7 +731,7 @@ INLINE static double D_131(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_140(double r_x, double r_y, double r_z, double r_inv) {
+double D_140(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_y * r_y * r_y * r_y) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -750,7 +747,7 @@ INLINE static double D_140(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_203(double r_x, double r_y, double r_z, double r_inv) {
+double D_203(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_z * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -768,7 +765,7 @@ INLINE static double D_203(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_212(double r_x, double r_y, double r_z, double r_inv) {
+double D_212(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_y * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -785,7 +782,7 @@ INLINE static double D_212(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_221(double r_x, double r_y, double r_z, double r_inv) {
+double D_221(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_y * r_y * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -802,7 +799,7 @@ INLINE static double D_221(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_230(double r_x, double r_y, double r_z, double r_inv) {
+double D_230(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_y * r_y * r_y) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -820,7 +817,7 @@ INLINE static double D_230(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_302(double r_x, double r_y, double r_z, double r_inv) {
+double D_302(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_x * r_z * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -838,7 +835,7 @@ INLINE static double D_302(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_311(double r_x, double r_y, double r_z, double r_inv) {
+double D_311(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_x * r_y * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -852,7 +849,7 @@ INLINE static double D_311(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_320(double r_x, double r_y, double r_z, double r_inv) {
+double D_320(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_x * r_y * r_y) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -870,7 +867,7 @@ INLINE static double D_320(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_401(double r_x, double r_y, double r_z, double r_inv) {
+double D_401(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_x * r_x * r_z) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -886,7 +883,7 @@ INLINE static double D_401(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_410(double r_x, double r_y, double r_z, double r_inv) {
+double D_410(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_x * r_x * r_y) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -901,7 +898,7 @@ INLINE static double D_410(double r_x, double r_y, double r_z, double r_inv) {
  *
  * Note that r_inv = 1./sqrt(r_x^2 + r_y^2 + r_z^2)
  */
-INLINE static double D_500(double r_x, double r_y, double r_z, double r_inv) {
+double D_500(double r_x, double r_y, double r_z, double r_inv) {
   return -945. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
              r_inv * r_inv * r_inv * (r_x * r_x * r_x * r_x * r_x) +
          105. * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv * r_inv *
@@ -924,7 +921,7 @@ void test(double x, double y, double tol, double min, const char* name) {
   /*   message("'%s' (%e -- %e) OK!", name, x, y); */
 }
 
-int main() {
+int main(int argc, char* argv[]) {
 
   /* Initialize CPU frequency, this also starts time. */
   unsigned long long cpufreq = 0;
@@ -956,7 +953,7 @@ int main() {
     /* Compute all derivatives */
     struct potential_derivatives_M2L pot;
     compute_potential_derivatives_M2L(dx, dy, dz, r2, r_inv, eps, eps_inv,
-                                      &pot);
+                                      /*periodic*/ 0, /* 1/r_s */ 0., &pot);
 
     /* Minimal value we care about */
     const double min = 1e-9;
diff --git a/tests/testGreetings.c b/tests/testGreetings.c
index 2f17bddf5731692d515675d2a21f6c3b4a725ebf..ea2819d4616ed1f1d87c61065bf09fce4043243a 100644
--- a/tests/testGreetings.c
+++ b/tests/testGreetings.c
@@ -19,7 +19,7 @@
 
 #include "swift.h"
 
-int main() {
+int main(int argc, char *argv[]) {
 
   greetings();
 
diff --git a/tests/testInteractions.c b/tests/testInteractions.c
index 9c5dd36415970ff2a53220aa56cecba6fc5fe193..b8d4073c179238370684c2b0cf15944e613ce002 100644
--- a/tests/testInteractions.c
+++ b/tests/testInteractions.c
@@ -24,7 +24,10 @@
 #include <unistd.h>
 #include "swift.h"
 
-#ifdef WITH_VECTORIZATION
+/* Other schemes need to be added here if they are not vectorized, otherwise
+ * this test will simply not compile. */
+
+#if defined(GADGET2_SPH) && defined(WITH_VECTORIZATION)
 
 #define array_align sizeof(float) * VEC_SIZE
 #define ACC_THRESHOLD 1e-5
@@ -71,7 +74,7 @@ struct part *make_particles(size_t count, double *offset, double spacing,
   p->h = h;
   p->id = ++(*partId);
 
-#if !defined(GIZMO_SPH) && !defined(SHADOWFAX_SPH)
+#if !defined(GIZMO_MFV_SPH) && !defined(SHADOWFAX_SPH)
   p->mass = 1.0f;
 #endif
 
@@ -104,7 +107,9 @@ struct part *make_particles(size_t count, double *offset, double spacing,
  */
 void prepare_force(struct part *parts, size_t count) {
 
-#if !defined(GIZMO_SPH) && !defined(SHADOWFAX_SPH) && !defined(MINIMAL_SPH)
+#if !defined(GIZMO_MFV_SPH) && !defined(SHADOWFAX_SPH) &&       \
+    !defined(MINIMAL_SPH) && !defined(MINIMAL_MULTI_MAT_SPH) && \
+    !defined(HOPKINS_PU_SPH)
   struct part *p;
   for (size_t i = 0; i < count; ++i) {
     p = &parts[i];
@@ -131,7 +136,8 @@ void dump_indv_particle_fields(char *fileName, struct part *p) {
           "%8.5f %8.5f %13e %13e %13e %13e %13e %8.5f %8.5f\n",
           p->id, p->x[0], p->x[1], p->x[2], p->v[0], p->v[1], p->v[2], p->h,
           hydro_get_comoving_density(p),
-#if defined(MINIMAL_SPH) || defined(SHADOWFAX_SPH)
+#if defined(MINIMAL_SPH) || defined(MINIMAL_MULTI_MAT_SPH) || \
+    defined(SHADOWFAX_SPH)
           0.f,
 #else
           p->density.div_v,
@@ -143,7 +149,7 @@ void dump_indv_particle_fields(char *fileName, struct part *p) {
           p->force.v_sig, p->entropy_dt, 0.f
 #elif defined(DEFAULT_SPH)
           p->force.v_sig, 0.f, p->force.u_dt
-#elif defined(MINIMAL_SPH)
+#elif defined(MINIMAL_SPH) || defined(HOPKINS_PU_SPH)
           p->force.v_sig, 0.f, p->u_dt
 #else
           0.f, 0.f, 0.f
@@ -547,7 +553,9 @@ void test_force_interactions(struct part test_part, struct part *parts,
       vizq[i] = pi_vec.v[2];
       rhoiq[i] = pi_vec.rho;
       grad_hiq[i] = pi_vec.force.f;
+#if !defined(HOPKINS_PU_SPH)
       pOrhoi2q[i] = pi_vec.force.P_over_rho2;
+#endif
       balsaraiq[i] = pi_vec.force.balsara;
       ciq[i] = pi_vec.force.soundspeed;
 
@@ -558,7 +566,9 @@ void test_force_interactions(struct part test_part, struct part *parts,
       vjzq[i] = pj_vec[i].v[2];
       rhojq[i] = pj_vec[i].rho;
       grad_hjq[i] = pj_vec[i].force.f;
+#if !defined(HOPKINS_PU_SPH)
       pOrhoj2q[i] = pj_vec[i].force.P_over_rho2;
+#endif
       balsarajq[i] = pj_vec[i].force.balsara;
       cjq[i] = pj_vec[i].force.soundspeed;
     }
@@ -638,7 +648,9 @@ void test_force_interactions(struct part test_part, struct part *parts,
     VEC_HADD(a_hydro_zSum, piq[0]->a_hydro[2]);
     VEC_HADD(h_dtSum, piq[0]->force.h_dt);
     VEC_HMAX(v_sigSum, piq[0]->force.v_sig);
+#if !defined(HOPKINS_PU_SPH)
     VEC_HADD(entropy_dtSum, piq[0]->entropy_dt);
+#endif
 
     vec_time += getticks() - vec_tic;
   }
@@ -736,6 +748,6 @@ int main(int argc, char *argv[]) {
 
 #else
 
-int main() { return 1; }
+int main(int argc, char *argv[]) { return 1; }
 
 #endif
diff --git a/tests/testKernel.c b/tests/testKernel.c
index 0658639070526f28ce1bceefc54d3f2d7a3ae765..e3a13a4d54697f32c100b1f149a768a342da37a7 100644
--- a/tests/testKernel.c
+++ b/tests/testKernel.c
@@ -17,46 +17,72 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
+#include "../config.h"
 
+#include "align.h"
 #include "kernel_hydro.h"
 #include "vector.h"
 
+#include <fenv.h>
 #include <stdlib.h>
 #include <strings.h>
 
-#define numPoints (1 << 4)
+const int numPoints = (1 << 28);
 
-int main() {
+int main(int argc, char *argv[]) {
 
-  const float h = 1.2348f;
+  /* Initialize CPU frequency, this also starts time. */
+  unsigned long long cpufreq = 0;
+  clocks_set_cpufreq(cpufreq);
+
+/* Choke on FPEs */
+#ifdef HAVE_FE_ENABLE_EXCEPT
+  feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+#endif
 
-  float u[numPoints] = {0.f};
-  float W[numPoints] = {0.f};
-  float dW[numPoints] = {0.f};
+  const float h = 1.2348f;
 
-  printf("\nSerial Output\n");
-  printf("-------------\n");
+  float *u, *W, *dW;
+  if (posix_memalign((void **)&u, SWIFT_CACHE_ALIGNMENT,
+                     numPoints * sizeof(float)) != 0)
+    error("Error allocating u");
+  if (posix_memalign((void **)&W, SWIFT_CACHE_ALIGNMENT,
+                     numPoints * sizeof(float)) != 0)
+    error("Error allocating W");
+  if (posix_memalign((void **)&dW, SWIFT_CACHE_ALIGNMENT,
+                     numPoints * sizeof(float)) != 0)
+    error("Error allocating dW");
+
+  message("Serial Output");
+  message("-------------");
   const float numPoints_inv = 1. / numPoints;
 
-  for (int i = 0; i < numPoints; ++i) {
-    u[i] = i * 2.25f * numPoints_inv / h;
-  }
+  for (int i = 0; i < numPoints; ++i)
+    u[i] = i * 1.2f * kernel_gamma * numPoints_inv / h;
 
   for (int i = 0; i < numPoints; ++i) {
 
     kernel_deval(u[i], &W[i], &dW[i]);
 
-    printf("%2d: h= %f H= %f x=%f W(x,h)=%f dW(x,h)=%f\n", i, h,
-           h * kernel_gamma, u[i] * h, W[i], dW[i]);
+    if (W[i] < 0.f) error("Kernel is negative u=%e W=%e", u[i], W[i]);
+    if (dW[i] > 0.f)
+      error("Kernel derivatibe is positive u=%e dW=%e", u[i], dW[i]);
   }
 
-  printf("\nVector Output for VEC_SIZE=%d\n", VEC_SIZE);
-  printf("-------------\n");
+  /* Test some additional special cases */
+  float Wtest, dWtest;
+  kernel_deval(1.930290, &Wtest, &dWtest);
+  if (Wtest < 0.f) error("Kernel is negative u=%e W=%e", 1.930290, Wtest);
+  if (dWtest > 0.f)
+    error("Kernel derivative is positive u=%e dW=%e", 1.930290, dWtest);
 
 #ifdef WITH_VECTORIZATION
 
-  printf("\nVector Output for kernel_deval_1_vec\n");
-  printf("-------------\n");
+  message("Vector Output for VEC_SIZE=%d", VEC_SIZE);
+  message("-------------");
+
+  message("Vector Output for kernel_deval_1_vec");
+  message("-------------");
 
   /* Test vectorised kernel that uses one vector. */
   for (int i = 0; i < numPoints; i += VEC_SIZE) {
@@ -64,33 +90,35 @@ int main() {
     vector vx, vx_h;
     vector W_vec, dW_vec;
 
-    for (int j = 0; j < VEC_SIZE; j++) {
-      vx.f[j] = (i + j) * 2.25f / numPoints;
-    }
+    for (int j = 0; j < VEC_SIZE; j++)
+      vx.f[j] = (i + j) * 1.2f * kernel_gamma / numPoints;
 
     vx_h.v = vec_mul(vx.v, vec_set1(1.f / h));
 
     kernel_deval_1_vec(&vx_h, &W_vec, &dW_vec);
 
     for (int j = 0; j < VEC_SIZE; j++) {
-      printf("%2d: h= %f H= %f x=%f W(x,h)=%f dW(x,h)=%f\n", i + j, h,
-             h * kernel_gamma, vx.f[j], W_vec.f[j], dW_vec.f[j]);
-
-      if (fabsf(W_vec.f[j] - W[i + j]) > 2e-7) {
-        printf("Invalid value ! scalar= %e, vector= %e\n", W[i + j],
-               W_vec.f[j]);
-        return 1;
-      }
-      if (fabsf(dW_vec.f[j] - dW[i + j]) > 2e-7) {
-        printf("Invalid value ! scalar= %e, vector= %e\n", dW[i + j],
-               dW_vec.f[j]);
-        return 1;
-      }
+
+      /* message("%2d: h= %f H= %f x=%f W(x,h)=%f dW(x,h)=%f\n", i + j, h, */
+      /*        h * kernel_gamma, vx.f[j], W_vec.f[j], dW_vec.f[j]); */
+
+      if (W_vec.f[j] < 0.f)
+        error("Kernel is negative u=%e W=%e", u[i + j], W_vec.f[j]);
+      if (dW_vec.f[j] > 0.f)
+        error("Kernel derivative is positive u=%e dW=%e", u[i + j],
+              dW_vec.f[j]);
+
+      if (fabsf(W_vec.f[j] - W[i + j]) > 2e-6)
+        error("Invalid Wvalue ! scalar= %e, vector= %e\n", W[i + j],
+              W_vec.f[j]);
+      if (fabsf(dW_vec.f[j] - dW[i + j]) > 2e-6)
+        error("Invalid dW value ! scalar= %e, vector= %e %e %e\n", dW[i + j],
+              dW_vec.f[j], fabsf(dW_vec.f[j] - dW[i + j]), fabsf(dW[i + j]));
     }
   }
 
-  printf("\nVector Output for kernel_deval_2_vec\n");
-  printf("-------------\n");
+  message("Vector Output for kernel_deval_2_vec");
+  message("-------------");
 
   /* Test vectorised kernel that uses two vectors. */
   for (int i = 0; i < numPoints; i += VEC_SIZE) {
@@ -102,8 +130,8 @@ int main() {
     vector W_vec_2, dW_vec_2;
 
     for (int j = 0; j < VEC_SIZE; j++) {
-      vx.f[j] = (i + j) * 2.25f / numPoints;
-      vx_2.f[j] = (i + j) * 2.25f / numPoints;
+      vx.f[j] = (i + j) * 1.2f * kernel_gamma / numPoints;
+      vx_2.f[j] = (i + j) * 1.2f * kernel_gamma / numPoints;
     }
 
     vx_h.v = vec_mul(vx.v, vec_set1(1.f / h));
@@ -113,41 +141,50 @@ int main() {
 
     /* Check first vector results. */
     for (int j = 0; j < VEC_SIZE; j++) {
-      printf("%2d: h= %f H= %f x=%f W(x,h)=%f dW(x,h)=%f\n", i + j, h,
-             h * kernel_gamma, vx.f[j], W_vec.f[j], dW_vec.f[j]);
-
-      if (fabsf(W_vec.f[j] - W[i + j]) > 2e-7) {
-        printf("Invalid value ! scalar= %e, vector= %e\n", W[i + j],
-               W_vec.f[j]);
-        return 1;
-      }
-      if (fabsf(dW_vec.f[j] - dW[i + j]) > 2e-7) {
-        printf("Invalid value ! scalar= %e, vector= %e\n", dW[i + j],
-               dW_vec.f[j]);
-        return 1;
-      }
+
+      /* message("%2d: h= %f H= %f x=%f W(x,h)=%f dW(x,h)=%f\n", i + j, h, */
+      /*        h * kernel_gamma, vx.f[j], W_vec.f[j], dW_vec.f[j]); */
+
+      if (W_vec.f[j] < 0.f)
+        error("Kernel is negative u=%e W=%e", u[i + j], W_vec.f[j]);
+      if (dW_vec.f[j] > 0.f)
+        error("Kernel derivative is positive u=%e dW=%e", u[i + j],
+              dW_vec.f[j]);
+
+      if (fabsf(W_vec.f[j] - W[i + j]) > 2e-6)
+        error("Invalid value ! scalar= %e, vector= %e\n", W[i + j], W_vec.f[j]);
+      if (fabsf(dW_vec.f[j] - dW[i + j]) > 2e-6)
+        error("Invalid value ! scalar= %e, vector= %e\n", dW[i + j],
+              dW_vec.f[j]);
     }
 
     /* Check second vector results. */
     for (int j = 0; j < VEC_SIZE; j++) {
-      printf("%2d: h= %f H= %f x=%f W(x,h)=%f dW(x,h)=%f\n", i + j, h,
-             h * kernel_gamma, vx_2.f[j], W_vec_2.f[j], dW_vec_2.f[j]);
-
-      if (fabsf(W_vec_2.f[j] - W[i + j]) > 2e-7) {
-        printf("Invalid value ! scalar= %e, vector= %e\n", W[i + j],
-               W_vec_2.f[j]);
-        return 1;
-      }
-      if (fabsf(dW_vec_2.f[j] - dW[i + j]) > 2e-7) {
-        printf("Invalid value ! scalar= %e, vector= %e\n", dW[i + j],
-               dW_vec_2.f[j]);
-        return 1;
-      }
+
+      /* message("%2d: h= %f H= %f x=%f W(x,h)=%f dW(x,h)=%f\n", i + j, h, */
+      /* h * kernel_gamma, vx_2.f[j], W_vec_2.f[j], dW_vec_2.f[j]); */
+
+      if (W_vec_2.f[j] < 0.f)
+        error("Kernel is negative u=%e W=%e", u[i + j], W_vec_2.f[j]);
+      if (dW_vec_2.f[j] > 0.f)
+        error("Kernel derivative is positive u=%e dW=%e", u[i + j],
+              dW_vec_2.f[j]);
+
+      if (fabsf(W_vec_2.f[j] - W[i + j]) > 2e-6)
+        error("Invalid value ! scalar= %e, vector= %e\n", W[i + j],
+              W_vec_2.f[j]);
+      if (fabsf(dW_vec_2.f[j] - dW[i + j]) > 2e-6)
+        error("Invalid value ! scalar= %e, vector= %e\n", dW[i + j],
+              dW_vec_2.f[j]);
     }
   }
 
-  printf("\nAll values are consistent\n");
+  message("All values are consistent");
 
 #endif
+
+  free(u);
+  free(W);
+  free(dW);
   return 0;
 }
diff --git a/tests/testKernelGrav.c b/tests/testKernelGrav.c
index b4a5e4d9f1ff05d8f34840dd19b2a2ccb9ec79b5..36d65ae1d0cc4a7807f60203e8f057e6a9d83cb5 100644
--- a/tests/testKernelGrav.c
+++ b/tests/testKernelGrav.c
@@ -58,7 +58,7 @@ float gadget(float r, float epsilon) {
   }
 }
 
-int main() {
+int main(int argc, char *argv[]) {
 
   const float h = 3.f;
   const float r_max = 6.f;
diff --git a/tests/testLogger.c b/tests/testLogger.c
index 9ec08607383fdb192b7ba994e4af506fde12fea9..b954b67ad6044ae5ec734706f7a1a4ff181541d8 100644
--- a/tests/testLogger.c
+++ b/tests/testLogger.c
@@ -63,7 +63,7 @@ void test_log_parts(struct dump *d) {
   /* Recover the last part from the dump. */
   bzero(&p, sizeof(struct part));
   size_t offset_old = offset;
-  int mask = logger_read_part(&p, &offset, d->data);
+  int mask = logger_read_part(&p, &offset, (const char *)d->data);
   printf(
       "Recovered part at offset %#016zx with mask %#04x: p.x[0]=%e, "
       "p.v[0]=%e.\n",
@@ -76,7 +76,7 @@ void test_log_parts(struct dump *d) {
   /* Recover the second part from the dump (only position). */
   bzero(&p, sizeof(struct part));
   offset_old = offset;
-  mask = logger_read_part(&p, &offset, d->data);
+  mask = logger_read_part(&p, &offset, (const char *)d->data);
   printf(
       "Recovered part at offset %#016zx with mask %#04x: p.x[0]=%e, "
       "p.v[0]=%e.\n",
@@ -89,7 +89,7 @@ void test_log_parts(struct dump *d) {
   /* Recover the first part from the dump. */
   bzero(&p, sizeof(struct part));
   offset_old = offset;
-  mask = logger_read_part(&p, &offset, d->data);
+  mask = logger_read_part(&p, &offset, (const char *)d->data);
   printf(
       "Recovered part at offset %#016zx with mask %#04x: p.x[0]=%e, "
       "p.v[0]=%e.\n",
@@ -131,7 +131,7 @@ void test_log_gparts(struct dump *d) {
   /* Recover the last part from the dump. */
   bzero(&p, sizeof(struct gpart));
   size_t offset_old = offset;
-  int mask = logger_read_gpart(&p, &offset, d->data);
+  int mask = logger_read_gpart(&p, &offset, (const char *)d->data);
   printf(
       "Recovered gpart at offset %#016zx with mask %#04x: p.x[0]=%e, "
       "p.v[0]=%e.\n",
@@ -144,7 +144,7 @@ void test_log_gparts(struct dump *d) {
   /* Recover the second part from the dump. */
   bzero(&p, sizeof(struct gpart));
   offset_old = offset;
-  mask = logger_read_gpart(&p, &offset, d->data);
+  mask = logger_read_gpart(&p, &offset, (const char *)d->data);
   printf(
       "Recovered gpart at offset %#016zx with mask %#04x: p.x[0]=%e, "
       "p.v[0]=%e.\n",
@@ -157,7 +157,7 @@ void test_log_gparts(struct dump *d) {
   /* Recover the first part from the dump. */
   bzero(&p, sizeof(struct gpart));
   offset_old = offset;
-  mask = logger_read_gpart(&p, &offset, d->data);
+  mask = logger_read_gpart(&p, &offset, (const char *)d->data);
   printf(
       "Recovered gpart at offset %#016zx with mask %#04x: p.x[0]=%e, "
       "p.v[0]=%e.\n",
@@ -189,7 +189,7 @@ void test_log_timestamps(struct dump *d) {
   /* Recover the three timestamps. */
   size_t offset_old = offset;
   t = 0;
-  int mask = logger_read_timestamp(&t, &offset, d->data);
+  int mask = logger_read_timestamp(&t, &offset, (const char *)d->data);
   printf("Recovered timestamp %020llu at offset %#016zx with mask %#04x.\n", t,
          offset_old, mask);
   if (t != 30) {
@@ -199,7 +199,7 @@ void test_log_timestamps(struct dump *d) {
 
   offset_old = offset;
   t = 0;
-  mask = logger_read_timestamp(&t, &offset, d->data);
+  mask = logger_read_timestamp(&t, &offset, (const char *)d->data);
   printf("Recovered timestamp %020llu at offset %#016zx with mask %#04x.\n", t,
          offset_old, mask);
   if (t != 20) {
@@ -209,7 +209,7 @@ void test_log_timestamps(struct dump *d) {
 
   offset_old = offset;
   t = 0;
-  mask = logger_read_timestamp(&t, &offset, d->data);
+  mask = logger_read_timestamp(&t, &offset, (const char *)d->data);
   printf("Recovered timestamp %020llu at offset %#016zx with mask %#04x.\n", t,
          offset_old, mask);
   if (t != 10) {
diff --git a/tests/testMaths.c b/tests/testMaths.c
index 3d8f9a8f9db0cf01276eff89aa44157008cbddc6..2abb3aa99902323597b3d20fb19769a8ea1bafbe 100644
--- a/tests/testMaths.c
+++ b/tests/testMaths.c
@@ -25,7 +25,7 @@
 #include <math.h>
 #include <stdio.h>
 
-int main() {
+int main(int argc, char *argv[]) {
 
   const int numPoints = 60000;
 
diff --git a/tests/testMatrixInversion.c b/tests/testMatrixInversion.c
index 9a45cd52d6f5d3ec96cc6d3f34fd683971f4cf19..a15e0dab7ec793cf4a914b6eb89c63863ab24fb0 100644
--- a/tests/testMatrixInversion.c
+++ b/tests/testMatrixInversion.c
@@ -95,7 +95,7 @@ void multiply_matrices(float A[3][3], float B[3][3], float C[3][3]) {
 #endif
 }
 
-int main() {
+int main(int argc, char* argv[]) {
 
   float A[3][3], B[3][3], C[3][3];
   setup_matrix(A);
diff --git a/tests/testParser.c b/tests/testParser.c
index f1211199924df728dfe57376781dc07fe862cec7..580ab873a2ffcf6825526b925610f220e8fa74ce 100644
--- a/tests/testParser.c
+++ b/tests/testParser.c
@@ -1,6 +1,7 @@
 /*******************************************************************************
  * This file is part of SWIFT.
  * Copyright (C) 2016 James Willis (james.s.willis@durham.ac.uk).
+ *               2018 Peter W. Draper (p.w.draper@durham.ac.uk)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -33,11 +34,9 @@ int main(int argc, char *argv[]) {
   parser_read_file(input_file, &param_file);
 
   /* Print the contents of the structure to stdout. */
+  printf("\n--- Values read from file:\n");
   parser_print_params(&param_file);
 
-  /* Print the contents of the structure to a file in YAML format. */
-  parser_write_params_to_file(&param_file, "parser_output.yml");
-
   /* Retrieve parameters and store them in variables defined above.
    * Have to specify the name of the parameter as it appears in the
    * input file: testParserInput.yaml.*/
@@ -50,17 +49,100 @@ int main(int argc, char *argv[]) {
       parser_get_param_double(&param_file, "Simulation:start_time");
   const int kernel = parser_get_param_int(&param_file, "kernel");
 
-  const int optional = parser_get_opt_param_int(&param_file, "optional", 1);
+  const int optional =
+      parser_get_opt_param_int(&param_file, "Simulation:optional", 1);
+
+  /* Optional things not mentioned in parameter file. Should be in output
+   * files.*/
+  parser_get_opt_param_int(&param_file, "Virtual:param1", 1);
+  parser_get_opt_param_int(&param_file, "Virtual:param2", 2);
+  parser_get_opt_param_int(&param_file, "Virtual:param3", 3);
+  parser_get_opt_param_int(&param_file, "Virtual:param4", 4);
 
   char ic_file[PARSER_MAX_LINE_SIZE];
   parser_get_param_string(&param_file, "IO:ic_file", ic_file);
 
+  char csides[3];
+  parser_get_param_char_array(&param_file, "Box:csides", 3, csides);
+
+  int isides[3];
+  parser_get_param_int_array(&param_file, "Box:isides", 3, isides);
+
+  float fsides[3];
+  parser_get_param_float_array(&param_file, "Box:fsides", 3, fsides);
+
+  double dsides[3];
+  parser_get_param_double_array(&param_file, "Box:dsides", 3, dsides);
+
+  int optsides[5] = {1, 2, 3, 4, 5};
+  int haveopt =
+      parser_get_opt_param_int_array(&param_file, "Box:moresides", 5, optsides);
+
+  char **var_result;
+  int nvar_result;
+  parser_get_param_string_array(&param_file, "Words:list", &nvar_result,
+                                &var_result);
+
+  printf("\nList from Words:list parameter\n");
+  for (int i = 0; i < nvar_result; i++) printf("   %d: %s\n", i, var_result[i]);
+
+  /* Get same list without []. */
+  char **var_result2;
+  int nvar_result2;
+  parser_get_param_string_array(&param_file, "Words:nonstdlist", &nvar_result2,
+                                &var_result2);
+
+  assert(nvar_result == nvar_result2);
+  for (int i = 0; i < nvar_result; i++)
+    assert(strcmp(var_result[i], var_result2[i]) == 0);
+
+  parser_free_param_string_array(nvar_result, var_result);
+  parser_free_param_string_array(nvar_result2, var_result2);
+
+  const char *optwords[4] = {"Word1", "Word2", "Word3", "Word4"};
+  int noptwords = 4;
+  int haveoptwords = parser_get_opt_param_string_array(
+      &param_file, "Simulation:optwords", &nvar_result, &var_result, noptwords,
+      optwords);
+  printf("\nList from Simulation:optwords parameter (%d of %d)\n", nvar_result,
+         noptwords);
+  assert(noptwords == nvar_result);
+  for (int i = 0; i < nvar_result; i++) {
+    assert(strcmp(optwords[i], var_result[i]) == 0);
+    printf("   %d: %s\n", i, var_result[i]);
+  }
+  parser_free_param_string_array(nvar_result, var_result);
+
+  /* Long list of values. */
+  parser_get_param_string_array(&param_file, "Words:long", &nvar_result,
+                                &var_result);
+  printf("No of letters in alphabet = %d\n", nvar_result);
+  assert(nvar_result == 26);
+  char alphabet[27];
+  for (int i = 0; i < nvar_result; i++) {
+    alphabet[i] = var_result[i][0];
+  }
+  alphabet[26] = '\0';
+  printf("The alphabet: %s\n", alphabet);
+  assert(strcmp("abcdefghijklmnopqrstuvwxyz", alphabet) == 0);
+  parser_free_param_string_array(nvar_result, var_result);
+
+  /* Print the contents of the structure to stdout now used. */
+  printf("\n--- Values after being used:\n");
+  parser_print_params(&param_file);
+
   /* Print the variables to check their values are correct. */
   printf(
-      "no_of_threads: %d, no_of_time_steps: %d, max_h: %f, start_time: %lf, "
-      "ic_file: %s, kernel: %d optional: %d\n",
+      "\nValues read from file:\n"
+      "no_of_threads: %d, no_of_time_steps: %d, max_h: %f, start_time: %lf,"
+      " ic_file: %s, kernel: %d optional: %d\n",
       no_of_threads, no_of_time_steps, max_h, start_time, ic_file, kernel,
       optional);
+  printf("Box: [%d, %d, %d]\n", isides[0], isides[1], isides[2]);
+
+  /* Print the contents of the structure to a file in YAML format. */
+  parser_write_params_to_file(&param_file, "used_parser_output.yml", 1);
+  parser_write_params_to_file(&param_file, "unused_parser_output.yml", 0);
 
   assert(no_of_threads == 16);
   assert(no_of_time_steps == 10);
@@ -70,5 +152,30 @@ int main(int argc, char *argv[]) {
   assert(kernel == 4);
   assert(optional == 1);
 
+  assert(csides[0] == 'a');
+  assert(csides[1] == 'b');
+  assert(csides[2] == 'c');
+
+  assert(isides[0] == 2);
+  assert(isides[1] == 3);
+  assert(isides[2] == 4);
+
+  assert(fsides[0] == 2.0);
+  assert(fsides[1] == 3.0);
+  assert(fsides[2] == 4.0);
+
+  assert(dsides[0] == 2.0);
+  assert(dsides[1] == 3.0);
+  assert(dsides[2] == 4.0);
+
+  assert(haveopt == 0);
+  assert(optsides[0] == 1);
+  assert(optsides[1] == 2);
+  assert(optsides[2] == 3);
+  assert(optsides[3] == 4);
+  assert(optsides[4] == 5);
+
+  assert(haveoptwords == 0);
+
   return 0;
 }
diff --git a/tests/testParserInput.yaml b/tests/testParserInput.yaml
index c7fefb3242ab4e140756789aad9979d024f83906..ce1b51399e59d0420836b49d67b95952deda21dc 100644
--- a/tests/testParserInput.yaml
+++ b/tests/testParserInput.yaml
@@ -9,11 +9,27 @@ Scheduler:
 kernel: 4
 
 Simulation:    
-  no_of_time_steps:   10
+  no_of_time_steps:   10    
   max_h:              1.1255
   start_time:         1.23456789
 
 IO:
   #Input file
   ic_file:            ic_file.ini
+
+Words:
+  list: ['xyz', 'ABC', "ab'c", "de:f", "g,hi", "zzz", Hello World, once-again]
+  nonstdlist: 'xyz', 'ABC', "ab'c", "de:f", "g,hi", "zzz", Hello World, once-again
+  long: [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]
+
+#  Spelling mistake, should go into unused list.
+Box:
+  csides: [a, b, c]
+  isydes: [2, 3, 4]
+  isides: [2, 3, 4]
+  fsides: [2.0, 3.0, 4.0]
+  dsides: 2.0, 3.0, 4.0
+
+Unused:
+  value: unused
 ...
diff --git a/tests/testPeriodicBC.c b/tests/testPeriodicBC.c
index 2aa15f0aa94acafa20c56767fa6d739798f6c7f3..ffaa3bda0ccb62cd44169e228086267d2399c31f 100644
--- a/tests/testPeriodicBC.c
+++ b/tests/testPeriodicBC.c
@@ -78,7 +78,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
                        enum velocity_types vel) {
   const size_t count = n * n * n;
   const double volume = size * size * size;
-  struct cell *cell = malloc(sizeof(struct cell));
+  struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
 
   if (posix_memalign((void **)&cell->parts, part_align,
@@ -129,7 +129,7 @@ struct cell *make_cell(size_t n, double *offset, double size, double h,
         h_max = fmax(h_max, part->h);
         part->id = ++(*partId);
 
-#if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
         part->conserved.mass = density * volume / count;
 
 #ifdef SHADOWFAX_SPH
@@ -237,7 +237,7 @@ void dump_particle_fields(char *fileName, struct cell *main_cell, int i, int j,
             main_cell->parts[pid].v[0], main_cell->parts[pid].v[1],
             main_cell->parts[pid].v[2],
             hydro_get_comoving_density(&main_cell->parts[pid]),
-#if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_SPH)
             0.f,
 #else
             main_cell->parts[pid].density.rho_dh,
@@ -512,9 +512,9 @@ int main(int argc, char *argv[]) {
   }
 
   /* Create output file names. */
-  sprintf(swiftOutputFileName, "swift_periodic_BC_%s.dat",
+  sprintf(swiftOutputFileName, "swift_periodic_BC_%.150s.dat",
           outputFileNameExtension);
-  sprintf(bruteForceOutputFileName, "brute_force_periodic_BC_%s.dat",
+  sprintf(bruteForceOutputFileName, "brute_force_periodic_BC_%.150s.dat",
           outputFileNameExtension);
 
   /* Delete files if they already exist. */
diff --git a/tests/testPotentialPair.c b/tests/testPotentialPair.c
index 53fc54ccdd63a9a9150b6701c1a76ac20af91d4c..0ca3d0ca9aa2adfc5d6237a668ee93c7c4daba63 100644
--- a/tests/testPotentialPair.c
+++ b/tests/testPotentialPair.c
@@ -39,10 +39,17 @@ const double eps = 0.1;
  * @param a First value
  * @param b Second value
  * @param s String used to identify this check in messages
+ * @param rel_tol Maximal relative error
+ * @param limit Minimal value to consider in the tests
  */
+void check_value_backend(double a, double b, const char *s, double rel_tol,
+                         double limit) {
+  if (fabs(a - b) / fabs(a + b) > rel_tol && fabs(a - b) > limit)
+    error("Values are inconsistent: SWIFT:%12.15e true:%12.15e (%s)!", a, b, s);
+}
+
 void check_value(double a, double b, const char *s) {
-  if (fabs(a - b) / fabs(a + b) > 2e-6 && fabs(a - b) > 1.e-6)
-    error("Values are inconsistent: %12.15e %12.15e (%s)!", a, b, s);
+  check_value_backend(a, b, s, 2e-6, 1e-6);
 }
 
 /* Definitions of the potential and force that match
@@ -82,13 +89,17 @@ double acceleration(double mass, double r, double H, double rlr) {
   return r * acc * (4. * x * S_prime(2 * x) - 2. * S(2. * x) + 2.);
 }
 
-int main() {
+int main(int argc, char *argv[]) {
 
   /* Initialize CPU frequency, this also starts time. */
   unsigned long long cpufreq = 0;
   clocks_set_cpufreq(cpufreq);
 
   /* Initialise a few things to get us going */
+
+  /* Non-truncated forces first */
+  double rlr = FLT_MAX;
+
   struct engine e;
   e.max_active_bin = num_time_bins;
   e.time = 0.1f;
@@ -97,14 +108,20 @@ int main() {
 
   struct space s;
   s.periodic = 0;
-  s.width[0] = 0.2;
-  s.width[1] = 0.2;
-  s.width[2] = 0.2;
   e.s = &s;
 
+  struct pm_mesh mesh;
+  mesh.periodic = 0;
+  mesh.dim[0] = 10.;
+  mesh.dim[1] = 10.;
+  mesh.dim[2] = 10.;
+  mesh.r_s = rlr;
+  mesh.r_s_inv = 1. / rlr;
+  mesh.r_cut_min = 0.;
+  mesh.r_cut_max = FLT_MAX;
+  e.mesh = &mesh;
+
   struct gravity_props props;
-  props.a_smooth = 1.25;
-  props.r_cut_min = 0.;
   props.theta_crit2 = 0.;
   props.epsilon_cur = eps;
   e.gravity_properties = &props;
@@ -113,8 +130,6 @@ int main() {
   bzero(&r, sizeof(struct runner));
   r.e = &e;
 
-  const double rlr = props.a_smooth * s.width[0] * FLT_MAX;
-
   /* Init the cache for gravity interaction */
   gravity_cache_init(&r.ci_gravity_cache, num_tests);
   gravity_cache_init(&r.cj_gravity_cache, num_tests);
@@ -150,8 +165,10 @@ int main() {
   cj.ti_gravity_end_max = 8;
 
   /* Allocate multipoles */
-  ci.multipole = malloc(sizeof(struct gravity_tensors));
-  cj.multipole = malloc(sizeof(struct gravity_tensors));
+  ci.multipole =
+      (struct gravity_tensors *)malloc(sizeof(struct gravity_tensors));
+  cj.multipole =
+      (struct gravity_tensors *)malloc(sizeof(struct gravity_tensors));
   bzero(ci.multipole, sizeof(struct gravity_tensors));
   bzero(cj.multipole, sizeof(struct gravity_tensors));
 
@@ -204,7 +221,7 @@ int main() {
 #endif
 
   /* Now compute the forces */
-  runner_dopair_grav_pp(&r, &ci, &cj);
+  runner_dopair_grav_pp(&r, &ci, &cj, 1, 1);
 
   /* Verify everything */
   for (int n = 0; n < num_tests; ++n) {
@@ -217,8 +234,8 @@ int main() {
     double acc_true =
         acceleration(ci.gparts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr);
 
-    message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] - gp2->x[0],
-            gp->a_grav[0], acc_true, gp->potential, pot_true);
+    /* message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] - gp2->x[0],
+     *         gp->a_grav[0], acc_true, gp->potential, pot_true); */
 
     check_value(gp->potential, pot_true, "potential");
     check_value(gp->a_grav[0], acc_true, "acceleration");
@@ -246,7 +263,7 @@ int main() {
   ci.multipole->m_pole.M_000 = 1.;
 
   /* Now compute the forces */
-  runner_dopair_grav_pp(&r, &ci, &cj);
+  runner_dopair_grav_pp(&r, &ci, &cj, 1, 1);
 
   /* Verify everything */
   for (int n = 0; n < num_tests; ++n) {
@@ -254,13 +271,13 @@ int main() {
     const struct gravity_tensors *mpole = ci.multipole;
     const double epsilon = gravity_get_softening(gp, &props);
 
-    double pot_true = potential(mpole->m_pole.M_000, gp->x[0] - mpole->CoM[0],
-                                epsilon, rlr * FLT_MAX);
-    double acc_true = acceleration(
-        mpole->m_pole.M_000, gp->x[0] - mpole->CoM[0], epsilon, rlr * FLT_MAX);
+    double pot_true =
+        potential(mpole->m_pole.M_000, gp->x[0] - mpole->CoM[0], epsilon, rlr);
+    double acc_true = acceleration(mpole->m_pole.M_000,
+                                   gp->x[0] - mpole->CoM[0], epsilon, rlr);
 
-    message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] - mpole->CoM[0],
-            gp->a_grav[0], acc_true, gp->potential, pot_true);
+    /* message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] -
+     * mpole->CoM[0], gp->a_grav[0], acc_true, gp->potential, pot_true); */
 
     check_value(gp->potential, pot_true, "potential");
     check_value(gp->a_grav[0], acc_true, "acceleration");
@@ -268,12 +285,52 @@ int main() {
 
   message("\n\t\t basic P-M interactions all good\n");
 
+#ifndef GADGET2_LONG_RANGE_CORRECTION
+
   /* Reset the accelerations */
   for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.gparts[n]);
 
-/***************************************/
-/* Test the high-order PM interactions */
-/***************************************/
+  /***************************************/
+  /* Test the truncated PM interactions  */
+  /***************************************/
+  rlr = 2.;
+  mesh.r_s = rlr;
+  mesh.r_s_inv = 1. / rlr;
+  mesh.periodic = 1;
+  s.periodic = 1;
+  props.epsilon_cur = FLT_MIN; /* No softening */
+
+  /* Now compute the forces */
+  runner_dopair_grav_pp(&r, &ci, &cj, 1, 1);
+
+  /* Verify everything */
+  for (int n = 0; n < num_tests; ++n) {
+    const struct gpart *gp = &cj.gparts[n];
+    const struct gravity_tensors *mpole = ci.multipole;
+    const double epsilon = gravity_get_softening(gp, &props);
+
+    double pot_true =
+        potential(mpole->m_pole.M_000, gp->x[0] - mpole->CoM[0], epsilon, rlr);
+    double acc_true = acceleration(mpole->m_pole.M_000,
+                                   gp->x[0] - mpole->CoM[0], epsilon, rlr);
+
+    message("x=%e f=%e f_true=%e pot=%e pot_true=%e", gp->x[0] - mpole->CoM[0],
+            gp->a_grav[0], acc_true, gp->potential, pot_true);
+
+    check_value(gp->potential, pot_true, "potential");
+    check_value(gp->a_grav[0], acc_true, "acceleration");
+  }
+
+  message("\n\t\t truncated P-M interactions all good\n");
+
+#endif
+
+  /************************************************/
+  /* Test the high-order periodic PM interactions */
+  /************************************************/
+
+  /* Reset the accelerations */
+  for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.gparts[n]);
 
 #if SELF_GRAVITY_MULTIPOLE_ORDER >= 3
 
@@ -316,17 +373,14 @@ int main() {
   gravity_reset(ci.multipole);
   gravity_P2M(ci.multipole, ci.gparts, ci.gcount);
 
-  // message("CoM=[%e %e %e]", ci.multipole->CoM[0], ci.multipole->CoM[1],
-  // ci.multipole->CoM[2]);
   gravity_multipole_print(&ci.multipole->m_pole);
 
   /* Compute the forces */
-  runner_dopair_grav_pp(&r, &ci, &cj);
+  runner_dopair_grav_pp(&r, &ci, &cj, 1, 1);
 
   /* Verify everything */
   for (int n = 0; n < num_tests; ++n) {
     const struct gpart *gp = &cj.gparts[n];
-    const struct gravity_tensors *mpole = ci.multipole;
 
     double pot_true = 0, acc_true[3] = {0., 0., 0.};
 
@@ -338,21 +392,20 @@ int main() {
                             gp2->x[2] - gp->x[2]};
       const double d = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]);
 
-      pot_true += potential(gp2->mass, d, epsilon, rlr * FLT_MAX);
-      acc_true[0] -=
-          acceleration(gp2->mass, d, epsilon, rlr * FLT_MAX) * dx[0] / d;
-      acc_true[1] -=
-          acceleration(gp2->mass, d, epsilon, rlr * FLT_MAX) * dx[1] / d;
-      acc_true[2] -=
-          acceleration(gp2->mass, d, epsilon, rlr * FLT_MAX) * dx[2] / d;
+      pot_true += potential(gp2->mass, d, epsilon, rlr);
+      acc_true[0] -= acceleration(gp2->mass, d, epsilon, rlr) * dx[0] / d;
+      acc_true[1] -= acceleration(gp2->mass, d, epsilon, rlr) * dx[1] / d;
+      acc_true[2] -= acceleration(gp2->mass, d, epsilon, rlr) * dx[2] / d;
     }
 
-    message("x=%e f=%e f_true=%e pot=%e pot_true=%e %e %e",
-            gp->x[0] - mpole->CoM[0], gp->a_grav[0], acc_true[0], gp->potential,
-            pot_true, acc_true[1], acc_true[2]);
+    /* const struct gravity_tensors *mpole = ci.multipole; */
+    /* message("x=%e f=%e f_true=%e pot=%e pot_true=%e %e %e", */
+    /*         gp->x[0] - mpole->CoM[0], gp->a_grav[0], acc_true[0],
+     * gp->potential, */
+    /*         pot_true, acc_true[1], acc_true[2]); */
 
-    // check_value(gp->potential, pot_true, "potential");
-    // check_value(gp->a_grav[0], acc_true[0], "acceleration");
+    check_value_backend(gp->potential, pot_true, "potential", 1e-2, 1e-6);
+    check_value_backend(gp->a_grav[0], acc_true[0], "acceleration", 1e-2, 1e-6);
   }
 
   message("\n\t\t high-order P-M interactions all good\n");
diff --git a/tests/testPotentialSelf.c b/tests/testPotentialSelf.c
index 6d31f079fa79f7463637ec71dc2c75f37a10b129..6c68825689ea0786d9bfdc8b57c5c5896d1565c8 100644
--- a/tests/testPotentialSelf.c
+++ b/tests/testPotentialSelf.c
@@ -85,12 +85,17 @@ double acceleration(double mass, double r, double H, double rlr) {
   return r * acc * (4. * x * S_prime(2 * x) - 2. * S(2. * x) + 2.);
 }
 
-int main() {
+int main(int argc, char *argv[]) {
 
   /* Initialize CPU frequency, this also starts time. */
   unsigned long long cpufreq = 0;
   clocks_set_cpufreq(cpufreq);
 
+/* Choke on FPEs */
+#ifdef HAVE_FE_ENABLE_EXCEPT
+  feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+#endif
+
   /* Initialise a few things to get us going */
   struct engine e;
   e.max_active_bin = num_time_bins;
@@ -98,11 +103,14 @@ int main() {
   e.ti_current = 8;
   e.time_base = 1e-10;
 
-  struct space s;
-  s.width[0] = 0.2;
-  s.width[1] = 0.2;
-  s.width[2] = 0.2;
-  e.s = &s;
+  struct pm_mesh mesh;
+  mesh.periodic = 0;
+  mesh.dim[0] = 10.;
+  mesh.dim[1] = 10.;
+  mesh.dim[2] = 10.;
+  mesh.r_s_inv = 0.;
+  mesh.r_cut_min = 0.;
+  e.mesh = &mesh;
 
   struct gravity_props props;
   props.a_smooth = 1.25;
@@ -113,7 +121,7 @@ int main() {
   bzero(&r, sizeof(struct runner));
   r.e = &e;
 
-  const double rlr = props.a_smooth * s.width[0];
+  const double rlr = FLT_MAX;
 
   /* Init the cache for gravity interaction */
   gravity_cache_init(&r.ci_gravity_cache, num_tests * 2);
@@ -168,7 +176,7 @@ int main() {
   }
 
   /* Now compute the forces */
-  runner_doself_grav_pp_truncated(&r, &c);
+  runner_doself_grav_pp(&r, &c);
 
   /* Verify everything */
   for (int n = 1; n < num_tests + 1; ++n) {
diff --git a/tests/testReading.c b/tests/testReading.c
index ca1e0ef69078c5e384a9cd4eab1098923ce9f279..5e6cee7f1e37f7615eb2c3b4edcaee1d4ebba319 100644
--- a/tests/testReading.c
+++ b/tests/testReading.c
@@ -23,7 +23,7 @@
 /* Includes. */
 #include "swift.h"
 
-int main() {
+int main(int argc, char *argv[]) {
 
   size_t Ngas = 0, Ngpart = 0, Nspart = 0;
   int periodic = -1;
@@ -48,8 +48,8 @@ int main() {
 
   /* Read data */
   read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &Ngas,
-                 &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, 1, 1, 0, 0, 1.,
-                 1, 0);
+                 &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, 1, 1, 0, 0, 0,
+                 1., 1., 1, 0);
 
   /* Check global properties read are correct */
   assert(dim[0] == boxSize);
diff --git a/tests/testRiemannExact.c b/tests/testRiemannExact.c
index b72b7b6ce9d7fb4c6f7449af73c6a67ce4d8d1e7..aa630a76f5f82bd87dc15f00d7d90dff2405e749 100644
--- a/tests/testRiemannExact.c
+++ b/tests/testRiemannExact.c
@@ -23,6 +23,12 @@
 #include <stdio.h>
 #include <string.h>
 
+/* Force use of exact Riemann solver */
+#undef RIEMANN_SOLVER_TRRS
+#undef RIEMANN_SOLVER_HLLC
+#undef RIEMANN_SOLVER_EXACT
+#define RIEMANN_SOLVER_EXACT 1
+
 /* Local headers. */
 #include "riemann/riemann_exact.h"
 #include "swift.h"
@@ -163,7 +169,7 @@ void check_riemann_solution(struct riemann_statevector* WL,
 /**
  * @brief Check the exact Riemann solver on the Toro test problems
  */
-void check_riemann_exact() {
+void check_riemann_exact(void) {
   struct riemann_statevector WL, WR, Whalf;
 
   /* Test 1 */
@@ -290,7 +296,7 @@ void check_riemann_exact() {
 /**
  * @brief Check the symmetry of the TRRS Riemann solver
  */
-void check_riemann_symmetry() {
+void check_riemann_symmetry(void) {
   float WL[5], WR[5], Whalf1[5], Whalf2[5], n_unit1[3], n_unit2[3], n_norm,
       vij[3], totflux1[5], totflux2[5];
 
@@ -389,7 +395,7 @@ void check_riemann_symmetry() {
 /**
  * @brief Check the exact Riemann solver
  */
-int main() {
+int main(int argc, char* argv[]) {
 
   /* Initialize CPU frequency, this also starts time. */
   unsigned long long cpufreq = 0;
diff --git a/tests/testRiemannHLLC.c b/tests/testRiemannHLLC.c
index e843b29093f88d54dd931becf966d46670707091..0cce0f9d144f7be9000d368d6ac28e8e49c7d9aa 100644
--- a/tests/testRiemannHLLC.c
+++ b/tests/testRiemannHLLC.c
@@ -24,6 +24,12 @@
 #include <stdio.h>
 #include <string.h>
 
+/* Force use of the HLLC Riemann solver */
+#undef RIEMANN_SOLVER_TRRS
+#undef RIEMANN_SOLVER_EXACT
+#undef RIEMANN_SOLVER_HLLC
+#define RIEMANN_SOLVER_HLLC 1
+
 /* Local headers. */
 #include "riemann/riemann_hllc.h"
 #include "swift.h"
@@ -79,7 +85,7 @@ int are_symmetric(float a, float b) {
 /**
  * @brief Check the symmetry of the HLLC Riemann solver for a random setup
  */
-void check_riemann_symmetry() {
+void check_riemann_symmetry(void) {
   float WL[5], WR[5], n_unit1[3], n_unit2[3], n_norm, vij[3], totflux1[5],
       totflux2[5];
 
@@ -144,7 +150,7 @@ void check_riemann_symmetry() {
 /**
  * @brief Check the HLLC Riemann solver
  */
-int main() {
+int main(int argc, char *argv[]) {
 
   /* Initialize CPU frequency, this also starts time. */
   unsigned long long cpufreq = 0;
diff --git a/tests/testRiemannTRRS.c b/tests/testRiemannTRRS.c
index 4a0eac0be23581e175d2c0e599b786fd4508b14a..2c7098367a1ca8db84f097ad01aa2e1e411c433d 100644
--- a/tests/testRiemannTRRS.c
+++ b/tests/testRiemannTRRS.c
@@ -105,7 +105,7 @@ void check_riemann_solution(struct riemann_statevector* WL,
 /**
  * @brief Check the TRRS Riemann solver on the Toro test problems
  */
-void check_riemann_trrs() {
+void check_riemann_trrs(void) {
   struct riemann_statevector WL, WR, Whalf;
 
   /* Test 1 */
@@ -232,7 +232,7 @@ void check_riemann_trrs() {
 /**
  * @brief Check the symmetry of the TRRS Riemann solver
  */
-void check_riemann_symmetry() {
+void check_riemann_symmetry(void) {
   float WL[5], WR[5], Whalf1[5], Whalf2[5], n_unit1[3], n_unit2[3], n_norm,
       vij[3], totflux1[5], totflux2[5];
 
@@ -311,7 +311,7 @@ void check_riemann_symmetry() {
 /**
  * @brief Check the TRRS Riemann solver
  */
-int main() {
+int main(int argc, char* argv[]) {
 
   /* check the TRRS Riemann solver */
   check_riemann_trrs();
diff --git a/tests/testSPHStep.c b/tests/testSPHStep.c
index 08d6abaa7521de2a7d12fd9672db0d24a5a20a97..63834d94b7696e160dd7ca487ab7e9f1e943abfb 100644
--- a/tests/testSPHStep.c
+++ b/tests/testSPHStep.c
@@ -27,7 +27,7 @@
  */
 struct cell *make_cell(size_t N, float cellSize, int offset[3], int id_offset) {
   size_t count = N * N * N;
-  struct cell *cell = malloc(sizeof(struct cell));
+  struct cell *cell = (struct cell *)malloc(sizeof(struct cell));
   bzero(cell, sizeof(struct cell));
   struct part *part;
   struct xpart *xpart;
@@ -93,7 +93,7 @@ void runner_dopair1_density(struct runner *r, struct cell *ci, struct cell *cj);
 void runner_dopair2_force(struct runner *r, struct cell *ci, struct cell *cj);
 
 /* Run a full time step integration for one cell */
-int main() {
+int main(int argc, char *argv[]) {
 
 #ifndef DEFAULT_SPH
   return 0;
diff --git a/tests/testSelectOutput.c b/tests/testSelectOutput.c
new file mode 100644
index 0000000000000000000000000000000000000000..0b0adfa4e5a96f3431b27052bbb079f9be8838f2
--- /dev/null
+++ b/tests/testSelectOutput.c
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2015 Matthieu Schaller (matthieu.schaller@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Some standard headers. */
+#include <stdlib.h>
+
+/* Includes. */
+#include "swift.h"
+
+void select_output_engine_init(struct engine *e, struct space *s,
+                               struct cosmology *cosmo,
+                               struct swift_params *params,
+                               struct cooling_function_data *cooling,
+                               struct hydro_props *hydro_properties) {
+  /* set structures */
+  e->s = s;
+  e->cooling_func = cooling;
+  e->parameter_file = params;
+  e->cosmology = cosmo;
+  e->policy = engine_policy_hydro;
+  e->hydro_properties = hydro_properties;
+
+  /* initialization of threadpool */
+  threadpool_init(&e->threadpool, 1);
+
+  /* set parameters */
+  e->verbose = 1;
+  e->time = 0;
+  e->snapshot_output_count = 0;
+  e->snapshot_compression = 0;
+  e->snapshot_label_delta = 1;
+};
+
+void select_output_space_init(struct space *s, double *dim, int periodic,
+                              size_t Ngas, size_t Nspart, size_t Ngpart,
+                              struct part *parts, struct spart *sparts,
+                              struct gpart *gparts) {
+  s->periodic = periodic;
+  for (int i = 0; i < 3; i++) {
+    s->dim[i] = dim[i];
+  }
+
+  /* init space particles */
+  s->nr_parts = Ngas;
+  s->nr_sparts = Nspart;
+  s->nr_gparts = Ngpart;
+
+  s->parts = parts;
+  s->gparts = gparts;
+  s->sparts = sparts;
+
+  /* Allocate the extra parts array for the gas particles. */
+  if (posix_memalign((void **)&s->xparts, xpart_align,
+                     Ngas * sizeof(struct xpart)) != 0)
+    error("Failed to allocate xparts.");
+  bzero(s->xparts, Ngas * sizeof(struct xpart));
+};
+
+void select_output_space_clean(struct space *s) { free(s->xparts); };
+
+void select_output_engine_clean(struct engine *e) {
+  threadpool_clean(&e->threadpool);
+}
+
+int main(int argc, char *argv[]) {
+
+  /* Initialize CPU frequency, this also starts time. */
+  unsigned long long cpufreq = 0;
+  clocks_set_cpufreq(cpufreq);
+
+  char *base_name = "testSelectOutput";
+  size_t Ngas = 0, Ngpart = 0, Nspart = 0;
+  int periodic = -1;
+  int flag_entropy_ICs = -1;
+  double dim[3];
+  struct part *parts = NULL;
+  struct gpart *gparts = NULL;
+  struct spart *sparts = NULL;
+
+  /* parse parameters */
+  message("Reading parameters.");
+  struct swift_params param_file;
+  char *input_file = "selectOutput.yml";
+  parser_read_file(input_file, &param_file);
+
+  /* Default unit system */
+  message("Initialization of the unit system.");
+  struct unit_system us;
+  units_init_cgs(&us);
+
+  /* Default physical constants */
+  message("Initialization of the physical constants.");
+  struct phys_const prog_const;
+  phys_const_init(&us, &param_file, &prog_const);
+
+  /* Read data */
+  message("Reading initial conditions.");
+  read_ic_single("input.hdf5", &us, dim, &parts, &gparts, &sparts, &Ngas,
+                 &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, 1, 0, 0, 0, 0,
+                 1., 1., 1, 0);
+
+  /* pseudo initialization of the space */
+  message("Initialization of the space.");
+  struct space s;
+  select_output_space_init(&s, dim, periodic, Ngas, Nspart, Ngpart, parts,
+                           sparts, gparts);
+
+  /* initialization of cosmology */
+  message("Initialization of the cosmology.");
+  struct cosmology cosmo;
+  cosmology_init_no_cosmo(&cosmo);
+
+  /* pseudo initialization of cooling */
+  message("Initialization of the cooling.");
+  struct cooling_function_data cooling;
+
+  /* pseudo initialization of hydro */
+  message("Initialization of the hydro.");
+  struct hydro_props hydro_properties;
+  hydro_props_init(&hydro_properties, &prog_const, &us, &param_file);
+
+  /* pseudo initialization of the engine */
+  message("Initialization of the engine.");
+  struct engine e;
+  select_output_engine_init(&e, &s, &cosmo, &param_file, &cooling,
+                            &hydro_properties);
+
+  /* check output selection */
+  message("Checking output parameters.");
+  long long N_total[swift_type_count] = {Ngas, Ngpart, 0, 0, Nspart, 0};
+  io_check_output_fields(&param_file, N_total);
+
+  /* write output file */
+  message("Writing output.");
+  write_output_single(&e, base_name, &us, &us);
+
+  /* Clean-up */
+  message("Cleaning memory.");
+  select_output_engine_clean(&e);
+  select_output_space_clean(&s);
+  cosmology_clean(&cosmo);
+  free(parts);
+  free(gparts);
+
+  return 0;
+}
diff --git a/tests/testSelectOutput.py b/tests/testSelectOutput.py
new file mode 100644
index 0000000000000000000000000000000000000000..aec7f4671fb2768acde768fd9929168559ebb3cb
--- /dev/null
+++ b/tests/testSelectOutput.py
@@ -0,0 +1,54 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2015 Bert Vandenbroucke (bert.vandenbroucke@ugent.be)
+ #                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+# Check the output done with swift
+
+import h5py
+
+filename = "testSelectOutput_0000.hdf5"
+log_filename = "select_output.log"
+
+# Read the simulation data
+sim = h5py.File(filename, "r")
+part0 = sim["/PartType0"]
+
+# check presence / absence fields
+if "Velocities" in part0:
+    raise Exception("`Velocities` present in HDF5 but should not be written")
+
+if "Coordinates" not in part0:
+    raise Exception("`Coordinates` not present in HDF5 but should be written")
+
+if "Masses" not in part0:
+    raise Exception("`Masses` not present in HDF5 but should be written")
+
+if "Density" not in part0:
+    raise Exception("`Density` not present in HDF5 but should be written")
+
+
+# check error detection
+with open(log_filename, "r") as f:
+    data = f.read()
+
+if "SelectOutput:Masses_Gas" not in data:
+    raise Exception("Input error in `SelectOuput:Masses_Gas` not detected")
+
+if "SelectOutput:Pot_Gas" not in data:
+    raise Exception("Parameter name error not detected for `SelectOutput:Pot_Gas`")
diff --git a/tests/testSelectOutput.sh.in b/tests/testSelectOutput.sh.in
new file mode 100644
index 0000000000000000000000000000000000000000..85fd999643f82fd10d96013ad360a75a441a9e1a
--- /dev/null
+++ b/tests/testSelectOutput.sh.in
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+echo "Creating initial conditions"
+python @srcdir@/makeInput.py
+
+echo "Generating output"
+./testSelectOutput 2>&1 | tee select_output.log
+
+echo "Checking output"
+python @srcdir@/testSelectOutput.py
+
+rm -f testSelectOutput_0000.hdf5 testSelectOutput.xmf select_output.log
+
+echo "Test passed"
diff --git a/tests/testSingle.c b/tests/testSingle.c
index e2ec35bc4382658be7754b9c11fc3a3dbe4bbdc1..52fe51c529b8c3b43f9c5f03fe44b5b742acfc07 100644
--- a/tests/testSingle.c
+++ b/tests/testSingle.c
@@ -142,6 +142,6 @@ int main(int argc, char *argv[]) {
 }
 #else
 
-int main() { return 0; }
+int main(int argc, char *argv[]) { return 0; }
 
 #endif
diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c
index 6c6cd291dc4b919650d568354d7949f9ed4e0155..886290ab984603d0afb3201377611598cd7163e4 100644
--- a/tests/testSymmetry.c
+++ b/tests/testSymmetry.c
@@ -16,7 +16,6 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-
 #include "../config.h"
 
 #include <fenv.h>
@@ -32,7 +31,7 @@ void print_bytes(void *p, size_t len) {
   printf(")\n");
 }
 
-void test() {
+void test(void) {
 
 #if defined(SHADOWFAX_SPH)
   /* Initialize the Voronoi simulation box */
@@ -41,7 +40,7 @@ void test() {
 /*  voronoi_set_box(box_anchor, box_side);*/
 #endif
 
-  /* Start with some values for the cosmological paramters */
+  /* Start with some values for the cosmological parameters */
   const float a = (float)random_uniform(0.8, 1.);
   const float H = 1.f;
 
@@ -62,7 +61,7 @@ void test() {
   pi.time_bin = 1;
   pj.time_bin = 1;
 
-#if defined(GIZMO_SPH) || defined(SHADOWFAX_SPH)
+#if defined(GIZMO_MFV_SPH) || defined(SHADOWFAX_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);
@@ -106,11 +105,12 @@ void test() {
   pj.primitives.gradients.P[0] = 0.0f;
   pj.primitives.gradients.P[1] = 0.0f;
   pj.primitives.gradients.P[2] = 0.0f;
+
+#ifdef SHADOWFAX_SPH
   /* set time step to reasonable value */
   pi.force.dt = 0.001;
   pj.force.dt = 0.001;
 
-#ifdef SHADOWFAX_SPH
   voronoi_cell_init(&pi.cell, pi.x, box_anchor, box_side);
   voronoi_cell_init(&pj.cell, pj.x, box_anchor, box_side);
 #endif
@@ -164,14 +164,14 @@ void test() {
     printParticle_single(&pi2, &xpi);
     print_bytes(&pj, sizeof(struct part));
     print_bytes(&pj2, sizeof(struct part));
-    error("Particles 'pi' do not match after force (byte = %d)", i_not_ok);
+    error("Particles 'pi' do not match after density (byte = %d)", i_not_ok);
   }
   if (j_not_ok) {
     printParticle_single(&pj, &xpj);
     printParticle_single(&pj2, &xpj);
     print_bytes(&pj, sizeof(struct part));
     print_bytes(&pj2, sizeof(struct part));
-    error("Particles 'pj' do not match after force (byte = %d)", j_not_ok);
+    error("Particles 'pj' do not match after density (byte = %d)", j_not_ok);
   }
 
   /* --- Test the force loop --- */
@@ -187,33 +187,33 @@ void test() {
   runner_iact_nonsym_force(r2, dx, pj2.h, pi2.h, &pj2, &pi2, a, H);
 
 /* Check that the particles are the same */
-#if defined(GIZMO_SPH)
+#if defined(GIZMO_MFV_SPH)
   i_not_ok = 0;
   j_not_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);
+    float aa = *(((float *)&pi) + i);
+    float bb = *(((float *)&pi2) + i);
+    float cc = *(((float *)&pj) + i);
+    float dd = *(((float *)&pj2) + i);
 
     int a_is_b;
-    if ((a + b)) {
-      a_is_b = (fabs((a - b) / (a + b)) > 1.e-4);
+    if ((aa + bb)) {
+      a_is_b = (fabs((aa - bb) / (aa + bb)) > 1.e-4);
     } else {
-      a_is_b = !(a == 0.0f);
+      a_is_b = !(aa == 0.0f);
     }
     int c_is_d;
-    if ((c + d)) {
-      c_is_d = (fabs((c - d) / (c + d)) > 1.e-4);
+    if ((cc + dd)) {
+      c_is_d = (fabs((cc - dd) / (cc + dd)) > 1.e-4);
     } else {
-      c_is_d = !(c == 0.0f);
+      c_is_d = !(cc == 0.0f);
     }
 
     if (a_is_b) {
-      message("%.8e, %.8e, %lu", a, b, i);
+      message("%.8e, %.8e, %lu", aa, bb, i);
     }
     if (c_is_d) {
-      message("%.8e, %.8e, %lu", c, d, i);
+      message("%.8e, %.8e, %lu", cc, dd, i);
     }
 
     i_not_ok |= a_is_b;
diff --git a/tests/testTimeIntegration.c b/tests/testTimeIntegration.c
index 972e6f2323c0401c70de2990bcb088f95b3dfd83..2034c402a2d626a7b503613f6cade821ec438151 100644
--- a/tests/testTimeIntegration.c
+++ b/tests/testTimeIntegration.c
@@ -26,7 +26,7 @@
  * @brief Test the kick-drift-kick leapfrog integration
  * via a Sun-Earth simulation
  */
-int main() {
+int main(int argc, char *argv[]) {
 
   struct cell c;
   int i;
@@ -63,10 +63,10 @@ int main() {
 
   /* Create a particle */
   struct part *parts = NULL;
-  parts = malloc(sizeof(struct part));
+  parts = (struct part *)malloc(sizeof(struct part));
   bzero(parts, sizeof(struct part));
   struct xpart *xparts = NULL;
-  xparts = malloc(sizeof(struct xpart));
+  xparts = (struct xpart *)malloc(sizeof(struct xpart));
   bzero(xparts, sizeof(struct xpart));
 
   /* Put the particle on the orbit */
diff --git a/tests/testUtilities.c b/tests/testUtilities.c
new file mode 100644
index 0000000000000000000000000000000000000000..963e4d2233bbd56f7d61d5e2a0d2424006aa63ab
--- /dev/null
+++ b/tests/testUtilities.c
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2018 Jacob Kegerreis (jacob.kegerreis@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#include "swift.h"
+#include "utilities.h"
+
+/**
+ * @brief Test generic utility functions
+ */
+int main(int argc, char *argv[]) {
+  /// Test find_value_in_monot_incr_array()
+  int n = 100;
+  float array[n];
+  int index;
+  float x;
+
+  // Initialise test array
+  for (int j = 0; j < n; j++) {
+    array[j] = j;
+  }
+
+  // Typical value
+  x = 42.42f;
+  index = find_value_in_monot_incr_array(x, array, n);
+  if (index != 42) {
+    error("Failed with a typical value ");
+  }
+
+  // Value on array element
+  x = 33.f;
+  index = find_value_in_monot_incr_array(x, array, n);
+  if (index != 33) {
+    error("Failed with an array element ");
+  }
+
+  // Value below array
+  x = -123.f;
+  index = find_value_in_monot_incr_array(x, array, n);
+  if (index != -1) {
+    error("Failed with a value below the array ");
+  }
+
+  // Value above array
+  x = 123.f;
+  index = find_value_in_monot_incr_array(x, array, n);
+  if (index != n) {
+    error("Failed with a value above the array ");
+  }
+
+  // Array slice with typical value
+  x = 9.81f;
+  n = 10;
+  index = find_value_in_monot_incr_array(x, array + 5, n);
+  if (index != 4) {
+    error("Failed with an array slice ");
+  }
+
+  return 0;
+}
diff --git a/tests/testVoronoi1D.c b/tests/testVoronoi1D.c
index d16a36d9449d7bfdb2c74408efad61b219b1d7e3..083d9aaa279f241ae1ac4d0bfaeb2780a39574a4 100644
--- a/tests/testVoronoi1D.c
+++ b/tests/testVoronoi1D.c
@@ -16,10 +16,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-
 #include "hydro/Shadowswift/voronoi1d_algorithm.h"
 
-int main() {
+int main(int argc, char *argv[]) {
 
   double box_anchor[1] = {-0.5};
   double box_side[1] = {2.};
diff --git a/tests/testVoronoi2D.c b/tests/testVoronoi2D.c
index 509d3ab69976fa8618db389ebd87eedb9ea34409..60a71624904c11a3cdb3b90906189df60bfc6956 100644
--- a/tests/testVoronoi2D.c
+++ b/tests/testVoronoi2D.c
@@ -16,14 +16,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-
 #include "hydro/Shadowswift/voronoi2d_algorithm.h"
 #include "tools.h"
 
 /* Number of cells used to test the 2D interaction algorithm */
 #define TESTVORONOI2D_NUMCELL 100
 
-int main() {
+int main(int argc, char *argv[]) {
 
   /* initialize simulation box */
   double anchor[3] = {-0.5f, -0.5f, -0.5f};
diff --git a/tests/testVoronoi3D.c b/tests/testVoronoi3D.c
index b4f219a41368bb3ce4e8111ae44c43e7fa1f7441..db5c33aa6e4ef0792373febd5d773a6d1198db29 100644
--- a/tests/testVoronoi3D.c
+++ b/tests/testVoronoi3D.c
@@ -53,7 +53,7 @@
  *
  * @return Volume of the simulation box as it is stored in the global variables.
  */
-float voronoi_get_box_volume() {
+float voronoi_get_box_volume(void) {
   return VORONOI3D_BOX_SIDE_X * VORONOI3D_BOX_SIDE_Y * VORONOI3D_BOX_SIDE_Z;
 }
 
@@ -129,7 +129,7 @@ float voronoi_get_box_face(unsigned long long id, float *face_midpoint) {
 /**
  * @brief Check if voronoi_volume_tetrahedron() works
  */
-void test_voronoi_volume_tetrahedron() {
+void test_voronoi_volume_tetrahedron(void) {
   float v1[3] = {0., 0., 0.};
   float v2[3] = {0., 0., 1.};
   float v3[3] = {0., 1., 0.};
@@ -142,7 +142,7 @@ void test_voronoi_volume_tetrahedron() {
 /**
  * @brief Check if voronoi_centroid_tetrahedron() works
  */
-void test_voronoi_centroid_tetrahedron() {
+void test_voronoi_centroid_tetrahedron(void) {
   float v1[3] = {0., 0., 0.};
   float v2[3] = {0., 0., 1.};
   float v3[3] = {0., 1., 0.};
@@ -158,7 +158,7 @@ void test_voronoi_centroid_tetrahedron() {
 /**
  * @brief Check if voronoi_calculate_cell() works
  */
-void test_calculate_cell() {
+void test_calculate_cell(void) {
 
   double box_anchor[3] = {VORONOI3D_BOX_ANCHOR_X, VORONOI3D_BOX_ANCHOR_Y,
                           VORONOI3D_BOX_ANCHOR_Z};
@@ -234,7 +234,7 @@ void test_calculate_cell() {
   assert(cell.face_midpoints[5][2] == face_midpoint[2] - cell.x[2]);
 }
 
-void test_paths() {
+void test_paths(void) {
   float u, l, q;
   int up, us, uw, lp, ls, lw, qp, qs, qw;
   float r2, dx[3];
@@ -1240,7 +1240,7 @@ void set_coordinates(struct part *p, double x, double y, double z,
 }
 #endif
 
-void test_degeneracies() {
+void test_degeneracies(void) {
 #ifdef SHADOWFAX_SPH
   int idx = 0;
   /* make a small cube */
@@ -1308,7 +1308,7 @@ void test_degeneracies() {
 #endif
 }
 
-int main() {
+int main(int argc, char *argv[]) {
 
   /* Set the all enclosing simulation box dimensions */
   double box_anchor[3] = {VORONOI3D_BOX_ANCHOR_X, VORONOI3D_BOX_ANCHOR_Y,
diff --git a/theory/Cosmology/coordinates.tex b/theory/Cosmology/coordinates.tex
index e3a22eaae025e6911e8aca92d6d29bf5fa82bf21..bc593606026217f345e2f77d90cee3f6632b3cef 100644
--- a/theory/Cosmology/coordinates.tex
+++ b/theory/Cosmology/coordinates.tex
@@ -40,19 +40,29 @@ $\Psi \equiv \frac{1}{2}a\dot{a}\mathbf{r}_i^2$ and obtain
   -\frac{\phi'}{a},\\
   \phi' &= a\phi + \frac{1}{2}a^2\ddot{a}\mathbf{r}_i'^2.\nonumber
 \end{align}
-Finally, we introduce the velocities $\mathbf{v}' \equiv
-a^2\dot{\mathbf{r}'}$ that are used internally by the code. Note that these
-velocities \emph{do not} have a physical interpretation. We caution that they
-are not the peculiar velocities, nor the Hubble flow, nor the total
-velocities\footnote{One additional inconvenience of our choice of
+Finally, we introduce the velocities
+$\mathbf{v}' \equiv a^2\dot{\mathbf{r}'}$ that are used internally by
+the code. Note that these velocities \emph{do not} have a physical
+interpretation. We caution that they are not the peculiar velocities
+($\mathbf{v}_{\rm p} \equiv a\dot{\mathbf{r}'} =
+\frac{1}{a}\mathbf{v}'$), nor the Hubble flow
+($\mathbf{v}_{\rm H} \equiv \dot{a}\mathbf{r}'$), nor the total
+velocities
+($\mathbf{v}_{\rm tot} \equiv \mathbf{v}_{\rm p} + \mathbf{v}_{\rm H}
+= \dot{a}\mathbf{r}' + \frac{1}{a}\mathbf{v}'$) and also differ from
+the convention used in \gadget snapshots
+($\sqrt{a} \dot{\mathbf{r}'}$) and other related simulation
+codes\footnote{One additional inconvenience of our choice of
   generalised coordinates is that our velocities $\mathbf{v}'$ and
   sound-speed $c'$ do not have the same dependencies on the
   scale-factor. The signal velocity entering the time-step calculation
-  will hence read $v_{\rm sig} = a\dot{\mathbf{r}'} + c = \frac{1}{a} \left(
+  will hence read
+  $v_{\rm sig} = a\dot{\mathbf{r}'} + c = \frac{1}{a} \left(
     |\mathbf{v}'| + a^{(5 - 3\gamma)/2}c'\right)$.}.
-This choice implies that $\dot{v}' = a \ddot{r}$. Using the SPH
-definition of density, $\rho_i' =
-\sum_jm_jW(\mathbf{r}_{j}'-\mathbf{r}_{i}',h_i') =
+% This choice implies that $\dot{v}' = a \ddot{r}$.
+
+Using the SPH definition of density,
+$\rho_i' = \sum_jm_jW(\mathbf{r}_{j}'-\mathbf{r}_{i}',h_i') =
 \sum_jm_jW_{ij}'(h_i')$, we can follow \cite{Price2012} and apply the
 Euler-Lagrange equations to write
 \begin{alignat}{3}
diff --git a/theory/Multipoles/bibliography.bib b/theory/Multipoles/bibliography.bib
index c3d1289584cab55cd8e0d4d0765d70e22f0fcf2e..077525a9e4db781ea58bd46ef2ba109d6c074be0 100644
--- a/theory/Multipoles/bibliography.bib
+++ b/theory/Multipoles/bibliography.bib
@@ -191,4 +191,90 @@ keywords = "adaptive algorithms"
   adsnote = {Provided by the SAO/NASA Astrophysics Data System}
 }
 
+@ARTICLE{Hubber2011,
+   author = {{Hubber}, D.~A. and {Batty}, C.~P. and {McLeod}, A. and {Whitworth}, A.~P.
+	},
+    title = "{SEREN - a new SPH code for star and planet formation simulations. Algorithms and tests}",
+  journal = {\aap},
+ keywords = {hydrodynamics, methods: numerical, stars: formation},
+     year = 2011,
+    month = may,
+   volume = 529,
+      eid = {A27},
+    pages = {A27},
+      doi = {10.1051/0004-6361/201014949},
+   adsurl = {http://adsabs.harvard.edu/abs/2011A%26A...529A..27H},
+  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
+}
+
+
+@ARTICLE{Klessen1997,
+   author = {{Klessen}, R.},
+    title = "{GRAPESPH with fully periodic boundary conditions - Fragmentation of molecular clouds}",
+  journal = {\mnras},
+ keywords = {Molecular Clouds, Interstellar Matter, Fragmentation, Astronomical Models, Computer Programs, Boundary Conditions},
+     year = 1997,
+    month = nov,
+   volume = 292,
+    pages = {11},
+      doi = {10.1093/mnras/292.1.11},
+   adsurl = {http://adsabs.harvard.edu/abs/1997MNRAS.292...11K},
+  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
+}
+
+@ARTICLE{Hernquist1991,
+   author = {{Hernquist}, L. and {Bouchet}, F.~R. and {Suto}, Y.},
+    title = "{Application of the Ewald method to cosmological N-body simulations}",
+  journal = {\apjs},
+ keywords = {Computational Astrophysics, Galactic Structure, Hubble Constant, Many Body Problem, Astronomical Models, Boundary Conditions, Spatial Resolution},
+     year = 1991,
+    month = feb,
+   volume = 75,
+    pages = {231-240},
+      doi = {10.1086/191530},
+   adsurl = {http://adsabs.harvard.edu/abs/1991ApJS...75..231H},
+  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
+}
+
+@ARTICLE{Ewald1921,
+   author = {{Ewald}, P.~P.},
+    title = "{Die Berechnung optischer und elektrostatischer Gitterpotentiale}",
+  journal = {Annalen der Physik},
+     year = 1921,
+   volume = 369,
+    pages = {253-287},
+      doi = {10.1002/andp.19213690304},
+   adsurl = {http://adsabs.harvard.edu/abs/1921AnP...369..253E},
+  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
+}
+
+@ARTICLE{Warren1995,
+   author = {{Warren}, M.~S. and {Salmon}, J.~K.},
+    title = "{A portable parallel particle program}",
+  journal = {Computer Physics Communications},
+     year = 1995,
+    month = may,
+   volume = 87,
+    pages = {266-290},
+      doi = {10.1016/0010-4655(94)00177-4},
+   adsurl = {http://adsabs.harvard.edu/abs/1995CoPhC..87..266W},
+  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
+}
+
+@ARTICLE{Barnes1986,
+   author = {{Barnes}, J. and {Hut}, P.},
+    title = "{A hierarchical O(N log N) force-calculation algorithm}",
+  journal = {\nat},
+ keywords = {Computational Astrophysics, Many Body Problem, Numerical Integration, Stellar Motions, Algorithms, Hierarchies},
+     year = 1986,
+    month = dec,
+   volume = 324,
+    pages = {446-449},
+      doi = {10.1038/324446a0},
+   adsurl = {http://adsabs.harvard.edu/abs/1986Natur.324..446B},
+  adsnote = {Provided by the SAO/NASA Astrophysics Data System}
+}
+
+
+
 
diff --git a/theory/Multipoles/exact_forces.tex b/theory/Multipoles/exact_forces.tex
new file mode 100644
index 0000000000000000000000000000000000000000..2602c2db1c06eb496d4c450e0d33ee87d28831e8
--- /dev/null
+++ b/theory/Multipoles/exact_forces.tex
@@ -0,0 +1,20 @@
+\subsection{Exact forces for accuracy checks}
+\label{ssec:exact_forces}
+
+To assess the accuracy of the gravity solver, \swift can also compute
+the gravitational forces and potential for a subset of particles using
+a simple direct summation method. This is obviously much slower and
+should only be used for code testing purposes. The forces for a
+selection of particles are computed every time-step if they are active
+and dumped to a file alongside the forces computed by the FMM method.
+
+In the case where periodic boundary conditions are used, we apply the
+\cite{Ewald1921} summation technique to include the contribution to
+the forces of all the infinite periodic replications of the particle
+distribution. We use the approximation to the infinite series of terms
+proposed by \cite{Hernquist1991}\footnote{Note, however, that there is
+a typo in their formula for the force correction terms. The correct
+expression is given by \cite{Klessen1997} \citep[see
+also][]{Hubber2011}.}, which we tabulate in one octant using 64
+equally spaced bins along each spatial direction spanning and the
+range $[0,L]$, where $L$ is the side-length of the box.
diff --git a/theory/Multipoles/fmm_standalone.tex b/theory/Multipoles/fmm_standalone.tex
index 65d6b522f3a6a1f9ede41091a39f4f5145cf041c..1b597fa636650cd09469b9952f7a14bdf22ce35f 100644
--- a/theory/Multipoles/fmm_standalone.tex
+++ b/theory/Multipoles/fmm_standalone.tex
@@ -35,6 +35,7 @@ Making gravity great again.
 \input{fmm_summary}
 %\input{gravity_derivatives}
 \input{mesh_summary}
+\input{exact_forces}
 
 \bibliographystyle{mnras}
 \bibliography{./bibliography.bib}
diff --git a/theory/Multipoles/fmm_summary.tex b/theory/Multipoles/fmm_summary.tex
index 051f01db866994b51e86d91059b8e3e06d317835..c263c241673094756966ffc9e5eae7111dd843ea 100644
--- a/theory/Multipoles/fmm_summary.tex
+++ b/theory/Multipoles/fmm_summary.tex
@@ -10,24 +10,21 @@ evaluate
   \mathbf{x}_b)\qquad \forall~a\in N
   \label{eq:fmm:n_body}
 \end{equation}
-efficiently for large numbers of particles $N$. In the case of collisionless
-dynamics, the particles are a mere Monte-Carlo sampling of the
-underlying coarse-grained phase-space distribution which justifies the
-use of approximate method to evaluate Eq.~\ref{eq:fmm:n_body}. The
-\emph{Fast Multipole Method} (FMM) \citep{Greengard1987, Cheng1999},
-popularized in the field and adapted specifically for gravity solvers
-by \cite{Dehnen2000, Dehnen2002}, is an $\mathcal{O}(N)$ method
-designed to solve Eq.~\ref{eq:fmm:n_body} by expanding the potential both
-around $\mathbf{x}_i$ and $\mathbf{x}_j$ and grouping similar terms
-arising from nearby particles. \\
+efficiently for large numbers of particles $N$. In the case of
+collisionless dynamics, the particles are a mere Monte-Carlo sampling
+of the underlying coarse-grained phase-space distribution which
+justifies the use of approximate method to evaluate
+Eq.~\ref{eq:fmm:n_body}. The \emph{Fast Multipole Method} (FMM)
+\citep{Greengard1987, Cheng1999}, popularized in astronomy and adapted
+specifically for gravity solvers by \cite{Dehnen2000, Dehnen2002} (see
+also \cite{Warren1995} for related ideas), is an $\mathcal{O}(N)$
+method designed to solve Eq.~\ref{eq:fmm:n_body} by expanding the
+potential in Taylor series \emph{both} around $\mathbf{x}_a$ and
+$\mathbf{x}_b$ and grouping similar terms arising from nearby
+particles. For comparison, a \cite{Barnes1986} tree-code expands the
+potential only around $\mathbf{x}_b$.
 
-In what follows, we use the compact multi-index notation of
-\cite{Dehnen2014} (repeated in appendix \ref{sec:multi_index_notation}
-for completeness) to simplify expressions and ease
-comparisons. $\mathbf{k}$, $\mathbf{m}$ and $\mathbf{n}$ are
-multi-indices and $\mathbf{r}$, $\mathbf{R}$, $\mathbf{x}$,
-$\mathbf{y}$ and $\mathbf{z}$ are vectors, whilst $a$ and $b$ are
-particle indices.\\
+\subsubsection{Double expansion of the potential}
 
 \begin{figure}
 \includegraphics[width=\columnwidth]{cells.pdf}
@@ -37,15 +34,22 @@ particle indices.\\
   around the distance vector $\mathbf{R}$ linking the two centres of mass
   ($\mathbf{z}_A$ and $\mathbf{z}_B$) of cell $A$ and $B$. The
   expansion converges towards the exact expression provided
-  $|\mathbf{R}|<|\mathbf{r}_a + \mathbf{r}_b|$.}
+  $|\mathbf{R}|>|\mathbf{r}_a + \mathbf{r}_b|$.}
 \label{fig:fmm:cells}
 \end{figure}
 
-
+In what follows, we use the compact multi-index notation of
+\cite{Dehnen2014} (repeated in appendix \ref{sec:multi_index_notation}
+for completeness) to simplify expressions and ease comparisons with
+other published work. $\mathbf{k}$, $\mathbf{m}$ and $\mathbf{n}$ are
+multi-indices and $\mathbf{r}$, $\mathbf{R}$, $\mathbf{x}$,
+$\mathbf{y}$ and $\mathbf{z}$ are vectors, whilst $a$ and $b$ are
+particle indices. Note also that we make no assumption on the specific
+functional form of the potential $\varphi$.\\
 For a single pair of particles $a$ and $b$ located in cell $A$ and $B$
-with centres of mass $\mathbf{z}_A$ and  $\mathbf{z}_B$
-respectively, as shown on Fig.~\ref{fig:fmm:cells}, the potential
-generated by $b$ at the location of $a$ can be rewritten as
+with centres of mass $\mathbf{z}_A$ and $\mathbf{z}_B$ respectively,
+as shown on Fig.~\ref{fig:fmm:cells}, the potential generated by $b$
+at the location of $a$ can be rewritten as
 \begin{align}
   \varphi(\mathbf{x}_a - \mathbf{x}_b)
   &= \varphi\left(\mathbf{x}_a - \mathbf{z}_A - \mathbf{x}_b +
@@ -62,6 +66,7 @@ generated by $b$ at the location of $a$ can be rewritten as
   &= \sum_\mathbf{n} \frac{1}{\mathbf{n}!} \mathbf{r}_a^{\mathbf{n}}
   \sum_\mathbf{m} \frac{1}{\mathbf{m}!}
   \left(-\mathbf{r}_b\right)^\mathbf{m} \nabla^{\mathbf{n}+\mathbf{m}} \varphi(\mathbf{R}),
+  \label{eq:fmm:expansion}
 \end{align}
 where we used the Taylor expansion of $\varphi$ around $\mathbf{R} \equiv
 \mathbf{z}_A - \mathbf{z}_B$ on the third line, used $\mathbf{r}_a
@@ -78,7 +83,7 @@ to order $p$, we get
   \label{eq:fmm:fmm_one_part}
 \end{equation}
 with the approximation converging as $p\rightarrow\infty$ towards the
-correct value provided $|\mathbf{R}|<|\mathbf{r}_a +
+correct value provided $|\mathbf{R}|>|\mathbf{r}_a +
 \mathbf{r}_b|$. If we now consider all the particles within $B$ and
 combine their contributions to the potential at location
 $\mathbf{x}_a$ in cell $A$, we get
@@ -95,6 +100,8 @@ This last equation forms the basis of the FMM. The algorithm
 decomposes the equation into three separated sums evaluated at
 different stages.\\
 
+\subsubsection{The FMM algorithm}
+
 In a first step, multipoles are constructed from the
 innermost sum. For each cell, we compute all the terms
 \begin{equation}
@@ -141,7 +148,6 @@ for the acceleration along $x$, we have:
   \frac{1}{\mathbf{n}!} \mathbf{r}_a^{\mathbf{n}}
   \mathsf{F}_{\mathbf{n}+\left(1,0,0\right)}(\mathbf{z}_A). \label{eq:fmm:L2P_force} 
 \end{equation}
-
 In practice, the multipoles can be constructed recursively from the
 leaves of the tree to the root and the local expansions from the root
 to the leaves by shifting the $\mathsf{M}$ and $\mathsf{F}$ tensors
@@ -159,12 +165,29 @@ read:
   \frac{\mathbf{y}^\mathbf{m}}{\mathbf{m}!}\mathsf{F}_{\mathbf{m} +
     \mathbf{n}}(\mathbf{x}). \label{eq:fmm:L2L} 
 \end{align}
-
-All the kernels (Eqs.~\ref{eq:fmm:P2M}-\ref{eq:fmm:L2L}) are rather
+One final, useful expression that enters some interaction between
+tree-leaves is the P2M kernel that directly applies the potential due
+to a multipole expansion to a particle without using the expansion of
+the potential $\mathsf{F}$ at the centre of mass of the cell. This
+kernel is obtained by setting $\mathbf{r}_a$ to zero in
+eq.~\ref{eq:fmm:expansion}, re-defining
+$\mathbf{R}\equiv\mathbf{x}_{\rm a} - \mathbf{z}_{\rm B}$ and
+constructing the same $\mathsf{M}$ and $\mathsf{D}$ tensor than for
+the other kernels:
+\begin{align}
+  \phi_{Ba}(\mathbf{x}_a) &= G\sum_{\mathbf{m}}^p \mathsf{M}_{\mathbf{m}} \mathsf{D}_{\mathbf{m}}(\mathbf{R}),\\
+  a_x(\mathbf{x}_a) &= G\sum_{\mathbf{m}}^p \mathsf{M}_{\mathbf{m}} \mathsf{D}_{\mathbf{m}+\left(1,0,0\right)}(\mathbf{R}).
+  \label{eq:fmm:M2P}
+\end{align}
+A traditional tree-code uses solely that kernel to obtain the forces
+from the multipoles (or often just monopoles, i.e. setting $p=0$ throughout)
+to the particles.\\
+All the kernels (Eqs.~\ref{eq:fmm:P2M}-\ref{eq:fmm:M2P}) are rather
 straightforward to evaluate as they are only made of additions and
 multiplications (provided $\mathsf{D}$ can be evaluated quickly),
-which are extremely efficient instructions on modern
-architectures. However, the fully expanded sums can lead to rather
+which are extremely efficient instructions on modern architectures
+(see Appendix \ref{sec:pot_derivatives} for the full
+expressions). However, the fully expanded sums can lead to rather
 large and prone to typo expressions. To avoid any mishaps, we use a
 \texttt{python} script to generate C code in which all the sums are
 unrolled and correct by construction. In \swift, we implemented the
@@ -180,3 +203,5 @@ $A$ rather than its geometrical centre. The first order multipoles
 construction. This allows us to simplify some of the expressions and
 helps reduce, albeit by a small fraction, the memory footprint of the
 tree structure.
+
+\subsubsection{The Multipole acceptance criterion}
diff --git a/theory/Multipoles/generate_multipoles/multipoles.py b/theory/Multipoles/generate_multipoles/multipoles.py
index 84c93cc53dfd3023c69e07df7444fd5330546567..ef263d09f22e0186bf3ae2e9572cb89cf156f8a0 100644
--- a/theory/Multipoles/generate_multipoles/multipoles.py
+++ b/theory/Multipoles/generate_multipoles/multipoles.py
@@ -332,3 +332,59 @@ if order > 0:
 print ""
 print "-------------------------------------------------"
 
+print "gravity_M2P():"
+print "-------------------------------------------------\n"
+
+if order > 0:
+    print "#if SELF_GRAVITY_MULTIPOLE_ORDER > %d\n"%(order-1)
+    
+print "/* %s order contributions */"%(ordinal(order))
+
+    
+for r in range(4):
+    if r == 0:
+        print "*f_x =",
+    if r == 1:
+        print "*f_y =",
+    if r == 2:
+        print "*f_z =",
+    if r == 3:
+        print "*pot =",
+        
+    first = True
+    for i in range(order+1):
+        for j in range(order+1):
+            for k in range(order+1):
+                if i + j + k == order:
+                    if first:
+                        first = False
+                    else:
+                        print "+",
+                    if r == 0:
+                        ii = i+1
+                        jj = j
+                        kk = k
+                    if r == 1:
+                        ii = i
+                        jj = j+1
+                        kk = k
+                    if r == 2:
+                        ii = i
+                        jj = j
+                        kk = k+1
+                    if r == 3:
+                        ii = i
+                        jj = j
+                        kk = k
+                    print "m->M_%d%d%d * d.D_%d%d%d"%(i,j,k,ii,jj,kk),
+                    
+    print ";"
+        
+print ""
+
+if order > 0:
+    print "#endif"
+
+print ""
+print "-------------------------------------------------"
+
diff --git a/theory/Multipoles/mesh_summary.tex b/theory/Multipoles/mesh_summary.tex
index 5e9a0a2fed2d95474a975aed39c17c117118970f..a4b0b3a1bbc23fb43a978ba040d25e5f29ed384f 100644
--- a/theory/Multipoles/mesh_summary.tex
+++ b/theory/Multipoles/mesh_summary.tex
@@ -5,44 +5,55 @@ We truncate the potential and forces computed via the FMM using a
 smooth function that drops quickly to zero at some scale $r_s$ set by
 the top-level mesh. Traditionally, implementations have used
 expressions which are cheap to evaluate in Fourier space
-\citep[e.g.][]{Bagla2003,
-  Springel2005}. This, however, implies a large cost for each
-interaction computed within the tree as the real-space truncation
-function won't have a simple analytic form that can be evaluated
-efficiently by computers. Since the FMM scheme involves to not only
-evaluate the forces but higher-order derivatives, a more appropiate
-choice is necessary. We use the sigmoid $S(x) \equiv \frac{e^x}{1 + e^x}$
-as the basis of our truncation function write for the potential:
+\citep[e.g.][]{Bagla2003, Springel2005}. This, however, implies a
+large cost for each interaction computed within the tree as the
+real-space truncation function won't have a simple analytic form that
+can be evaluated efficiently even by modern architectures (typically
+an $\mathrm{erf}()$ function). Since the FMM scheme involves to not
+only evaluate the forces but higher-order derivatives, a more
+appropiate choice is necessary. We use the sigmoid
+$\sigma(w) \equiv \frac{e^w}{1 + e^w}$ as the basis of our truncation
+function and write for the potential:
 
 \begin{align}
-  \varphi_s(r) &= \frac{1}{r} \chi(r, r_s) = \frac{1}{r}\times\left[2 - 2S\left(\frac{2r}{r_s}\right)\right].% \nonumber\\
+  \varphi_s(r) &= \frac{1}{r} \times \chi(r, r_s) = \frac{1}{r}\times\left[2 - 2\sigma\left(\frac{2r}{r_s}\right)\right].% \nonumber\\
   %&= \frac{1}{r}\left[2 - \frac{2e^{\frac{2r}{r_s}}}{1+e^{\frac{2r}{r_s}}}.\right] 
 \end{align}
-This function alongside the trunctation function used in \gadget is
-shown on Fig.~\ref{fig:fmm:potential_short}. This choice of $S(x)$ can
-seem rather cumbersome at first but writing $\alpha(x) \equiv (1+e^x)^{-1}$,
-one can expressed all derivatives of $S(x)$ as simple polynomials in
-$\alpha(x)$, which are easy to evaluate. For instance, in the case of
-the direct force evaluation between two particles, we obtain 
-
-
+This function alongside the trunctation function used in
+\gadget\footnote{For completeness, the \gadget expression reads:\\
+ $\varphi_s(r) = \frac{1}{r} \times \mathrm{erfc}(\frac{1}{2}\frac{r}{r_s})$.} is shown
+on Fig.~\ref{fig:fmm:potential_short}. This choice of $\sigma(w)$ can
+seem rather cumbersome at first but writing
+$\alpha(w) \equiv (1+e^w)^{-1}$, one can express all derivatives of
+$\sigma(w)$ as simple polynomials in $\alpha(w)$ (with an identical
+$e^w$ pre-factor), which are easy and cheap to evaluate (see Appendix
+\ref{sec:pot_derivatives}). For instance, in the case of the direct
+force evaluation between two particles, we obtain
 \begin{align}
-  |\mathbf{f}_s(r)| &=
-  \frac{1}{r^2}\times\left[\frac{4r}{r_s}S'\left(\frac{2r}{r_s}\right) -
-    2S\left(\frac{2r}{r_s}\right) + 2\right] \nonumber \\
-  %&=
-  %\frac{1}{r^2}\left[\frac{4r}{r_s}\frac{e^{\frac{2r}{r_s}}}{(1+e^{\frac{2r}{r_s}})^2}
-  %- \frac{2e^{\frac{2r}{r_s}}}{1+e^{\frac{2r}{r_s}}} + 2\right]
+  |\mathbf{f}_s(r)| &= \left|\frac{\partial}{\partial r}\varphi_s(r)\right|
+                      = \left|\frac{\partial}{\partial r}\left(\frac{1}{r} \chi(r, r_s)\right) \right|\nonumber \\
+  &=  \frac{1}{r^2}\times\left[-\frac{4r}{r_s}\sigma'\left(\frac{2r}{r_s}\right) -
+    2\sigma\left(\frac{2r}{r_s}\right) + 2\right] \nonumber \\
   &=
-  \frac{1}{r^2}\times 2 \left[x\alpha(x) - x\alpha(x)^2 - e^x\alpha(x) + 1\right],
+  % \frac{1}{r^2}\times 2 \left[x\alpha(x) - x\alpha(x)^2 - e^x\alpha(x) + 1\right],
+  % \frac{1}{r^2}\times 2 \left[1 - e^x\alpha(x) - xe^x\alpha^2(x)\right],
+    \frac{1}{r^2}\times 2 \left[1 - e^x\left(\alpha(x) - x\alpha(x)^2\right) \right]
 \end{align}
 with $x\equiv2r/r_s$. The truncated force is compared to the Newtonian
-force on Fig.~\ref{fig:fmm:force_short}. At distance $r<r_s/10$, the
+force and to the \gadget truncated forces\footnote{For completeness,
+  the \gadget expression for the norm of the truncated forces is:
+  $|\mathbf{f}_s(r)| = \frac{1}{r^2} \times
+  \left[\mathrm{erfc}\left(\frac{1}{2}\frac{r}{r_s}\right) +
+    \frac{1}{\sqrt{\upi}}\frac{r}{r_s}\exp\left(-\frac{1}{4}\frac{r^2}{r_s^2}\right)\right]$.}
+on Fig.~\ref{fig:fmm:force_short}. At distance $r<r_s/10$, the
 truncation term is negligibly close to one and the truncated forces
 can be replaced by their Newtonian equivalent. We use this
 optimization in \swift and only compute truncated forces between pairs
 of particles that are in tree-leaves larger than $1/10$ of the mesh
-size or between two tree-leaves distant by more than that amount.
+size or between
+two tree-leaves distant by more than that amount.\\
+
+MORE WORDS HERE.\\
 
 The truncation function in Fourier space reads
 
@@ -67,9 +78,9 @@ The truncation function in Fourier space reads
 
 \begin{figure}
 \includegraphics[width=\columnwidth]{force_short.pdf}
-\caption{used in \swift (green line) and \gadget
-  (yellow line) alongside the full Newtonian force term (blue dasheed
-  line). The green dash-dotted line corresponds to the same
+\caption{Norm of the truncated forces used in \swift (green line) and
+  \gadget (yellow line) alongside the full Newtonian force term (blue
+  dasheed line). The green dash-dotted line corresponds to the same
   trunctation function where the exponential in the sigmoid is
   replaced by a sixth order Taylor expansion. At $r<r_s/10$, the
   truncated forces becomes almost equal to the Newtonian ones and can
diff --git a/theory/Multipoles/plot_derivatives.py b/theory/Multipoles/plot_derivatives.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd086608c1a8bd8874eb147cd3b42b6485468736
--- /dev/null
+++ b/theory/Multipoles/plot_derivatives.py
@@ -0,0 +1,175 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016  Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+import matplotlib
+matplotlib.use("Agg")
+from pylab import *
+from scipy import integrate
+from scipy.optimize import curve_fit
+from scipy.optimize import fsolve
+from matplotlib.font_manager import FontProperties
+import numpy
+import math
+
+params = {'axes.labelsize': 9,
+'axes.titlesize': 10,
+'font.size': 10,
+'legend.fontsize': 10,
+'xtick.labelsize': 8,
+'ytick.labelsize': 8,
+'text.usetex': True,
+'figure.figsize' : (3.15,3.15),
+'figure.subplot.left'    : 0.12,
+'figure.subplot.right'   : 0.99  ,
+'figure.subplot.bottom'  : 0.065  ,
+'figure.subplot.top'     : 0.99  ,
+'figure.subplot.wspace'  : 0.  ,
+'figure.subplot.hspace'  : 0.  ,
+'lines.markersize' : 6,
+'lines.linewidth' : 3.,
+'text.latex.unicode': True
+}
+rcParams.update(params)
+rc('font',**{'family':'sans-serif','sans-serif':['Times']})
+
+# Parameters
+r_min = 0.
+r_max = 10.
+r_s = 1.7
+
+# Radius
+r = linspace(r_min, r_max, 401)
+w = 2 * r / r_s
+
+# Powers of alpha ####################################################
+alpha = 1. / (1. + exp(w))
+alpha2 = alpha**2
+alpha3 = alpha**3
+alpha4 = alpha**4
+alpha5 = alpha**5
+alpha6 = alpha**6
+
+figure()
+plot(w, alpha, label="$\\alpha^1$")
+plot(w, alpha2, label="$\\alpha^2$")
+plot(w, alpha3, label="$\\alpha^3$")
+plot(w, alpha4, label="$\\alpha^4$")
+plot(w, alpha5, label="$\\alpha^5$")
+plot(w, alpha6, label="$\\alpha^6$")
+
+xlabel("w", labelpad=-4.)
+ylabel("$\\alpha^n(w)$", labelpad=-4.)
+
+xlim(0, 7.2)
+ylim(0., 0.52)
+
+legend(loc="upper right")
+
+savefig("alpha_powers.pdf")
+
+
+# Derivatives of alpha ###############################################
+alpha_1 = -alpha + alpha2
+alpha_2 = alpha - 3.* alpha2 + 2.*alpha3
+alpha_3 = -alpha + 7.*alpha2 - 12.*alpha3 + 6.*alpha4
+alpha_4 = alpha - 15. * alpha2 + 50.*alpha3 - 60.*alpha4 + 24.*alpha5
+alpha_5 = -alpha + 31. * alpha2 - 180. * alpha3 + 390.*alpha4 - 360.*alpha5 + 120. * alpha6
+
+
+figure()
+plot(w, alpha, label="$\\alpha^{(0)}$")
+plot(w, alpha_1, label="$\\alpha^{(1)}$")
+plot(w, alpha_2, label="$\\alpha^{(2)}$")
+plot(w, alpha_3, label="$\\alpha^{(3)}$")
+plot(w, alpha_4, label="$\\alpha^{(4)}$")
+plot(w, alpha_5, label="$\\alpha^{(5)}$")
+
+xlabel("w", labelpad=-4.)
+ylabel("$\\alpha^{(n)}(w)$", labelpad=-5.)
+
+xlim(0, 7.2)
+ylim(-0.26, 0.16)
+
+legend(loc="lower right")
+
+savefig("alpha_derivatives.pdf")
+
+
+
+# Derivatives of sigma ###############################################
+sigma = exp(w) * alpha
+sigma_1 = exp(w) * alpha2
+sigma_2 = exp(w) * (2*alpha3 - alpha2)
+sigma_3 = exp(w) * (6*alpha4 - 6*alpha3 + alpha2)
+sigma_4 = exp(w) * (24*alpha5 -36*alpha4 + 14*alpha3 - alpha2)
+sigma_5 = exp(w) * (120*alpha6 -240*alpha5 + 150*alpha4 - 30*alpha3 + alpha2)
+
+
+figure()
+plot(w, sigma, label="$\\sigma^{(0)}$")
+plot(w, sigma_1, label="$\\sigma^{(1)}$")
+plot(w, sigma_2, label="$\\sigma^{(2)}$")
+plot(w, sigma_3, label="$\\sigma^{(3)}$")
+plot(w, sigma_4, label="$\\sigma^{(4)}$")
+plot(w, sigma_5, label="$\\sigma^{(5)}$")
+
+xlabel("w", labelpad=-4.)
+ylabel("$\\sigma^{(n)}(w)$", labelpad=-5.)
+
+xlim(0, 7.2)
+ylim(-0.22, 1.02)
+
+legend(loc="center right")
+
+savefig("sigma_derivatives.pdf")
+
+
+
+# Derivatives of chi ###############################################
+c1 = 2 / r_s
+c2 = (2 / r_s)**2
+c3 = (2 / r_s)**3
+c4 = (2 / r_s)**4
+c5 = (2 / r_s)**5
+
+chi = 2 - 2 * exp(w) * alpha
+chi_1 = -2 * c1 * exp(w) * alpha2
+chi_2 = -2 * c2 * exp(w) * (2*alpha3 - alpha2)
+chi_3 = -2 * c3 * exp(w) * (6*alpha4 - 6*alpha3 + alpha2)
+chi_4 = -2 * c4 * exp(w) * (24*alpha5 - 36*alpha4 + 14*alpha3 - alpha2)
+chi_5 = -2 * c5 * exp(w) * (120*alpha6 - 240*alpha5 + 150*alpha4 - 30*alpha3 + alpha2)
+
+figure()
+plot(r, chi, label="$\\chi^{(0)}$")
+plot(r, chi_1, label="$\\chi^{(1)}$")
+plot(r, chi_2, label="$\\chi^{(2)}$")
+plot(r, chi_3, label="$\\chi^{(3)}$")
+plot(r, chi_4, label="$\\chi^{(4)}$")
+plot(r, chi_5, label="$\\chi^{(5)}$")
+
+plot([r_s, r_s], [-10, 10], 'k--', lw=1)
+
+xlabel("r", labelpad=-4.)
+ylabel("$\\chi^{(n)}(r,r_s)$", labelpad=-5.)
+
+xlim(0, 7.2)
+ylim(-1.52, 1.02)
+
+legend(loc="lower right")
+
+savefig("chi_derivatives.pdf")
diff --git a/theory/Multipoles/plot_mesh.py b/theory/Multipoles/plot_mesh.py
index 2468625d1408556f4f5fb00db17d5a331becdac4..64f88c4e4ee751e1df9654de9fd7d10c6f85c828 100644
--- a/theory/Multipoles/plot_mesh.py
+++ b/theory/Multipoles/plot_mesh.py
@@ -35,9 +35,9 @@ params = {'axes.labelsize': 9,
 'ytick.labelsize': 8,
 'text.usetex': True,
 'figure.figsize' : (3.15,3.15),
-'figure.subplot.left'    : 0.12,
+'figure.subplot.left'    : 0.14,
 'figure.subplot.right'   : 0.99  ,
-'figure.subplot.bottom'  : 0.09  ,
+'figure.subplot.bottom'  : 0.1  ,
 'figure.subplot.top'     : 0.99  ,
 'figure.subplot.wspace'  : 0.  ,
 'figure.subplot.hspace'  : 0.  ,
@@ -111,6 +111,9 @@ ylim(-1.1, 1.1)
 xlim(-4.1, 4.1)
 savefig("temp.pdf")
 
+def alpha(x):
+    return 1. / (1. + exp(x))
+
 # Correction in real space
 corr_short_gadget2 = special.erf(r / (2.*r_s))
 corr_short_swift = swift_corr(r / (2.*r_s))
@@ -119,6 +122,13 @@ eta_short_gadget2 = special.erfc(r / (2.*r_s)) + (r / (r_s * math.sqrt(math.pi))
 eta_short_swift = 4. * (r / r_s) * d_sigmoid(2. * r / r_s) - 2. * sigmoid(2 * r / r_s) + 2.
 eta_short_swift2 = 4. * (r / r_s) * my_d_sigmoid(2. * r / r_s) - 2. * my_sigmoid(2 * r / r_s) + 2.
 
+#x = 2. * r / r_s
+#force_corr = 2. * (1. - exp(x) * (alpha(x) - x * alpha(x)**2))
+#force_corr = 2. * (1.- x*exp(x)*alpha(x)**2 - exp(x)*alpha(x))
+#force_corr = 2. * (x*alpha(x) - x*alpha(x)**2 -exp(x)*alpha(x) + 1)
+#force_corr = abs(2 * (1. - exp(x) * alpha(x) + x * exp(2*x)*alpha(x)**2 - x*exp(x)*alpha(x)))
+#force_corr = abs(force_corr)
+
 # Corection in Fourier space
 corr_long_gadget2 = exp(-k**2*r_s**2)
 corr_long_swift = math.pi * k * r_s * csch(0.5 * math.pi * r_s * k) / 2.
@@ -148,25 +158,25 @@ subplot(311, xscale="log", yscale="log")
 plot(r_rs, phi_newton, '--', lw=1.4, label="${\\rm Newtonian}$", color=colors[0])
 plot(r_rs, phi_short_gadget2, '-', lw=1.4, label="${\\rm Gadget}$", color=colors[2])
 plot(r_rs, phi_short_swift, '-', lw=1.4, label="${\\rm SWIFT}$", color=colors[3])
-plot(r_rs, phi_short_swift2, '-.', lw=1.4, color=colors[3])
-plot([1., 1.], [1e-5, 1e5], 'k-', alpha=0.5, lw=0.5)
+plot(r_rs, phi_short_swift2, ':', lw=1.4, color=colors[3])
+plot([1., 1.], [1e-5, 1e5], 'k-.', alpha=0.5, lw=0.5)
 
 xlim(1.1*r_min/ r_s, 0.9*r_max / r_s)
 ylim(1.1/r_max, 0.9/r_min)
 ylabel("$\\varphi_s(r)$", labelpad=-3)
 
-legend(loc="upper right", frameon=True, handletextpad=0.1, handlelength=3.2, fontsize=8)
+legend(loc="upper right", frameon=True, handletextpad=0.3, handlelength=1.6, fontsize=8)
 
 # Correction
 subplot(312, xscale="log", yscale="log")
 plot(r_rs, np.ones(np.size(r)), '--', lw=1.4, color=colors[0])
 plot(r_rs, 1. - corr_short_gadget2, '-', lw=1.4, color=colors[2])
 plot(r_rs, 1. - corr_short_swift, '-', lw=1.4, color=colors[3])
-plot(r_rs, 1. - corr_short_swift2, '-.', lw=1.4, color=colors[3])
-plot(r_rs, np.ones(np.size(r))*0.01, 'k:', alpha=0.5, lw=0.5)
-plot([1., 1.], [-1e5, 1e5], 'k-', alpha=0.5, lw=0.5)
+plot(r_rs, 1. - corr_short_swift2, ':', lw=1.4, color=colors[3])
+plot(r_rs, np.ones(np.size(r))*0.01, 'k-.', alpha=0.5, lw=0.5)
+plot([1., 1.], [-1e5, 1e5], 'k-.', alpha=0.5, lw=0.5)
 plot([-1, -1], [-1, -1], 'k-', lw=1.2, label="${\\textrm{Exact}~e^x}$")
-plot([-1, -1], [-1, -1], 'k-.', lw=1.2, label="${6^\\textrm{th}~\\textrm{order~series}~e^x}$")
+plot([-1, -1], [-1, -1], 'k:', lw=1.2, label="${6^\\textrm{th}~\\textrm{order~series}~e^x}$")
 
 yticks([1e-2, 1e-1, 1], ["$0.01$", "$0.1$", "$1$"])
 xlim(1.1*r_min/r_s, 0.9*r_max/r_s)
@@ -174,17 +184,17 @@ ylim(3e-3, 1.5)
 #ylabel("$\\chi_s(r)$", labelpad=-3)
 ylabel("$\\varphi_s(r) \\times r$", labelpad=-2)
 
-legend(loc="center left", frameon=False, handletextpad=0.1, handlelength=2.2, fontsize=7)
+legend(loc="center left", frameon=False, handletextpad=0.3, handlelength=1.6, fontsize=7)
 
 # 1 - Correction
 subplot(313, xscale="log", yscale="log")
 plot(r_rs, corr_short_gadget2, '-', lw=1.4, color=colors[2])
 plot(r_rs, corr_short_swift, '-', lw=1.4, color=colors[3])
-plot(r_rs, corr_short_swift2, '-.', lw=1.4, color=colors[3])
+plot(r_rs, corr_short_swift2, ':', lw=1.4, color=colors[3])
 
-plot([1., 1.], [1e-5, 1e5], 'k-', alpha=0.5, lw=0.5)
-plot(r_rs, np.ones(np.size(r)), 'k:', alpha=0.5, lw=0.5)
-plot(r_rs, np.ones(np.size(r))*0.01, 'k:', alpha=0.5, lw=0.5)
+plot([1., 1.], [1e-5, 1e5], 'k-.', alpha=0.5, lw=0.5)
+plot(r_rs, np.ones(np.size(r)), 'k-.', alpha=0.5, lw=0.5)
+plot(r_rs, np.ones(np.size(r))*0.01, 'k-.', alpha=0.5, lw=0.5)
 
 xlim(1.1*r_min/r_s, 0.9*r_max/r_s)
 ylim(3e-3, 1.5)
@@ -205,48 +215,47 @@ subplot(311, xscale="log", yscale="log")
 plot(r_rs, force_newton, '--', lw=1.4, label="${\\rm Newtonian}$", color=colors[0])
 plot(r_rs, force_short_gadget2, '-', lw=1.4, label="${\\rm Gadget}$", color=colors[2])
 plot(r_rs, force_short_swift, '-', lw=1.4, label="${\\rm SWIFT}$", color=colors[3])
-plot(r_rs, force_short_swift2, '-.', lw=1.4, color=colors[3])
-plot([1., 1.], [1e-5, 1e5], 'k-', alpha=0.5, lw=0.5)
+#plot(r_rs, (1./r**2) * force_corr, '-', lw=1.2, color='r')
+plot(r_rs, force_short_swift2, ':', lw=1.4, color=colors[3])
+plot([1., 1.], [1e-5, 1e5], 'k-.', alpha=0.5, lw=0.5)
 
 xlim(1.1*r_min/ r_s, 0.9*r_max / r_s)
 ylim(1.1/r_max**2, 0.9/r_min**2)
 ylabel("$|\\mathbf{f}_s(r)|$", labelpad=-3)
 yticks([1e-4, 1e-2, 1e0, 1e2], ["$10^{-4}$", "$10^{-2}$", "$10^{0}$", "$10^{2}$"])
 
-legend(loc="upper right", frameon=True, handletextpad=0.1, handlelength=3.2, fontsize=8)
+legend(loc="upper right", frameon=True, handletextpad=0.3, handlelength=1.6, fontsize=8)
 
 # Correction
 subplot(312, xscale="log", yscale="log")
 plot(r_rs, np.ones(np.size(r)), '--', lw=1.4, color=colors[0])
 plot(r_rs, eta_short_gadget2, '-', lw=1.4, color=colors[2])
 plot(r_rs, eta_short_swift, '-', lw=1.4, color=colors[3])
-plot(r_rs, eta_short_swift2, '-.', lw=1.4, color=colors[3])
-plot(r_rs, np.ones(np.size(r))*0.01, 'k:', alpha=0.5, lw=0.5)
-plot([1., 1.], [-1e5, 1e5], 'k-', alpha=0.5, lw=0.5)
+plot(r_rs, eta_short_swift2, ':', lw=1.4, color=colors[3])
+plot(r_rs, np.ones(np.size(r))*0.01, 'k-.', alpha=0.5, lw=0.5)
+plot([1., 1.], [-1e5, 1e5], 'k-.', alpha=0.5, lw=0.5)
 plot([-1, -1], [-1, -1], 'k-', lw=1.2, label="${\\textrm{Exact}~e^x}$")
-plot([-1, -1], [-1, -1], 'k-.', lw=1.2, label="${6^\\textrm{th}~\\textrm{order~series}~e^x}$")
+plot([-1, -1], [-1, -1], 'k:', lw=1.2, label="${6^\\textrm{th}~\\textrm{order~series}~e^x}$")
 
 yticks([1e-2, 1e-1, 1], ["$0.01$", "$0.1$", "$1$"])
 xlim(1.1*r_min/r_s, 0.9*r_max/r_s)
 ylim(3e-3, 1.5)
-#ylabel("$\\eta_s(r)$", labelpad=-3)
 ylabel("$|\\mathbf{f}_s(r)|\\times r^2$", labelpad=-2)
 
-legend(loc="center left", frameon=False, handletextpad=0.1, handlelength=2.2, fontsize=7)
+legend(loc="center left", frameon=False, handletextpad=0.3, handlelength=1.6, fontsize=7)
 
 # 1 - Correction
 subplot(313, xscale="log", yscale="log")
 plot(r_rs, 1. - eta_short_gadget2, '-', lw=1.4, color=colors[2])
 plot(r_rs, 1. - eta_short_swift, '-', lw=1.4, color=colors[3])
-plot(r_rs, 1. - eta_short_swift2, '-.', lw=1.4, color=colors[3])
+plot(r_rs, 1. - eta_short_swift2, ':', lw=1.4, color=colors[3])
 
-plot([1., 1.], [1e-5, 1e5], 'k-', alpha=0.5, lw=0.5)
-plot(r_rs, np.ones(np.size(r)), 'k:', alpha=0.5, lw=0.5)
-plot(r_rs, np.ones(np.size(r))*0.01, 'k:', alpha=0.5, lw=0.5)
+plot([1., 1.], [1e-5, 1e5], 'k-.', alpha=0.5, lw=0.5)
+plot(r_rs, np.ones(np.size(r)), 'k-.', alpha=0.5, lw=0.5)
+plot(r_rs, np.ones(np.size(r))*0.01, 'k-.', alpha=0.5, lw=0.5)
 
 xlim(1.1*r_min/r_s, 0.9*r_max/r_s)
 ylim(3e-3, 1.5)
-#ylabel("$1 - \\eta_s(r)$", labelpad=-2)
 ylabel("$1 - |\\mathbf{f}_s(r)|\\times r^2$", labelpad=-3)
 yticks([1e-2, 1e-1, 1], ["$0.01$", "$0.1$", "$1$"])
 xlabel("$r / r_s$", labelpad=1)
@@ -262,9 +271,9 @@ subplot(311, xscale="log", yscale="log")
 plot(k_rs, phit_newton, '--', lw=1.4, label="${\\rm Newtonian}$", color=colors[0])
 plot(k_rs, phit_long_gadget2, '-', lw=1.4, label="${\\rm Gadget}$", color=colors[2])
 plot(k_rs, phit_long_swift, '-', lw=1.4, label="${\\rm SWIFT}$", color=colors[3])
-plot([1., 1.], [1e-5, 1e5], 'k-', alpha=0.5, lw=0.5)
+plot([1., 1.], [1e-5, 1e5], 'k-.', alpha=0.5, lw=0.5)
 
-legend(loc="lower left", frameon=True, handletextpad=0.1, handlelength=3.2, fontsize=8)
+legend(loc="lower left", frameon=True, handletextpad=0.3, handlelength=1.6, fontsize=8)
 
 xlim(1.1*r_min/ r_s, 0.9*r_max / r_s)
 ylim(1.1/r_max**2, 0.9/r_min**2)
@@ -277,8 +286,8 @@ subplot(312, xscale="log", yscale="log")
 plot(k_rs, phit_newton * k**2, '--', lw=1.4, label="${\\rm Newtonian}$", color=colors[0])
 plot(k_rs, phit_long_gadget2 * k**2, '-', lw=1.4, label="${\\rm Gadget}$", color=colors[2])
 plot(k_rs, phit_long_swift * k**2, '-', lw=1.4, label="${\\rm SWIFT}$", color=colors[3])
-plot([1., 1.], [1e-5, 1e5], 'k-', alpha=0.5, lw=0.5)
-plot(r_rs, np.ones(np.size(r))*0.01, 'k:', alpha=0.5, lw=0.5)
+plot([1., 1.], [1e-5, 1e5], 'k-.', alpha=0.5, lw=0.5)
+plot(r_rs, np.ones(np.size(r))*0.01, 'k-.', alpha=0.5, lw=0.5)
 
 xlim(1.1*r_min/ r_s, 0.9*r_max / r_s)
 ylim(3e-3, 1.5)
@@ -290,8 +299,8 @@ subplot(313, xscale="log", yscale="log")
 plot(k_rs, 1. - phit_long_gadget2 * k**2, '-', lw=1.4, label="${\\rm Gadget}$", color=colors[2])
 plot(k_rs, 1. - phit_long_swift * k**2, '-', lw=1.4, label="${\\rm SWIFT}$", color=colors[3])
 plot([1., 1.], [1e-5, 1e5], 'k-', alpha=0.5, lw=0.5)
-plot(r_rs, np.ones(np.size(r)), 'k:', alpha=0.5, lw=0.5)
-plot(r_rs, np.ones(np.size(r))*0.01, 'k:', alpha=0.5, lw=0.5)
+plot(r_rs, np.ones(np.size(r)), 'k-.', alpha=0.5, lw=0.5)
+plot(r_rs, np.ones(np.size(r))*0.01, 'k-.', alpha=0.5, lw=0.5)
 
 xlim(1.1*r_min/ r_s, 0.9*r_max / r_s)
 ylim(3e-3, 1.5)
diff --git a/theory/Multipoles/plot_potential.py b/theory/Multipoles/plot_potential.py
index 8761314572cdbda1304cdf882f920651b58be08e..56e8dc37be581e02a59db51c2579ea80c6109987 100644
--- a/theory/Multipoles/plot_potential.py
+++ b/theory/Multipoles/plot_potential.py
@@ -34,9 +34,9 @@ params = {'axes.labelsize': 9,
 'ytick.labelsize': 8,
 'text.usetex': True,
 'figure.figsize' : (3.15,3.15),
-'figure.subplot.left'    : 0.115,
+'figure.subplot.left'    : 0.14,
 'figure.subplot.right'   : 0.99  ,
-'figure.subplot.bottom'  : 0.065  ,
+'figure.subplot.bottom'  : 0.1  ,
 'figure.subplot.top'     : 0.99  ,
 'figure.subplot.wspace'  : 0.  ,
 'figure.subplot.hspace'  : 0.  ,
@@ -117,12 +117,12 @@ colors=['#4477AA', '#CC6677', '#DDCC77', '#117733']
 subplot(311)
 plot(r, W_newton, '--', lw=1.4, label="${\\rm Newtonian}$", color=colors[0])
 plot(r, W_plummer, ':', lw=1.4, label="${\\rm Plummer}$", color=colors[1])
-plot(r, W_gadget2, '-', lw=1.4, label="${\\rm Gadget}$", color=colors[2])
+plot(r, W_gadget2, '-', lw=1.4, label="${\\rm Spline}$", color=colors[2])
 plot(r, W, '-', lw=1.4, label="${\\rm SWIFT}$", color=colors[3])
 plot([epsilon, epsilon], [0, 10], 'k-', alpha=0.5, lw=0.5)
 plot([epsilon/plummer_equivalent_factor, epsilon/plummer_equivalent_factor], [0, 10], 'k-', alpha=0.5, lw=0.5)
 
-legend(loc="upper right", frameon=True, handletextpad=0.1, handlelength=3.2, fontsize=8)
+legend(loc="upper right", frameon=True, handletextpad=0.3, handlelength=1.6, fontsize=8, framealpha=1.)
 
 xlim(0,r_max_plot)
 xticks([0., 0.5, 1., 1.5, 2., 2.5], ["", "", "", "", "", ""])
@@ -135,7 +135,7 @@ ylabel("$\\rho(r)$", labelpad=2)
 subplot(312)
 plot(r, phi_newton, '--', lw=1.4, label="${\\rm Newtonian}$", color=colors[0])
 plot(r, phi_plummer, ':', lw=1.4, label="${\\rm Plummer}$", color=colors[1])
-plot(r, phi_gadget2, '-', lw=1.4, label="${\\rm Gadget}$", color=colors[2])
+plot(r, phi_gadget2, '-', lw=1.4, label="${\\rm Spline}$", color=colors[2])
 plot(r, phi, '-', lw=1.4, label="${\\rm SWIFT}$", color=colors[3])
 plot([epsilon, epsilon], [-10, 10], 'k-', alpha=0.5, lw=0.5)
 plot([epsilon/plummer_equivalent_factor, epsilon/plummer_equivalent_factor], [0, 10], 'k-', alpha=0.5, lw=0.5)
@@ -160,7 +160,7 @@ text(epsilon/plummer_equivalent_factor+0.03, 0.05, "$\\epsilon_{\\rm Plummer}$",
 
 xlim(0,r_max_plot)
 xticks([0., 0.5, 1., 1.5, 2., 2.5], ["$%.1f$"%(0./epsilon), "", "$%.1f$"%(1./epsilon), "", "$%.1f$"%(2./epsilon)])
-xlabel("$r/H$", labelpad=-7)
+xlabel("$r/H$", labelpad=-2.)
 
 ylim(0, 0.95)
 ylabel("$|\\overrightarrow{\\nabla}\\varphi(r)|$", labelpad=0)
diff --git a/theory/Multipoles/potential_derivatives.tex b/theory/Multipoles/potential_derivatives.tex
index c71cc849d5c5e0f7fbddb6b43e745838addd08cb..14ddbb9792b72e3e815f8362858785f6e889192c 100644
--- a/theory/Multipoles/potential_derivatives.tex
+++ b/theory/Multipoles/potential_derivatives.tex
@@ -4,24 +4,75 @@
 For completeness, we give here the full expression for the first few
 derivatives of the potential that are used in our FMM scheme. We use
 the notation $\mathbf{r}=(r_x, r_y, r_z)$, $r = |\mathbf{r}|$, $u=r/H$
-and $x=2r/r_s$. We also assume $H \ll r_s$. We can construct the higher order derivatives by
-successively applying the "chain rule". We show representative
-examples of the first few relevant ones here split by order. We start
-by constructing derivatives of the truncated potentials:
-
-\begin{align}
-  \alpha(x) &= \left(1 + e^x\right)^{-1}  \nonumber \\
-  \chi(r, r_s) &= 2\left(1 - e^{2r/r_s}\alpha(2r/r_s) \right) \nonumber \\
-  \chi'(r, r_s) &= \frac{2}{r_s}\left(2\alpha(x)^2 - 2\alpha(x)\right) \nonumber \\
-  \chi''(r, r_s) &= \frac{4}{r_s^2}\left(4\alpha(x)^3 - 6\alpha(x)^2 + 2\alpha(x)\right) \nonumber \\
-  \chi^{(3)}(r, r_s) &= \frac{8}{r_s^3} \left(12\alpha(x)^4 - 24\alpha(x)^3 + 14\alpha(x)^2 -2 \alpha(x)\right) \nonumber \\
-  \chi^{(4)}(r, r_s) &= \frac{16}{r_s^4} \left(48\alpha(x)^5 - 120\alpha(x)^4 + 100\alpha(x)^3 -30 \alpha(x)^2 + 2\alpha(x)\right) \nonumber \\ 
-  \chi^{(5)}(r, r_s) &= \frac{32}{r_s^5} \left(240\alpha(x)^6 - 720\alpha(x)^5 + 780\alpha(x)^4 - 360\alpha(x)^3 + 62\alpha(x)^2 - 2\alpha(x) \right) \nonumber
+and $x=2r/r_s$. We also assume $H \ll r_s$. We can construct the
+higher order derivatives by successively applying the "chain rule". We
+show representative examples of the first few relevant ones here split
+by order. We start by constructing derivatives of the truncated
+potentials. The first step is the construction of derivatives of the
+long-range truncation function. We define
+\begin{equation}
+  \alpha(w) \equiv \left(1 + e^w\right)^{-1} \nonumber
+\end{equation}
+and compute its derivatives in terms of powers of $\alpha$ (note the
+difference in notation between powers $\alpha^n$ and n-th derivatives
+$\alpha^{(n)}$)\footnote{This can be computed by \textsc{Mathematica}
+  using the expression \texttt{Apart[D[1/(1 + Exp[w]), {w, n}]]} for
+  the n-th derivative.}:
+\begin{align}
+  \alpha^{(0)}(w) &= +\alpha(w), \nonumber\\
+  \alpha^{(1)}(w) &= -\alpha(w) + \alpha^2(w),  \nonumber\\
+  \alpha^{(2)}(w) &= +\alpha(w) - 3\alpha^2(w) + 2\alpha^3(w),  \nonumber\\
+  \alpha^{(3)}(w) &= -\alpha(w) + 7\alpha^2(w) - 12\alpha^3(w) + 6\alpha^4(w),   \nonumber\\
+  \alpha^{(4)}(w) &= +\alpha(w) - 15\alpha^2(w) + 50\alpha^3(w) - 60\alpha^4(w) + 24\alpha^5(w),    \nonumber\\
+  \alpha^{(5)}(w) &= -\alpha(w) + 31\alpha^2(w) -180\alpha^3(w) + 390\alpha^4(w) -360\alpha^5(w) + 120\alpha^6(w).\nonumber                                      
+\end{align}
+We can then construct our sigmoid $\sigma(w) \equiv e^w\alpha(w)$ and
+its derivatives, again in terms of powers of $\alpha(w)$ only:
+\begin{align}
+  \sigma^{(0)}(w) &= e^w\alpha(w), \nonumber\\
+  \sigma^{(1)}(w) &= e^w\left(\alpha^{(1)}(w) + \alpha(w) \right) \nonumber\\
+                  &= e^w \alpha^2(w), \nonumber \\
+  \sigma^{(2)}(w) &= e^w\left(\alpha(w) +2\alpha^{(1)}(w) + \alpha^{(2)}(w)  \right) \nonumber\\
+                  &= e^w \left(2\alpha^3(w) - \alpha^2(w)\right), \nonumber \\
+  \sigma^{(3)}(w) &= e^w\left(\alpha(w) + 3\alpha^{(1)}(w) + 3\alpha^{(2)}(w) + \alpha^{(3)}(w)  \right) \nonumber\\
+                  &= e^w \left(6\alpha^4(w) - 6\alpha^3(w) + \alpha^2(w)\right), \nonumber \\
+  \sigma^{(4)}(w) &= e^w\left(\alpha(w) + 4\alpha^{(1)}(w) + 6\alpha^{(2)}(w) + 4\alpha^{(3)}(w) + \alpha^{(4)}(w)  \right) \nonumber\\
+                  &= e^w \left(24\alpha^5(w) - 36\alpha^4(w) + 14\alpha^3(w) - \alpha^2(w)\right), \nonumber \\
+  \sigma^{(5)}(w) &= e^w\left(\alpha(w) + 5\alpha^{(1)}(w) + 10\alpha^{(2)}(w) + 10\alpha^{(3)}(w) + 5\alpha^{(4)}(w) + \alpha^{(5)}(w)  \right) \nonumber\\
+                  &= e^w \left(120\alpha^6(w) - 240\alpha^5(w) + 150\alpha^4(w) - 30\alpha^3(w) + \alpha^2(w)\right). \nonumber 
+\end{align}
+We can finally construct our long-range truncation function
+$\chi(r,r_s) \equiv 2 - 2\sigma(2r/r_s)$ and its derivatives
+\begin{align}
+  \chi(r,r_s) &= 2 - 2e^{2r/r_s} \alpha(2r/r_s), \nonumber \\
+  \frac{\partial}{\partial r} \chi(r, r_s) &= -2 \left(\frac{2}{r_s}\right)^1 \sigma^{(1)}\left(\frac{2r}{r_s}\right) = -2 \left(\frac{2}{r_s}\right)^1 e^{\frac{2r}{r_s}} \alpha^2 \left(\frac{2r}{r_s}\right), \nonumber\\
+  \frac{\partial^2}{\partial r^2} \chi(r, r_s) &= -2 \left(\frac{2}{r_s}\right)^2 \sigma^{(2)}\left(\frac{2r}{r_s}\right) = -2 \left(\frac{2}{r_s}\right)^2  e^{\frac{2r}{r_s}} \left[2\alpha^3 \left(\frac{2r}{r_s}\right) - \alpha^2 \left(\frac{2r}{r_s}\right) \right], \nonumber\\
+  \frac{\partial^3}{\partial r^3} \chi(r, r_s) &= -2 \left(\frac{2}{r_s}\right)^3 \sigma^{(3)}\left(\frac{2r}{r_s}\right) = -2 \left(\frac{2}{r_s}\right)^3  e^{\frac{2r}{r_s}} \left[6\alpha^4 \left(\frac{2r}{r_s}\right) - 6\alpha^3 \left(\frac{2r}{r_s}\right) + \alpha^2 \left(\frac{2r}{r_s}\right) \right],\nonumber \\
+  \frac{\partial^4}{\partial r^4} \chi(r, r_s) &= -2 \left(\frac{2}{r_s}\right)^4 \sigma^{(4)}\left(\frac{2r}{r_s}\right) = -2 \left(\frac{2}{r_s}\right)^4  e^{\frac{2r}{r_s}} \left[24\alpha^5 \left(\frac{2r}{r_s}\right) - 36\alpha^4 \left(\frac{2r}{r_s}\right) + 14\alpha^3 \left(\frac{2r}{r_s}\right) - \alpha^2 \left(\frac{2r}{r_s}\right) \right],\nonumber \\
+  \frac{\partial^5}{\partial r^5} \chi(r, r_s) &= -2 \left(\frac{2}{r_s}\right)^5 \sigma^{(5)}\left(\frac{2r}{r_s}\right) = -2 \left(\frac{2}{r_s}\right)^5  e^{\frac{2r}{r_s}} \left[120\alpha^6 \left(\frac{2r}{r_s}\right) - 240\alpha^5 \left(\frac{2r}{r_s}\right) + 150\alpha^4 \left(\frac{2r}{r_s}\right) - 30\alpha^3 \left(\frac{2r}{r_s}\right) + \alpha^2 \left(\frac{2r}{r_s}\right) \right].\nonumber
 \end{align}
 In the Newtonian limit ($r_s\rightarrow\infty$) the first expression
 reduces to $\chi(r,r_s) = 1$ whilst all higher-order derivatives
-vanish. We can now construct common quantities that appear in
-derivatives of multiple orders:
+vanish. In the limit $r\rightarrow\infty$ (and $r_s < \infty$), all terms vanish. All these
+derivatives can be computed easily as they are simple polynomials of
+$\alpha(2r/r_s)$. They involve one exponential and one inversion for
+the initial calculation of $\alpha$ and all the other terms can be
+obtained very eeficiently on modern architectures as they only involve
+\emph{fused-multiple-add} operations.We can now construct common
+quantities that appear in derivatives of multiple orders of the
+truncated an softened gravity field $\varphi (\mathbf{r}, r_s, H)
+\equiv \frac{1}{r}\chi(r,r_s)$:
+
+
+% \begin{align}
+%   \alpha(x) &= \left(1 + e^x\right)^{-1}  \nonumber \\
+%   \chi(r, r_s) &= 2\left(1 - e^{2r/r_s}\alpha(2r/r_s) \right) \nonumber \\
+%   \chi'(r, r_s) &= \frac{2}{r_s}\left(2\alpha(x)^2 - 2\alpha(x)\right) \nonumber \\
+%   \chi''(r, r_s) &= \frac{4}{r_s^2}\left(4\alpha(x)^3 - 6\alpha(x)^2 + 2\alpha(x)\right) \nonumber \\
+%   \chi^{(3)}(r, r_s) &= \frac{8}{r_s^3} \left(12\alpha(x)^4 - 24\alpha(x)^3 + 14\alpha(x)^2 -2 \alpha(x)\right) \nonumber \\
+%   \chi^{(4)}(r, r_s) &= \frac{16}{r_s^4} \left(48\alpha(x)^5 - 120\alpha(x)^4 + 100\alpha(x)^3 -30 \alpha(x)^2 + 2\alpha(x)\right) \nonumber \\ 
+%   \chi^{(5)}(r, r_s) &= \frac{32}{r_s^5} \left(240\alpha(x)^6 - 720\alpha(x)^5 + 780\alpha(x)^4 - 360\alpha(x)^3 + 62\alpha(x)^2 - 2\alpha(x) \right) \nonumber
+% \end{align}
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 \begin{align}
diff --git a/theory/Multipoles/potential_softening.tex b/theory/Multipoles/potential_softening.tex
index aa9ee12340a3492a19dcf9048548952ef7e141e1..4041bdad3cdfd470c90a46739f487caa4787892d 100644
--- a/theory/Multipoles/potential_softening.tex
+++ b/theory/Multipoles/potential_softening.tex
@@ -11,9 +11,9 @@ is cheaper to compute and has a very similar overall shape. The C2
 kernel has the advantage of being branch-free leading to an expression
 which is faster to evaluate using vector units available on modern
 architectures; it also does not require any divisions to evaluate the
-softened forces. We set $\tilde\delta(\mathbf{x}) =
-\rho(|\mathbf{x}|) = W(|\mathbf{x}|, 3\epsilon_{\rm Plummer})$, with
-$W(r, H)$ given by
+softened forces. We set
+$\tilde\delta(\mathbf{x}) = \rho(|\mathbf{x}|) = W(|\mathbf{x}|,
+3\epsilon_{\rm Plummer})$, with $W(r, H)$ given by
 
 \begin{align}
 W(r,H) &= \frac{21}{2\pi H^3} \times \nonumber \\
@@ -35,24 +35,24 @@ and $u = r/H$. The potential $\varphi(r,H)$ corresponding to this density distri
 \end{align}
 
 These choices, lead to a potential at $|\mathbf{x}| = 0$ equal to the
-central potential of a Plummer sphere (i.e. $1/\epsilon_{\rm
-Plummer}$)\footnote{Note the factor $3$ in the definition of
-$\rho(|\mathbf{x}|)$ which differs from the factor $2.8$ used
-in \textsc{Gadget} as a consequence of the change of kernel
-shape.}. The softened density profile, its corresponding potential and
-resulting forces are shown on Fig. \ref{fig:fmm:softening} (for
-details see Sec. 2 of~\cite{Price2007}).
+central potential of a Plummer sphere (i.e.
+$\varphi(0) = 1/\epsilon_{\rm Plummer}$)\footnote{Note the factor $3$
+  in the definition of $\rho(|\mathbf{x}|)$ which differs from the
+  factor $2.8$ used in \textsc{Gadget} as a consequence of the change
+  of kernel shape.}. The softened density profile, its corresponding
+potential and resulting forces are shown on
+Fig. \ref{fig:fmm:softening} (for details of these are obtained see
+section 2 of~\cite{Price2007}). For comparison purposes, we also
+implemented the more traditional spline-kernel softening in \swift.
 
 
 \begin{figure}
 \includegraphics[width=\columnwidth]{potential.pdf}
 \caption{The density (top), potential (middle) and forces (bottom)
-  generated py a point mass in our softened gravitational scheme.
-  A Plummer-equivalent sphere is shown for comparison. The spline
-  kernel of \citet{Monaghan1985}, used in \textsc{Gadget}, is shown
-  for comparison but note that it has not been re-scaled to match the
-  Plummer-sphere potential at $r=0$.  %(for completeness, we chose
-  %$\epsilon=2$).
-  }
+  generated py a point mass in our softened gravitational scheme.  A
+  Plummer-equivalent sphere is shown for comparison. The spline kernel
+  of \citet{Monaghan1985}, used for instance in \textsc{Gadget}, is
+  shown for comparison but note that it has not been re-scaled to
+  match the Plummer-sphere potential at $r=0$.  }
 \label{fig:fmm:softening}
 \end{figure}
diff --git a/theory/Multipoles/run.sh b/theory/Multipoles/run.sh
index fc376188ad2e69d2879ce963ddc7069c736fc8b7..eaaa9bc94d78d7cb3f55d7e669314aae24306d68 100755
--- a/theory/Multipoles/run.sh
+++ b/theory/Multipoles/run.sh
@@ -9,6 +9,11 @@ then
     echo "Generating 2nd figures..."
     python plot_mesh.py
 fi
+if [ ! -e alpha_powers.pdf ]
+then
+    echo "Generating derivative figures..."
+    python plot_derivatives.py
+fi
 echo "Generating PDF..."
 pdflatex -jobname=fmm fmm_standalone.tex
 bibtex fmm.aux
diff --git a/theory/SPH/Flavours/plotSoundspeed.py b/theory/SPH/Flavours/plotSoundspeed.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a08ea08f4ad4cbecc1b6b22608de32691eac67b
--- /dev/null
+++ b/theory/SPH/Flavours/plotSoundspeed.py
@@ -0,0 +1,91 @@
+"""
+Makes a movie of the Sedov 2D data. Adapted from
+
+    KelvinHelmholtz_2D/makeMovie.py
+
+You will need to run your movie with far higher time-resolution than usual to
+get a nice movie; around 450 snapshots over 6s is required.
+
+Edit this file near the bottom with the number of snaps you have.
+
+Written by Josh Borrow (joshua.borrow@durham.ac.uk)
+"""
+
+import numpy as np
+
+if __name__ == "__main__":
+    import matplotlib
+    matplotlib.use("Agg")
+    params = {'axes.labelsize': 9,
+        'axes.titlesize': 10,
+        'font.size': 12,
+        'legend.fontsize': 12,
+        'xtick.labelsize': 9,
+        'ytick.labelsize': 9,
+        'text.usetex': True,
+        'figure.figsize' : (3.15,2.60),
+        'figure.subplot.left'    : 0.17,
+        'figure.subplot.right'   : 0.99  ,
+        'figure.subplot.bottom'  : 0.08  ,
+        'figure.subplot.top'     : 0.99  ,
+        'figure.subplot.wspace'  : 0.  ,
+        'figure.subplot.hspace'  : 0.  ,
+        'lines.markersize' : 6,
+        'lines.linewidth' : 3.,
+        'text.latex.unicode': True}
+    matplotlib.rcParams.update(params)
+    matplotlib.rc('font',**{'family':'sans-serif','sans-serif':['Times']})
+
+    from matplotlib.colors import LogNorm
+
+    import matplotlib.pyplot as plt
+
+    filename = "sedov"
+    dpi = 1024 
+
+
+    # Creation of first frame
+    fig, ax = plt.subplots(1, 1, frameon=False)
+
+    with np.load("sedov_soundspeed_ratio_data.npz") as file:
+        mesh = file.items()[0][1]
+
+    # Global variable for set_array
+    img = ax.imshow(mesh,
+        extent=[0,1,0,1],
+        animated=True,
+        interpolation="none",
+        norm=LogNorm())
+
+    circle = matplotlib.patches.Circle([0.5, 0.5],
+        radius=0.18242863869665918,
+        animated=True,
+        lw=1,
+        fill=False,
+        ec="red")
+
+    ax.add_artist(circle)
+
+    fig.colorbar(img,
+        label=r"$c_{s, {\rm smoothed}}$ / $c_{s, {\rm gas}}$",
+        pad=0.0)
+
+
+    plt.tick_params(axis='x',         
+        which='both',     
+        bottom=False,      
+        top=False,         
+        labelbottom=False) 
+    plt.tick_params(axis='y',        
+        which='both',    
+        left=False,      
+        right=False,     
+        labelleft=False) 
+
+    plt.xlim(0.2, 0.8)
+    plt.ylim(0.2, 0.8)
+
+    # Actually make the movie
+    plt.tight_layout()
+
+    plt.savefig("sedov_blast_soundspeed.pdf", dpi=300)
diff --git a/theory/SPH/Flavours/run.sh b/theory/SPH/Flavours/run.sh
index 6d0791823d93f7feb8f469747f81b24032612523..39677c961cfa6557ae0db52d04ff15e39949c80c 100755
--- a/theory/SPH/Flavours/run.sh
+++ b/theory/SPH/Flavours/run.sh
@@ -1,4 +1,6 @@
 #!/bin/bash
+python plotSoundspeed.py
+
 pdflatex -jobname=sph_flavours sph_flavours_standalone.tex
 bibtex sph_flavours.aux 
 pdflatex -jobname=sph_flavours sph_flavours_standalone.tex
diff --git a/theory/SPH/Flavours/sedov_soundspeed_ratio_data.npz b/theory/SPH/Flavours/sedov_soundspeed_ratio_data.npz
new file mode 100644
index 0000000000000000000000000000000000000000..1111a2696b76d6ea5a733b29eec47648d9c15a55
Binary files /dev/null and b/theory/SPH/Flavours/sedov_soundspeed_ratio_data.npz differ
diff --git a/theory/SPH/Flavours/sph_flavours.tex b/theory/SPH/Flavours/sph_flavours.tex
index 3c80fefb4989505b76cfaf8b38676ac4276b8da8..5d62af3aab777e66f0b33b89e861d2b21e10b38c 100644
--- a/theory/SPH/Flavours/sph_flavours.tex
+++ b/theory/SPH/Flavours/sph_flavours.tex
@@ -6,7 +6,8 @@ vector quantity $\Wij \equiv \frac{1}{2}\left(W(\vec{x}_{ij}, h_i) +
 \nabla_x W(\vec{x}_{ij},h_j)\right)$.
 
 
-%#######################################################################################################
+%##############################################################################
+
 \subsection{\MinimalSPH}
 \label{sec:sph:minimal}
 
@@ -33,7 +34,8 @@ and the derivative of its density with respect to $h$:
 
 \begin{equation}
     \label{eq:sph:minimal:rho_dh}
-  \rho_{\partial h_i} \equiv \dd{\rho}{h}(\vec{x}_i) = \sum_j m_j \dd{W}{h}(\vec{x}_{ij}
+  \rho_{\partial h_i} \equiv \dd{\rho}{h}(\vec{x}_i) = \sum_j m_j
+\dd{W}{h}(\vec{x}_{ij}
   , h_i).
 \end{equation}
 This corresponds to $x_i = \tilde{x}_i = m_i$, and $y_i =\tilde{y}_i = \rho_i$
@@ -56,8 +58,8 @@ $\rho_i$ and $u_i$:
 
 \subsubsection{Hydrodynamical accelerations (\nth{2} neighbour loop)}
 
-We can then proceed with the second loop over
-neighbours. The signal velocity $v_{{\rm sig},ij}$ between two particles is given by
+We can then proceed with the second loop over neighbours. The signal velocity
+$v_{{\rm sig},ij}$ between two particles is given by
 
 \begin{align}
   \mu_{ij} &=
@@ -91,7 +93,7 @@ and the change in internal energy,
     &+\left. \frac{1}{2}\nu_{ij}\vec{v}_{ij}\cdot\Big. \Wij\right], \nonumber
 \end{align}
 where in both cases the first line corresponds to the standard SPH
-term and the second line to the viscuous accelerations.
+term and the second line to the viscous accelerations.
 
 We also compute an estimator of the change in smoothing length to be
 used in the prediction step. This is an estimate of the local
@@ -119,13 +121,17 @@ For each particle, we compute a time-step given by the CFL condition:
   \Delta t = 2 C_{\rm CFL} \frac{H_i}{v_{{\rm sig},i}},
     \label{eq:sph:minimal:dt}
 \end{equation}
-where $C_{\rm CFL}$ is a free dimensionless parameter and $H_i = \gamma h_i$ is the
-kernel support size. Particles can then be ``kicked'' forward in time:
+where $C_{\rm CFL}$ is a free dimensionless parameter and $H_i = \gamma h_i$ is
+the kernel support size. Particles can then be ``kicked'' forward in time:
 \begin{align}
-  \vec{v}_i &\rightarrow \vec{v}_i + \frac{d\vec{v}_i}{dt} \Delta t  \label{eq:sph:minimal:kick_v}\\
-  u_i &\rightarrow u_i + \frac{du_i}{dt} \Delta t \label{eq:sph:minimal:kick_u}\\
-  P_i &\rightarrow P_{\rm eos}\left(\rho_i, u_i\right) \label{eq:sph:minimal:kick_P}, \\
-  c_i &\rightarrow c_{\rm eos}\left(\rho_i, u_i\right) \label{eq:sph:minimal:kick_c},
+  \vec{v}_i &\rightarrow \vec{v}_i + \frac{d\vec{v}_i}{dt} \Delta t 
+\label{eq:sph:minimal:kick_v}\\
+  u_i &\rightarrow u_i + \frac{du_i}{dt} \Delta t
+\label{eq:sph:minimal:kick_u}\\
+  P_i &\rightarrow P_{\rm eos}\left(\rho_i, u_i\right)
+\label{eq:sph:minimal:kick_P}, \\
+  c_i &\rightarrow c_{\rm eos}\left(\rho_i, u_i\right)
+\label{eq:sph:minimal:kick_c},
 \end{align}
 where we used the pre-defined equation of state to compute the new
 value of the pressure and sound-speed.
@@ -136,12 +142,14 @@ Inactive particles need to have their quantities predicted forward in
 time in the ``drift'' operation. We update them as follows:
 
 \begin{align}
-  \vec{x}_i &\rightarrow \vec{x}_i + \vec{v}_i \Delta t  \label{eq:sph:minimal:drift_x} \\
+  \vec{x}_i &\rightarrow \vec{x}_i + \vec{v}_i \Delta t 
+\label{eq:sph:minimal:drift_x} \\
   h_i &\rightarrow h_i \exp\left(\frac{1}{h_i} \frac{dh_i}{dt}
   \Delta t\right), \label{eq:sph:minimal:drift_h}\\
   \rho_i &\rightarrow \rho_i \exp\left(-\frac{3}{h_i} \frac{dh_i}{dt}
   \Delta t\right), \label{eq:sph:minimal:drift_rho} \\
-  P_i &\rightarrow P_{\rm eos}\left(\rho_i, u_i + \frac{du_i}{dt} \Delta t\right), \label{eq:sph:minimal:drift_P}\\
+  P_i &\rightarrow P_{\rm eos}\left(\rho_i, u_i + \frac{du_i}{dt} \Delta
+t\right), \label{eq:sph:minimal:drift_P}\\
   c_i &\rightarrow c_{\rm eos}\left(\rho_i, u_i + \frac{du_i}{dt}
   \Delta t\right) \label{eq:sph:minimal:drift_c},
 \end{align}
@@ -149,7 +157,7 @@ where, as above, the last two updated quantities are obtained using
 the pre-defined equation of state. Note that the thermal energy $u_i$
 itself is \emph{not} updated.
 
-%#######################################################################################################
+%##############################################################################
 
 \subsection{Gadget-2 SPH}
 \label{sec:sph:gadget2}
@@ -158,15 +166,16 @@ This flavour of SPH is the one implemented in the \gadget-2 code
 \citep{Springel2005}. The basic equations were derived by
 \cite{Springel2002} and also includes a \cite{Balsara1995} switch for
 the suppression of viscosity. The implementation here follows closely the
-presentation of \cite{Springel2005}. Specifically, we use their equations (5), (7),
-(8), (9), (10), (13), (14) and (17). We summarize them here for completeness.
+presentation of \cite{Springel2005}. Specifically, we use their equations (5),
+(7), (8), (9), (10), (13), (14) and (17). We summarise them here for
+completeness.
 
 \subsubsection{Density and other fluid properties (\nth{1} neighbour loop)}
 
 For a set of particles $i$ with positions $\vec{x}_i$ with velocities
 $\vec{v}_i$, masses $m_i$, entropic function per unit mass $A_i$ and
-smoothing length $h_i$, we compute the density, derivative of the density with respect
-to $h$ and the ``h-terms'' in a similar way to the minimal-SPH case
+smoothing length $h_i$, we compute the density, derivative of the density with
+respect to $h$ and the ``h-terms'' in a similar way to the minimal-SPH case
 (Eq. \ref{eq:sph:minimal:rho}, \ref{eq:sph:minimal:rho_dh} and
 \ref{eq:sph:minimal:f_i}). From these the pressure and sound-speed can
 be computed using the pre-defined equation of state:
@@ -175,13 +184,16 @@ be computed using the pre-defined equation of state:
   P_i &= P_{\rm eos}(\rho_i, A_i),   \label{eq:sph:gadget2:P}\\
   c_i &= c_{\rm eos}(\rho_i, A_i).   \label{eq:sph:gadget2:c}
 \end{align}
-We additionally compute the divergence and
-curl of the velocity field using standard SPH expressions:
+We additionally compute the divergence and curl of the velocity field using
+standard SPH expressions:
 
 \begin{align}
-  \nabla\cdot\vec{v}_i &\equiv\nabla\cdot \vec{v}(\vec{x}_i) = \frac{1}{\rho_i} \sum_j m_j
-  \vec{v}_{ij}\cdot\nabla_x W(\vec{x}_{ij}, h_i) \label{eq:sph:gadget2:div_v},\\ 
-    \nabla\times\vec{v}_i &\equiv \nabla\times \vec{v}(\vec{x}_i) = \frac{1}{\rho_i} \sum_j m_j
+  \nabla\cdot\vec{v}_i &\equiv\nabla\cdot \vec{v}(\vec{x}_i) = \frac{1}{\rho_i}
+\sum_j m_j
+  \vec{v}_{ij}\cdot\nabla_x W(\vec{x}_{ij}, h_i)
+\label{eq:sph:gadget2:div_v},\\ 
+    \nabla\times\vec{v}_i &\equiv \nabla\times \vec{v}(\vec{x}_i) =
+\frac{1}{\rho_i} \sum_j m_j
   \vec{v}_{ij}\times\nabla_x W(\vec{x}_{ij}, h_i) \label{eq:sph:gadget2:rot_v}.
 \end{align}
 These are used to construct the \cite{Balsara1995} switch $B_i$:
@@ -190,7 +202,8 @@ These are used to construct the \cite{Balsara1995} switch $B_i$:
   B_i = \frac{|\nabla\cdot\vec{v}_i|}{|\nabla\cdot\vec{v}_i| +
     |\nabla\times\vec{v}_i| + 10^{-4}c_i / h_i}, \label{eq:sph:gadget2:balsara}
 \end{equation}
-where the last term in the denominator is added to prevent numerical instabilities.
+where the last term in the denominator is added to prevent numerical
+instabilities.
 
 \subsubsection{Hydrodynamical accelerations (\nth{2} neighbour loop)}
 
@@ -199,7 +212,8 @@ case with the exception of the viscosity term which is now modified to
 include the switch. Instead of Eq. \ref{eq:sph:minimal:nu_ij}, we get:
 
 \begin{equation}
-\nu_{ij} = -\frac{1}{2}\frac{\alpha \bar B_{ij} \mu_{ij} v_{{\rm sig},ij}}{\bar\rho_{ij}},
+\nu_{ij} = -\frac{1}{2}\frac{\alpha \bar B_{ij} \mu_{ij} v_{{\rm
+sig},ij}}{\bar\rho_{ij}},
   \label{eq:sph:gadget2:nu_ij}  
 \end{equation}
 whilst equations \ref{eq:sph:minimal:v_sig},
@@ -207,8 +221,8 @@ whilst equations \ref{eq:sph:minimal:v_sig},
 \ref{eq:sph:minimal:v_sig_update} remain unchanged. The only other
 change is the equation of motion for the thermodynamic variable which
 now has to be describing the evolution of the entropic function and
-not the evolution of the thermal energy. Instead of
-eq. \ref{eq:sph:minimal:du_dt}, we have
+not the evolution of the thermal energy. Instead of eq.
+\ref{eq:sph:minimal:du_dt}, we have
 
 \begin{equation}
 \frac{dA_i}{dt} = \frac{1}{2} A_{\rm eos}\left(\rho_i, \sum_j
@@ -228,10 +242,14 @@ by an integration for the the entropy:
 
 
 \begin{align}
-  \vec{v}_i &\rightarrow \vec{v}_i + \frac{d\vec{v}_i}{dt} \Delta t  \label{eq:sph:gadget2:kick_v}\\
-  A_i &\rightarrow A_i + \frac{dA_i}{dt} \Delta t \label{eq:sph:gadget2:kick_A}\\
-  P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i\right) \label{eq:sph:gadget2:kick_P}, \\
-  c_i &\rightarrow c_{\rm eos}\left(\rho_i, A_i\right) \label{eq:sph:gadget2:kick_c},
+  \vec{v}_i &\rightarrow \vec{v}_i + \frac{d\vec{v}_i}{dt} \Delta t 
+\label{eq:sph:gadget2:kick_v}\\
+  A_i &\rightarrow A_i + \frac{dA_i}{dt} \Delta t
+\label{eq:sph:gadget2:kick_A}\\
+  P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i\right)
+\label{eq:sph:gadget2:kick_P}, \\
+  c_i &\rightarrow c_{\rm eos}\left(\rho_i, A_i\right)
+\label{eq:sph:gadget2:kick_c},
 \end{align}
 where, once again, we made use of the equation of state relating
 thermodynamical quantities.
@@ -242,12 +260,14 @@ The prediction step is also identical to the \MinimalSPH case with the
 entropic function replacing the thermal energy.
 
 \begin{align}
-  \vec{x}_i &\rightarrow \vec{x}_i + \vec{v}_i \Delta t  \label{eq:sph:gadget2:drift_x} \\
+  \vec{x}_i &\rightarrow \vec{x}_i + \vec{v}_i \Delta t 
+\label{eq:sph:gadget2:drift_x} \\
   h_i &\rightarrow h_i \exp\left(\frac{1}{h_i} \frac{dh_i}{dt}
   \Delta t\right), \label{eq:sph:gadget2:drift_h}\\
   \rho_i &\rightarrow \rho_i \exp\left(-\frac{3}{h_i} \frac{dh_i}{dt}
   \Delta t\right), \label{eq:sph:gadget2:drift_rho} \\
-  P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt} \Delta t\right), \label{eq:sph:gadget2:drift_P}\\
+  P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt} \Delta
+t\right), \label{eq:sph:gadget2:drift_P}\\
   c_i &\rightarrow c_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt}
   \Delta t\right) \label{eq:sph:gadget2:drift_c},
 \end{align}
@@ -255,8 +275,39 @@ where, as above, the last two updated quantities are obtained using
 the pre-defined equation of state. Note that the entropic function $A_i$
 itself is \emph{not} updated.
 
+\subsection{Weighted-Pressure SPH Validity}
+
+A new class of Lagrangian SPH methods were introduced to the astrophysical
+community by \citet{Hopkins2013} and \citet{Saitoh2013}. Two of these methods,
+Pressure-Entropy (used in the original ANARCHY implementation in EAGLE) and
+Pressure-Energy, are implemented for use in \swift{}. Before considering the
+use of these methods, though, it is important to pause for a moment and
+consider where it is valid to use them in a cosmological context. These methods
+(as implemented in \swift{}) are only valid for cases that use an \emph{ideal
+gas equation of state}, i.e. one in which
+\begin{equation}
+  P = (\gamma - 1) u \rho \propto \rho.
+  \nonumber
+\end{equation}
+Implementations that differ from this, such as the original ANARCHY scheme in
+EAGLE, may have some problems with energy conservation \cite[see][]{Hosono2013}
+and other properties as at their core they assume that pressure is proportional
+to the local energy density, i.e. $P \propto \rho u$. This is most easily shown
+in Pressure-Energy SPH where the weighted pressure $\bar{P}$ is written as
+\begin{equation}
+  \bar{P} = \sum_j \frac{P_j}{\rho_j} W_{ij} = \sum_j m_j (\gamma - 1) u_j
+W_{ij},
+\end{equation}
+and the right-hand side is what is actually calculated using the scheme. It is 
+clear that this does not give a valid weighted pressure for any scheme using a
+non-ideal equation of state. Fortunately, there is a general prescription for
+including non-ideal equations of state in the P-X formalisms, but this is yet
+to be implemented in \swift{} and requires an extra density loop. Attempting to
+use these weighted schemes with a non-ideal equation of state will lead to an
+incorrect calculation of both the pressure and the equation fo motion. How
+incorrect this estimate is, however, remains to be seen.
 
-%#######################################################################################################
+%##############################################################################
 
 \subsection{Pressure-Entropy SPH}
 \label{sec:sph:pe}
@@ -284,7 +335,8 @@ which enters many equations. This allows us to construct the
 entropy-weighted density $\bar\rho_i$:
 
 \begin{equation}
-  \bar\rho_i = \frac{1}{\tilde{A_i}} \sum_j m_j \tilde{A_j} W(\vec{x}_{ij}, h_i),
+  \bar\rho_i = \frac{1}{\tilde{A_i}} \sum_j m_j \tilde{A_j} W(\vec{x}_{ij},
+h_i),
   \label{eq:sph:pe:rho_bar}
 \end{equation}
 which can then be used to construct an entropy-weighted sound-speed
@@ -318,8 +370,11 @@ P_{\partial h_i}$ and $\rho_{\partial h_i}$ (eq. \ref{eq:sph:minimal:rho_dh}):
 The accelerations are given by the following term:
 
 \begin{align}
-  \frac{d\vec{v}_i}{dt} = -\sum_j m_j &\left[\frac{\bar P_i}{\bar\rho_i^2} \left(\frac{\tilde A_j}{\tilde A_i} - \frac{f_i}{\tilde A_i}\right)\nabla_x W(\vec{x}_{ij}, h_i) \right.  \nonumber \\
-  &+\frac{P_j}{\rho_j^2} \left(\frac{\tilde A_i}{\tilde A_j} - \frac{f_j}{\tilde A_j}\right)\nabla_x W(\vec{x}_{ij},h_j) \\
+  \frac{d\vec{v}_i}{dt} = -\sum_j m_j &\left[\frac{\bar P_i}{\bar\rho_i^2}
+\left(\frac{\tilde A_j}{\tilde A_i} - \frac{f_i}{\tilde A_i}\right)\nabla_x
+W(\vec{x}_{ij}, h_i) \right.  \nonumber \\
+  &+\frac{P_j}{\rho_j^2} \left(\frac{\tilde A_i}{\tilde A_j} -
+\frac{f_j}{\tilde A_j}\right)\nabla_x W(\vec{x}_{ij},h_j) \\
   &+ \left. \bigg.\nu_{ij} \Wij \right], \label{eq:sph:pe:dv_dt}
 \end{align}
 where the viscosity term $\nu_{ij}$ has been computed as in
@@ -342,9 +397,11 @@ internal energy (Eq. \ref{eq:sph:minimal:kick_u}) which gets replaced
 by an integration for the the entropy:
 
 \begin{align}
-  \vec{v}_i &\rightarrow \vec{v}_i + \frac{d\vec{v}_i}{dt} \Delta t  \label{eq:sph:pe:kick_v}\\
+  \vec{v}_i &\rightarrow \vec{v}_i + \frac{d\vec{v}_i}{dt} \Delta t 
+\label{eq:sph:pe:kick_v}\\
   A_i &\rightarrow A_i + \frac{dA_i}{dt} \Delta t \label{eq:sph:pe:kick_A}\\
-  P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i\right) \label{eq:sph:pe:kick_P}, \\
+  P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i\right)
+\label{eq:sph:pe:kick_P}, \\
   c_i &\rightarrow c_{\rm eos}\left(\rho_i,
   A_i\right) \label{eq:sph:pe:kick_c}, \\
   \tilde A_i &= A_i^{1/\gamma}
@@ -359,14 +416,16 @@ The prediction step is also identical to the \MinimalSPH case with the
 entropic function replacing the thermal energy.
 
 \begin{align}
-  \vec{x}_i &\rightarrow \vec{x}_i + \vec{v}_i \Delta t  \label{eq:sph:pe:drift_x} \\
+  \vec{x}_i &\rightarrow \vec{x}_i + \vec{v}_i \Delta t 
+\label{eq:sph:pe:drift_x} \\
   h_i &\rightarrow h_i \exp\left(\frac{1}{h_i} \frac{dh_i}{dt}
   \Delta t\right), \label{eq:sph:pe:drift_h}\\
   \rho_i &\rightarrow \rho_i \exp\left(-\frac{3}{h_i} \frac{dh_i}{dt}
   \Delta t\right), \label{eq:sph:pe:drift_rho} \\
   \tilde A_i &\rightarrow \left(A_i + \frac{dA_i}{dt}
   \Delta t\right)^{1/\gamma} \label{eq:sph:pe:drift_A_tilde}, \\
-  P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt} \Delta t\right), \label{eq:sph:pe:drift_P}\\
+  P_i &\rightarrow P_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt} \Delta
+t\right), \label{eq:sph:pe:drift_P}\\
   c_i &\rightarrow c_{\rm eos}\left(\rho_i, A_i + \frac{dA_i}{dt}
   \Delta t\right) \label{eq:sph:pe:drift_c}, 
 \end{align}
@@ -377,7 +436,160 @@ itself is \emph{not} updated.
 \subsection{Pressure-Energy SPH}
 \label{sec:sph:pu}
 
-Section 2.2.2 of \cite{Hopkins2013}.\\ \tbd
+Section 2.2.2 of \cite{Hopkins2013} describes the equations for Pressure-Energy
+(P-U) SPH; they are reproduced here with some more details.
+
+P-U SPH depends on the calculation of a smoothed pressure, and follows the
+evolution of the internal energy, as opposed to the entropy.
+
+For P-U, the following choice of parameters in the formalism of \S
+\ref{sec:derivation} provides convenient properties:
+\begin{align}
+  x_i =&~ (\gamma - 1) m_i u_i, \\
+  \tilde{x}_i =&~ 1,
+  \label{eq:sph:pu:xichoice}
+\end{align}
+leading to the following requirements to ensure correct volume elements:
+\begin{align}
+  y_i =& \sum_{j} (\gamma - 1) m_j u_j W_{ij} = \bar{P}_i,\\
+  \tilde{y}_i =& \sum_{j} W_{ij} = \bar{n}_i,
+  \label{eq:sph:pu:yichoice}
+\end{align}
+with the resulting variables representing a smoothed pressure and particle
+number density. This choice of variables leads to the following equation of
+motion:
+\begin{align}
+  \frac{\mathrm{d} \mathbf{v}_i}{\mathrm{d} t} = -\sum_j (\gamma - 1)^2 m_j u_j
+u_i
+	 &\left[\frac{f_{ij}}{\bar{P}_i} \nabla_i W_{ij}(h_i) ~+ \right. \nonumber \\
+	       &\frac{f_{ji}}{\bar{P}_j} \nabla_i W_{ji}(h_j) ~+ \nonumber \\
+	       & \left.\nu_{ij}\bar{\nabla_i W_{ij}}\right]~.
+  \label{eq:sph:pu:eom}
+\end{align}
+which includes the Monaghan artificial viscosity term and Balsara switch in
+the final term.
+
+The $h$-terms are given as
+\begin{align}
+  f_{ij} = 1 - \left[\frac{h_i}{n_d (\gamma - 1) \bar{n}_i \left\{m_j
+u_j\right\}}
+		   \frac{\partial \bar{P}_i}{\partial h_i} \right]
+		   \left( 1 + \frac{h_i}{n_d \bar{n}_i}
+		   \frac{\partial \bar{n}_i}{\partial h_i} \right)^{-1}
+  \label{eq:sph:pu:fij}
+\end{align}
+with $n_d$ the number of dimensions. In practice, the majority of $f_{ij}$ is
+precomputed in {\tt hydro\_prepare\_force} as only the curly-bracketed term
+depends on the $j$ particle. This cuts out on the majority of operations,
+including expensive divisions.
+
+In a similar fashion to \MinimalSPH, the internal energy must also be
+evolved. Following \cite{Hopkins2013}, this is calculated as
+\begin{align}
+  \frac{\mathrm{d}u_i}{\mathrm{d}t} = \sum_j (\gamma - 1)^2 m_j u_j u_i
+	\frac{f_{ij}}{\bar{P}_i}(\mathbf{v}_i - \mathbf{v}_j) \cdot
+	\nabla_i W_{ij}(h_i)~.
+  \label{eq:sph:pu:dudt}
+\end{align}
+The sound-speed in P-U requires some consideration. To see what the `correct'
+sound-speed
+is, it is worth looking at the equation of motion (Equation
+\ref{eq:sph:pu:eom}) in
+contrast with that of the EoM for Density-Energy SPH (Equation
+\ref{eq:sph:minimal:dv_dt})
+to see what terms are applicable. For Density-Energy SPH, we see that
+\begin{align}
+  \frac{\mathrm{d}\mathbf{v}_i}{\mathrm{d} t} \sim \frac{c_{s, i}}{\rho_i}
+\nabla_i W_{ij},
+  \nonumber
+\end{align}
+and for Pressure-Energy SPH
+\begin{align}
+  \frac{\mathrm{d}\mathbf{v}_i}{\mathrm{d} t} \sim (\gamma - 1)^2
+  \frac{u_i u_j}{\bar{P}_i} \nabla_i W_{ij}.
+  \nonumber
+\end{align}
+From this it is reasonable to assume that the sound-speed, i.e. the speed at
+which information propagates in the system through pressure waves, is given by
+the expression
+\begin{align}
+  c_{s, i} = (\gamma - 1) u_i \sqrt{\gamma \frac{\rho_i}{\bar{P_i}}}.
+  \label{eq:sph:pu:soundspeedfromeom}
+\end{align}
+This expression is dimensionally consistent with a sound-speed, and includes
+the gas density information (through $\rho$), traditionally used for
+sound-speeds, as well as including the extra information from the smoothed
+pressure $\bar{P}$. However, this scheme causes some problems, which can be
+illustrated using the Sedov Blast test. Such a sound-speed leads to a
+considerably \emph{higher} time-step in front of the shock wave (where the
+smoothed pressure is higher, but the SPH density is relatively constant),
+leading to integration problems. An alternative to this is to use the smoothed
+pressure in the place of the ``real" pressure. Whilst it is well understood
+that $\bar{P}$ should not be used to replace the real pressure in general, here
+(in the sound-speed) it is only used as part of the time-stepping condition.
+Using
+\begin{align}
+  c_{s, i} = \sqrt{\gamma \frac{\bar{P}_i}{\rho_i}}
+  \label{eq:sph:pu:soundspeed}
+\end{align}
+instead of Equation \ref{eq:sph:pu:soundspeedfromeom} leads to a much improved
+time-stepping condition that actually allows particles to be woken up before
+being hit by a shock (see Figure \ref{fig:sph:soundspeed}).
+
+\begin{figure}
+  \centering
+  \includegraphics[width=\columnwidth]{sedov_blast_soundspeed.pdf}
+  \caption{The ratio of the sound-speed calculated in Equation
+    \ref{eq:sph:pu:soundspeed} to the gas sound-speed,
+    $c_s = \sqrt{\gamma(\gamma - 1) u}$ with $u$ the internal energy. Note how
+    the sound-speed increases ahead of the shock, leading to a much smaller
+    time-step for these particles ($\Delta t \propto c_s^{-1}$), waking them up
+    just before they are hit by a shock. The physical reasoning behind using
+    this particular metric for the sound-speed is weak, but as a time-step
+    criterion it appears to be useful. The smoothed pressure calculation
+    ``catches" the hot particles from the shock that is incoming and is
+    increased for those in front of the shock. Thankfully, particles that are
+    behind the shock seem to be relatively unaffected. The red line shows the
+    shock front.}
+  \label{fig:sph:soundspeed}  
+\end{figure}
+
+
+\subsubsection{Time integration}
+
+Time integration follows exactly the same scheme as \MinimalSPH, as the
+fundamental equations are exactly the same (just slightly different quantities
+enter the equations of motion). The CFL condition is used, along with the
+sound-speed that is discussed above, such that
+\begin{align}
+  \Delta t = 2 C_{\rm CFL} \frac{H_i}{v_{{\rm sig},i}},
+  \label{eq:sph:pu:dt}
+\end{align}
+where $C_{\rm CFL}$ is a free dimensionless parameter and $H_i = \gamma h_i$ is
+the kernel support size. There is an additional requirement placed on the
+maximal change in $u$ such that
+\begin{align}
+  \Delta t = C_{u} \frac{u}{du/dt},
+  \label{eq:sph:pu:dt_du}
+\end{align}
+with $C_{u}$ a free dimensionless parameter that describes the maximal change
+in $u$ that is allowed.
+
+\subsubsection{Particle properties prediction}
+
+The prediction of particle properties follows exactly the same scheme as
+\MinimalSPH, with the exception of course of the additional smoothed quantity
+$\bar{P}$. This is drifted in the same way as the density; they should both
+follow from the same differential equation with an additional $u$ factor on
+both sides, such that
+\begin{align}
+  \bar{P}_i \rightarrow \bar{P}_i
+  \exp\left(-\frac{3}{h_i}\frac{dh_i}{dt} \Delta t\right). 
+  \label{eq:sph:pu:drift}
+\end{align}
+
+%##############################################################################
+
 \subsection{Anarchy SPH}
 Dalla Vecchia (\textit{in prep.}), also described in section 2.2.2 of
 \cite{Schaller2015}.\\
diff --git a/theory/SPH/bibliography.bib b/theory/SPH/bibliography.bib
index 4bcb0e1939a257d54c4c0aa99495d7568838b4f8..2c34da68ec1ed8e4b0321d9312773a308f629b7f 100644
--- a/theory/SPH/bibliography.bib
+++ b/theory/SPH/bibliography.bib
@@ -34,12 +34,14 @@ archivePrefix = "arXiv",
 
 @ARTICLE{Hopkins2013,
    author = {{Hopkins}, P.~F.},
-    title = "{A general class of Lagrangian smoothed particle hydrodynamics methods and implications for fluid mixing problems}",
+title = "{A general class of Lagrangian smoothed particle hydrodynamics methods
+and implications for fluid mixing problems}",
   journal = {\mnras},
 archivePrefix = "arXiv",
    eprint = {1206.5006},
  primaryClass = "astro-ph.IM",
- keywords = {hydrodynamics, instabilities, turbulence, methods: numerical, cosmology: theory},
+keywords = {hydrodynamics, instabilities, turbulence, methods: numerical,
+cosmology: theory},
      year = 2013,
     month = feb,
    volume = 428,
@@ -51,10 +53,12 @@ archivePrefix = "arXiv",
 
 @ARTICLE{Springel2002,
    author = {{Springel}, V. and {Hernquist}, L.},
-    title = "{Cosmological smoothed particle hydrodynamics simulations: the entropy equation}",
+title = "{Cosmological smoothed particle hydrodynamics simulations: the entropy
+equation}",
   journal = {\mnras},
    eprint = {astro-ph/0111016},
- keywords = {methods: numerical, galaxies: evolution, galaxies: starburst, methods: numerical, galaxies: evolution, galaxies: starburst},
+keywords = {methods: numerical, galaxies: evolution, galaxies: starburst,
+methods: numerical, galaxies: evolution, galaxies: starburst},
      year = 2002,
     month = jul,
    volume = 333,
@@ -66,7 +70,8 @@ archivePrefix = "arXiv",
 
 @ARTICLE{Balsara1995,
    author = {{Balsara}, D.~S.},
-    title = "{von Neumann stability analysis of smooth particle hydrodynamics--suggestions for optimal algorithms}",
+title = "{von Neumann stability analysis of smooth particle
+hydrodynamics--suggestions for optimal algorithms}",
   journal = {Journal of Computational Physics},
      year = 1995,
    volume = 121,
@@ -81,11 +86,13 @@ archivePrefix = "arXiv",
    author = {{Schaller}, M. and {Dalla Vecchia}, C. and {Schaye}, J. and 
 	{Bower}, R.~G. and {Theuns}, T. and {Crain}, R.~A. and {Furlong}, M. and 
 	{McCarthy}, I.~G.},
-    title = "{The EAGLE simulations of galaxy formation: the importance of the hydrodynamics scheme}",
+title = "{The EAGLE simulations of galaxy formation: the importance of the
+hydrodynamics scheme}",
   journal = {\mnras},
 archivePrefix = "arXiv",
    eprint = {1509.05056},
- keywords = {methods: numerical, galaxies: clusters: intracluster medium, galaxies: formation, cosmology: theory},
+keywords = {methods: numerical, galaxies: clusters: intracluster medium,
+galaxies: formation, cosmology: theory},
      year = 2015,
     month = dec,
    volume = 454,
@@ -100,7 +107,8 @@ archivePrefix = "arXiv",
 
 @ARTICLE{Dehnen2012,
    author = {{Dehnen}, W. and {Aly}, H.},
-    title = "{Improving convergence in smoothed particle hydrodynamics simulations without pairing instability}",
+title = "{Improving convergence in smoothed particle hydrodynamics simulations
+without pairing instability}",
   journal = {\mnras},
 archivePrefix = "arXiv",
    eprint = {1204.2471},
@@ -114,3 +122,44 @@ archivePrefix = "arXiv",
    adsurl = {http://adsabs.harvard.edu/abs/2012MNRAS.425.1068D},
   adsnote = {Provided by the SAO/NASA Astrophysics Data System}
 }
+
+
+
+@article{Saitoh2013,
+archivePrefix = {arXiv},
+arxivId = {1202.4277},
+author = {Saitoh, Takayuki R. and Makino, Junichiro},
+doi = {10.1088/0004-637X/768/1/44},
+eprint = {1202.4277},
+file = {:Users/josh/Downloads/Saitoh{\_}2013{\_}ApJ{\_}768{\_}44.pdf:pdf},
+isbn = {0004-637X$\backslash$r1538-4357},
+issn = {15384357},
+journal = {Astrophysical Journal},
+keywords = {galaxies: ISM,galaxies: evolution,methods: numerical},
+number = {1},
+title = {{A density-independent formulation of smoothed particle
+hydrodynamics}},
+volume = {768},
+year = {2013}
+}
+
+
+
+@article{Hosono2013,
+archivePrefix = {arXiv},
+arxivId = {1307.0916},
+author = {Hosono, Natsuki and Saitoh, Takayuki R. and Makino, Junichiro},
+doi = {10.1093/pasj/65.5.108},
+eprint = {1307.0916},
+file = {:Users/josh/Downloads/pasj65-0108.pdf:pdf},
+issn = {0004-6264},
+keywords = {hydrodynamics,methods,numerical},
+number = {May},
+pages = {1--11},
+title = {{Density Independent Smoothed Particle Hydrodynamics for Non-Ideal
+Equation of State}},
+url =
+{http://arxiv.org/abs/1307.0916{\%}0Ahttp://dx.doi.org/10.1093/pasj/65.5.108},
+year = {2013}
+}
+
diff --git a/theory/SPH/run.sh b/theory/SPH/run.sh
index 651aadad79c2471f58221e2b4fcad7a09ab12256..8d33be12825a31b2906e1259e31185fee2cc74bf 100755
--- a/theory/SPH/run.sh
+++ b/theory/SPH/run.sh
@@ -4,6 +4,10 @@ python kernels.py
 cp kernels.pdf ..
 cp kernel_derivatives.pdf ..
 cd ..
+cd Flavours
+python plotSoundspeed.py
+cp sedov_blast_soundspeed.pdf ..
+cd ..
 pdflatex swift_sph.tex
 bibtex swift_sph.aux 
 pdflatex swift_sph.tex
diff --git a/theory/SPH/swift_sph.tex b/theory/SPH/swift_sph.tex
index 4be8b965b2888bb04b5c150b41ccc38ef2ec3f95..e9c185c3cd0b845bff75be2092846bffbdcfd1a9 100644
--- a/theory/SPH/swift_sph.tex
+++ b/theory/SPH/swift_sph.tex
@@ -29,7 +29,7 @@
 \section{Equation of state}
 \input{EoS/eos}
 
-\section{Derivation of the Equation of Motion}
+\section{Derivation of the Equation of Motion}\label{sec:derivation}
 \input{Derivation/sph_derivation.tex}
 
 \section{SPH flavours}