diff --git a/.gitignore b/.gitignore
index 6f5e46bbef3d87edb80fb26c072e857c0e2dbe0e..f6a8b1799c52929d53d8ba4f531bec52c2e9db3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 *~
+*.hdf5
 
 Makefile
 Makefile.in
@@ -23,14 +24,12 @@ doc/Doxyfile
 examples/swift
 examples/swift_mpi
 examples/*/*.xmf
-examples/*/*.hdf5
 examples/*/*.h5
 examples/*/*.png
 examples/*/*.txt
 examples/*/*.dot
 examples/*/used_parameters.yml
 examples/*/*/*.xmf
-examples/*/*/*.hdf5
 examples/*/*/*.png
 examples/*/*/*.txt
 examples/*/*/*.dot
@@ -78,7 +77,6 @@ tests/test_nonsym_force_1_vec.dat
 tests/test_nonsym_force_2_vec.dat
 tests/testGreetings
 tests/testReading
-tests/input.hdf5
 tests/testSingle
 tests/testTimeIntegration
 tests/testSPHStep
@@ -112,6 +110,8 @@ tests/testDump
 tests/testLogger
 tests/benchmarkInteractions
 tests/testGravityDerivatives
+tests/testPotentialSelf
+tests/testPotentialPair
 
 theory/latex/swift.pdf
 theory/SPH/Kernels/kernels.pdf
@@ -282,9 +282,14 @@ _minted*
 *.sympy
 sympy-plots-for-*.tex/
 
+# python
+*.pyc
+
 # todonotes
 *.tdo
 
 # xindy
 *.xdy
 
+# macOS
+*.DS_Store
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8ef8a65e890507c0467a965405e488aeff8ecf36
--- /dev/null
+++ b/README.md
@@ -0,0 +1,93 @@
+SWIFT: SPH WIth Fine-grained inter-dependent Tasking
+====================================================
+
+[![Build Status](https://gitlab.cosma.dur.ac.uk/jenkins/job/GNU%20SWIFT%20build/badge/icon)](https://gitlab.cosma.dur.ac.uk/jenkins/job/GNU%20SWIFT%20build/)
+
+SWIFT is a gravity and SPH solver designed to run cosmological simulations
+on peta-scale machines, scaling well up to 10's of thousands of compute
+node.
+
+More general information about SWIFT is available on the project
+[webpages](http://www.swiftsim.com).
+
+For information on how to _run_ SWIFT, please consult the onboarding guide
+available [here](http://www.swiftsim.com/onboarding.pdf). This includes
+dependencies, and a few examples to get you going.
+
+We suggest that you use the latest release branch of SWIFT, rather than the
+current master branch as this will change rapidly. We do, however, like to
+ensure that the master branch will build and run.
+
+This GitHub repository is designed to be an issue tracker, and a space for
+the public to submit patches through pull requests. It is synchronised with
+the main development repository that is available on the
+[ICC](http://icc.dur.ac.uk)'s GitLab server which is available
+[here](https://gitlab.cosma.dur.ac.uk/swift/swiftsim).
+
+Please feel free to submit issues to this repository, or even pull
+requests. We will try to deal with them as soon as possible, but as the
+core development team is quite small this could take some time.
+
+Contribution Guidelines
+-----------------------
+
+The SWIFT source code uses a variation of the 'Google' formatting style.
+The script 'format.sh' in the root directory applies the clang-format-3.8
+tool with our style choices to all the SWIFT C source file. Please apply
+the formatting script to the files before submitting a pull request.
+
+Please check that the test suite still runs with your changes applied before
+submitting a pull request and add relevant unit tests probing the correctness
+of new modules. An example of how to add a test to the suite can be found by
+considering the tests/testGreeting case.
+
+Any contributions that fail any of the automated tests will not be accepted.
+Contributions that include tests of the proposed modules (or any current ones!)
+are highly encouraged.
+
+```
+ Welcome to the cosmological hydrodynamical code
+    ______       _________________
+   / ___/ |     / /  _/ ___/_  __/
+   \__ \| | /| / // // /_   / /   
+  ___/ /| |/ |/ // // __/  / /    
+ /____/ |__/|__/___/_/    /_/     
+ SPH With Inter-dependent Fine-grained Tasking
+
+ Website: www.swiftsim.com
+ Twitter: @SwiftSimulation
+
+See INSTALL.swift for install instructions.
+
+Usage: swift [OPTION]... PARAMFILE
+       swift_mpi [OPTION]... PARAMFILE
+
+Valid options are:
+  -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.
+  -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 this help message and exit.
+
+See the file examples/parameter_example.yml for an example of parameter file.
+```
diff --git a/configure.ac b/configure.ac
index c95548229667a3a90941f723da93f997cb03b66e..e730619ea17ce87df7b5c62225094e1d7d0e34db 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,7 +16,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # Init the project.
-AC_INIT([SWIFT],[0.6.0],[https://gitlab.cosma.dur.ac.uk/swift/swiftsim])
+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.
@@ -407,13 +407,47 @@ AC_HEADER_STDC
 # Check for the libraries we will need.
 AC_CHECK_LIB(m,sqrt,,AC_MSG_ERROR(something is wrong with the math library!))
 
-# Check for GSL
+# Check for GSL. We test for this in the standard directories by default,
+# and only disable if using --with-gsl=no or --without-gsl. When a value
+# is given GSL must be found.
 have_gsl="no"
-AC_CHECK_LIB([gslcblas], [cblas_dgemm])
-AC_CHECK_LIB([gsl], [gsl_integration_qag])
-if test "x$ac_cv_lib_gslcblas_cblas_dgemm" != "x"; then
-   have_gsl="yes"
+AC_ARG_WITH([gsl],
+    [AS_HELP_STRING([--with-gsl=PATH],
+       [root directory where GSL is installed @<:@yes/no@:>@]
+    )],
+    [with_gsl="$withval"],
+    [with_gsl="test"]
+)
+if test "x$with_gsl" != "xno"; then
+   if test "x$with_gsl" != "xyes" -a "x$with_gsl" != "xtest" -a "x$with_gsl" != "x"; then
+      GSL_LIBS="-L$with_gsl/lib -lgsl -lgslcblas"
+      GSL_INCS="-I$with_gsl/include"
+   else
+      GSL_LIBS="-lgsl -lgslcblas"
+      GSL_INCS=""
+   fi
+   #  GSL is not specified, so just check if we have it.
+   if test "x$with_gsl" = "xtest"; then
+      AC_CHECK_LIB([gslcblas],[cblas_dgemm],[have_gsl="yes"],[have_gsl="no"],$GSL_LIBS)
+      if test "x$have_gsl" != "xno"; then
+         AC_DEFINE([HAVE_LIBGSLCBLAS],1,[The GSL CBLAS library appears to be present.])
+         AC_CHECK_LIB([gsl],[gsl_integration_qag],
+            AC_DEFINE([HAVE_LIBGSL],1,[The GSL library appears to be present.]),
+            [have_gsl="no"],$GSL_LIBS)
+      fi
+   else
+      AC_CHECK_LIB([gslcblas],[cblas_dgemm],
+         AC_DEFINE([HAVE_LIBGSLCBLAS],1,[The GSL CBLAS library appears to be present.]),
+         AC_MSG_ERROR(something is wrong with the GSL CBLAS library!), $GSL_LIBS)
+      AC_CHECK_LIB([gsl],[gsl_integration_qag],
+         AC_DEFINE([HAVE_LIBGSL],1,[The GSL library appears to be present.]),
+         AC_MSG_ERROR(something is wrong with the GSL library!), $GSL_LIBS)
+      have_gsl="yes"
+   fi
 fi
+AC_SUBST([GSL_LIBS])
+AC_SUBST([GSL_INCS])
+AM_CONDITIONAL([HAVEGSL],[test -n "$GSL_LIBS"])
 
 # Check for pthreads.
 AX_PTHREAD([LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
@@ -445,7 +479,7 @@ AC_ARG_WITH([metis],
     [AS_HELP_STRING([--with-metis=PATH],
        [root directory where metis is installed @<:@yes/no@:>@]
     )],
-    [],
+    [with_metis="$withval"],
     [with_metis="no"]
 )
 if test "x$with_metis" != "xno"; then
@@ -473,13 +507,13 @@ AH_VERBATIM([__STDC_FORMAT_MACROS],
 #define __STDC_FORMAT_MACROS 1
 #endif])
 
-# Check for grackle. 
+# 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
@@ -492,9 +526,9 @@ if test "x$with_grackle" != "xno"; then
       GRACKLE_LIBS="-lgrackle"
       GRACKLE_INCS=""
    fi
-   
+
    have_grackle="yes"
-      
+
    AC_CHECK_LIB(
       [grackle],
       [initialize_chemistry_data],
@@ -753,22 +787,22 @@ AC_ARG_WITH([hydro],
 case "$with_hydro" in
    gadget2)
       AC_DEFINE([GADGET2_SPH], [1], [Gadget-2 SPH])
-   ;; 
+   ;;
    minimal)
       AC_DEFINE([MINIMAL_SPH], [1], [Minimal SPH])
-   ;; 
+   ;;
    hopkins)
       AC_DEFINE([HOPKINS_PE_SPH], [1], [Pressure-Entropy SPH])
-   ;; 
+   ;;
    default)
       AC_DEFINE([DEFAULT_SPH], [1], [Default SPH])
-   ;; 
+   ;;
    gizmo)
       AC_DEFINE([GIZMO_SPH], [1], [GIZMO SPH])
-   ;; 
+   ;;
    shadowfax)
       AC_DEFINE([SHADOWFAX_SPH], [1], [Shadowfax SPH])
-   ;; 
+   ;;
 
    *)
       AC_MSG_ERROR([Unknown hydrodynamics scheme: $with_hydro])
@@ -793,7 +827,7 @@ if test "$enable_debug_interactions" != "no"; then
       AC_DEFINE_UNQUOTED([MAX_NUM_OF_NEIGHBOURS], [$enableval] ,[The maximum number of particle neighbours to be logged])
       [enable_debug_interactions="yes (Logging up to $enableval neighbours)"]
     fi
-  else 
+  else
     [enable_debug_interactions="no (only available for gadget2 hydro scheme)"]
   fi
 fi
@@ -809,22 +843,22 @@ AC_ARG_WITH([kernel],
 case "$with_kernel" in
    cubic-spline)
       AC_DEFINE([CUBIC_SPLINE_KERNEL], [1], [Cubic spline kernel])
-   ;;    
+   ;;
    quartic-spline)
       AC_DEFINE([QUARTIC_SPLINE_KERNEL], [1], [Quartic spline kernel])
-   ;;    
+   ;;
    quintic-spline)
       AC_DEFINE([QUINTIC_SPLINE_KERNEL], [1], [Quintic spline kernel])
-   ;;    
+   ;;
    wendland-C2)
       AC_DEFINE([WENDLAND_C2_KERNEL], [1], [Wendland-C2 kernel])
-   ;;    
+   ;;
    wendland-C4)
       AC_DEFINE([WENDLAND_C4_KERNEL], [1], [Wendland-C4 kernel])
-   ;;    
+   ;;
    wendland-C6)
       AC_DEFINE([WENDLAND_C6_KERNEL], [1], [Wendland-C6 kernel])
-   ;;    
+   ;;
    *)
       AC_MSG_ERROR([Unknown kernel function: $with_kernel])
    ;;
@@ -841,13 +875,13 @@ AC_ARG_WITH([hydro-dimension],
 case "$with_dimension" in
    1)
       AC_DEFINE([HYDRO_DIMENSION_1D], [1], [1D solver])
-   ;; 
+   ;;
    2)
       AC_DEFINE([HYDRO_DIMENSION_2D], [2], [2D solver])
-   ;; 
+   ;;
    3)
       AC_DEFINE([HYDRO_DIMENSION_3D], [3], [3D solver])
-   ;; 
+   ;;
    *)
       AC_MSG_ERROR([Dimensionality must be 1, 2 or 3])
    ;;
@@ -864,10 +898,10 @@ AC_ARG_WITH([equation-of-state],
 case "$with_eos" in
    ideal-gas)
       AC_DEFINE([EOS_IDEAL_GAS], [1], [Ideal gas equation of state])
-   ;; 
+   ;;
    isothermal-gas)
       AC_DEFINE([EOS_ISOTHERMAL_GAS], [1], [Isothermal gas equation of state])
-   ;; 
+   ;;
    *)
       AC_MSG_ERROR([Unknown equation of state: $with_eos])
    ;;
@@ -884,16 +918,16 @@ AC_ARG_WITH([adiabatic-index],
 case "$with_gamma" in
    5/3)
       AC_DEFINE([HYDRO_GAMMA_5_3], [5./3.], [Adiabatic index is 5/3])
-   ;; 
+   ;;
    7/5)
       AC_DEFINE([HYDRO_GAMMA_7_5], [7./5.], [Adiabatic index is 7/5])
-   ;; 
+   ;;
    4/3)
       AC_DEFINE([HYDRO_GAMMA_4_3], [4./3.], [Adiabatic index is 4/3])
-   ;; 
+   ;;
    2)
       AC_DEFINE([HYDRO_GAMMA_2_1], [2.], [Adiabatic index is 2])
-   ;; 
+   ;;
    *)
       AC_MSG_ERROR([Unknown adiabatic index: $with_gamma])
    ;;
@@ -910,16 +944,16 @@ AC_ARG_WITH([riemann-solver],
 case "$with_riemann" in
    none)
       AC_DEFINE([RIEMANN_SOLVER_NONE], [1], [No Riemann solver])
-   ;; 
+   ;;
    exact)
       AC_DEFINE([RIEMANN_SOLVER_EXACT], [1], [Exact Riemann solver])
-   ;; 
+   ;;
    trrs)
       AC_DEFINE([RIEMANN_SOLVER_TRRS], [1], [Two Rarefaction Riemann Solver])
-   ;; 
+   ;;
    hllc)
       AC_DEFINE([RIEMANN_SOLVER_HLLC], [1], [Harten-Lax-van Leer-Contact Riemann solver])
-   ;; 
+   ;;
    *)
       AC_MSG_ERROR([Unknown Riemann solver: $with_riemann])
    ;;
@@ -936,19 +970,19 @@ AC_ARG_WITH([cooling],
 case "$with_cooling" in
    none)
       AC_DEFINE([COOLING_NONE], [1], [No cooling function])
-   ;; 
+   ;;
    const-du)
       AC_DEFINE([COOLING_CONST_DU], [1], [Const du/dt cooling function])
-   ;; 
+   ;;
    const-lambda)
       AC_DEFINE([COOLING_CONST_LAMBDA], [1], [Const Lambda cooling function])
-   ;; 
+   ;;
    grackle)
       AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
-   ;; 
+   ;;
    EAGLE)
       AC_DEFINE([COOLING_EAGLE], [1], [Cooling following the EAGLE model])
-   ;; 
+   ;;
    *)
       AC_MSG_ERROR([Unknown cooling function: $with_cooling])
    ;;
@@ -965,13 +999,13 @@ AC_ARG_WITH([chemistry],
 case "$with_chemistry" in
    none)
       AC_DEFINE([CHEMISTRY_NONE], [1], [No chemistry function])
-   ;; 
+   ;;
    gear)
       AC_DEFINE([CHEMISTRY_GEAR], [1], [Chemistry taken from the GEAR model])
-   ;; 
+   ;;
    EAGLE)
       AC_DEFINE([CHEMISTRY_EAGLE], [1], [Chemistry taken from the EAGLE model])
-   ;; 
+   ;;
    *)
       AC_MSG_ERROR([Unknown chemistry function: $with_chemistry])
    ;;
@@ -980,7 +1014,7 @@ esac
 #  External potential
 AC_ARG_WITH([ext-potential],
    [AS_HELP_STRING([--with-ext-potential=<pot>],
-      [external potential @<:@none, point-mass, isothermal, softened-isothermal, disc-patch, sine-wave default: none@:>@]
+      [external potential @<:@none, point-mass, point-mass-ring, point-mass-softened, isothermal, softened-isothermal, disc-patch, sine-wave, default: none@:>@]
    )],
    [with_potential="$withval"],
    [with_potential="none"]
@@ -988,19 +1022,25 @@ AC_ARG_WITH([ext-potential],
 case "$with_potential" in
    none)
       AC_DEFINE([EXTERNAL_POTENTIAL_NONE], [1], [No external potential])
-   ;; 
+   ;;
    point-mass)
       AC_DEFINE([EXTERNAL_POTENTIAL_POINTMASS], [1], [Point-mass external potential])
-   ;; 
+   ;;
    isothermal)
       AC_DEFINE([EXTERNAL_POTENTIAL_ISOTHERMAL], [1], [Isothermal external potential])
-   ;; 
+   ;;
    disc-patch)
       AC_DEFINE([EXTERNAL_POTENTIAL_DISC_PATCH], [1], [Disc-patch external potential])
-   ;; 
+   ;;
    sine-wave)
       AC_DEFINE([EXTERNAL_POTENTIAL_SINE_WAVE], [1], [Sine wave external potential in 1D])
-   ;; 
+   ;;
+   point-mass-ring)
+      AC_DEFINE([EXTERNAL_POTENTIAL_POINTMASS_RING], [1], [Point mass potential for Keplerian Ring (Hopkins 2015).])
+   ;;
+   point-mass-softened)
+      AC_DEFINE([EXTERNAL_POTENTIAL_POINTMASS_SOFT], [1], [Softened point-mass potential with form 1/(r^2 + softening^2).])
+   ;;
    *)
       AC_MSG_ERROR([Unknown external potential: $with_potential])
    ;;
@@ -1048,7 +1088,7 @@ touch src/version.c
 AC_OUTPUT
 
 # Report general configuration.
-AC_MSG_RESULT([ 
+AC_MSG_RESULT([
  ------- Summary --------
 
    $PACKAGE_NAME v.$PACKAGE_VERSION
@@ -1078,7 +1118,7 @@ AC_MSG_RESULT([
    Riemann solver     : $with_riemann
    Cooling function   : $with_cooling
    Chemistry          : $with_chemistry
-   
+
    External potential  : $with_potential
    Multipole order     : $with_multipole_order
    No gravity below ID : $no_gravity_below_id
diff --git a/examples/EAGLE_100/eagle_100.yml b/examples/EAGLE_100/eagle_100.yml
index 4c3caa08f694ba9d40d9278dc7dcc1a339eed2a4..b3cbbccc7a89131fedc27aa1024ed8f460494582 100644
--- a/examples/EAGLE_100/eagle_100.yml
+++ b/examples/EAGLE_100/eagle_100.yml
@@ -6,6 +6,15 @@ InternalUnitSystem:
   UnitCurrent_in_cgs:  1             # Amperes
   UnitTemp_in_cgs:     1             # Kelvin
 
+# Cosmological parameters
+Cosmology:
+  h:              0.6777        # Reduced Hubble constant
+  a_begin:        0.9090909     # Initial scale-factor of the simulation
+  a_end:          1.0           # Final scale factor of the simulation
+  Omega_m:        0.307         # Matter density parameter
+  Omega_lambda:   0.693         # Dark-energy density parameter
+  Omega_b:        0.0455        # Baryon density parameter
+  
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
@@ -19,7 +28,7 @@ Scheduler:
 # Parameters governing the snapshots
 Snapshots:
   basename:            eagle # Common part of the name of output files
-  time_first:          0.    # Time of the first output (in internal units)
+  time_first:          1.    # Time of the first output (in internal units)
   delta_time:          1e-3  # Time difference between consecutive outputs (in internal units)
 
 # Parameters governing the conserved quantities statistics
@@ -28,15 +37,18 @@ Statistics:
 
 # Parameters for the self-gravity scheme
 Gravity:
-  eta:                   0.025    # Constant dimensionless multiplier for time integration. 
-  epsilon:               0.0001   # Softening length (in internal units).
-  theta:                 0.7      # Opening angle (Multipole acceptance criterion)
-  
+  eta:                    0.025    # Constant dimensionless multiplier for time integration. 
+  theta:                  0.85      # 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).
+
 # Parameters for the hydrodynamics scheme
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:   100      # (internal units)
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_100.hdf5     # The file to read
+  cleanup_h_factors: 1                 # Remove the h-factors inherited from Gadget
diff --git a/examples/EAGLE_100/run.sh b/examples/EAGLE_100/run.sh
index 6ef47d5d98172cc8a318242923ede37332bd5590..9310fc370acb51256b46e6ba0fb739fb2728caf9 100755
--- a/examples/EAGLE_100/run.sh
+++ b/examples/EAGLE_100/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../swift -s -t 16 eagle_100.yml 2>&1 | tee output.log
+../swift -c -s -t 16 eagle_100.yml 2>&1 | tee output.log
 
diff --git a/examples/EAGLE_12/eagle_12.yml b/examples/EAGLE_12/eagle_12.yml
index af55da76ff513548bee52e51993fa8b78cfdc9e3..933f085ccd0bcbaf80a6ca83b7cb06c5763d4a20 100644
--- a/examples/EAGLE_12/eagle_12.yml
+++ b/examples/EAGLE_12/eagle_12.yml
@@ -6,13 +6,6 @@ InternalUnitSystem:
   UnitCurrent_in_cgs:  1             # Amperes
   UnitTemp_in_cgs:     1             # Kelvin
 
-# Parameters governing the time integration
-TimeIntegration:
-  time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   1e-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).
-
 # Cosmological parameters
 Cosmology:
   h:              0.6777        # Reduced Hubble constant
@@ -21,6 +14,13 @@ Cosmology:
   Omega_m:        0.307         # Matter density parameter
   Omega_lambda:   0.693         # Dark-energy density parameter
   Omega_b:        0.0455        # Baryon density parameter
+
+# 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).
   
 Scheduler:
   max_top_level_cells:    15
@@ -30,6 +30,7 @@ Snapshots:
   basename:            eagle # Common part of the name of output files
   time_first:          1.    # Time of the first output (in internal units)
   delta_time:          1e-3  # Time difference between consecutive outputs (in internal units)
+  compression:         4
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -37,16 +38,18 @@ Statistics:
 
 # Parameters for the self-gravity scheme
 Gravity:
-  eta:                   0.025    # Constant dimensionless multiplier for time integration.
-  epsilon:               0.001    # Softening length (in internal units).
-  theta:                 0.85     # Opening angle (Multipole acceptance criterion)
+  eta:                    0.025     # Constant dimensionless multiplier for time integration.
+  theta:                  0.85      # 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).
   
 # Parameters for the hydrodynamics scheme
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:   100      # (internal units)
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_12.hdf5     # The file to read
-
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
diff --git a/examples/EAGLE_12/run.sh b/examples/EAGLE_12/run.sh
index 21b965050ab1c786fd08c0dfd28b1612ac09e9e5..82d0e3e4d4418f0d00851f1279587a1ed45144df 100755
--- a/examples/EAGLE_12/run.sh
+++ b/examples/EAGLE_12/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../swift -s -t 16 eagle_12.yml 2>&1 | tee output.log
+../swift -c -s -t 16 eagle_12.yml 2>&1 | tee output.log
 
diff --git a/examples/EAGLE_25/eagle_25.yml b/examples/EAGLE_25/eagle_25.yml
index bf2d5d013a2ec16cbca3dab8add3cc0c76a6d63d..dccb9df55bea81d49376bcaa8eb39c5ce9dfa92c 100644
--- a/examples/EAGLE_25/eagle_25.yml
+++ b/examples/EAGLE_25/eagle_25.yml
@@ -14,6 +14,15 @@ VelociraptorUnitSystem:
   UnitCurrent_in_cgs:  1             # Amperes
   UnitTemp_in_cgs:     1             # Kelvin
 
+# Cosmological parameters
+Cosmology:
+  h:              0.6777        # Reduced Hubble constant
+  a_begin:        0.9090909     # Initial scale-factor of the simulation
+  a_end:          1.0           # Final scale factor of the simulation
+  Omega_m:        0.307         # Matter density parameter
+  Omega_lambda:   0.693         # Dark-energy density parameter
+  Omega_b:        0.0455        # Baryon density parameter
+  
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
@@ -27,8 +36,9 @@ Scheduler:
 # Parameters governing the snapshots
 Snapshots:
   basename:            eagle # Common part of the name of output files
-  time_first:          0.    # Time of the first output (in internal units)
+  time_first:          1.    # Time of the first output (in internal units)
   delta_time:          1e-3  # Time difference between consecutive outputs (in internal units)
+  compression:         4
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -36,16 +46,19 @@ Statistics:
 
 # Parameters for the self-gravity scheme
 Gravity:
-  eta:                   0.025    # Constant dimensionless multiplier for time integration. 
-  epsilon:               0.001    # Softening length (in internal units).
-  theta:                 0.85     # Opening angle (Multipole acceptance criterion)
-  
+  eta:                    0.025    # Constant dimensionless multiplier for time integration. 
+  theta:                  0.85     # 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).
+
 # Parameters for the hydrodynamics scheme
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:   100      # (internal units)
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_25.hdf5     # The file to read
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
 
diff --git a/examples/EAGLE_25/run.sh b/examples/EAGLE_25/run.sh
index 21d0c7baf4482bdc6df83c552253a02d47432ba2..ae36e0d8e6ae6377682f259f506f720fbd585b29 100755
--- a/examples/EAGLE_25/run.sh
+++ b/examples/EAGLE_25/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../swift -s -t 16 eagle_25.yml 2>&1 | tee output.log
+../swift -c -s -t 16 eagle_25.yml 2>&1 | tee output.log
 
diff --git a/examples/EAGLE_50/eagle_50.yml b/examples/EAGLE_50/eagle_50.yml
index 0fe1007414e7190ae9cc6f31aa5a16cec75290db..021e6b8ab347d38b7ecaeddd5a198324ab57f31b 100644
--- a/examples/EAGLE_50/eagle_50.yml
+++ b/examples/EAGLE_50/eagle_50.yml
@@ -6,6 +6,15 @@ InternalUnitSystem:
   UnitCurrent_in_cgs:  1             # Amperes
   UnitTemp_in_cgs:     1             # Kelvin
 
+# Cosmological parameters
+Cosmology:
+  h:              0.6777        # Reduced Hubble constant
+  a_begin:        0.9090909     # Initial scale-factor of the simulation
+  a_end:          1.0           # Final scale factor of the simulation
+  Omega_m:        0.307         # Matter density parameter
+  Omega_lambda:   0.693         # Dark-energy density parameter
+  Omega_b:        0.0455        # Baryon density parameter
+  
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
@@ -19,7 +28,7 @@ Scheduler:
 # Parameters governing the snapshots
 Snapshots:
   basename:            eagle # Common part of the name of output files
-  time_first:          0.    # Time of the first output (in internal units)
+  time_first:          1.    # Time of the first output (in internal units)
   delta_time:          1e-3  # Time difference between consecutive outputs (in internal units)
 
 # Parameters governing the conserved quantities statistics
@@ -28,16 +37,19 @@ Statistics:
 
 # Parameters for the self-gravity scheme
 Gravity:
-  eta:                   0.025    # Constant dimensionless multiplier for time integration.
-  epsilon:               0.001    # Softening length (in internal units).
-  theta:                 0.85     # Opening angle (Multipole acceptance criterion)
+  eta:                    0.025    # Constant dimensionless multiplier for time integration.
+  theta:                  0.85     # 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).
 
 # Parameters for the hydrodynamics scheme
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:   100      # (internal units)
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_50.hdf5     # The file to read
+  cleanup_h_factors: 1                # Remove the h-factors inherited from Gadget
 
diff --git a/examples/EAGLE_50/run.sh b/examples/EAGLE_50/run.sh
index 253e7e6eb11a04e1731c605e9ea067ba2bd13221..c3b93e84192ff58e445d4e164a4d6f66d19f85fe 100755
--- a/examples/EAGLE_50/run.sh
+++ b/examples/EAGLE_50/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../swift -s -t 16 eagle_50.yml 2>&1 | tee output.log
+../swift -c -s -t 16 eagle_50.yml 2>&1 | tee output.log
 
diff --git a/examples/EAGLE_6/eagle_6.yml b/examples/EAGLE_6/eagle_6.yml
index 741970c46d8c744f090b64b9b457c8c6a1f835fa..05d34ff6359327268c1754c342f87cbae1e043bd 100644
--- a/examples/EAGLE_6/eagle_6.yml
+++ b/examples/EAGLE_6/eagle_6.yml
@@ -14,6 +14,15 @@ VelociraptorUnitSystem:
   UnitCurrent_in_cgs:  1             # Amperes
   UnitTemp_in_cgs:     1             # Kelvin
 
+# Cosmological parameters
+Cosmology:
+  h:              0.6777        # Reduced Hubble constant
+  a_begin:        0.9090909     # Initial scale-factor of the simulation
+  a_end:          1.0           # Final scale factor of the simulation
+  Omega_m:        0.307         # Matter density parameter
+  Omega_lambda:   0.693         # Dark-energy density parameter
+  Omega_b:        0.0455        # Baryon density parameter
+  
 Scheduler:
   max_top_level_cells: 8
   
@@ -23,12 +32,13 @@ TimeIntegration:
   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).
-
+  
 # Parameters governing the snapshots
 Snapshots:
   basename:            eagle # Common part of the name of output files
-  time_first:          0.    # Time of the first output (in internal units)
+  time_first:          1.    # Time of the first output (in internal units)
   delta_time:          1e-3  # Time difference between consecutive outputs (in internal units)
+  compression:         4
 
 # Parameters governing the conserved quantities statistics
 Statistics:
@@ -36,16 +46,19 @@ Statistics:
 
 # Parameters for the self-gravity scheme
 Gravity:
-  eta:                   0.025    # Constant dimensionless multiplier for time integration.
-  epsilon:               0.001    # Softening length (in internal units).
-  theta:                 0.85     # Opening angle (Multipole acceptance criterion)
+  eta:                    0.025    # Constant dimensionless multiplier for time integration.
+  theta:                  0.85     # 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).
   
 # Parameters for the hydrodynamics scheme
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:   100      # (internal units)
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./EAGLE_ICs_6.hdf5     # The file to read
+  cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
 
diff --git a/examples/EAGLE_6/run.sh b/examples/EAGLE_6/run.sh
index d8e5592467a115460bb455ab31bb5e1f4017a948..3e1d21954d8402c9e1929119db50708859332b6c 100755
--- a/examples/EAGLE_6/run.sh
+++ b/examples/EAGLE_6/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../swift -s -t 16 eagle_6.yml 2>&1 | tee output.log
+../swift -c -s -t 16 eagle_6.yml 2>&1 | tee output.log
 
diff --git a/examples/EvrardCollapse_3D/evrard.yml b/examples/EvrardCollapse_3D/evrard.yml
index 006a22e65d3f674f124ce6c4994e752ba39cd1e1..a2b39fb1e7fa36098abe720945ece4b611eb4fe8 100644
--- a/examples/EvrardCollapse_3D/evrard.yml
+++ b/examples/EvrardCollapse_3D/evrard.yml
@@ -18,7 +18,8 @@ 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
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-2 # Time between statistics output
@@ -30,11 +31,10 @@ SPH:
 
 # Parameters for the self-gravity scheme
 Gravity:
-  eta:                   0.025    # Constant dimensionless multiplier for time integration.
-  epsilon:               0.001      # Softening length (in internal units).
-  theta:                 0.9
-  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:                 4.5      # (Optional) Cut-off in number of top-level cells beyond which no FMM forces are computed (this is the default value).
+  eta:                    0.025    # Constant dimensionless multiplier for time integration.
+  theta:                  0.9
+  comoving_softening:     0.001    # Comoving softening length (in internal units).
+  max_physical_softening: 0.001    # Physical softening length (in internal units).
 
 # Parameters related to the initial conditions
 InitialConditions:
diff --git a/examples/EvrardCollapse_3D/makeIC.py b/examples/EvrardCollapse_3D/makeIC.py
index b68f0da0e1869ce54ae7ab2ad3b33b8c3deb8b51..f4d3c4c5bf7f91e5f79cfcd4e9ae23388932144e 100644
--- a/examples/EvrardCollapse_3D/makeIC.py
+++ b/examples/EvrardCollapse_3D/makeIC.py
@@ -18,9 +18,24 @@
 ################################################################################
 
 import h5py
+import argparse as ap
 from numpy import *
 
-# Generates a swift IC file for the Evrard collapse
+parser = ap.ArgumentParser(
+    description="Generates a swift IC file for the Evrard collapse"
+)
+
+parser.add_argument(
+    "-n",
+    "--nparts",
+    help="""
+         Number of particles to be used in the Evrard collapse.
+         """,
+    required=False,
+    default=100000
+)
+
+args = vars(parser.parse_args())
 
 # Parameters
 gamma = 5. / 3.      # Gas adiabatic index
@@ -28,7 +43,7 @@ M = 1.  # total mass of the sphere
 R = 1.               # radius of the sphere
 u0 = 0.05 / M        # initial thermal energy
 fileName = "evrard.hdf5" 
-numPart = 100000
+numPart = int(args["nparts"])
 
 r = R * sqrt(random.random(numPart))
 phi = 2. * pi * random.random(numPart)
diff --git a/examples/GreshoVortex_3D/gresho.yml b/examples/GreshoVortex_3D/gresho.yml
index df941450196a7de6cd1471e1d258756ca8c36fb1..f42fcbfd00941e7b9c5c09c0d2e3118f5cc1f57d 100644
--- a/examples/GreshoVortex_3D/gresho.yml
+++ b/examples/GreshoVortex_3D/gresho.yml
@@ -21,7 +21,8 @@ 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
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-2 # Time between statistics output
diff --git a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml
index 380dc2ab3a530e89b952aa41f425e50709d73ee9..3133e2769e81b80c18760e8258665fc1a6eee6ca 100644
--- a/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml
+++ b/examples/KelvinHelmholtzGrowthRate_3D/kelvinHelmholtzGrowthRate.yml
@@ -18,7 +18,8 @@ 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
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-2 # Time between statistics output
diff --git a/examples/KeplerianRing/README.md b/examples/KeplerianRing/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d486cbfe412c7ff9ca121c87bbc548d0ece078dd
--- /dev/null
+++ b/examples/KeplerianRing/README.md
@@ -0,0 +1,130 @@
+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
+(http://arxiv.org/abs/1006.1524) Section 4.3.
+
+It is key that for this test in particular that the initial conditions are
+perfectly arranged (here we use the same methodology as described in the paper
+referenced above).
+
+The test uses:
+
++ $M = 10^10$ for a central point mass
++ $r$ up to 5 (particles created to edge of box when relevant)
++ Approximately 16000 particles
++ $c_s = 0.01 \ll v_\phi = 10$, enforced by giving them a mass of 1 unit and an
+  internal energy of 0.015.
+
+Please note that the initial condition generator **requires python3 rather than
+python2**, as well as the plotting script.
+
+
+Code Setup
+----------
+
+You will need to recompile SWIFT with an external potential. This can be done
+by using
+
+    ./configure --with-ext-potential=point-mass
+
+in the root directory of the project. We suggest leaving all other code options
+as their defaults.
+
+Please also consider using:
+
+    ./configure --with-ext-potential=point-mass-softened
+
+if you are running with the initial conditions generated with the script and
+using a nonzero softening.
+
+
+Initial Conditions Generation
+-----------------------------
+
+The initial condition generation is handled by ```makeIC.py```, for which
+the options are available with
+
+    python3 makeIC.py --help
+
+however the defaults are very sensible. If you wish to change the central mass,
+you will need to change the external point mass in ```keplerian_ring.yml``` as
+well.
+
+There are three main types of generation, handled through `--generationmethod`:
+
++ `spiral`: using the original spiral method with a density distribution
+            created using different mass particles.
++ `gaussian`: a guassian density profile with density distribution changed
+              through the _distribution_ of equal mass particles.
++ `grid`: a grid with a flat density profile (similar to Hopkins 2013) imposed
+          on it through different particle masses.
+
+All of them have their benefits and drawbacks, so give each a go.
+
+Plotting
+--------
+
+Once you have ran swift (we suggest that you use the following)
+
+    ../swift -g -S -s -t 16 keplerian_ring.yml 2>&1 | tee output.log
+
+there will be around 350 ```.hdf5``` files in your directory. To check out
+the results of the example use the plotting script:
+
+    python3 plotSolution.py
+
+which will produce a png file in the directory. Check out `plotSolution.py
+--help` for more options.
+
+You can also try the `make_movie.py` script which should make a movie.
+
+
+Theory
+------
+
+We use the 'spiral' technique to generate the initial conditions. To do this,
+we first calculate
+$$
+    m_i = \frac{i - \frac{1}{2}}{N}
+$$
+for each particle $i$ with $N$ the total number of required particles. Then the
+radius of each particle is given by
+$$
+    r_i = f^{-1}(m_i)
+$$
+where for us the PDF $f$ is a Gaussian.
+
+To choose the radial positions, we then choose an initial angle $\theta_1$, and
+from there
+$$
+    \delta \theta_i = \left[2\pi\left( 1 - \frac{r_{i-1}}{r_i} \right)\right]~.
+$$
+
+### Inverse CDF of Gaussian
+
+We need:
+$$
+    f^{-1}(m_i) = r_i = r_{central} + \sqrt{2}\sigma \mathrm{erf}^{-1}(2m_i - 1),
+$$
+and thankfully scipy has an inverse error function built in.
+
+
+Implementation Details
+----------------------
+
++ The $m_i$ are generated by ```generate_m_i```.
++ The inverse Gaussian PDF is implemented as ```inverse_gaussian```.
++ The $\theta_i$ are generated by ```generate_theta_i```.
+
+
+Useful Information
+------------------
+
+$G$ in simulation units (by default using this yaml) is given by:
+$$
+    4.300970\times10^{-6}
+$$
+This is used to convert the mass of the central potential (used in the
+parameterfile) to the $GM$ required in the initial conditions generator.
diff --git a/examples/KeplerianRing/keplerian_ring.yml b/examples/KeplerianRing/keplerian_ring.yml
new file mode 100644
index 0000000000000000000000000000000000000000..421d1e89255195cd05a66975d838c59a4ad77c72
--- /dev/null
+++ b/examples/KeplerianRing/keplerian_ring.yml
@@ -0,0 +1,46 @@
+# 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:   50    # 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-3  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            keplerian_ring # Common part of the name of output files
+  time_first:          0.        # Time of the first output (in internal units)
+  delta_time:          0.2      # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-2 # Time between statistics output
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# 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
+  softening:       0.05
diff --git a/examples/KeplerianRing/makeIC.py b/examples/KeplerianRing/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..e99b7f3b5176f0c7c857e7407634caa78ed6c431
--- /dev/null
+++ b/examples/KeplerianRing/makeIC.py
@@ -0,0 +1,740 @@
+"""
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2017
+#
+# 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/>.
+#
+###############################################################################
+"""
+
+
+import numpy as np
+import write_gadget as wg
+import h5py as h5
+
+from scipy.special import erfinv
+
+
+class Particles(object):
+    """
+    Holder class for particle properties. Also contains some methods to e.g.
+    set their keplerian velocities based on their positions. These properties
+    are set using the 'generationmethod' functions below.
+    """
+    def __init__(self, meta):
+        self.gravitymass = meta["gravitymass"]
+        self.nparts = meta["nparts"]**2
+        self.particlemass = meta["particlemass"]
+        self.softening = meta["softening"]
+        self.boxsize = meta["boxsize"]
+
+        self.smoothing = np.zeros(self.nparts) + meta["smoothing"]
+        self.internalenergy = np.zeros(self.nparts) + meta["internalenergy"]
+
+        self.positions = np.array([])
+        self.radii = np.array([])
+        self.theta = np.array([])
+        self.phi = np.array([])
+        self.velocities = np.array([])
+        self.ids = np.array([])
+        self.densities = np.array([])
+        self.masses = np.array([])
+
+        return
+
+
+    def calculate_masses(self):
+        """
+        Calculate the individual masses for the particles such that we have
+        a uniform thickness disk, given their densities.
+        """
+        mass_factor = self.particlemass / self.densities.max()
+        self.masses = self.densities * mass_factor
+
+        return self.masses
+   
+
+    def calculate_velocities(self, angle=0):
+        """
+        Calculates keplerian orbit velocities for the, well, keplerian ring.
+        Requires that radius and phi are set already.
+
+        Please give angle in radians.
+        """
+        force_modifier = np.sqrt(self.gravitymass / (self.radii**2 + self.softening**2)**(3/2)) * self.radii 
+        try:
+            v_x = np.sin(self.theta) * np.sin(self.phi) * np.cos(angle)
+        except ValueError:
+            # Phi are not yet set. Make them and then move on to v_x
+            if angle:
+                raise ValueError("Unable to find phi.")
+            
+            # If we have angle of inclination 0 we can set phi.
+            self.phi = np.zeros_like(self.theta) + np.pi / 2
+            v_x = np.sin(self.theta) * np.sin(self.phi) * np.cos(angle)
+
+        v_y = np.cos(self.phi) * np.sin(angle) - np.cos(self.theta) * np.sin(self.phi) * np.cos(angle)
+        v_z = np.sin(self.theta) * np.sin(self.phi) * np.sin(angle)
+
+        self.velocities = (force_modifier * np.array([v_x, v_y, v_z])).T
+
+        return self.velocities
+
+
+    def generate_ids(self):
+        """
+        Generate consecutive IDs from 0 based on the number of particles
+        currently in the object.
+        """
+        self.ids = np.arange(self.nparts)
+
+        return self.ids
+
+
+    def convert_polar_to_cartesian(self, centre_of_ring=(5, 5), boxsize=None):
+        """
+        Converts self.radii, self.theta to self.positions.
+
+        If self.phi is set, it also uses that to conver to z.
+        """
+        if len(self.phi) == 0:
+            x = self.radii * np.cos(self.theta) + centre_of_ring[0]
+            y = self.radii * np.sin(self.theta) + centre_of_ring[1]
+            z = np.zeros_like(x) + boxsize/2
+        else:
+            x = self.radii * np.cos(self.theta) * np.sin(self.phi) + centre_of_ring[0]
+            y = self.radii * np.sin(self.theta) * np.sin(self.phi) + centre_of_ring[1]
+            try:
+                z = self.radii * np.cos(self.phi) + centre_of_ring[2]
+            except AttributeError:
+                # Just set the central z value to the middle of the box
+                z = self.radii * np.cos(self.phi) + boxsize/2
+
+        self.positions = np.array([x, y, z]).T
+
+        return self.positions
+
+
+    def convert_cartesian_to_polar(self, centre_of_ring=(5, 5)):
+        """
+        Converts self.positions to self.radii, self.theta, and self.phi.
+        """
+        x, y, z = self.positions.T
+
+        try:
+            x = x - centre_of_ring[0]
+            y = y - centre_of_ring[1]
+            z = z - centre_of_ring[2]
+        except AttributeError:
+            raise AttributeError("Invalid array for centre_of_ring provided.")
+
+        xsquare = x*x
+        ysquare = y*y
+        zsquare = z*z
+
+        r = np.sqrt(xsquare + ysquare + zsquare)
+
+        # We need to mask over the x = 0 cases (theta = 0).
+        mask = np.isclose(x, 0, 1e-6)
+
+        masked_x = np.ma.array(x, mask=mask)
+        theta_for_unmasked = np.arctan2(y, masked_x)
+
+        theta = theta_for_unmasked + mask * 0
+
+        # Thankfully we have already ensured that r != 0.
+        phi = np.arccos(z/r)
+
+
+        self.radii = r
+        self.theta = theta
+        self.phi = phi
+
+        return r, theta, phi
+
+
+    def wiggle_positions(self, tol=1e-3):
+        """
+        'Wiggle' the positions to avoid precision issues.
+        
+        Note that this does not touch r, theta, phi.
+        """
+        self.positions += np.random.random(self.positions.shape) * tol
+
+        return
+
+
+    def exclude_particles(self, range):
+        """
+        Exclude all particles that are *not* within range (of radii).
+        """
+        mask = np.logical_or(
+            self.radii < range[0],
+            self.radii > range[1],
+        )
+
+        x  = np.ma.array(self.positions[:, 0], mask=mask).compressed()
+        y  = np.ma.array(self.positions[:, 1], mask=mask).compressed()
+        z  = np.ma.array(self.positions[:, 2], mask=mask).compressed()
+
+        self.positions = np.array([x, y, z]).T
+        
+        try:
+            v_x  = np.ma.array(self.velocities[:, 0], mask=mask).compressed()
+            v_y  = np.ma.array(self.velocities[:, 1], mask=mask).compressed()
+            v_z  = np.ma.array(self.velocities[:, 2], mask=mask).compressed()
+
+            self.velocities = np.array([v_x, v_y, v_z])
+        except IndexError:
+            # We haven't filled them yet anyway...
+            pass
+    
+        try:
+            self.ids = np.ma.array(self.ids, mask=mask).compressed()
+        except np.ma.core.MaskError:
+            # Not filled yet.
+            pass
+
+        try:
+            self.densities = np.ma.array(self.densities, mask=mask).compressed()
+        except np.ma.core.MaskError:
+            # Not filled yet.
+            pass
+
+        # QSP Fix has modified us, so first we need to chop off extras.
+        # Then, as they are all the same, we don't need to remove 'specific'
+        # values, and so we can just chop off the ends.
+        self.smoothing = self.smoothing[:len(x)]
+        self.internalenergy = self.internalenergy[:len(x)]
+
+        try:
+            self.masses = np.ma.array(self.masses, mask=mask).compressed()
+        except np.ma.core.MaskError:
+            # Not filled yet.
+            pass
+
+        self.radii = np.ma.array(self.radii, mask=mask).compressed()
+        self.theta = np.ma.array(self.theta, mask=mask).compressed()
+
+        try:
+            self.phi = np.ma.array(self.phi, mask=mask).compressed()
+        except np.ma.core.MaskError:
+            # We have not allocated our phis,
+            pass
+
+        self.nparts = len(self.radii)
+
+        return
+
+
+    def tilt_particles(self, angle, center=(5, 5, 5)):
+        """
+        Tilts the particles around the x-axis around the point given.
+
+        Assumes that the particles already have set r, theta, phi.
+        """
+        angle_radians = angle * np.pi / 180
+        rotation_matrix = np.array(
+            [
+                [  np.cos(angle_radians), 0, np.sin(angle_radians)  ],
+                [            0,           1,          0             ],
+                [ -np.sin(angle_radians), 0, np.cos(angle_radians)  ]
+            ]
+        )
+
+        self.positions = (
+            (
+                np.matmul(
+                    rotation_matrix,
+                    (self.positions - np.array(center)).T
+                )
+            ).T
+        ) + np.array(center)
+
+        self.velocities = (
+            (
+                np.matmul(
+                    rotation_matrix,
+                    (self.velocities).T
+                )
+            ).T
+        )
+        
+        self.convert_cartesian_to_polar(center)
+
+        return
+
+
+    def save_to_gadget(self, filename, boxsize=10.):
+        """
+        Save the particle data to a GADGET .hdf5 file.
+
+        Uses the internal options, but you must specify a filename.
+        """
+        with h5.File(filename, "w") as handle:
+            wg.write_header(
+                handle,
+                boxsize=boxsize,
+                flag_entropy=0,
+                np_total=np.array([self.nparts, 0, 0, 0, 0, 0]),
+                np_total_hw=np.array([0, 0, 0, 0, 0, 0]),
+                other={
+                    "MassTable" : np.array([self.particlemass, 0, 0, 0, 0, 0]),
+                    "Time" : 0,
+                }
+            )
+
+            wg.write_runtime_pars(
+                handle,
+                periodic_boundary=1,
+            )
+
+            wg.write_units(
+                handle,
+                current=1.,
+                length=1.,
+                mass=1,
+                temperature=1.,
+                time=1.,
+            )
+
+            wg.write_block(
+                handle,
+                0,  # gas
+                self.positions,
+                self.velocities,
+                self.ids,
+                mass=self.masses,
+                int_energy=self.internalenergy,
+                smoothing=self.smoothing,
+                other={"Density": self.densities},
+            )
+
+        return
+
+
+def __sigma(r):
+    """
+    Density distribution of the ring, this comes directly from Hopkins 2015.
+    """
+    if r < 0.5:
+        return 0.01 + (r/0.5)**3
+    elif r <= 2:
+        return 1.01
+    else:
+        return 0.01 + (1 + (r-2)/0.1)**(-3)
+
+
+# This is required because of the if, else statement above that does not
+# play nicely with our numpy arrays.
+sigma = np.vectorize(__sigma)
+
+
+def generate_theta(r, theta_initial=0.):
+    """
+    Generate the theta associated with the particles based on their radii.
+    This uses the method from The effect of Poisson noise on SPH calculations,
+    Annabel Cartwright, Dimitris Stamatellos and Anthony P. Whitworth 2009.
+
+    @param: r | float / array-like
+        - the radii of the particles.
+
+    @param: theta_initial | float | optional
+        - initial radius to start the generation at.
+
+    ---------------------------------------------------------------------------
+
+    @return: theta_i | numpy.array
+        - angles associated with the particles in the plane.
+    """
+    radii_fraction = r[:-1] / r[1:]
+    d_theta_i = np.sqrt(2 * np.pi * (1 - radii_fraction))
+
+    theta_i = np.empty_like(r)
+    theta_i[0] = theta_initial
+
+    # Need to do this awful loop because each entry relies on the one before it.
+    # Unless someone knows how to do this in numpy.
+    for i in range(len(d_theta_i)):  # first is theta_initial
+        theta_i[i+1] = theta_i[i] + d_theta_i[i]
+
+    return theta_i
+
+
+def QSP_fix(r_i, theta_i):
+    """
+    The start and end of the disk will have the end of the spiral there. That
+    is no good and introduces some shear forces, so we need to move them to
+    concentric circles. We'll also add some extra particles on the final
+    'layer' of this giant onion to ensure that all of the circles are complete.
+
+    @param: r_i | numpy.array
+        - the original r_i generated from inverse_gaussian (and perhaps
+          afterwards masked).
+
+    @param: theta_i | numpy.array
+        - the original theta_i generated from generate_theta_i.
+
+    ---------------------------------------------------------------------------
+
+    @return r_i_fixed | numpy.array
+        - the fixed, concentric circle-like r_i. Note that these arrays do not
+          necessarily have the same length as the r_i, theta_i that are
+          passed in and you will have to re-calculate the number of particles
+          in the system.
+
+    @return theta_i_fixed | numpy.array
+        - the fixed, concentric circle-like theta_i
+    """
+
+    # Thankfully, the delta_thetas are not wrapped (i.e. they keep on going
+    # from 2 pi -- we need to split the arrays into 'circles' by splitting
+    # the theta_i array every 2 pi.
+
+    rotations = 1
+    circles = []
+
+    these_r_i = []
+    these_theta_i = []
+
+    for radius, theta in zip(r_i, theta_i):
+        if theta > rotations * 2 * np.pi:
+            circles.append([these_r_i, these_theta_i])
+            these_r_i = []
+            these_theta_i = []
+            rotations += 1
+
+        these_r_i.append(radius)
+        these_theta_i.append(theta)
+
+    # Now we need to do the averaging.
+    # We want to have all particles in each circle a fixed radius away from the
+    # centre, as well as having even spacing between each particle. The final
+    # ring may be a bit dodgy still, but we will see.
+    
+    r_i_fixed = []
+    theta_i_fixed = []
+
+    for circle in circles:
+        n_particles = len(circle[0])
+        radius = sum(circle[0]) / n_particles
+        theta_initial = circle[1][0]
+
+        theta_sep = 2 * np.pi / n_particles
+        
+        theta = [t * theta_sep for t in range(n_particles)]
+        radii = [radius] * n_particles
+
+        r_i_fixed += radii
+        theta_i_fixed += theta
+
+    return np.array(r_i_fixed), np.array(theta_i_fixed)
+
+
+def gen_particles_grid(meta):
+    """
+    Generates particles on a grid and returns a filled Particles object.
+    """
+    particles = Particles(meta)
+    range = (0, meta["boxsize"])
+    centre_of_ring = [meta["boxsize"]/2.] * 3
+
+    # Because we are using a uniform grid we actually use the same x and y
+    # range for the initial particle setup.
+    step = (range[1] - range[0])/meta["nparts"]
+    
+    x_values = np.arange(0, range[1] - range[0], step)
+
+    # These are 2d arrays which isn't actually that helpful.
+    x, y = np.meshgrid(x_values, x_values)
+    x = x.flatten() + centre_of_ring[0] - (range[1] - range[0])/2
+    y = y.flatten() + centre_of_ring[1] - (range[1] - range[0])/2
+    z = np.zeros_like(x) + meta["boxsize"]/2
+
+    particles.positions = np.array([x, y, z]).T
+    particles.radii = np.sqrt((x - centre_of_ring[0])**2 + (y - centre_of_ring[1])**2)
+    particles.theta = np.arctan2(y - centre_of_ring[1], x - centre_of_ring[0])
+    particles.exclude_particles((particles.softening, 100.))
+
+    particles.densities = sigma(particles.radii)
+    particles.calculate_velocities()
+    particles.calculate_masses()
+
+    particles.generate_ids()
+
+    if meta["angle"]:
+        particles.tilt_particles(meta["angle"], centre_of_ring)
+
+    return particles
+
+
+def gen_particles_spiral(meta):
+    """
+    Generates particles on concentric circles and returns a filled Particles
+    object. Based on Cartwright, Stamatellos & Whitworth (2009).
+    """
+    particles = Particles(meta)
+    centre_of_ring = (meta["boxsize"]/2., meta["boxsize"]/2., meta["boxsize"]/2.)
+    max_r = meta["boxsize"]/2.
+
+    m = (np.arange(particles.nparts) + 0.5)/particles.nparts
+    r = max_r * m
+    theta = generate_theta(r)
+
+    particles.radii, particles.theta = QSP_fix(r, theta)
+    # We must do this afterwards as QSP does not always return the same number of parts.
+    phi = np.zeros_like(particles.theta) + np.pi/2
+    particles.phi = phi
+
+    particles.convert_polar_to_cartesian(centre_of_ring, meta["boxsize"])
+    particles.nparts = len(particles.radii)
+
+    particles.exclude_particles((particles.softening, 100.))
+    
+    # This way of doing densities does not work for different sized patches.
+    # Therefore we need to weight by effecitve area.
+    dtheta = (particles.theta[1:] - particles.theta[:-1]) / 2
+    areas = np.square(4 * particles.radii[1:] * np.sin(dtheta))
+    areas = np.hstack((np.array([1]), areas))
+
+    # Now do the 'actual' density calculation.
+    particles.densities = areas * sigma(particles.radii)
+    particles.calculate_velocities()
+    particles.calculate_masses()
+
+    particles.generate_ids()
+
+    if meta["angle"]:
+        particles.tilt_particles(meta["angle"], centre_of_ring)
+
+
+    return particles
+
+
+def gen_particles_gaussian(meta):
+    """
+    Generates particles on concentric circles and returns a filled Particles
+    object. Based on Cartwright, Stamatellos & Whitworth (2009).
+
+    This generation function uses a Gaussian PDF.
+    """
+    particles = Particles(meta)
+    centre_of_ring = (meta["boxsize"]/2., meta["boxsize"]/2., meta["boxsize"]/2.)
+    max_r = meta["boxsize"]/2.
+
+    m = (np.arange(particles.nparts) + 0.5)/particles.nparts
+    error_function = erfinv(2*m - 1)
+    r = 2 + 0.5 * error_function
+
+    theta = generate_theta(r)
+
+    particles.radii, particles.theta = QSP_fix(r, theta)
+    # We must do this afterwards as QSP does not always return the same number of parts.
+    phi = np.zeros_like(particles.theta) + np.pi/2
+    particles.phi = phi
+
+    particles.convert_polar_to_cartesian(centre_of_ring, meta["boxsize"])
+    particles.nparts = len(particles.radii)
+
+    particles.exclude_particles((particles.softening, 100.))
+    
+    # This way of doing densities does not work for different sized patches.
+    particles.densities = np.zeros_like(particles.radii)
+    particles.calculate_velocities()
+    particles.masses = np.ones_like(particles.radii)
+
+    particles.generate_ids()
+
+    if meta["angle"]:
+        particles.tilt_particles(meta["angle"], centre_of_ring)
+
+
+    return particles
+
+
+if __name__ == "__main__":
+    import argparse as ap
+
+    PARSER = ap.ArgumentParser(
+        description="""
+                    Initial conditions generator for the Keplerian Ring
+                    example. It has sensible defaults for GIZMO, but if you
+                    wish to run the example with SPH you sould use
+                    --generationmethod spiral.
+                    """
+    )
+
+    PARSER.add_argument(
+        "-m",
+        "--gravitymass",
+        help="""
+             GM for the central point mass. Default: 1.
+             """,
+        required=False,
+        default=1.,
+    )
+
+    PARSER.add_argument(
+        "-f",
+        "--filename",
+        help="""
+             Filename for your initial conditions.
+             Default: initial_conditions.hdf5.
+             """,
+        required=False,
+        default="initial_conditions.hdf5",
+    )
+
+    PARSER.add_argument(
+        "-n",
+        "--nparts",
+        help="""
+             Square-root of the number of particles, i.e. the default
+             nparts=128 leads to a ring with 128^2 particles in it.
+             Note that for the spiral generation method this number is 
+             somewhat approximate.
+             """,
+        required=False,
+        default=128,
+    )
+
+    PARSER.add_argument(
+        "-p",
+        "--particlemass",
+        help="""
+             Maximum mass of the gas particles. Default: 10.
+             """,
+        required=False,
+        default=10.,
+    )
+
+    PARSER.add_argument(
+        "-e",
+        "--softening",
+        help="""
+             Gravitational softening for the centre of the ring. We also
+             exclude all particles within this radius from the centre of the
+             ring. Default: 0.05.
+             """,
+        required=False,
+        default=0.05,
+    )
+
+    PARSER.add_argument(
+        "-s",
+        "--smoothing",
+        help="""
+             Initial smoothing length for all of the particles.
+             Default: Boxsize/N
+             """,
+        required=False,
+        default=-1,
+    )
+    
+    PARSER.add_argument(
+        "-i",
+        "--internalenergy",
+        help="""
+             Initial internal energy for all of the particles. Note that this
+             should be low to ensure that the ring is very cold (see Inviscid
+             SPH for details). Default: 0.015.
+             """,
+        required=False,
+        default=0.015
+    )
+
+    PARSER.add_argument(
+        "-g",
+        "--generationmethod",
+        help="""
+             Generation method for the particles. Choose between grid, spiral,
+             and gaussian, where spiral ensures that the particles are generated
+             in a way that minimises the energy in SPH. For more details on
+             this method see Cartwright, Stamatellos & Whitworth (2009).
+             Default: grid.
+             """,
+        required=False,
+        default="grid",
+    )
+
+    PARSER.add_argument(
+        "-b",
+        "--boxsize",
+        help="""
+             The box size. Default: 10.
+             """,
+        required=False,
+        default=10.
+    )
+
+    PARSER.add_argument(
+        "-a",
+        "--angle",
+        help="""
+             Angle of incline on the disk (degrees). Default: 0.
+             Note that tilting the ring may cause some issues at the
+             disk boundary as the 'box' is no longer filled with
+             particles.
+             """,
+        required=False,
+        default=0.
+    )
+
+
+    ### --- ### --- Argument Parsing --- ### --- ###
+
+    ARGS = vars(PARSER.parse_args())
+
+    if ARGS["generationmethod"] == "grid":
+        gen_particles = gen_particles_grid
+    elif ARGS["generationmethod"] == "spiral":
+        gen_particles = gen_particles_spiral
+    elif ARGS["generationmethod"] == "gaussian":
+        gen_particles = gen_particles_gaussian
+    else:
+        print(
+            "ERROR: {} is an invalid generation method. Exiting.".format(
+                ARGS["generationmethod"]
+            )
+        )
+        exit(1)
+
+
+    if ARGS["smoothing"] == -1:
+        smoothing = float(ARGS["boxsize"]) / int(ARGS["nparts"])
+    else:
+        smoothing = float(ARGS["smoothing"])
+
+
+    META = {
+        "gravitymass": float(ARGS["gravitymass"]),
+        "nparts": int(ARGS["nparts"]),
+        "particlemass": float(ARGS["particlemass"]),
+        "smoothing": smoothing,
+        "softening": float(ARGS["softening"]),
+        "internalenergy": float(ARGS["internalenergy"]),
+        "boxsize": float(ARGS["boxsize"]),
+        "angle" : float(ARGS["angle"])
+    }
+
+    PARTICLES = gen_particles(META)
+
+    PARTICLES.save_to_gadget(
+        filename=ARGS["filename"],
+        boxsize=ARGS["boxsize"],
+    )
+
diff --git a/examples/KeplerianRing/make_movie.py b/examples/KeplerianRing/make_movie.py
new file mode 100644
index 0000000000000000000000000000000000000000..83e783924086377a0b2aa384d2c4634bfd40443f
--- /dev/null
+++ b/examples/KeplerianRing/make_movie.py
@@ -0,0 +1,310 @@
+"""
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2017
+#
+# 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/>.
+#
+#
+# -----------------------------------------------------------------------------
+#
+# This program creates test plots for the initial condition generator provided
+# for the Keplerian Ring example.
+#
+###############################################################################
+"""
+
+import matplotlib
+matplotlib.use("Agg")
+
+
+import matplotlib.pyplot as plt
+import matplotlib.animation as anim
+import numpy as np
+import h5py as h5
+
+from tqdm import tqdm
+
+
+def get_colours(numbers, norm=None, cm_name="viridis"):
+    if norm is None:
+        norm = matplotlib.colors.Normalize(vmax=numbers.max(), vmin=numbers.min())
+
+    cmap = matplotlib.cm.get_cmap(cm_name)
+
+    return cmap(norm(numbers)), norm
+        
+
+def load_data(filename, silent=True, extra=None, logextra=True, exclude=None):
+    if not silent:
+        print(f"Loading data from {filename}")
+
+    with h5.File(filename, "r") as file_handle:
+        coords = file_handle['PartType0']['Coordinates'][...]
+        time = float(file_handle['Header'].attrs['Time'])
+        boxsize = file_handle['Header'].attrs['BoxSize']
+
+        # For some old runs we have z=0
+        if np.sum(coords[:, 2]) == 0:
+            centre_of_box = np.array(list(boxsize[:2]/2) + [0])
+        else:
+            centre_of_box = boxsize/2
+
+        
+        if exclude is not None:
+            distance_from_centre = coords - centre_of_box
+            r2 = np.sum(distance_from_centre * distance_from_centre, 1)
+            mask = r2 < (exclude * exclude)
+
+            masked = np.ma.array(coords, mask=np.array([mask]*3))
+
+            coords = masked.compressed()
+        
+
+        if extra is not None:
+            other = file_handle['PartType0'][extra][...]
+            if exclude is not None:
+                masked = np.ma.array(other, mask=mask)
+
+                other = masked.compressed()
+
+            if logextra:
+                other = np.log(other)
+
+            return coords, time, other
+        else:
+            return coords, time
+
+
+def rms(x):
+    return np.sqrt(sum(x**2))
+
+
+def rotation_velocity_at_r(r, params):
+    """
+    Gets the rotation velocity at a given radius r by assuming it is keplerian.
+
+    Assumes we are in cgs units, which may one day be our downfall.
+    """
+
+    unit_length = float(params[r"InternalUnitSystem:UnitLength_in_cgs"])
+
+    if unit_length != 1.:
+        print(f"Your unit length: {unit_length}")
+        raise InternalUnitSystemError(
+            "This function is only created to handle CGS units."
+        )
+
+    central_mass = float(params["PointMassPotential:mass"])
+    G = 6.674e-8
+
+    v = np.sqrt( G * central_mass / r)
+
+    return v
+
+
+def get_rotation_period_at_r(r, params):
+    """
+    Gets the rotation period at a given radius r, assuming a keplerian
+    orbit.
+    """
+    v = rotation_velocity_at_r(r, params)
+
+    return 2*np.pi / v
+
+
+def get_metadata(filename, r=1):
+    """ The metadata should be extracted from the first snapshot. """
+    with h5.File(filename, "r") as file_handle:
+        header = file_handle['Header'].attrs
+        code = file_handle['Code'].attrs
+        hydro = file_handle['HydroScheme'].attrs
+        params = file_handle['Parameters'].attrs
+
+        period = get_rotation_period_at_r(r, params)
+
+        return_values = {
+            "header" : dict(header),
+            "code" : dict(code),
+            "period" : float(period),
+            "hydro" : dict(hydro),
+            "params" : dict(params)
+        }
+
+    return return_values
+
+
+def plot_single(number, scatter, text, metadata, ax, extra=None, norm=None):
+    filename = "keplerian_ring_{:04d}.hdf5".format(number)
+
+    if extra is not None:
+        coordinates, time, other = load_data(filename, extra=extra)
+    else:
+        coordinates, time = load_data(filename)
+
+
+    text.set_text(
+        "Time: {:1.2f} | Rotations {:1.2f}".format(
+            time,
+            time/metadata['period'],
+        )
+    )
+
+    data = coordinates[:, 0:2]
+    scatter.set_offsets(data)
+
+    if extra is not None:
+        colours, _ = get_colours(other, norm)
+        scatter.set_color(colours)
+
+    return scatter,
+
+
+if __name__ == "__main__":
+    import os
+    import argparse as ap
+
+    parser = ap.ArgumentParser(
+        description="""
+                   Plots a movie of the current snapshots in your
+                   directory. Can also colourmap your information.
+                   """
+    )
+
+    parser.add_argument(
+        "-e",
+        "--exclude_central",
+        help="""
+             Region from the centre of the ring to exclude from the
+             vmin/vmax of the colourmap. Note that these particles are
+             still plotted -- they are just excluded for the purposes
+             of colourmapping. Default: 1 simulation unit.
+             """,
+        default=1.,
+        required=False
+    )
+
+    parser.add_argument(
+        "-c",
+        "--cmap",
+        help="""
+             The item from the GADGET hdf5 file to clourmap with.
+             Examples include Density, InternalEnergy.
+             Default: don't use a colourmap. (Much faster).
+             """,
+        required=False,
+        default=None
+    )
+
+    parser.add_argument(
+        "-m",
+        "--max",
+        help="""
+             Maximum radii to plot.
+             Default: 2.5.
+             """,
+        required=False,
+        default=2.5,
+    )
+    
+    args = vars(parser.parse_args())
+
+    # Look for the number of files in the directory.
+    i = 0
+    while True:
+        if os.path.isfile("keplerian_ring_{:04d}.hdf5".format(i)):
+            i += 1
+        else:
+            break
+
+        if i > 10000:
+            break
+
+
+   
+    # Now deal with the colourmapping (if present)
+    if args["cmap"] is not None:
+        _, _, numbers0 = load_data("keplerian_ring_0000.hdf5", extra=args["cmap"], exclude=float(args["exclude_central"]))
+        _, _, numbersend = load_data("keplerian_ring_{:04d}.hdf5".format(i-1), extra=args["cmap"], exclude=float(args["exclude_central"]))
+        vmax = max([numbers0.max(), numbersend.max()])
+        vmin = min([numbers0.min(), numbersend.min()])
+
+        norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax)
+    else:
+        norm = None
+
+
+    # Initial plot setup
+
+    metadata = get_metadata("keplerian_ring_0000.hdf5")
+    n_particle = metadata['header']['NumPart_Total'][0]
+
+    fig = plt.figure(figsize=(8, 8))
+    ax = fig.add_subplot(111)
+
+    scatter = ax.scatter([0]*n_particle, [0]*n_particle, s=0.5, marker="o")
+    diff = float(args["max"])
+    left = metadata['header']['BoxSize'][0]/2 - diff
+    right = metadata['header']['BoxSize'][0]/2 + diff
+    ax.set_xlim(left, right)
+    ax.set_ylim(left, right)
+
+    offset = 0.25
+    time_text = ax.text(
+        offset + left,
+        offset + left,
+        "Time: {:1.2f} | Rotations {:1.2f}".format(
+            0,
+            0/metadata['period'],
+        )
+    )
+
+    ax.text(
+        offset + left,
+        right-offset-0.35,
+        "Code: {} {} | {} {} \nHydro {}\n$\eta$={:1.4f}".format(
+            metadata['code']['Git Branch'].decode("utf-8"),
+            metadata['code']['Git Revision'].decode("utf-8"),
+            metadata['code']['Compiler Name'].decode("utf-8"),
+            metadata['code']['Compiler Version'].decode("utf-8"),
+            metadata['hydro']['Scheme'].decode("utf-8"),
+            metadata['hydro']['Kernel eta'][0],
+        )
+    )
+
+    ax.set_title("Keplerian Ring Test")
+    ax.set_xlabel("$x$ position")
+    ax.set_ylabel("$y$ position")
+
+    
+    anim = anim.FuncAnimation(
+        fig,
+        plot_single,
+        tqdm(np.arange(i)),
+        fargs = [
+            scatter,
+            time_text,
+            metadata,
+            ax,
+            args["cmap"],
+            norm
+        ],
+        interval=50,
+        repeat_delay=3000,
+        blit=True,
+    )
+
+    anim.save("keplerian_ring.mp4", dpi=int(640/8))
diff --git a/examples/KeplerianRing/plotSolution.py b/examples/KeplerianRing/plotSolution.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d6411106f7f5ddd047148927a03fbb4800e0874
--- /dev/null
+++ b/examples/KeplerianRing/plotSolution.py
@@ -0,0 +1,627 @@
+"""
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2017
+#
+# 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/>.
+#
+###############################################################################
+"""
+
+# Plotting script for the Keplerian Ring example.
+# We use yt for the projection rendering of the ring,
+# and then our own density as a function of radius calculation.
+
+import matplotlib
+matplotlib.use("Agg")
+matplotlib.rc("text", usetex=True)
+
+try:
+    import yt
+    ytavail = True
+except ImportError:
+    print("yt not found. Falling back on homebrew plots.")
+    ytavail = False
+
+import h5py
+import os
+
+import matplotlib.pyplot as plt
+import numpy as np
+import matplotlib.gridspec as gridspec
+
+try:
+    from tqdm import tqdm
+except ImportError:
+    tqdm = lambda x: x
+
+from make_movie import get_metadata
+
+
+yt.funcs.mylog.setLevel(50)
+tqdm.monitor_interval = 0
+
+
+class InternalUnitSystemError(Exception):
+    pass
+
+
+def get_axes_grid(figure):
+    """
+    Grab our axes grid.
+    """
+    gs = gridspec.GridSpec(2, 30)
+
+    grid = []
+    
+    grid.append(figure.add_subplot(gs[0, 0:10]))
+    grid.append(figure.add_subplot(gs[0, 10:20]))
+    grid.append(figure.add_subplot(gs[0, 20:]))
+    grid.append(figure.add_subplot(gs[1, 0:9]))
+    grid.append(figure.add_subplot(gs[1, 11:20]))
+    grid.append(figure.add_subplot(gs[1, 21:]))
+
+    return grid
+
+
+def get_yt_actual_data(plot, name="density"):
+    """
+    Extracts the image data and colourmap from a yt plot.
+    
+    This is used to put on our own grid.
+    """
+
+    data = plot.plots[name].image.get_array()
+    cmap = plot.plots[name].image.cmap
+
+    return data, cmap
+
+
+def chi_square(observed, expected):
+    """
+    The chi squared statistic.
+        
+    This also looks for where expected == 0 and masks over those particles to
+    avoid divide by zero errors and unrealistically high chi squared.
+    """
+
+    mask = np.array(expected) != 0
+
+    masked_expected = np.array(expected)[mask]
+    masked_observed = np.array(observed)[mask]
+
+    return sum(((masked_observed - masked_expected)**2)/masked_expected**2)
+
+
+def load_data(filename):
+    """
+    Loads the data and extracts the relevant information for
+    calculating the chi squared statistic and density(r) profiles.
+    """
+
+    with h5py.File(filename, "r") as file:
+        boxsize = np.array(file["Header"].attrs["BoxSize"])
+
+        # Check if z = 0 for all particles. If so we need to set the cetnre
+        # to have z = 0 also.
+        if np.sum(file["PartType0"]["Coordinates"][:, 2]) == 0:
+            centre = [boxsize[0] / 2., boxsize[0] / 2., 0.]
+        else:
+            centre = boxsize / 2.
+
+        radii = np.sqrt(np.sum(((file["PartType0"]["Coordinates"][...] - centre).T)**2, 0))
+        masses = file["PartType0"]["Masses"][...]
+
+    return radii, masses
+
+
+def bin_density_r(radii, density, binrange, binnumber):
+    """
+    Bins the density as a funciton of radius.
+    """
+
+    bins = np.linspace(*binrange, binnumber)
+    indicies = np.digitize(radii, bins)
+
+    binned_masses = np.zeros(len(bins) - 1)
+    
+    for index, bin in enumerate(indicies):
+        if bin >= len(bins) - 1:
+            continue
+
+        binned_masses[bin] += density[index]
+
+    areas = [np.pi * (a**2 - b**2) for a, b in zip(bins[1:], bins[:-1])]
+    binned_densities = binned_masses/areas
+    
+    return bins, binned_densities
+
+
+def get_density_r(snapshot, filename="keplerian_ring", binrange=(0, 5), binnumber=50):
+    """
+    Gets the binned density as a function of radius.
+    """
+    snap = "{:04d}".format(snapshot)
+    filename = f"{filename}_{snap}.hdf5"
+
+    data = load_data(filename)
+
+    return bin_density_r(*data, binrange, binnumber)
+
+
+def get_mass_outside_inside(snap, radius=2, filename="keplerian_ring"):
+    """
+    Finds the mass outside and inside a radius. This is used to look at mass
+    flow inside and outside of the ring with a postprocessing routine in
+    get_derived_data.
+    """
+    snapshot = "{:04d}".format(snap)
+    filename = f"{filename}_{snapshot}.hdf5"
+
+    radii, masses = load_data(filename)
+
+    below = sum(masses[radii < radius])
+    above = sum(masses[radii > radius])
+
+    return below, above
+
+
+def get_derived_data(minsnap, maxsnap, filename="keplerian_ring", binnumber=50, radius=2):
+    """
+    Gets the derived data from our snapshots, i.e. the
+    density(r) profile and the chi squared (based on the
+    difference between the minsnap and the current snapshot).
+    """
+
+    initial = get_density_r(minsnap, filename, binnumber=binnumber)
+    other_densities = [
+        get_density_r(snap, binnumber=binnumber)[1] for snap in tqdm(
+            range(minsnap+1, maxsnap+1), desc="Densities"
+        )
+    ]
+    densities = [initial[1]] + other_densities
+
+    masses_inside_outside = [
+        get_mass_outside_inside(snap, radius=radius, filename=filename)[0] for snap in tqdm(
+            range(minsnap, maxsnap+1), desc="Mass Flow"
+        )
+    ]
+
+    # Between the initial conditions and the first snapshot we hope that there
+    # has been no mass flow, hence the [0.] + 
+    mass_flows = [0.] + [
+        y - x for x, y in zip(
+            masses_inside_outside[1:],
+            masses_inside_outside[:-1]
+        )
+    ]
+
+    cumulative_mass_flows = [
+        sum(mass_flows[:x])/masses_inside_outside[0] for x in range(len(mass_flows))
+    ]
+
+    chisq = [
+        chi_square(
+            dens[int(0.1*binnumber):int(0.4*binnumber)],
+            initial[1][int(0.1*binnumber):int(0.4*binnumber)]
+        ) for dens in tqdm(densities, desc="Chi Squared")
+    ]
+
+    return initial[0], densities, chisq, cumulative_mass_flows
+
+
+def plot_chisq(ax, minsnap, maxsnap, chisq, filename="keplerian_ring"):
+    """
+    Plot the chisq(rotation).
+    """
+    snapshots = np.arange(minsnap, maxsnap + 1)
+    rotations = [convert_snapshot_number_to_rotations_at(1, snap, filename) for snap in snapshots]
+    ax.plot(rotations, np.array(chisq)/max(chisq))
+
+    ax.set_xlabel("Number of rotations")
+    ax.set_ylabel("$\chi^2 / \chi^2_{{max}}$ = {:3.5f}".format(max(chisq)))
+
+    return
+
+
+def plot_mass_flow(ax, minsnap, maxsnap, mass_flow, filename="keplerian_ring"):
+    """
+    Plot the mass_flow(rotation).
+    """
+    snapshots = np.arange(minsnap, maxsnap + 1)
+    rotations = [convert_snapshot_number_to_rotations_at(1, snap, filename) for snap in snapshots]
+
+    ax.plot(rotations, mass_flow)
+
+    ax.set_xlabel("Number of rotations")
+    ax.set_ylabel(r"Mass flow out of ring ($M_{\rm ring}$)")
+
+    return
+
+
+def plot_density_r(ax, bins, densities, snaplist, filename="keplerian_ring"):
+    """
+    Make the density(r) plots.
+
+    Densities is the _full_ list of density profiles, and
+    snaplist is the ones that you wish to plot.
+    """
+    radii = [(x + y)/2 for x, y in zip(bins[1:], bins[:-1])]
+
+    for snap in snaplist:
+        index = snap - snaplist[0]
+        rotations = convert_snapshot_number_to_rotations_at(1, snap, filename)
+        ax.plot(radii, densities[index], label="{:2.2f} Rotations".format(rotations))
+
+    ax.legend()
+    ax.set_xlabel("Radius")
+    ax.set_ylabel("Azimuthally Averaged Surface Density")
+
+    return
+
+
+def plot_extra_info(ax, filename):
+    """
+    Plots all of the extra information on the final axis.
+
+    Give it a filename of any of the snapshots.
+    """
+
+    metadata = get_metadata(filename)
+    
+    git = metadata['code']['Git Revision'].decode("utf-8")
+    compiler_name = metadata['code']['Compiler Name'].decode("utf-8")
+    compiler_version = metadata['code']['Compiler Version'].decode("utf-8")
+    scheme = metadata['hydro']['Scheme'].decode("utf-8")
+    kernel = metadata['hydro']['Kernel function'].decode("utf-8")
+    gas_gamma = metadata["hydro"]["Adiabatic index"][0]
+    neighbors = metadata["hydro"]["Kernel target N_ngb"][0]
+    eta = metadata["hydro"]["Kernel eta"][0]
+    
+
+    ax.text(-0.49, 0.9, "Keplerian Ring with  $\\gamma={:4.4f}$ in 2/3D".format(gas_gamma), fontsize=11)
+    ax.text(-0.49, 0.8, f"Compiler: {compiler_name} {compiler_version}", fontsize=10)
+    ax.text(-0.49, 0.7, "Rotations are quoted at $r=1$", fontsize=10)
+    ax.plot([-0.49, 0.1], [0.62, 0.62], 'k-', lw=1)
+    ax.text(-0.49, 0.5, f"$\\textsc{{Swift}}$ {git}", fontsize=10)
+    ax.text(-0.49, 0.4, scheme, fontsize=10)
+    ax.text(-0.49, 0.3, kernel, fontsize=10)
+    ax.text(-0.49, 0.2, "${:2.2f}$ neighbours ($\\eta={:3.3f}$)".format(neighbors, eta), fontsize=10)
+
+    ax.set_axis_off()
+    ax.set_xlim(-0.5, 0.5)
+    ax.set_ylim(0, 1)
+
+    return
+
+
+def surface_density_plot_no_yt(ax, snapnum, filename="keplerian_ring", density_limits=None, vlim=None):
+    """
+    Make the surface density plot (sans yt).
+
+    Also returns the max and minimum values for the density so these can
+    be passed to the next call, as well as vlim which are the colourmap
+    max/min.
+    """
+
+    with h5py.File("{}_{:04d}.hdf5".format(filename, snapnum)) as filehandle:
+        density = filehandle["PartType0"]["Density"][...]
+        x, y = filehandle["PartType0"]["Coordinates"][:, 0:2].T
+
+    new_vlim = (density.min(), density.max())
+
+    if vlim is None:
+        vlim = new_vlim
+
+    ax.scatter(x, y, c=density, vmin=vlim[0], vmax=vlim[1], s=0.1)
+
+
+    metadata = get_metadata("{}_{:04d}.hdf5".format(filename, snapnum))
+    period = metadata["period"]
+
+    t = ax.text(
+        2.5,
+        7.5,
+        "Snapshot = {:04d}\nRotations = {:1.2f}".format(
+            snapnum,
+            float(metadata["header"]["Time"])/period
+        ),
+        color='black'
+    )
+
+    t.set_bbox(dict(alpha=0.5, color="white"))
+
+    ax.axis("equal")
+
+    ax.set_xlim(2, 8)
+    ax.set_ylim(2, 8)
+
+    # We now want to remove all of the ticklabels.
+
+    for axis in ['x', 'y']:
+        ax.tick_params(
+            axis=axis,          
+            which='both',      
+            bottom='off',      
+            top='off',         
+            left='off',
+            right='off',
+            labelleft='off',
+            labelbottom='off'
+        ) 
+
+    return density_limits, vlim
+
+
+
+def surface_density_plot(ax, snapnum, filename="keplerian_ring", density_limits=None, vlim=None):
+    """
+    Make the surface density plot (via yt).
+
+    Also returns the max and minimum values for the density so these can
+    be passed to the next call, as well as vlim which are the colourmap
+    max/min.
+    """
+
+    unit_base = {
+        'length': (1.0, 'cm'),
+        'velocity': (1.0, 'cm/s'),
+        'mass': (1.0, 'g')
+    }
+
+    filename = "{}_{:04d}.hdf5".format(filename, snapnum)
+
+    try:
+        snap = yt.load(filename, unit_base=unit_base, over_refine_factor=2)
+    except yt.utilities.exceptions.YTOutputNotIdentified:
+        # Probably the file isn't here because we supplied a too high snapshot
+        # number. Just return what we're given.
+        return density_limits, vlim
+
+    projection_plot = yt.ProjectionPlot(
+        snap,
+        "z",
+        ("gas", "cell_mass"),
+        width=5.5
+    )
+
+    max_density = snap.all_data()[("gas", "cell_mass")].max()
+    min_density = snap.all_data()[("gas", "cell_mass")].min()
+    
+    new_density_limits = (min_density, max_density)
+
+    if density_limits is None:
+        density_limits = new_density_limits
+
+    projection_plot.set_zlim("cell_mass", *density_limits)
+
+    data = get_yt_actual_data(projection_plot, ("gas", "cell_mass"))
+
+    # Becuase of the way plotting works, we also need a max/min for the colourmap.
+
+    new_vlim = (data[0].min(), data[0].max())
+
+    if vlim is None:
+        vlim = new_vlim
+
+    ax.imshow(
+        data[0],
+        cmap=data[1],
+        vmin=vlim[0],
+        vmax=vlim[1]
+    )
+
+    metadata = get_metadata(filename)
+    period = metadata["period"]
+
+    ax.text(
+        20,
+        80,
+        "Snapshot = {:04d}\nRotations = {:1.2f}".format(
+            snapnum,
+            float(snap.current_time)/period
+        ),
+        color='white'
+    )
+
+    # We now want to remove all of the ticklabels.
+
+    for axis in ['x', 'y']:
+        ax.tick_params(
+            axis=axis,          
+            which='both',      
+            bottom='off',      
+            top='off',         
+            left='off',
+            right='off',
+            labelleft='off',
+            labelbottom='off'
+        ) 
+
+    return density_limits, vlim
+
+
+def convert_snapshot_number_to_rotations_at(r, snapnum, filename):
+    """
+    Opens the file and extracts metadata to find the number of rotations.
+    """
+
+    metadata = get_metadata("{}_{:04d}.hdf5".format(filename, snapnum))
+
+    t = metadata["period"]
+    current_time = float(metadata["header"]["Time"])
+
+    return current_time / t
+
+
+if __name__ == "__main__":
+    import argparse as ap
+
+    parser = ap.ArgumentParser(
+        description="""
+                    Plotting code for the Keplerian Ring test. Uses yt to make
+                    surface density plots.
+                    """
+    )
+
+    parser.add_argument(
+        "-b",
+        "--beginning",
+        help="""
+             Initial snapshot to start analysis at.
+             Default: First snapshot in the folder.
+             """,
+        default=-1,
+        required=False
+    )
+
+    parser.add_argument(
+        "-m",
+        "--middle",
+        help="""
+             Middle snapshot in the top row of three surface density plots.
+             Default: (end - beginning) // 2.
+             """,
+        default=-1,
+        required=False
+    )
+
+    parser.add_argument(
+        "-e",
+        "--end",
+        help="""
+             Snapshot to end the analysis at.
+             Default: Last snapshot in the folder.
+             """,
+        default=-1,
+        required=False
+    )
+
+    parser.add_argument(
+        "-f",
+        "--filename",
+        help="""
+             Filename of the output.
+             Default: plot.png
+             """,
+        default="plot.png",
+        required=False
+    )
+
+    parser.add_argument(
+        "-n",
+        "--nbins",
+        help="""
+             Number of bins in histogramming (used for the density(r) plots).
+             Default: 100.
+             """,
+        default=100,
+        required=False
+    )
+
+    parser.add_argument(
+        "-p",
+        "--plotmassflow",
+        help="""
+             Plot the mass flow instead of several density(r) plots.
+             Set this to a nonzero number to do this plot.
+             Default: 0.
+             """,
+        default=0,
+        required=False
+    )
+
+    parser.add_argument(
+        "-s",
+        "--slug",
+        help="""
+             The first part of the output filename. For example, for snapshots
+             with the naming scheme keplerian_ring_xxxx.hdf5, this would be the
+             default value of keplerian_ring.
+             """,
+        default="keplerian_ring",
+        required=False
+    )
+    
+    parser.add_argument(
+        "-y",
+        "--yt",
+        help="""
+             Use yt to do the plots at the top of the page. If set to anything
+             other than a 'truthy' value, we will use a homebrew plotting
+             setup. Default: False
+             """,
+        default=False,
+        required=False
+    )
+
+    args = vars(parser.parse_args())
+
+    filename = args["slug"]
+
+    if args["beginning"] == -1:
+        # We look for the maximum number of snapshots.
+        numbers = [
+            int(x[len(filename)+1:-5])
+            for x in os.listdir() if
+            x[:len(filename)] == filename and x[-1] == "5"
+        ]
+
+        snapshots = [min(numbers), (max(numbers) - min(numbers)) // 2, max(numbers)]
+    else:
+        snapshots = [args["beginning"], args["middle"], args["end"]]
+
+    figure = plt.figure(figsize=(12, 10))
+    axes = get_axes_grid(figure)
+
+    density_limits = None
+    vlim = None
+
+    for snap, ax in zip(snapshots, tqdm(axes[0:3], desc="Images")):
+        if args["yt"] and ytavail:
+            density_limits, vlim = surface_density_plot(
+                ax,
+                snap,
+                density_limits=density_limits,
+                vlim=vlim
+            )
+
+            figure.subplots_adjust(hspace=0, wspace=0)
+        else:
+            density_limits, vlim = surface_density_plot_no_yt(
+                ax,
+                snap,
+                density_limits=density_limits,
+                vlim=vlim
+            )
+
+    # Now we need to do the density(r) plot.
+
+    # Derived data includes density profiles and chi squared
+    derived_data = get_derived_data(snapshots[0], snapshots[2], binnumber=int(args["nbins"]))
+
+    if args["plotmassflow"]:
+        plot_mass_flow(axes[3], snapshots[0], snapshots[2], derived_data[3])
+    else:
+        plot_density_r(axes[3], derived_data[0], derived_data[1], snapshots)
+
+    plot_chisq(axes[4], snapshots[0], snapshots[2], derived_data[2])
+
+    plot_extra_info(axes[5], "keplerian_ring_0000.hdf5")
+
+    figure.savefig(args["filename"], dpi=300)
+
+
diff --git a/examples/KeplerianRing/run.sh b/examples/KeplerianRing/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ee8f7b247f38b74a204f9caf2bd543b72c4f94fa
--- /dev/null
+++ b/examples/KeplerianRing/run.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+if [ ! -e initial_conditions.hdf5 ]
+then
+    echo "Generating initial conditions for the keplerian ring example..."
+    echo "Please consider choosing your own options before continuing..."
+    python3 makeIC.py
+fi
+
+rm -rf keplerian_ring_*.hdf5
+../swift -g -s -t 1 -v 1 keplerian_ring.yml 2>&1 | tee output.log
diff --git a/examples/KeplerianRing/testplots.py b/examples/KeplerianRing/testplots.py
new file mode 100644
index 0000000000000000000000000000000000000000..1de4964a5618636b73657eec229cbf6e83ef0ff9
--- /dev/null
+++ b/examples/KeplerianRing/testplots.py
@@ -0,0 +1,45 @@
+"""
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2017
+#
+# 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/>.
+#
+#
+# -----------------------------------------------------------------------------
+#
+# This program creates test plots for the initial condition generator provided
+# for the Keplerian Ring example.
+#
+###############################################################################
+"""
+
+
+if __name__ == "__main__":
+    import yt
+
+    data = yt.load("initial_conditions.hdf5")
+
+    plot = yt.ParticlePlot(
+        data,
+        "particle_position_x",
+        "particle_position_y",
+        "density"
+    )
+
+    plot.save("test.png")
+
+
diff --git a/examples/KeplerianRing/write_gadget.py b/examples/KeplerianRing/write_gadget.py
new file mode 100644
index 0000000000000000000000000000000000000000..d2519cdaf4d78d9c7327419283072b8001e70fcb
--- /dev/null
+++ b/examples/KeplerianRing/write_gadget.py
@@ -0,0 +1,309 @@
+"""
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2017
+#
+# 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/>.
+#
+# -----------------------------------------------------------------------------
+#
+# This program is a set of helper functions that write particle data to a
+# GADGET .hdf5 file *handle*. It should also function as a piece of
+# documentation on the required attributes for SWIFT to function when running
+# in GADGET compatibility mode.
+# 
+# Example Usage:
+#
+# import write_gadget as wg
+# import h5py as h5
+$ import numpy as np
+#
+#
+# N_PARTICLES = 1000
+#
+#
+# with h5.File("test.hdf5", "w") as f:
+#    wg.write_header(
+#        f,
+#        boxsize=100.,
+#        flag_entropy=1,
+#        np_total=[N_PARTICLES, 0, 0, 0, 0, 0],
+#        np_total_hw=[0, 0, 0, 0, 0, 0]
+#    )
+#
+#    wg.write_runtime_pars(
+#        f,
+#        periodic_boundary=1,
+#    )
+#
+#    wg.write_units(
+#        f,
+#        current=1.,
+#        length=1.,
+#        mass=1.,
+#        temperature=1.,
+#        time=1.
+#    )
+#
+#    wg.write_block(
+#       f,
+#       part_type=0,
+#       pos=np.array([np.arange(N_PARTICLES)] * 3).T,
+#       vel=np.array([np.zeros(N_PARTICLES)] * 3).T,
+#       ids=np.arange(N_PARTICLES),
+#       mass=np.ones(N_PARTICLES,
+#       int_energy=np.zeros(N_PARTICLES),
+#       smoothing=np.ones(NP_ARTICLES),
+#
+###############################################################################
+"""
+
+
+def write_header(f, boxsize, flag_entropy, np_total, np_total_hw, other=False):
+    """ Writes the "Header" section of the hdf5 file. The parameters in this
+        function that are required are the ones that are required for SWIFT
+        to function; note that the MassTable is **ignored** and that all
+        particle masses should be placed into the particle data arrays.
+
+        @param: f | file handle
+            - the file handle of the hdf5 object (use h5py.File(filename, "w")
+              to open a file handle of the correct type).
+
+        @param boxsize | float / list (2D / 3D)
+            - the boxsize. If a float is given it is assumed that the box is
+              the same size in all directions.
+
+        @param flag_entropy | int (0/1)
+            - sets Flag_Entropy_ICs. This is a historical variable for cross
+              compatibility with Gadget-3
+
+        @param np_total | list (6D)
+            - the total number of particles required of each type.
+
+                Type/Index | Symbolic Type Name
+               ------------|--------------------
+                    0      |       Gas
+                    1      |       Halo
+                    2      |       Disk
+                    3      |       Bulge
+                    4      |       Stars
+                    5      |       Bndry
+
+        @param np_total_hw | list (6D)
+            - the number of high-word particles in the file.
+
+
+        @param other | dictionary | optional
+            - a dictionary with any other parameters that you wish to pass into
+              the file header. They will be passed such that the key is the
+              name of the attribute in the hdf5 file.
+
+    """
+
+    # We'll first build a dictionary to iterate through.
+
+    default_attributes = {
+        "BoxSize" : boxsize,
+        "Flag_Entropy_ICs" : flag_entropy,
+        "NumPart_Total" : np_total,
+        "NumPart_Total_HighWord" : np_total_hw,
+        "NumFilesPerSnapshot" : 1,  # required for backwards compatibility
+        "NumPart_ThisFile" : np_total, # Also required for bw compatibility
+    }
+
+    if other:
+        attributes = dict(default_attributes, **other)
+    else:
+        attributes = default_attributes
+
+    header = f.create_group("Header")
+
+    # For some reason there isn't a direct dictionary interface (at least one
+    # that is documented, so we are stuck doing this loop...
+
+    for name, value in attributes.items():
+        header.attrs[name] = value
+
+    return
+
+
+def write_runtime_pars(f, periodic_boundary, other=False):
+    """ Writes the "RuntimeParams" section in the hdf5 file. The parameters in
+        this function that are required are also required for SWIFT to function.
+        If you wish to pass extra arguments into the runtime parameters you
+        may do that by providing a dictionary to other.
+
+        @param: f | file handle
+            - the file handle of the hdf5 object (use h5py.File(filename, "w")
+              to open a file handle of the correct type).
+
+        @param: periodic_boundary | int (0/1)
+            - the condition for periodic boundary conditions -- they are 'on'
+              if this variable is 1, and off if it is 0. Note that SWIFT
+              currently requires periodic boundary conditions to run (as of
+              August 2017).
+
+        @param other | dictionary | optional
+            - a dictionary with any other parameters that you wish to pass into
+              the RuntimePars. They will be passed such that the key is the
+              name of the attribute in the hdf5 file.
+    """
+
+    # First build the dictionary
+
+    default_attributes = {
+        "PeriodicBoundariesOn" : periodic_boundary,
+    }
+
+    if other:
+        attributes = dict(default_attributes, **other)
+    else:
+        attributes = default_attributes
+
+    runtime = f.create_group("RuntimePars")
+
+    for name, value in attributes.items():
+        runtime.attrs[name] = value
+
+    return
+
+
+def write_units(f, current, length, mass, temperature, time, other=False):
+    """ Writes the "RuntimeParams" section in the hdf5 file. The parameters in
+        this function that are required are also required for SWIFT to function.
+        If you wish to pass extra arguments into the runtime parameters you
+        may do that by providing a dictionary to other.
+
+        @param: f | file handle
+            - the file handle of the hdf5 object (use h5py.File(filename, "w")
+              to open a file handle of the correct type).
+
+        @param: current | float
+            - the current conversion factor in cgs units.
+
+        @param: length | float
+            - the length conversion factor in cgs units.
+
+        @param: mass | float
+            - the mass conversion factor in cgs units.
+
+        @param: temperature | float
+            - the temperature conversion factor in cgs units.
+
+        @param: time | float
+            - the time conversion factor in cgs units.
+
+        @param: other | dictionary | optional
+            - a dictionary with any other parameters that you wish to pass into
+              the units attributes. They will be passed such that the key is
+              the name of the attribute in the hdf5 file.
+    """
+
+    # First build the dictionary
+
+    default_attributes = {
+        "Unit current in cgs (U_I)": current,
+        "Unit length in cgs (U_L)": length,
+        "Unit mass in cgs (U_M)": mass,
+        "Unit temperature in cgs (U_T)": temperature,
+        "Unit time in cgs (U_t)": time,
+    }
+
+    if other:
+        attributes = dict(default_attributes, **other)
+    else:
+        attributes = default_attributes
+
+    units = f.create_group("Units")
+
+    for name, value in attributes.items():
+        units.attrs[name] = value
+
+    return
+
+
+def write_block(f, part_type, pos, vel, ids, mass, int_energy, smoothing, other=False):
+    """ Writes a given block of data to PartType{part_type}. The above
+        required parameters are required for SWIFT to run.
+
+        @param: f | file handle
+            - the file handle of the hdf5 object.
+
+        @param part_type | int (0-5):
+            - the identifiying number of the particle type.
+
+                Type/Index | Symbolic Type Name
+               ------------|--------------------
+                    0      |       Gas
+                    1      |       Halo
+                    2      |       Disk
+                    3      |       Bulge
+                    4      |       Stars
+                    5      |       Bndry
+
+        @param pos | numpy.array
+            - the array of particle positions with shape (n_particles, 3).
+
+        @param vel | numpy.array
+            - the array of particle velocities with shape (n_particles, 3).
+
+        @param ids | numpy.array
+            - the ids of the particles. Please note that particle IDs in
+              SWIFT must be strictly positive. Shape (n_particles, 1)
+
+        @param mass | numpy.array
+            - the masses of the particles. In SWIFT MassTable in the header
+              is ignored and so particle masses must be provided here. Shape
+              (n_particles, 1)
+
+        @param int_energy | numpy.array
+            - the internal energies of the particles. Shape (n_particles, 1)
+
+        @param smoothing | numpy.array
+            - the smoothing lenghts of the individual particles. Please note 
+              that this cannot be supplied only in the parameterfile and must
+              be provided on a particle-by-particle basis in SWIFT. Shape
+              (n_particles, 1)
+
+        @param: other | dictionary | optional
+            - a dictionary with any other parameters that you wish to pass into
+              the particle data. They will be passed such that the key is
+              the name of the dataset in the hdf5 file.
+    """
+    
+    # Build the dictionary
+
+    default_data = {
+        "Coordinates" : pos,
+        "Velocities" : vel,
+        "ParticleIDs" : ids,
+        "Masses" : mass,
+        "InternalEnergy" : int_energy,
+        "SmoothingLength" : smoothing,
+    }
+
+    if other:
+        data = dict(default_data, **other)
+    else:
+        data = default_data
+
+    particles = f.create_group(f"PartType{part_type}")
+
+    for name, value in data.items():
+        particles.create_dataset(name, data=value)
+
+    return
+
diff --git a/examples/Makefile.am b/examples/Makefile.am
index bddb32943781e9326e05cc5c55a87086fc96cf02..a589b184350b9fddc7028d60709f18c2003a2bea 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -19,12 +19,12 @@
 MYFLAGS = 
 
 # Add the source directory and debug to CFLAGS
-AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS)
+AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS)
 
 AM_LDFLAGS = $(HDF5_LDFLAGS)
 
 # Extra libraries.
-EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(GRACKLE_LIBS)
+EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS)
 
 # MPI libraries.
 MPI_LIBS = $(METIS_LIBS) $(MPI_THREAD_LIBS)
diff --git a/examples/Noh_3D/noh.yml b/examples/Noh_3D/noh.yml
index 1d126f19babd0c9fe28afff907b3fe8259467a24..88119827501e17cc26742e8cad92a3611e83faa7 100644
--- a/examples/Noh_3D/noh.yml
+++ b/examples/Noh_3D/noh.yml
@@ -18,7 +18,8 @@ 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
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          3e-3 # Time between statistics output
diff --git a/examples/SedovBlast_3D/sedov.yml b/examples/SedovBlast_3D/sedov.yml
index b80be1954491b19234ce7385baed83931d82433f..2df2c432cef8afec49c687b643a872b06f9abb60 100644
--- a/examples/SedovBlast_3D/sedov.yml
+++ b/examples/SedovBlast_3D/sedov.yml
@@ -18,7 +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
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-3 # Time between statistics output
@@ -30,6 +31,6 @@ SPH:
 
 # Parameters related to the initial conditions
 InitialConditions:
-  file_name:  ./sedov.hdf5          # The file to read
-  h_scaling:           3.33
+  file_name:                    ./sedov.hdf5          
+  smoothing_length_scaling:     3.33
 
diff --git a/examples/SodShockSpherical_3D/sodShock.yml b/examples/SodShockSpherical_3D/sodShock.yml
index a26ab95b21c782ce83310038432ac08df0e024c3..52c06dd65ec0b37a0ec0707315ccd15356a7b2d6 100644
--- a/examples/SodShockSpherical_3D/sodShock.yml
+++ b/examples/SodShockSpherical_3D/sodShock.yml
@@ -18,7 +18,8 @@ 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
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-2 # Time between statistics output
diff --git a/examples/SodShock_3D/sodShock.yml b/examples/SodShock_3D/sodShock.yml
index 51a188b6d4537d490cb837a03dab15f74c3b083c..26fbfe4faacf2bbbfd9b077bf9f9c075ce93ef6d 100644
--- a/examples/SodShock_3D/sodShock.yml
+++ b/examples/SodShock_3D/sodShock.yml
@@ -18,7 +18,8 @@ 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
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-2 # Time between statistics output
diff --git a/examples/SquareTest_2D/run.sh b/examples/SquareTest_2D/run.sh
index 242f1b8b729979b026cfe31002e84cd9ef741129..7d77e9c5bd89732970b47feb3a297ef92b345a01 100755
--- a/examples/SquareTest_2D/run.sh
+++ b/examples/SquareTest_2D/run.sh
@@ -11,4 +11,4 @@ fi
 ../swift -s -t 1 square.yml 2>&1 | tee output.log
 
 # Plot the solution
-python plotSolution.py 40
+python plotSolution.py 5
diff --git a/examples/VacuumSpherical_3D/vacuum.yml b/examples/VacuumSpherical_3D/vacuum.yml
index 881b155b62c7f1f2af12a1d013ff5c05f1c16a88..92164a18a46404ad7730f3411dc14953139501bb 100644
--- a/examples/VacuumSpherical_3D/vacuum.yml
+++ b/examples/VacuumSpherical_3D/vacuum.yml
@@ -18,7 +18,8 @@ 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
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-2 # Time between statistics output
diff --git a/examples/Vacuum_3D/vacuum.yml b/examples/Vacuum_3D/vacuum.yml
index 5ef5ce3da68febb086a14ad1a2207711f680d9ff..45a6b73d54de69d71e194ec074ebdd00cd6a57c0 100644
--- a/examples/Vacuum_3D/vacuum.yml
+++ b/examples/Vacuum_3D/vacuum.yml
@@ -18,7 +18,8 @@ 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
+  
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-2 # Time between statistics output
diff --git a/examples/analyse_dump_cells.py b/examples/analyse_dump_cells.py
index 3140e799566c75fe494a75895db0f4a8dcff4e57..2adfaf319e9c0da33f86a6158da68e6620c47361 100755
--- a/examples/analyse_dump_cells.py
+++ b/examples/analyse_dump_cells.py
@@ -39,9 +39,11 @@ zcol = 2
 xwcol = 3
 ywcol = 4
 zwcol = 5
-localcol = 18
 supercol = 15
-activecol = 16
+topcol = 16
+activecol = 17
+localcol = 19
+mpicol = 20
 
 #  Command-line arguments.
 if len(sys.argv) < 5:
@@ -59,11 +61,12 @@ for i in range(4, len(sys.argv)):
 
     #  Read the file.
     data = pl.loadtxt(sys.argv[i])
-    #print data
+    if len(data) == 0 or len(data) == 20:
+        continue
 
-    #  Select cells that are on the current rank and are super cells.
+    #  Select cells that are on the current rank and are top-level cells.
     rdata = data[data[:,localcol] == 1]
-    tdata = rdata[rdata[:,supercol] == 1]
+    tdata = rdata[rdata[:,topcol] == 1]
 
     #  Separation of the cells is in data.
     xwidth = tdata[0,xwcol]
diff --git a/examples/main.c b/examples/main.c
index 11aaaaf02774085493aecf8e5c2d6f49c350d1ec..f0775b3616348de2222177dcf0c54c85282de06a 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -490,7 +490,7 @@ int main(int argc, char *argv[]) {
 #endif
 
   /* Common variables for restart and IC sections. */
-  int clean_h_values = 0;
+  int clean_smoothing_length_values = 0;
   int flag_entropy_ICs = 0;
 
   /* Work out where we will read and write restart files. */
@@ -612,23 +612,35 @@ int main(int argc, char *argv[]) {
       cosmology_init(params, &us, &prog_const, &cosmo);
     else
       cosmology_init_no_cosmo(&cosmo);
-    if (with_cosmology) cosmology_print(&cosmo);
+    if (myrank == 0 && with_cosmology) cosmology_print(&cosmo);
 
     /* Initialise the hydro properties */
-    if (with_hydro) hydro_props_init(&hydro_properties, params);
+    if (with_hydro)
+      hydro_props_init(&hydro_properties, &prog_const, &us, params);
     if (with_hydro) eos_init(&eos, params);
 
     /* Initialise the gravity properties */
-    if (with_self_gravity) gravity_props_init(&gravity_properties, params);
+    if (with_self_gravity)
+      gravity_props_init(&gravity_properties, params, &cosmo);
 
     /* Read particles and space information from (GADGET) ICs */
     char ICfileName[200] = "";
     parser_get_param_string(params, "InitialConditions:file_name", ICfileName);
     const int replicate =
         parser_get_opt_param_int(params, "InitialConditions:replicate", 1);
-    clean_h_values =
-        parser_get_opt_param_int(params, "InitialConditions:cleanup_h", 0);
+    clean_smoothing_length_values = parser_get_opt_param_int(
+        params, "InitialConditions:cleanup_smoothing_lengths", 0);
+    const int cleanup_h = parser_get_opt_param_int(
+        params, "InitialConditions:cleanup_h_factors", 0);
+    const int generate_gas_in_ics = parser_get_opt_param_int(
+        params, "InitialConditions:generate_gas_in_ics", 0);
+    if (generate_gas_in_ics && flag_entropy_ICs)
+      error("Can't generate gas if the entropy flag is set in the ICs.");
+    if (generate_gas_in_ics && !with_cosmology)
+      error("Can't generate gas if the run is not cosmological.");
     if (myrank == 0) message("Reading ICs from file '%s'", ICfileName);
+    if (myrank == 0 && cleanup_h)
+      message("Cleaning up h-factors (h=%f)", cosmo.h);
     fflush(stdout);
 
     /* Get ready to read particles of all kinds */
@@ -641,20 +653,20 @@ 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,
-                     myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL,
-                     nr_threads, dry_run);
+                     cleanup_h, cosmo.h, 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,
-                   myrank, nr_nodes, MPI_COMM_WORLD, MPI_INFO_NULL, nr_threads,
-                   dry_run);
+                   cleanup_h, cosmo.h, 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,
-                   nr_threads, dry_run);
+                   cleanup_h, cosmo.h, nr_threads, dry_run);
 #endif
     if (myrank == 0) {
       clocks_gettime(&toc);
@@ -665,14 +677,18 @@ int main(int argc, char *argv[]) {
 
 #ifdef SWIFT_DEBUG_CHECKS
     /* Check once and for all that we don't have unwanted links */
-    if (!with_stars) {
+    if (!with_stars && !dry_run) {
       for (size_t k = 0; k < Ngpart; ++k)
         if (gparts[k].type == swift_type_star) error("Linking problem");
     }
-    if (!with_hydro) {
+    if (!with_hydro && !dry_run) {
       for (size_t k = 0; k < Ngpart; ++k)
         if (gparts[k].type == swift_type_gas) error("Linking problem");
     }
+
+    /* Check that the other links are correctly set */
+    if (!dry_run)
+      part_verify_links(parts, gparts, sparts, Ngas, Ngpart, Nspart, 1);
 #endif
 
     /* Get the total number of particles across all nodes. */
@@ -696,8 +712,9 @@ int main(int argc, char *argv[]) {
 
     /* Initialize the space with these data. */
     if (myrank == 0) clocks_gettime(&tic);
-    space_init(&s, params, dim, parts, gparts, sparts, Ngas, Ngpart, Nspart,
-               periodic, replicate, with_self_gravity, talking, dry_run);
+    space_init(&s, params, &cosmo, dim, parts, gparts, sparts, Ngas, Ngpart,
+               Nspart, periodic, replicate, generate_gas_in_ics,
+               with_self_gravity, talking, dry_run);
 
     if (myrank == 0) {
       clocks_gettime(&toc);
@@ -706,6 +723,11 @@ int main(int argc, char *argv[]) {
       fflush(stdout);
     }
 
+    /* Check that the matter content matches the cosmology given in the
+     * parameter file. */
+    if (with_cosmology && with_self_gravity && !dry_run)
+      space_check_cosmology(&s, &cosmo, myrank);
+
 /* Also update the total counts (in case of changes due to replication) */
 #if defined(WITH_MPI)
     N_long[0] = s.nr_parts;
@@ -843,7 +865,7 @@ int main(int argc, char *argv[]) {
 #endif
 
     /* Initialise the particles */
-    engine_init_particles(&e, flag_entropy_ICs, clean_h_values);
+    engine_init_particles(&e, flag_entropy_ICs, clean_smoothing_length_values);
 
     /* Write the state of the system before starting time integration. */
     engine_dump_snapshot(&e);
@@ -861,10 +883,10 @@ int main(int argc, char *argv[]) {
 
   /* Legend */
   if (myrank == 0)
-    printf("# %6s %14s %14s %14s %9s %12s %12s %12s %16s [%s] %6s\n", "Step",
-           "Time", "Scale-factor", "Time-step", "Time-bins", "Updates",
-           "g-Updates", "s-Updates", "Wall-clock time", clocks_getunit(),
-           "Props");
+    printf("# %6s %14s %14s %10s %14s %9s %12s %12s %12s %16s [%s] %6s\n",
+           "Step", "Time", "Scale-factor", "Redshift", "Time-step", "Time-bins",
+           "Updates", "g-Updates", "s-Updates", "Wall-clock time",
+           clocks_getunit(), "Props");
 
   /* File for the timers */
   if (with_verbose_timers) timers_open_file(myrank);
@@ -1016,6 +1038,24 @@ int main(int argc, char *argv[]) {
            (double)runner_hist_bins[k]);
 #endif
 
+  /* Write final time information */
+  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);
+    fflush(stdout);
+
+    fprintf(e.file_timesteps,
+            "  %6d %14e %14e %14e %4d %4d %12zu %12zu %12zu %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);
+    fflush(e.file_timesteps);
+  }
+
   /* Write final output. */
   engine_drift_all(&e);
   engine_print_stats(&e);
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index 4784a877a21e698af28f72aab473ac9c3cad605e..0a81716a3e34bbb305916b1148a9d8199b84fa5a 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -30,15 +30,19 @@ SPH:
   h_max:                 10.      # (Optional) Maximal allowed smoothing length in internal units. Defaults to FLT_MAX if unspecified.
   max_volume_change:     1.4      # (Optional) Maximal allowed change of kernel volume over one time-step.
   max_ghost_iterations:  30       # (Optional) Maximal number of iterations allowed to converge towards the smoothing length.
-
+  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:
-  eta:          0.025    # Constant dimensionless multiplier for time integration.
-  theta:        0.7      # Opening angle (Multipole acceptance criterion)
-  epsilon:      0.1      # Softening length (in internal units).
-  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).
+  eta:          0.025               # Constant dimensionless multiplier for time integration.
+  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).
+  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).
 
 # Parameters for the task scheduling
 Scheduler:
@@ -81,12 +85,14 @@ Statistics:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  SedovBlast/sedov.hdf5 # The file to read
-  cleanup_h:   0                    # (Optional) Clean the values of h that are read in. Set to 1 to activate.
-  h_scaling:  1.                    # (Optional) A scaling factor to apply to all smoothing lengths in the ICs.
+  generate_gas_in_ics:         0    # (Optional) Generate gas particles from the DM-only ICs (e.g. from panphasia).
+  cleanup_h_factors:           0    # (Optional) Clean up the h-factors used in the ICs (e.g. in Gadget files).
+  cleanup_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.
-  replicate:  2                     # (Optional) Replicate all particles along each axis a given number of times. Default 1.
+  replicate:  2                     # (Optional) Replicate all particles along each axis a given integer number of times. Default 1.
 
 # Parameters controlling restarts
 Restarts:
diff --git a/examples/plot_gravity_checks.py b/examples/plot_gravity_checks.py
index 41d9d629899f5c34cbb0264b38547a3439c04bf3..de4f37af32cf0d051afb4c5090075654e6fcd65c 100644
--- a/examples/plot_gravity_checks.py
+++ b/examples/plot_gravity_checks.py
@@ -13,7 +13,7 @@ params = {'axes.labelsize': 14,
 'xtick.labelsize': 14,
 'ytick.labelsize': 14,
 'text.usetex': True,
-'figure.figsize': (10, 10),
+'figure.figsize': (12, 10),
 'figure.subplot.left'    : 0.06,
 'figure.subplot.right'   : 0.99  ,
 'figure.subplot.bottom'  : 0.06  ,
@@ -60,11 +60,13 @@ data = np.loadtxt('gravity_checks_exact_step%d.dat'%step)
 exact_ids = data[:,0]
 exact_pos = data[:,1:4]
 exact_a = data[:,4:7]
+exact_pot = data[:,7]
 # Sort stuff
 sort_index = np.argsort(exact_ids)
 exact_ids = exact_ids[sort_index]
 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)
     
 # Start the plot
@@ -166,12 +168,14 @@ for i in range(num_order):
     ids = data[:,0]
     pos = data[:,1:4]
     a_grav = data[:, 4:7]
+    pot = data[:, 7]
 
     # Sort stuff
     sort_index = np.argsort(ids)
     ids = ids[sort_index]
     pos = pos[sort_index, :]
     a_grav = a_grav[sort_index, :]        
+    pot = pot[sort_index]
 
     # Cross-checks
     if not np.array_equal(exact_ids, ids):
@@ -185,6 +189,7 @@ for i in range(num_order):
     
     # Compute the error norm
     diff = exact_a - a_grav
+    diff_pot = exact_pot - pot
 
     norm_diff = np.sqrt(diff[:,0]**2 + diff[:,1]**2 + diff[:,2]**2)
 
@@ -192,73 +197,87 @@ for i in range(num_order):
     error_x = abs(diff[:,0]) / exact_a_norm
     error_y = abs(diff[:,1]) / exact_a_norm
     error_z = abs(diff[:,2]) / exact_a_norm
+    error_pot = abs(diff_pot) / abs(exact_pot)
     
     # Bin the error
     norm_error_hist,_ = np.histogram(norm_error, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size)
     error_x_hist,_ = np.histogram(error_x, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size)
     error_y_hist,_ = np.histogram(error_y, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size)
     error_z_hist,_ = np.histogram(error_z, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size)
+    error_pot_hist,_ = np.histogram(error_pot, bins=bin_edges, density=False) / (np.size(norm_error) * bin_size)
 
     norm_median = np.median(norm_error)
     median_x = np.median(error_x)
     median_y = np.median(error_y)
     median_z = np.median(error_z)
+    median_pot = np.median(error_pot)
 
     norm_per99 = np.percentile(norm_error,99)
     per99_x = np.percentile(error_x,99)
     per99_y = np.percentile(error_y,99)
     per99_z = np.percentile(error_z,99)
+    per99_pot = np.percentile(error_pot, 99)
 
     norm_max = np.max(norm_error)
     max_x = np.max(error_x)
     max_y = np.max(error_y)
     max_z = np.max(error_z)
+    max_pot = np.max(error_pot)
 
     print "Order %d ---- "%order[i]
     print "Norm: median= %f 99%%= %f max= %f"%(norm_median, norm_per99, norm_max)
     print "X   : median= %f 99%%= %f max= %f"%(median_x, per99_x, max_x)
     print "Y   : median= %f 99%%= %f max= %f"%(median_y, per99_y, max_y)
     print "Z   : median= %f 99%%= %f max= %f"%(median_z, per99_z, max_z)
+    print "Pot : median= %f 99%%= %f max= %f"%(median_pot, per99_pot, max_pot)
     print ""
     
-    plt.subplot(221)
-    plt.semilogx(bins, norm_error_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i])
-    plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(norm_median, norm_per99), ha="left", va="top", color=cols[i])
-    plt.subplot(222)    
+    plt.subplot(231)    
     plt.semilogx(bins, error_x_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i])
     plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_x, per99_x), ha="left", va="top", color=cols[i])
-    plt.subplot(223)    
+    plt.subplot(232)    
     plt.semilogx(bins, error_y_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i])
     plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_y, per99_y), ha="left", va="top", color=cols[i])
-    plt.subplot(224)    
+    plt.subplot(233)    
     plt.semilogx(bins, error_z_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i])
     plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_z, per99_z), ha="left", va="top", color=cols[i])
+    plt.subplot(234)
+    plt.semilogx(bins, norm_error_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i])
+    plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(norm_median, norm_per99), ha="left", va="top", color=cols[i])
+    plt.subplot(235)    
+    plt.semilogx(bins, error_pot_hist, color=cols[i],label="SWIFT m-poles order %d"%order[i])
+    plt.text(min_error * 1.5, 1.5 - count/10., "$50\\%%\\rightarrow%.4f~~ 99\\%%\\rightarrow%.4f$"%(median_pot, per99_pot), ha="left", va="top", color=cols[i])
 
     count += 1
 
-plt.subplot(221)
-plt.xlabel("$|\delta \overrightarrow{a}|/|\overrightarrow{a}_{exact}|$")
-#plt.ylabel("Density")
-plt.xlim(min_error, max_error)
-plt.ylim(0,2.5)
-plt.legend(loc="upper left")
-plt.subplot(222)    
+plt.subplot(231)    
 plt.xlabel("$\delta a_x/|\overrightarrow{a}_{exact}|$")
 #plt.ylabel("Density")
 plt.xlim(min_error, max_error)
 plt.ylim(0,1.75)
 #plt.legend(loc="center left")
-plt.subplot(223)    
+plt.subplot(232)    
 plt.xlabel("$\delta a_y/|\overrightarrow{a}_{exact}|$")
 #plt.ylabel("Density")
 plt.xlim(min_error, max_error)
 plt.ylim(0,1.75)
 #plt.legend(loc="center left")
-plt.subplot(224)    
+plt.subplot(233)    
 plt.xlabel("$\delta a_z/|\overrightarrow{a}_{exact}|$")
 #plt.ylabel("Density")
 plt.xlim(min_error, max_error)
 plt.ylim(0,1.75)
+plt.subplot(234)
+plt.xlabel("$|\delta \overrightarrow{a}|/|\overrightarrow{a}_{exact}|$")
+#plt.ylabel("Density")
+plt.xlim(min_error, max_error)
+plt.ylim(0,2.5)
+plt.legend(loc="upper left")
+plt.subplot(235)    
+plt.xlabel("$\delta \phi/\phi_{exact}$")
+#plt.ylabel("Density")
+plt.xlim(min_error, max_error)
+plt.ylim(0,1.75)
 #plt.legend(loc="center left")
 
 
diff --git a/examples/process_cells b/examples/process_cells
index eead4572387f2732cf0b86265f14d20039f73ae1..b57ed9e73c76807707d158485e31a5b643d8dec0 100755
--- a/examples/process_cells
+++ b/examples/process_cells
@@ -48,7 +48,7 @@ echo "Number of steps = $steps"
 
 #  And process them,
 echo "Processing cell dumps files..."
-#echo $files | xargs -P $NPROCS -n 4 /bin/bash -c "${SCRIPTHOME}/process_cells_helper $NX $NY $NZ \$0 \$1 \$2 \$3"
+echo $files | xargs -P $NPROCS -n 4 /bin/bash -c "${SCRIPTHOME}/process_cells_helper $NX $NY $NZ \$0 \$1 \$2 \$3"
 
 #  Create summary.
 grep "top cells" step*-active-cells.dat | sort -h > active_cells.log
diff --git a/src/Makefile.am b/src/Makefile.am
index fc839d03319eda7a3f54d594bcc5c9a7e13bcbab..5c097d13c2e64f786bbde5f01af680555994b42d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,7 +16,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # Add the debug flag to the whole thing
-AM_CFLAGS = $(HDF5_CPPFLAGS)
+AM_CFLAGS = $(HDF5_CPPFLAGS) $(GSL_INCS)
 
 # Assign a "safe" version number
 AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0
@@ -25,7 +25,7 @@ AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0
 GIT_CMD = @GIT_CMD@
 
 # Additional dependencies for shared libraries.
-EXTRA_LIBS = $(HDF5_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(GRACKLE_LIB)
+EXTRA_LIBS = $(HDF5_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(GRACKLE_LIB) $(GSL_LIBS)
 
 # MPI libraries.
 MPI_LIBS = $(METIS_LIBS) $(MPI_THREAD_LIBS)
diff --git a/src/approx_math.h b/src/approx_math.h
index 48319ddfd7a86c132a1cd18b4a08fa849a36a15a..aa9ed2b4efa6e0542e2eb2432132f4b0232f7403 100644
--- a/src/approx_math.h
+++ b/src/approx_math.h
@@ -49,4 +49,16 @@ __attribute__((always_inline)) INLINE static float good_approx_expf(float x) {
                              x * ((1.f / 120.f) + (1.f / 720.f) * x)))));
 }
 
+/**
+ * @brief Approximate version of exp(x) using a 6th order Taylor expansion
+ */
+__attribute__((always_inline)) INLINE static double good_approx_exp(double x) {
+  return 1. +
+         x * (1. +
+              x * (0.5 +
+                   x * ((1. / 6.) +
+                        x * ((1. / 24.) +
+                             x * ((1. / 120.) + (1. / 720.) * x)))));
+}
+
 #endif /* SWIFT_APPROX_MATH_H */
diff --git a/src/cell.c b/src/cell.c
index b415cc077b569c84c85462634458e68aa5e4833a..ea036c97081e625c4e4ca2bb1d06cd9ea8251bd3 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -1081,23 +1081,6 @@ void cell_sanitize(struct cell *c, int treated) {
   c->h_max = h_max;
 }
 
-/**
- * @brief Converts hydro quantities to a valid state after the initial density
- * calculation
- *
- * @param c Cell to act upon
- * @param data Unused parameter
- */
-void cell_convert_hydro(struct cell *c, void *data) {
-
-  struct part *p = c->parts;
-  struct xpart *xp = c->xparts;
-
-  for (int i = 0; i < c->count; ++i) {
-    hydro_convert_quantities(&p[i], &xp[i]);
-  }
-}
-
 /**
  * @brief Cleans the links in a given cell.
  *
diff --git a/src/cell.h b/src/cell.h
index 91663d45bf048fff6e4bbf02d404ec62d216365b..87af742baf935668b0f065e70f065b978b6c9b9f 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -492,7 +492,6 @@ int cell_getsize(struct cell *c);
 int cell_link_parts(struct cell *c, struct part *parts);
 int cell_link_gparts(struct cell *c, struct gpart *gparts);
 int cell_link_sparts(struct cell *c, struct spart *sparts);
-void cell_convert_hydro(struct cell *c, void *data);
 void cell_clean_links(struct cell *c, void *data);
 void cell_make_multipoles(struct cell *c, integertime_t ti_current);
 void cell_check_multipole(struct cell *c, void *data);
diff --git a/src/common_io.c b/src/common_io.c
index 67326817397118568e060f5dff49421bb32fba4d..19adc57fbd51972336840669dab473f846d23dee 100644
--- a/src/common_io.c
+++ b/src/common_io.c
@@ -457,6 +457,7 @@ void io_convert_part_f_mapper(void* restrict temp, int N,
 
   const struct io_props props = *((const struct io_props*)extra_data);
   const struct part* restrict parts = props.parts;
+  const struct xpart* restrict xparts = props.xparts;
   const struct engine* e = props.e;
   const size_t dim = props.dimension;
 
@@ -465,7 +466,8 @@ void io_convert_part_f_mapper(void* restrict temp, int N,
   const ptrdiff_t delta = (temp_f - props.start_temp_f) / dim;
 
   for (int i = 0; i < N; i++)
-    props.convert_part_f(e, parts + delta + i, &temp_f[i * dim]);
+    props.convert_part_f(e, parts + delta + i, xparts + delta + i,
+                         &temp_f[i * dim]);
 }
 
 /**
@@ -477,6 +479,7 @@ void io_convert_part_d_mapper(void* restrict temp, int N,
 
   const struct io_props props = *((const struct io_props*)extra_data);
   const struct part* restrict parts = props.parts;
+  const struct xpart* restrict xparts = props.xparts;
   const struct engine* e = props.e;
   const size_t dim = props.dimension;
 
@@ -485,7 +488,8 @@ void io_convert_part_d_mapper(void* restrict temp, int N,
   const ptrdiff_t delta = (temp_d - props.start_temp_d) / dim;
 
   for (int i = 0; i < N; i++)
-    props.convert_part_d(e, parts + delta + i, &temp_d[i * dim]);
+    props.convert_part_d(e, parts + delta + i, xparts + delta + i,
+                         &temp_d[i * dim]);
 }
 
 /**
@@ -704,7 +708,7 @@ void io_duplicate_hydro_gparts_mapper(void* restrict data, int Ngas,
     gparts[i + Ndm].type = swift_type_gas;
 
     /* Link the particles */
-    gparts[i + Ndm].id_or_neg_offset = -i;
+    gparts[i + Ndm].id_or_neg_offset = -(long long)(offset + i);
     parts[i].gpart = &gparts[i + Ndm];
   }
 }
@@ -760,7 +764,7 @@ void io_duplicate_hydro_sparts_mapper(void* restrict data, int Nstars,
     gparts[i + Ndm].type = swift_type_star;
 
     /* Link the particles */
-    gparts[i + Ndm].id_or_neg_offset = -i;
+    gparts[i + Ndm].id_or_neg_offset = -(long long)(offset + i);
     sparts[i].gpart = &gparts[i + Ndm];
   }
 }
diff --git a/src/cosmology.c b/src/cosmology.c
index 9b301388e08ca0f4aa2a7d5d8f351ab88682c8ba..a5034240d07b9b9bf401d470c0bacbf6962a8006 100644
--- a/src/cosmology.c
+++ b/src/cosmology.c
@@ -130,9 +130,11 @@ double cosmology_get_time_since_big_bang(const struct cosmology *c, double a) {
  * @brief Update the cosmological parameters to the current simulation time.
  *
  * @param c The #cosmology struct.
+ * @param phys_const The physical constants in the internal units.
  * @param ti_current The current (integer) time.
  */
-void cosmology_update(struct cosmology *c, integertime_t ti_current) {
+void cosmology_update(struct cosmology *c, const struct phys_const *phys_const,
+                      integertime_t ti_current) {
 
   /* Get scale factor and powers of it */
   const double a = c->a_begin * exp(ti_current * c->time_base);
@@ -173,6 +175,10 @@ void cosmology_update(struct cosmology *c, integertime_t ti_current) {
   /* Expansion rate */
   c->a_dot = c->H * c->a;
 
+  /* Critical density */
+  c->critical_density =
+      3. * c->H * c->H / (8. * M_PI * phys_const->const_newton_G);
+
   /* Time-step conversion factor */
   c->time_step_factor = c->H;
 
@@ -284,8 +290,6 @@ double time_integrand(double a, void *param) {
  */
 void cosmology_init_tables(struct cosmology *c) {
 
-  const ticks tic = getticks();
-
 #ifdef HAVE_LIBGSL
 
   /* Retrieve some constants */
@@ -373,9 +377,6 @@ void cosmology_init_tables(struct cosmology *c) {
   error("Code not compiled with GSL. Can't compute cosmology integrals.");
 
 #endif
-
-  message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
-          clocks_getunit());
 }
 
 /**
@@ -431,7 +432,7 @@ void cosmology_init(const struct swift_params *params,
   cosmology_init_tables(c);
 
   /* Set remaining variables to alid values */
-  cosmology_update(c, 0);
+  cosmology_update(c, phys_const, 0);
 
   /* Update the times */
   c->time_begin = cosmology_get_time_since_big_bang(c, c->a_begin);
@@ -454,7 +455,7 @@ void cosmology_init_no_cosmo(struct cosmology *c) {
   c->Omega_b = 0.;
   c->w_0 = 0.;
   c->w_a = 0.;
-  c->h = 0.;
+  c->h = 1.;
   c->w = 0.;
 
   c->a_begin = 1.;
@@ -474,6 +475,8 @@ void cosmology_init_no_cosmo(struct cosmology *c) {
   c->a_factor_hydro_accel = 1.;
   c->a_factor_grav_accel = 1.;
 
+  c->critical_density = 0.;
+
   c->time_step_factor = 1.;
 
   c->a_dot = 0.;
@@ -667,13 +670,15 @@ void cosmology_struct_dump(const struct cosmology *cosmology, FILE *stream) {
  * @brief Restore a cosmology struct from the given FILE as a stream of
  * bytes.
  *
+ * @param enabled whether cosmology is enabled.
  * @param cosmology the struct
  * @param stream the file stream
  */
-void cosmology_struct_restore(struct cosmology *cosmology, FILE *stream) {
+void cosmology_struct_restore(int enabled, struct cosmology *cosmology,
+                              FILE *stream) {
   restart_read_blocks((void *)cosmology, sizeof(struct cosmology), 1, stream,
                       NULL, "cosmology function");
 
-  /* Re-initialise the tables */
-  cosmology_init_tables(cosmology);
+  /* Re-initialise the tables if using a cosmology. */
+  if (enabled) cosmology_init_tables(cosmology);
 }
diff --git a/src/cosmology.h b/src/cosmology.h
index b1b33930bad7a49c0f2abe3a2201d71c9be57305..109b80a57d8dbc4eb942dd4ecbbc0db84198100b 100644
--- a/src/cosmology.h
+++ b/src/cosmology.h
@@ -70,6 +70,9 @@ struct cosmology {
   /*! Hubble constant at the current redshift (in internal units) */
   double H;
 
+  /*! The critical density at the current redshift (in internal units) */
+  double critical_density;
+
   /*! Conversion factor from internal time-step size to cosmological step */
   double time_step_factor;
 
@@ -160,7 +163,8 @@ struct cosmology {
   double universe_age_at_present_day;
 };
 
-void cosmology_update(struct cosmology *c, integertime_t ti_current);
+void cosmology_update(struct cosmology *c, const struct phys_const *phys_const,
+                      integertime_t ti_current);
 
 double cosmology_get_drift_factor(const struct cosmology *cosmo,
                                   integertime_t ti_start, integertime_t ti_end);
@@ -192,6 +196,7 @@ void cosmology_write_model(hid_t h_grp, const struct cosmology *c);
 
 /* Dump/restore. */
 void cosmology_struct_dump(const struct cosmology *cosmology, FILE *stream);
-void cosmology_struct_restore(struct cosmology *cosmology, FILE *stream);
+void cosmology_struct_restore(int enabled, struct cosmology *cosmology,
+                              FILE *stream);
 
 #endif /* SWIFT_COSMOLOGY_H */
diff --git a/src/debug.c b/src/debug.c
index 77d791a31787c7997a9a56e3204dac1160178976..b8938346f7dc253f92795db1d170e421684ae5c0 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -271,17 +271,17 @@ int checkCellhdxmax(const struct cell *c, int *depth) {
 }
 
 /**
- * @brief map function for dumping cells. In MPI mode locally active cells
- * only.
+ * @brief map function for dumping cells.
  */
 static void dumpCells_map(struct cell *c, void *data) {
   size_t *ldata = (size_t *)data;
   FILE *file = (FILE *)ldata[0];
   struct engine *e = (struct engine *)ldata[1];
   float ntasks = c->nr_tasks;
-  int active = (int)ldata[2];
-  int mpiactive = (int)ldata[3];
-  int pactive = (int)ldata[4];
+  int super = (int)ldata[2];
+  int active = (int)ldata[3];
+  int mpiactive = (int)ldata[4];
+  int pactive = (int)ldata[5];
 
 #if SWIFT_DEBUG_CHECKS
   /* The c->nr_tasks field does not include all the tasks. So let's check this
@@ -307,9 +307,11 @@ static void dumpCells_map(struct cell *c, void *data) {
 
 /* In MPI mode we may only output cells with foreign partners.
  * These define the edges of the partitions. */
+    int ismpiactive = 0;
 #if WITH_MPI
+    ismpiactive = (c->send_xv != NULL);
     if (mpiactive)
-      mpiactive = (c->send_xv != NULL);
+      mpiactive = ismpiactive;
     else
       mpiactive = 1;
 #else
@@ -322,9 +324,9 @@ static void dumpCells_map(struct cell *c, void *data) {
     else
       active = 1;
 
-    /* So output local super cells that are active and have MPI tasks as
-     * requested. */
-    if (c->nodeID == e->nodeID && (c->super == c) && active && mpiactive) {
+    /* So output local super cells that are active and have MPI
+     * tasks as requested. */
+    if (c->nodeID == e->nodeID && (!super ||(super && c->super == c)) && active && mpiactive) {
 
       /* If requested we work out how many particles are active in this cell. */
       int pactcount = 0;
@@ -342,12 +344,13 @@ static void dumpCells_map(struct cell *c, void *data) {
 
       fprintf(file,
               "  %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6d %6d %6d %6d %6d %6d "
-              "%6.1f %20lld %6d %6d %6d %6d %6d\n",
+              "%6.1f %20lld %6d %6d %6d %6d %6d %6d %6d\n",
               c->loc[0], c->loc[1], c->loc[2], c->width[0], c->width[1],
               c->width[2], e->step, c->count, c->gcount, c->scount, pactcount,
               c->depth, ntasks, c->ti_hydro_end_min,
               get_time_bin(c->ti_hydro_end_min), (c->super == c),
-              cell_is_active_hydro(c, e), c->nodeID, c->nodeID == e->nodeID);
+              (c->parent == NULL), cell_is_active_hydro(c, e), c->nodeID,
+              c->nodeID == e->nodeID, ismpiactive);
     }
   }
 }
@@ -359,6 +362,7 @@ static void dumpCells_map(struct cell *c, void *data) {
  *
  * @param prefix base output filename, result is written to
  *               %prefix%_%rank%_%step%.dat
+ * @param super just output the super cells.
  * @param active just output active cells.
  * @param mpiactive just output MPI active cells, i.e. those with foreign cells.
  * @param pactive also output a count of active particles.
@@ -366,7 +370,7 @@ static void dumpCells_map(struct cell *c, void *data) {
  * @param rank node ID of MPI rank, or 0 if not relevant.
  * @param step the current engine step, or some unique integer.
  */
-void dumpCells(const char *prefix, int active, int mpiactive, int pactive,
+void dumpCells(const char *prefix, int super, int active, int mpiactive, int pactive, 
                struct space *s, int rank, int step) {
 
   FILE *file = NULL;
@@ -379,17 +383,18 @@ void dumpCells(const char *prefix, int active, int mpiactive, int pactive,
   /* Header. */
   fprintf(file,
           "# %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s "
-          "%20s %6s %6s %6s %6s %6s\n",
+          "%20s %6s %6s %6s %6s %6s %6s %6s\n",
           "x", "y", "z", "xw", "yw", "zw", "step", "count", "gcount", "scount",
           "actcount", "depth", "tasks", "ti_end_min", "timebin", "issuper",
-          "active", "rank", "local");
+          "istop", "active", "rank", "local", "mpiactive");
 
-  size_t data[5];
+  size_t data[6];
   data[0] = (size_t)file;
   data[1] = (size_t)s->e;
-  data[2] = (size_t)active;
-  data[3] = (size_t)mpiactive;
-  data[4] = (size_t)pactive;
+  data[2] = (size_t)super;
+  data[3] = (size_t)active;
+  data[4] = (size_t)mpiactive;
+  data[5] = (size_t)pactive;
   space_map_cells_pre(s, 1, dumpCells_map, &data);
   fclose(file);
 }
diff --git a/src/debug.h b/src/debug.h
index 5a646948204cc809c66430bb01c9ee90e21d720a..1e482c05c5af2dfebff1a254018fb1802df6cc5d 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -36,8 +36,8 @@ void printgParticle_single(struct gpart *gp);
 
 int checkSpacehmax(struct space *s);
 int checkCellhdxmax(const struct cell *c, int *depth);
-void dumpCells(const char *prefix, int active, int mpiactive, int pactive,
-               struct space *s, int rank, int step);
+void dumpCells(const char *prefix, int super, int active, int mpiactive,
+               int pactive, struct space *s, int rank, int step);
 
 #if defined(WITH_MPI) && defined(HAVE_METIS)
 #include "metis.h"
diff --git a/src/engine.c b/src/engine.c
index 1537119fc11054ebbf8e36980b9d1d03c3ea27bc..0ddcedd13507993080da79cf350d1fba3588ab4a 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -3652,7 +3652,7 @@ int engine_estimate_nr_tasks(struct engine *e) {
 #endif
 
   double ntasks = n1 * ntop + n2 * (ncells - ntop);
-  tasks_per_cell = ceil(ntasks / ncells);
+  if (ncells > 0) tasks_per_cell = ceil(ntasks / ncells);
 
   if (tasks_per_cell < 1.0) tasks_per_cell = 1.0;
   if (e->verbose)
@@ -3666,10 +3666,10 @@ int engine_estimate_nr_tasks(struct engine *e) {
  * @brief Rebuild the space and tasks.
  *
  * @param e The #engine.
- * @param clean_h_values Are we cleaning up the values of h before building
- * the tasks ?
+ * @param clean_smoothing_length_values Are we cleaning up the values of
+ * the smoothing lengths before building the tasks ?
  */
-void engine_rebuild(struct engine *e, int clean_h_values) {
+void engine_rebuild(struct engine *e, int clean_smoothing_length_values) {
 
   const ticks tic = getticks();
 
@@ -3680,7 +3680,7 @@ void engine_rebuild(struct engine *e, int clean_h_values) {
   space_rebuild(e->s, e->verbose);
 
   /* Initial cleaning up session ? */
-  if (clean_h_values) space_sanitize(e->s);
+  if (clean_smoothing_length_values) space_sanitize(e->s);
 
 /* If in parallel, exchange the cell structure, top-level and neighbouring
  * multipoles. */
@@ -4372,10 +4372,10 @@ void engine_step(struct engine *e) {
   if (e->nodeID == 0) {
 
     /* Print some information to the screen */
-    printf("  %6d %14e %14e %14e %4d %4d %12zu %12zu %12zu %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);
+    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);
     fflush(stdout);
 
     fprintf(e->file_timesteps,
@@ -4396,7 +4396,7 @@ void engine_step(struct engine *e) {
 
   if (e->policy & engine_policy_cosmology) {
     e->time_old = e->time;
-    cosmology_update(e->cosmology, e->ti_current);
+    cosmology_update(e->cosmology, e->physical_constants, e->ti_current);
     e->time = e->cosmology->time;
     e->time_step = e->time - e->time_old;
   } else {
@@ -4405,6 +4405,10 @@ void engine_step(struct engine *e) {
     e->time_step = (e->ti_current - e->ti_old) * e->time_base;
   }
 
+  /* Update the softening lengths */
+  if (e->policy & engine_policy_self_gravity)
+    gravity_update(e->gravity_properties, e->cosmology);
+
   /* Prepare the tasks to be launched, rebuild or repartition if needed. */
   engine_prepare(e);
 
@@ -4428,8 +4432,8 @@ void engine_step(struct engine *e) {
   /* Print the number of active tasks ? */
   if (e->verbose) engine_print_task_counts(e);
 
-/* Dump local cells and active particle counts. */
-/* dumpCells("cells", 0, 0, 1, e->s, e->nodeID, e->step); */
+  /* Dump local cells and active particle counts. */
+  /* dumpCells("cells", 0, 0, 0, 0, e->s, e->nodeID, e->step); */
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that we have the correct total mass in the top-level multipoles */
@@ -5197,7 +5201,7 @@ void engine_init(
     long long Ngas, long long Ndm, 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, const struct gravity_props *gravity,
+    const struct hydro_props *hydro, struct gravity_props *gravity,
     const struct external_potential *potential,
     const struct cooling_function_data *cooling_func,
     const struct chemistry_data *chemistry, struct sourceterms *sourceterms) {
@@ -5825,11 +5829,13 @@ void engine_compute_next_snapshot_time(struct engine *e) {
     if (e->policy & engine_policy_cosmology) {
       const float next_snapshot_time =
           exp(e->ti_nextSnapshot * e->time_base) * e->cosmology->a_begin;
-      message("Next output time set to a=%e.", next_snapshot_time);
+      if (e->verbose)
+        message("Next output time set to a=%e.", next_snapshot_time);
     } else {
       const float next_snapshot_time =
           e->ti_nextSnapshot * e->time_base + e->time_begin;
-      message("Next output time set to t=%e.", next_snapshot_time);
+      if (e->verbose)
+        message("Next output time set to t=%e.", next_snapshot_time);
     }
   }
 }
@@ -5928,7 +5934,7 @@ void engine_struct_restore(struct engine *e, FILE *stream) {
 
   struct cosmology *cosmo =
       (struct cosmology *)malloc(sizeof(struct cosmology));
-  cosmology_struct_restore(cosmo, stream);
+  cosmology_struct_restore(e->policy & engine_policy_cosmology, cosmo, stream);
   e->cosmology = cosmo;
 
 #ifdef WITH_MPI
diff --git a/src/engine.h b/src/engine.h
index e95a2c950e19af268d004d66977b7f65341a69bf..f74659ae9e56c3c52a7c0e5dbee04d7b0b9c0106 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -279,7 +279,7 @@ struct engine {
   const struct hydro_props *hydro_properties;
 
   /* Properties of the self-gravity scheme */
-  const struct gravity_props *gravity_properties;
+  struct gravity_props *gravity_properties;
 
   /* Properties of external gravitational potential */
   const struct external_potential *external_potential;
@@ -336,7 +336,7 @@ void engine_init(
     long long Ngas, long long Ndm, 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, const struct gravity_props *gravity,
+    const struct hydro_props *hydro, struct gravity_props *gravity,
     const struct external_potential *potential,
     const struct cooling_function_data *cooling_func,
     const struct chemistry_data *chemistry, struct sourceterms *sourceterms);
diff --git a/src/gravity.c b/src/gravity.c
index 05f4f3724414287e5aeaa6e932ff4df7810914d9..0d951b4a9f8babb68d413e1183b1e2b52129abaf 100644
--- a/src/gravity.c
+++ b/src/gravity.c
@@ -54,6 +54,7 @@ struct exact_force_data {
 static float fewald_x[Newald + 1][Newald + 1][Newald + 1];
 static float fewald_y[Newald + 1][Newald + 1][Newald + 1];
 static float fewald_z[Newald + 1][Newald + 1][Newald + 1];
+static float potewald[Newald + 1][Newald + 1][Newald + 1];
 
 /* Factor used to normalize the access to the Ewald table */
 float ewald_fac;
@@ -83,6 +84,8 @@ void gravity_exact_force_ewald_init(double boxSize) {
   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_inv2 = 1.f / (boxSize * boxSize);
 
   /* Ewald factor to access the table */
@@ -92,6 +95,9 @@ void gravity_exact_force_ewald_init(double boxSize) {
   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) {
@@ -114,6 +120,7 @@ void gravity_exact_force_ewald_init(double boxSize) {
         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) {
@@ -130,15 +137,17 @@ void gravity_exact_force_ewald_init(double boxSize) {
               const float r_tilde_inv3 =
                   r_tilde_inv * r_tilde_inv * r_tilde_inv;
 
-              const float val =
-                  erfcf(alpha * r_tilde) +
-                  factor_exp1 * r_tilde * expf(-alpha2 * r_tilde2);
+              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 * r_tilde_inv3;
+              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;
             }
           }
         }
@@ -149,16 +158,23 @@ void gravity_exact_force_ewald_init(double boxSize) {
 
               const float h2 = h_i * h_i + h_j * h_j + h_k * h_k;
 
+              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 val = 2.f * h2_inv * expf(h2 * factor_exp2) *
-                                sinf(factor_sin * h_dot_x);
+              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_f = 2.f * common * sinf(factor_sin * h_dot_x);
 
               /* Second correction term */
-              f_x -= val * h_i;
-              f_y -= val * h_j;
-              f_z -= val * h_k;
+              f_x -= val_f * h_i;
+              f_y -= val_f * h_j;
+              f_z -= val_f * h_k;
+              pot -= val_pot;
             }
           }
         }
@@ -167,6 +183,7 @@ void gravity_exact_force_ewald_init(double boxSize) {
         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;
       }
     }
   }
@@ -196,6 +213,11 @@ void gravity_exact_force_ewald_init(double boxSize) {
   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
@@ -207,6 +229,7 @@ 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;
       }
     }
   }
@@ -229,11 +252,12 @@ void gravity_exact_force_ewald_init(double boxSize) {
  * @param rx x-coordinate of distance vector.
  * @param ry y-coordinate of distance vector.
  * @param rz z-coordinate of distance vector.
- * @param corr (return) The Ewald correction.
+ * @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[3]) {
+                                   double corr_f[3], double *corr_p) {
 
   const double s_x = (rx < 0.) ? 1. : -1.;
   const double s_y = (ry < 0.) ? 1. : -1.;
@@ -258,40 +282,51 @@ gravity_exact_force_ewald_evaluate(double rx, double ry, double rz,
   const double tz = 1. - dz;
 
   /* Interpolation in X */
-  corr[0] = 0.;
-  corr[0] += fewald_x[i + 0][j + 0][k + 0] * tx * ty * tz;
-  corr[0] += fewald_x[i + 0][j + 0][k + 1] * tx * ty * dz;
-  corr[0] += fewald_x[i + 0][j + 1][k + 0] * tx * dy * tz;
-  corr[0] += fewald_x[i + 0][j + 1][k + 1] * tx * dy * dz;
-  corr[0] += fewald_x[i + 1][j + 0][k + 0] * dx * ty * tz;
-  corr[0] += fewald_x[i + 1][j + 0][k + 1] * dx * ty * dz;
-  corr[0] += fewald_x[i + 1][j + 1][k + 0] * dx * dy * tz;
-  corr[0] += fewald_x[i + 1][j + 1][k + 1] * dx * dy * dz;
-  corr[0] *= s_x;
+  corr_f[0] = 0.;
+  corr_f[0] += fewald_x[i + 0][j + 0][k + 0] * tx * ty * tz;
+  corr_f[0] += fewald_x[i + 0][j + 0][k + 1] * tx * ty * dz;
+  corr_f[0] += fewald_x[i + 0][j + 1][k + 0] * tx * dy * tz;
+  corr_f[0] += fewald_x[i + 0][j + 1][k + 1] * tx * dy * dz;
+  corr_f[0] += fewald_x[i + 1][j + 0][k + 0] * dx * ty * tz;
+  corr_f[0] += fewald_x[i + 1][j + 0][k + 1] * dx * ty * dz;
+  corr_f[0] += fewald_x[i + 1][j + 1][k + 0] * dx * dy * tz;
+  corr_f[0] += fewald_x[i + 1][j + 1][k + 1] * dx * dy * dz;
+  corr_f[0] *= s_x;
 
   /* Interpolation in Y */
-  corr[1] = 0.;
-  corr[1] += fewald_y[i + 0][j + 0][k + 0] * tx * ty * tz;
-  corr[1] += fewald_y[i + 0][j + 0][k + 1] * tx * ty * dz;
-  corr[1] += fewald_y[i + 0][j + 1][k + 0] * tx * dy * tz;
-  corr[1] += fewald_y[i + 0][j + 1][k + 1] * tx * dy * dz;
-  corr[1] += fewald_y[i + 1][j + 0][k + 0] * dx * ty * tz;
-  corr[1] += fewald_y[i + 1][j + 0][k + 1] * dx * ty * dz;
-  corr[1] += fewald_y[i + 1][j + 1][k + 0] * dx * dy * tz;
-  corr[1] += fewald_y[i + 1][j + 1][k + 1] * dx * dy * dz;
-  corr[1] *= s_y;
+  corr_f[1] = 0.;
+  corr_f[1] += fewald_y[i + 0][j + 0][k + 0] * tx * ty * tz;
+  corr_f[1] += fewald_y[i + 0][j + 0][k + 1] * tx * ty * dz;
+  corr_f[1] += fewald_y[i + 0][j + 1][k + 0] * tx * dy * tz;
+  corr_f[1] += fewald_y[i + 0][j + 1][k + 1] * tx * dy * dz;
+  corr_f[1] += fewald_y[i + 1][j + 0][k + 0] * dx * ty * tz;
+  corr_f[1] += fewald_y[i + 1][j + 0][k + 1] * dx * ty * dz;
+  corr_f[1] += fewald_y[i + 1][j + 1][k + 0] * dx * dy * tz;
+  corr_f[1] += fewald_y[i + 1][j + 1][k + 1] * dx * dy * dz;
+  corr_f[1] *= s_y;
 
   /* Interpolation in Z */
-  corr[2] = 0.;
-  corr[2] += fewald_z[i + 0][j + 0][k + 0] * tx * ty * tz;
-  corr[2] += fewald_z[i + 0][j + 0][k + 1] * tx * ty * dz;
-  corr[2] += fewald_z[i + 0][j + 1][k + 0] * tx * dy * tz;
-  corr[2] += fewald_z[i + 0][j + 1][k + 1] * tx * dy * dz;
-  corr[2] += fewald_z[i + 1][j + 0][k + 0] * dx * ty * tz;
-  corr[2] += fewald_z[i + 1][j + 0][k + 1] * dx * ty * dz;
-  corr[2] += fewald_z[i + 1][j + 1][k + 0] * dx * dy * tz;
-  corr[2] += fewald_z[i + 1][j + 1][k + 1] * dx * dy * dz;
-  corr[2] *= s_z;
+  corr_f[2] = 0.;
+  corr_f[2] += fewald_z[i + 0][j + 0][k + 0] * tx * ty * tz;
+  corr_f[2] += fewald_z[i + 0][j + 0][k + 1] * tx * ty * dz;
+  corr_f[2] += fewald_z[i + 0][j + 1][k + 0] * tx * dy * tz;
+  corr_f[2] += fewald_z[i + 0][j + 1][k + 1] * tx * dy * dz;
+  corr_f[2] += fewald_z[i + 1][j + 0][k + 0] * dx * ty * tz;
+  corr_f[2] += fewald_z[i + 1][j + 0][k + 1] * dx * ty * dz;
+  corr_f[2] += fewald_z[i + 1][j + 1][k + 0] * dx * dy * tz;
+  corr_f[2] += fewald_z[i + 1][j + 1][k + 1] * dx * dy * dz;
+  corr_f[2] *= s_z;
+
+  /* Interpolation for potential */
+  *corr_p = 0.;
+  *corr_p += potewald[i + 0][j + 0][k + 0] * tx * ty * tz;
+  *corr_p += potewald[i + 0][j + 0][k + 1] * tx * ty * dz;
+  *corr_p += potewald[i + 0][j + 1][k + 0] * tx * dy * tz;
+  *corr_p += potewald[i + 0][j + 1][k + 1] * tx * dy * dz;
+  *corr_p += potewald[i + 1][j + 0][k + 0] * dx * ty * tz;
+  *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;
 }
 #endif
 
@@ -383,6 +418,7 @@ void gravity_exact_force_compute_mapper(void *map_data, int nr_gparts,
 
       /* Be ready for the calculation */
       double a_grav[3] = {0., 0., 0.};
+      double pot = 0.;
 
       /* Interact it with all other particles in the space.*/
       for (int j = 0; j < (int)s->nr_gparts; ++j) {
@@ -408,37 +444,42 @@ void gravity_exact_force_compute_mapper(void *map_data, int nr_gparts,
         const double r_inv = 1. / sqrt(r2);
         const double r = r2 * r_inv;
         const double mj = gpj->mass;
-        double f;
+        double f, phi;
 
         if (r >= hi) {
 
           /* Get Newtonian gravity */
           f = mj * r_inv * r_inv * r_inv;
+          phi = -mj * r_inv;
 
         } else {
 
           const double ui = r * hi_inv;
-          double W;
+          double Wf, Wp;
 
-          kernel_grav_eval_double(ui, &W);
+          kernel_grav_eval_force_double(ui, &Wf);
+          kernel_grav_eval_pot_double(ui, &Wp);
 
           /* Get softened gravity */
-          f = mj * hi_inv3 * W;
+          f = mj * hi_inv3 * Wf;
+          phi = mj * hi_inv * Wp;
         }
 
         a_grav[0] += f * dx;
         a_grav[1] += f * dy;
         a_grav[2] += f * dz;
+        pot += phi;
 
         /* Apply Ewald correction for periodic BC */
         if (periodic && r > 1e-5 * hi) {
 
-          double corr[3];
-          gravity_exact_force_ewald_evaluate(dx, dy, dz, corr);
+          double corr_f[3], corr_pot;
+          gravity_exact_force_ewald_evaluate(dx, dy, dz, corr_f, &corr_pot);
 
-          a_grav[0] += mj * corr[0];
-          a_grav[1] += mj * corr[1];
-          a_grav[2] += mj * corr[2];
+          a_grav[0] += mj * corr_f[0];
+          a_grav[1] += mj * corr_f[1];
+          a_grav[2] += mj * corr_f[2];
+          pot += mj * corr_pot;
         }
       }
 
@@ -446,6 +487,7 @@ void gravity_exact_force_compute_mapper(void *map_data, int nr_gparts,
       gpi->a_grav_exact[0] = a_grav[0] * const_G;
       gpi->a_grav_exact[1] = a_grav[1] * const_G;
       gpi->a_grav_exact[2] = a_grav[2] * const_G;
+      gpi->potential_exact = pot * const_G;
 
       counter++;
     }
@@ -529,8 +571,9 @@ void gravity_exact_force_check(struct space *s, const struct engine *e,
   fprintf(file_swift, "# theta= %16.8e\n", e->gravity_properties->theta_crit);
   fprintf(file_swift, "# Git Branch: %s\n", git_branch());
   fprintf(file_swift, "# Git Revision: %s\n", git_revision());
-  fprintf(file_swift, "# %16s %16s %16s %16s %16s %16s %16s\n", "id", "pos[0]",
-          "pos[1]", "pos[2]", "a_swift[0]", "a_swift[1]", "a_swift[2]");
+  fprintf(file_swift, "# %16s %16s %16s %16s %16s %16s %16s %16s\n", "id",
+          "pos[0]", "pos[1]", "pos[2]", "a_swift[0]", "a_swift[1]",
+          "a_swift[2]", "potential");
 
   /* Output particle SWIFT accelerations  */
   for (size_t i = 0; i < s->nr_gparts; ++i) {
@@ -541,9 +584,10 @@ void gravity_exact_force_check(struct space *s, const struct engine *e,
     if (gpi->id_or_neg_offset % SWIFT_GRAVITY_FORCE_CHECKS == 0 &&
         gpart_is_starting(gpi, e)) {
 
-      fprintf(file_swift, "%18lld %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e \n",
+      fprintf(file_swift,
+              "%18lld %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e\n",
               gpi->id_or_neg_offset, gpi->x[0], gpi->x[1], gpi->x[2],
-              gpi->a_grav[0], gpi->a_grav[1], gpi->a_grav[2]);
+              gpi->a_grav[0], gpi->a_grav[1], gpi->a_grav[2], gpi->potential);
     }
   }
 
@@ -569,9 +613,9 @@ void gravity_exact_force_check(struct space *s, const struct engine *e,
     fprintf(file_exact, "# periodic= %d\n", s->periodic);
     fprintf(file_exact, "# Git Branch: %s\n", git_branch());
     fprintf(file_exact, "# Git Revision: %s\n", git_revision());
-    fprintf(file_exact, "# %16s %16s %16s %16s %16s %16s %16s\n", "id",
+    fprintf(file_exact, "# %16s %16s %16s %16s %16s %16s %16s %16s\n", "id",
             "pos[0]", "pos[1]", "pos[2]", "a_exact[0]", "a_exact[1]",
-            "a_exact[2]");
+            "a_exact[2]", "potential");
 
     /* Output particle exact accelerations  */
     for (size_t i = 0; i < s->nr_gparts; ++i) {
@@ -582,10 +626,11 @@ void gravity_exact_force_check(struct space *s, const struct engine *e,
       if (gpi->id_or_neg_offset % SWIFT_GRAVITY_FORCE_CHECKS == 0 &&
           gpart_is_starting(gpi, e)) {
 
-        fprintf(
-            file_exact, "%18lld %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e \n",
-            gpi->id_or_neg_offset, gpi->x[0], gpi->x[1], gpi->x[2],
-            gpi->a_grav_exact[0], gpi->a_grav_exact[1], gpi->a_grav_exact[2]);
+        fprintf(file_exact,
+                "%18lld %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e %16.8e\n",
+                gpi->id_or_neg_offset, gpi->x[0], gpi->x[1], gpi->x[2],
+                gpi->a_grav_exact[0], gpi->a_grav_exact[1],
+                gpi->a_grav_exact[2], gpi->potential_exact);
       }
     }
 
diff --git a/src/gravity.h b/src/gravity.h
index 85e42370bc456dceb577c42ee609e3f0724e14ea..33bef8ba329aa1264334e3319b6250c01b974338 100644
--- a/src/gravity.h
+++ b/src/gravity.h
@@ -24,16 +24,17 @@
 
 /* Local headers. */
 #include "const.h"
-#include "engine.h"
 #include "inline.h"
 #include "part.h"
-#include "space.h"
 
 /* So far only one model here */
 /* Straight-forward import */
 #include "./gravity/Default/gravity.h"
 #include "./gravity/Default/gravity_iact.h"
 
+struct engine;
+struct space;
+
 void gravity_exact_force_ewald_init(double boxSize);
 void gravity_exact_force_ewald_free();
 void gravity_exact_force_compute(struct space *s, const struct engine *e);
diff --git a/src/gravity/Default/gravity.h b/src/gravity/Default/gravity.h
index bc06b17188f99785404a1162d46384ee26c884ea..eabc95fc7eb1e1d4039dc9cea6611f1d17451a37 100644
--- a/src/gravity/Default/gravity.h
+++ b/src/gravity/Default/gravity.h
@@ -21,8 +21,10 @@
 #define SWIFT_DEFAULT_GRAVITY_H
 
 #include <float.h>
+
 #include "cosmology.h"
 #include "gravity_properties.h"
+#include "kernel_gravity.h"
 #include "minmax.h"
 
 /**
@@ -40,24 +42,38 @@ __attribute__((always_inline)) INLINE static float gravity_get_mass(
  * @brief Returns the softening of a particle
  *
  * @param gp The particle of interest
+ * @param grav_props The global gravity properties.
  */
 __attribute__((always_inline)) INLINE static float gravity_get_softening(
-    const struct gpart* restrict gp) {
+    const struct gpart* gp, const struct gravity_props* restrict grav_props) {
 
-  return gp->epsilon;
+  return grav_props->epsilon_cur;
 }
 
 /**
- * @brief Returns the potential of a particle
+ * @brief Returns the comoving potential of a particle
  *
  * @param gp The particle of interest
  */
-__attribute__((always_inline)) INLINE static float gravity_get_potential(
-    const struct gpart* restrict gp) {
+__attribute__((always_inline)) INLINE static float
+gravity_get_comoving_potential(const struct gpart* restrict gp) {
 
   return gp->potential;
 }
 
+/**
+ * @brief Returns the physical potential of a particle
+ *
+ * @param gp The particle of interest.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static float
+gravity_get_physical_potential(const struct gpart* restrict gp,
+                               const struct cosmology* cosmo) {
+
+  return gp->potential * cosmo->a_inv;
+}
+
 /**
  * @brief Computes the gravity time-step of a given particle due to self-gravity
  *
@@ -89,12 +105,10 @@ gravity_compute_timestep_self(const struct gpart* const gp,
 
   const float ac_inv = (ac2 > 0.f) ? 1.f / sqrtf(ac2) : FLT_MAX;
 
-  // MATTHIEU cosmological evolution of the softening?
-  const float epsilon = gravity_get_softening(gp);
+  const float epsilon = gravity_get_softening(gp, grav_props);
 
-  /* Note that 0.66666667 = 2. (from Gadget) / 3. (Plummer softening) */
-  const float dt =
-      sqrtf(0.66666667f * cosmo->a * grav_props->eta * epsilon * ac_inv);
+  const float dt = sqrtf(2. * kernel_gravity_softening_plummer_equivalent_inv *
+                         cosmo->a * grav_props->eta * epsilon * ac_inv);
 
   return dt;
 }
@@ -136,6 +150,7 @@ __attribute__((always_inline)) INLINE static void gravity_end_force(
   gp->a_grav[0] *= const_G;
   gp->a_grav[1] *= const_G;
   gp->a_grav[2] *= const_G;
+  gp->potential *= const_G;
 }
 
 /**
@@ -169,7 +184,6 @@ __attribute__((always_inline)) INLINE static void gravity_first_init_gpart(
     struct gpart* gp, const struct gravity_props* grav_props) {
 
   gp->time_bin = 0;
-  gp->epsilon = grav_props->epsilon;
 
   gravity_init_gpart(gp);
 }
diff --git a/src/gravity/Default/gravity_debug.h b/src/gravity/Default/gravity_debug.h
index b83f8c73b4678145f2ecd6d94b136b5aadffccbd..dce038c58e1769446861bdf6c9a2a44415642c68 100644
--- a/src/gravity/Default/gravity_debug.h
+++ b/src/gravity/Default/gravity_debug.h
@@ -22,9 +22,9 @@
 __attribute__((always_inline)) INLINE static void gravity_debug_particle(
     const struct gpart* p) {
   printf(
-      "mass=%.3e epsilon=%.5e time_bin=%d\n"
+      "mass=%.3e time_bin=%d\n"
       "x=[%.5e,%.5e,%.5e], v_full=[%.5e,%.5e,%.5e], a=[%.5e,%.5e,%.5e]\n",
-      p->mass, p->epsilon, p->time_bin, p->x[0], p->x[1], p->x[2], p->v_full[0],
+      p->mass, p->time_bin, p->x[0], p->x[1], p->x[2], p->v_full[0],
       p->v_full[1], p->v_full[2], p->a_grav[0], p->a_grav[1], p->a_grav[2]);
 #ifdef SWIFT_DEBUG_CHECKS
   printf("num_interacted=%lld ti_drift=%lld ti_kick=%lld\n", p->num_interacted,
diff --git a/src/gravity/Default/gravity_iact.h b/src/gravity/Default/gravity_iact.h
index 1f4310eea49a1b1b298fe00f00e1ae0802d9b688..3951f77d59e71439d2f621dd2a69fd62d39ba4f0 100644
--- a/src/gravity/Default/gravity_iact.h
+++ b/src/gravity/Default/gravity_iact.h
@@ -38,9 +38,11 @@
  * @param h_inv3 Cube of the inverse of the softening length.
  * @param mass Mass of the point-mass.
  * @param f_ij (return) The force intensity.
+ * @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 r2, float h2, float h_inv, float h_inv3, float mass, float *f_ij,
+    float *pot_ij) {
 
   /* Get the inverse distance */
   const float r_inv = 1.f / sqrtf(r2);
@@ -50,17 +52,20 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_full(
 
     /* Get Newtonian gravity */
     *f_ij = mass * r_inv * r_inv * r_inv;
+    *pot_ij = -mass * r_inv;
 
   } else {
 
     const float r = r2 * r_inv;
     const float ui = r * h_inv;
-    float W_ij;
 
-    kernel_grav_eval(ui, &W_ij);
+    float W_f_ij, W_pot_ij;
+    kernel_grav_force_eval(ui, &W_f_ij);
+    kernel_grav_pot_eval(ui, &W_pot_ij);
 
     /* Get softened gravity */
-    *f_ij = mass * h_inv3 * W_ij;
+    *f_ij = mass * h_inv3 * W_f_ij;
+    *pot_ij = mass * h_inv * W_pot_ij;
   }
 }
 
@@ -78,10 +83,11 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_full(
  * @param mass Mass of the point-mass.
  * @param rlr_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 *f_ij, float *pot_ij) {
 
   /* Get the inverse distance */
   const float r_inv = 1.f / sqrtf(r2);
@@ -92,23 +98,28 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated(
 
     /* Get Newtonian gravity */
     *f_ij = mass * r_inv * r_inv * r_inv;
+    *pot_ij = -mass * r_inv;
 
   } else {
 
     const float ui = r * h_inv;
-    float W_ij;
+    float W_f_ij, W_pot_ij;
 
-    kernel_grav_eval(ui, &W_ij);
+    kernel_grav_force_eval(ui, &W_f_ij);
+    kernel_grav_pot_eval(ui, &W_pot_ij);
 
     /* Get softened gravity */
-    *f_ij = mass * h_inv3 * W_ij;
+    *f_ij = mass * h_inv3 * W_f_ij;
+    *pot_ij = mass * h_inv * W_pot_ij;
   }
 
   /* Get long-range correction */
   const float u_lr = r * rlr_inv;
-  float corr_lr;
-  kernel_long_grav_eval(u_lr, &corr_lr);
-  *f_ij *= corr_lr;
+  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);
+  *f_ij *= corr_f_lr;
+  *pot_ij *= corr_pot_lr;
 }
 
 /**
@@ -127,33 +138,43 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated(
  * @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(
     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) {
+    const struct multipole *m, float *f_x, float *f_y, float *f_z, float *pot) {
 
 #if SELF_GRAVITY_MULTIPOLE_ORDER < 3
-  runner_iact_grav_pp_full(r2, h * h, h_inv, h_inv3, m->M_000, f_ij);
+  float f_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;
 #else
 
   /* Get the inverse distance */
   const float r_inv = 1.f / sqrtf(r2);
 
-  struct potential_derivatives_M2P pot;
-  compute_potential_derivatives_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, &pot);
+  struct potential_derivatives_M2P d;
+  compute_potential_derivatives_M2P(r_x, r_y, r_z, r2, r_inv, h, h_inv, &d);
 
   /* 1st order terms (monopole) */
-  *f_x = m->M_000 * pot.D_100;
-  *f_y = m->M_000 * pot.D_010;
-  *f_z = m->M_000 * pot.D_001;
+  *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;
 
   /* 3rd order terms (quadrupole) */
-  *f_x += m->M_200 * pot.D_300 + m->M_020 * pot.D_120 + m->M_002 * pot.D_102;
-  *f_y += m->M_200 * pot.D_210 + m->M_020 * pot.D_030 + m->M_002 * pot.D_012;
-  *f_z += m->M_200 * pot.D_201 + m->M_020 * pot.D_021 + m->M_002 * pot.D_003;
-  *f_x += m->M_110 * pot.D_210 + m->M_101 * pot.D_201 + m->M_011 * pot.D_111;
-  *f_y += m->M_110 * pot.D_120 + m->M_101 * pot.D_111 + m->M_011 * pot.D_021;
-  *f_z += m->M_110 * pot.D_111 + m->M_101 * pot.D_102 + m->M_011 * pot.D_012;
+  *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;
+
+  *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;
 
 #endif
 }
diff --git a/src/gravity/Default/gravity_io.h b/src/gravity/Default/gravity_io.h
index 26f7a6d15e11cf2e2d9ccd9978a1c67bab17d852..2508f69a636b93c218cdcb0770cf00ef969389c2 100644
--- a/src/gravity/Default/gravity_io.h
+++ b/src/gravity/Default/gravity_io.h
@@ -35,6 +35,38 @@ 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) {
+
+  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, gp->time_bin);
+  const integertime_t ti_end = get_integer_time_end(ti_current, gp->time_bin);
+
+  /* Get time-step since the last kick */
+  float dt_kick_grav;
+  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);
+  } else {
+    dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base;
+  }
+
+  /* Extrapolate the velocites to the current time */
+  ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav;
+  ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav;
+  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;
+}
+
 /**
  * @brief Specifies which g-particle fields to read from a dataset
  *
@@ -69,20 +101,20 @@ void darkmatter_read_particles(struct gpart* gparts, struct io_props* list,
 void darkmatter_write_particles(const struct gpart* gparts,
                                 struct io_props* list, int* num_fields) {
 
-  /* Say how much we want to read */
+  /* Say how much we want to write */
   *num_fields = 5;
 
-  /* List what we want to read */
+  /* List what we want to write */
   list[0] = io_make_output_field_convert_gpart(
       "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, gparts, convert_gpart_pos);
-  list[1] = io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED,
-                                 gparts, v_full);
+  list[1] = io_make_output_field_convert_gpart(
+      "Velocities", FLOAT, 3, UNIT_CONV_SPEED, gparts, convert_gpart_vel);
   list[2] =
       io_make_output_field("Masses", FLOAT, 1, UNIT_CONV_MASS, gparts, mass);
   list[3] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
                                  UNIT_CONV_NO_UNITS, gparts, id_or_neg_offset);
-  list[4] = io_make_output_field("Acceleration", FLOAT, 3,
-                                 UNIT_CONV_ACCELERATION, gparts, a_grav);
+  list[4] = io_make_output_field("Potential", FLOAT, 1, UNIT_CONV_POTENTIAL,
+                                 gparts, potential);
 }
 
 #endif /* SWIFT_DEFAULT_GRAVITY_IO_H */
diff --git a/src/gravity/Default/gravity_part.h b/src/gravity/Default/gravity_part.h
index 33c444e24e4bc9681dc3138298b5fd9301d084d3..06b9c52e14a655aa97b6c36ff9dca1f1cdd6e9a0 100644
--- a/src/gravity/Default/gravity_part.h
+++ b/src/gravity/Default/gravity_part.h
@@ -22,35 +22,32 @@
 /* Gravity particle. */
 struct gpart {
 
-  /* Particle ID. If negative, it is the negative offset of the #part with
+  /*! Particle ID. If negative, it is the negative offset of the #part with
      which this gpart is linked. */
   long long id_or_neg_offset;
 
-  /* Particle position. */
+  /*! Particle position. */
   double x[3];
 
-  /* Offset between current position and position at last tree rebuild. */
+  /*! Offset between current position and position at last tree rebuild. */
   float x_diff[3];
 
-  /* Particle velocity. */
+  /*! Particle velocity. */
   float v_full[3];
 
-  /* Particle acceleration. */
+  /*! Particle acceleration. */
   float a_grav[3];
 
-  /* Particle mass. */
+  /*! Particle mass. */
   float mass;
 
-  /* Gravitational potential */
+  /*! Gravitational potential */
   float potential;
 
-  /* Softening length */
-  float epsilon;
-
-  /* Time-step length */
+  /*! Time-step length */
   timebin_t time_bin;
 
-  /* Type of the #gpart (DM, gas, star, ...) */
+  /*! Type of the #gpart (DM, gas, star, ...) */
   enum part_type type;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -71,6 +68,8 @@ struct gpart {
   /* Brute-force particle acceleration. */
   double a_grav_exact[3];
 
+  /* Brute-force particle potential. */
+  double potential_exact;
 #endif
 
 } SWIFT_STRUCT_ALIGN;
diff --git a/src/gravity_cache.h b/src/gravity_cache.h
index d46dc3cb1f307aee0b5c7352ea3c5b27b640cfdc..0012be193c657abccd5d83ed872e4d21764065a0 100644
--- a/src/gravity_cache.h
+++ b/src/gravity_cache.h
@@ -59,6 +59,9 @@ struct gravity_cache {
   /*! #gpart z acceleration. */
   float *restrict a_z SWIFT_CACHE_ALIGN;
 
+  /*! #gpart potential. */
+  float *restrict pot SWIFT_CACHE_ALIGN;
+
   /*! Is this #gpart active ? */
   int *restrict active SWIFT_CACHE_ALIGN;
 
@@ -85,6 +88,7 @@ static INLINE void gravity_cache_clean(struct gravity_cache *c) {
     free(c->a_x);
     free(c->a_y);
     free(c->a_z);
+    free(c->pot);
     free(c->active);
     free(c->use_mpole);
   }
@@ -120,6 +124,7 @@ static INLINE void gravity_cache_init(struct gravity_cache *c, int count) {
   e += posix_memalign((void **)&c->a_x, SWIFT_CACHE_ALIGNMENT, sizeBytesF);
   e += posix_memalign((void **)&c->a_y, SWIFT_CACHE_ALIGNMENT, sizeBytesF);
   e += posix_memalign((void **)&c->a_z, SWIFT_CACHE_ALIGNMENT, sizeBytesF);
+  e += posix_memalign((void **)&c->pot, SWIFT_CACHE_ALIGNMENT, sizeBytesF);
   e += posix_memalign((void **)&c->active, SWIFT_CACHE_ALIGNMENT, sizeBytesI);
   e +=
       posix_memalign((void **)&c->use_mpole, SWIFT_CACHE_ALIGNMENT, sizeBytesI);
@@ -144,14 +149,16 @@ static INLINE void gravity_cache_init(struct gravity_cache *c, int count) {
  * @param shift A shift to apply to all the particles.
  * @param CoM The position of the multipole.
  * @param r_max2 The square of the multipole radius.
- * @param theta_crit2 The square of the opening angle.
  * @param cell The cell we play with (to get reasonable padding positions).
+ * @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, float theta_crit2,
-    const struct cell *cell) {
+    const double shift[3], const float CoM[3], float r_max2,
+    const struct cell *cell, const struct gravity_props *grav_props) {
+
+  const float theta_crit2 = grav_props->theta_crit2;
 
   /* Make the compiler understand we are in happy vectorization land */
   swift_declare_aligned_ptr(float, x, c->x, SWIFT_CACHE_ALIGNMENT);
@@ -169,7 +176,7 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate(
     x[i] = (float)(gparts[i].x[0] - shift[0]);
     y[i] = (float)(gparts[i].x[1] - shift[1]);
     z[i] = (float)(gparts[i].x[2] - shift[2]);
-    epsilon[i] = gparts[i].epsilon;
+    epsilon[i] = gravity_get_softening(&gparts[i], grav_props);
     m[i] = gparts[i].mass;
     active[i] = (int)(gparts[i].time_bin <= max_active_bin);
 
@@ -215,13 +222,15 @@ __attribute__((always_inline)) INLINE static void gravity_cache_populate(
  * multiple of the vector length.
  * @param shift A shift to apply to all the particles.
  * @param cell The cell we play with (to get reasonable padding positions).
+ * @param grav_props The global gravity properties.
  */
 __attribute__((always_inline)) INLINE static void
 gravity_cache_populate_no_mpole(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 cell *cell,
+                                const struct gravity_props *grav_props) {
 
   /* Make the compiler understand we are in happy vectorization land */
   swift_declare_aligned_ptr(float, x, c->x, SWIFT_CACHE_ALIGNMENT);
@@ -237,7 +246,7 @@ gravity_cache_populate_no_mpole(timebin_t max_active_bin,
     x[i] = (float)(gparts[i].x[0] - shift[0]);
     y[i] = (float)(gparts[i].x[1] - shift[1]);
     z[i] = (float)(gparts[i].x[2] - shift[2]);
-    epsilon[i] = gparts[i].epsilon;
+    epsilon[i] = gravity_get_softening(&gparts[i], grav_props);
     m[i] = gparts[i].mass;
     active[i] = (int)(gparts[i].time_bin <= max_active_bin);
   }
@@ -278,6 +287,7 @@ __attribute__((always_inline)) INLINE void gravity_cache_write_back(
   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_declare_aligned_ptr(int, active, c->active, SWIFT_CACHE_ALIGNMENT);
 
   /* Write stuff back to the particles */
@@ -286,6 +296,7 @@ __attribute__((always_inline)) INLINE void gravity_cache_write_back(
       gparts[i].a_grav[0] += a_x[i];
       gparts[i].a_grav[1] += a_y[i];
       gparts[i].a_grav[2] += a_z[i];
+      gparts[i].potential += pot[i];
     }
   }
 }
diff --git a/src/gravity_derivatives.h b/src/gravity_derivatives.h
index cf8aa54338b2e87e8bf5f2cc453ad7417eea5804..d698c66e418a120e5e7ebbe1cba0a5a9af8cd1f1 100644
--- a/src/gravity_derivatives.h
+++ b/src/gravity_derivatives.h
@@ -95,9 +95,16 @@ struct potential_derivatives_M2L {
  */
 struct potential_derivatives_M2P {
 
+  /* 0th order terms */
+  float D_000;
+
   /* 1st order terms */
   float D_100, D_010, D_001;
 
+  /* 2nd order terms */
+  float D_200, D_020, D_002;
+  float D_110, D_101, D_011;
+
   /* 3rd order terms */
   float D_300, D_030, D_003;
   float D_210, D_201;
@@ -368,11 +375,22 @@ compute_potential_derivatives_M2P(float r_x, float r_y, float r_z, float r2,
   const float r_y3 = r_y2 * r_y;
   const float r_z3 = r_z2 * r_z;
 
+  /* 0th order derivative */
+  pot->D_000 = Dt_1;
+
   /* 1st order derivatives */
   pot->D_100 = r_x * Dt_3;
   pot->D_010 = r_y * Dt_3;
   pot->D_001 = r_z * Dt_3;
 
+  /* 2nd order derivatives */
+  pot->D_200 = r_x2 * Dt_5 + Dt_3;
+  pot->D_020 = r_y2 * Dt_5 + Dt_3;
+  pot->D_002 = r_z2 * Dt_5 + Dt_3;
+  pot->D_110 = r_x * r_y * Dt_5;
+  pot->D_101 = r_x * r_z * Dt_5;
+  pot->D_011 = r_y * r_z * Dt_5;
+
   /* 3rd order derivatives */
   pot->D_300 = r_x3 * Dt_7 + 3.f * r_x * Dt_5;
   pot->D_030 = r_y3 * Dt_7 + 3.f * r_y * Dt_5;
diff --git a/src/gravity_properties.c b/src/gravity_properties.c
index 1df421a8b21424dc99e52bc9dbc59560e54d14a5..79be11a8bab2389c19c58a59851379ea23d5fb3f 100644
--- a/src/gravity_properties.c
+++ b/src/gravity_properties.c
@@ -37,7 +37,8 @@
 #define gravity_props_default_r_cut_min 0.1f
 
 void gravity_props_init(struct gravity_props *p,
-                        const struct swift_params *params) {
+                        const struct swift_params *params,
+                        const struct cosmology *cosmo) {
 
   /* Tree-PM parameters */
   p->a_smooth = parser_get_opt_param_float(params, "Gravity:a_smooth",
@@ -56,11 +57,35 @@ void gravity_props_init(struct gravity_props *p,
   p->theta_crit2 = p->theta_crit * p->theta_crit;
   p->theta_crit_inv = 1. / p->theta_crit;
 
-  /* Softening lengths */
-  p->epsilon = 3. * parser_get_param_double(params, "Gravity:epsilon");
-  p->epsilon2 = p->epsilon * p->epsilon;
-  p->epsilon_inv = 1.f / p->epsilon;
-  p->epsilon_inv3 = p->epsilon_inv * p->epsilon_inv * p->epsilon_inv;
+  /* 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");
+
+  /* Set the softening to the current time */
+  gravity_update(p, cosmo);
+}
+
+void gravity_update(struct gravity_props *p, const struct cosmology *cosmo) {
+
+  /* Current softening lengths */
+  double softening;
+  if (p->epsilon_comoving * cosmo->a > p->epsilon_max_physical)
+    softening = p->epsilon_max_physical / cosmo->a;
+  else
+    softening = p->epsilon_comoving;
+
+  /* Plummer equivalent -> internal */
+  softening *= kernel_gravity_softening_plummer_equivalent;
+
+  /* Store things */
+  p->epsilon_cur = softening;
+
+  /* Other factors */
+  p->epsilon_cur2 = softening * softening;
+  p->epsilon_cur_inv = 1. / softening;
+  p->epsilon_cur_inv3 = 1. / (softening * softening * softening);
 }
 
 void gravity_props_print(const struct gravity_props *p) {
@@ -72,8 +97,17 @@ void gravity_props_print(const struct gravity_props *p) {
 
   message("Self-gravity opening angle:  theta=%.4f", p->theta_crit);
 
-  message("Self-gravity softening:    epsilon=%.4f (Plummer equivalent: %.4f)",
-          p->epsilon, p->epsilon / 3.);
+  message(
+      "Self-gravity comoving softening:    epsilon=%.4f (Plummer equivalent: "
+      "%.4f)",
+      p->epsilon_comoving * kernel_gravity_softening_plummer_equivalent,
+      p->epsilon_comoving);
+
+  message(
+      "Self-gravity maximal physical softening:    epsilon=%.4f (Plummer "
+      "equivalent: %.4f)",
+      p->epsilon_max_physical * kernel_gravity_softening_plummer_equivalent,
+      p->epsilon_max_physical);
 
   message("Self-gravity mesh smoothing-scale: a_smooth=%f", p->a_smooth);
 
@@ -86,9 +120,18 @@ 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_f(h_grpgrav, "Softening length", p->epsilon);
-  io_write_attribute_f(h_grpgrav, "Softening length (Plummer equivalent)",
-                       p->epsilon / 3.);
+  io_write_attribute_f(
+      h_grpgrav, "Comoving softening length",
+      p->epsilon_comoving * kernel_gravity_softening_plummer_equivalent);
+  io_write_attribute_f(h_grpgrav,
+                       "Comoving Softening length (Plummer equivalent)",
+                       p->epsilon_comoving);
+  io_write_attribute_f(
+      h_grpgrav, "Maximal physical softening length",
+      p->epsilon_max_physical * kernel_gravity_softening_plummer_equivalent);
+  io_write_attribute_f(h_grpgrav,
+                       "Maximal physical softening length (Plummer equivalent)",
+                       p->epsilon_max_physical);
   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);
diff --git a/src/gravity_properties.h b/src/gravity_properties.h
index 826ffd0de05d376b930523c8a5d937e457c6d795..f36a39a6e187b8b397a5f597692f5a37c982aa7a 100644
--- a/src/gravity_properties.h
+++ b/src/gravity_properties.h
@@ -27,6 +27,7 @@
 #endif
 
 /* Local includes. */
+#include "cosmology.h"
 #include "parser.h"
 #include "restart.h"
 
@@ -58,22 +59,30 @@ struct gravity_props {
   /*! Inverse of opening angle */
   double theta_crit_inv;
 
-  /*! Softening length */
-  float epsilon;
+  /*! Comoving softening */
+  double epsilon_comoving;
 
-  /*! Square of softening length */
-  float epsilon2;
+  /*! Maxium physical softening */
+  double epsilon_max_physical;
 
-  /*! Inverse of softening length */
-  float epsilon_inv;
+  /*! Current sftening length */
+  float epsilon_cur;
 
-  /*! Cube of the inverse of softening length */
-  float epsilon_inv3;
+  /*! Square of current softening length */
+  float epsilon_cur2;
+
+  /*! Inverse of current softening length */
+  float epsilon_cur_inv;
+
+  /*! Cube of the inverse of current oftening length */
+  float epsilon_cur_inv3;
 };
 
 void gravity_props_print(const struct gravity_props *p);
 void gravity_props_init(struct gravity_props *p,
-                        const struct swift_params *params);
+                        const struct swift_params *params,
+                        const struct cosmology *cosmo);
+void gravity_update(struct gravity_props *p, const struct cosmology *cosmo);
 
 #if defined(HAVE_HDF5)
 void gravity_props_print_snapshot(hid_t h_grpsph,
diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h
index 1228aa69abfe812e5b2e73f066bb13be3292aa20..7da005456233b800e05e75389922395ea3c1702c 100644
--- a/src/hydro/Default/hydro.h
+++ b/src/hydro/Default/hydro.h
@@ -160,6 +160,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass(
   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.
  *
@@ -492,7 +504,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @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) {}
+    struct part *restrict p, struct xpart *restrict xp, float dt_therm,
+    const struct cosmology *cosmo, const struct hydro_props *hydro_props) {}
 
 /**
  *  @brief Converts hydro quantity of a particle at the start of a run
@@ -500,9 +513,12 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
  * Requires the density to be known
  *
  * @param p The particle to act upon
+ * @param xp The extended data.
+ * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
-    struct part *restrict p, struct xpart *restrict xp) {}
+    struct part *restrict p, struct xpart *restrict xp,
+    const struct cosmology *cosmo) {}
 
 /**
  * @brief Initialises the particles for the first time
@@ -529,4 +545,21 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part(
   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_DEFAULT_HYDRO_H */
diff --git a/src/hydro/Default/hydro_io.h b/src/hydro/Default/hydro_io.h
index 2567545b25f55e03b104844a4142da9cbe397fb5..e70a26f9edc3f7497dd4c548efd464726efe0e39 100644
--- a/src/hydro/Default/hydro_io.h
+++ b/src/hydro/Default/hydro_io.h
@@ -54,7 +54,7 @@ void hydro_read_particles(struct part* parts, struct io_props* list,
 }
 
 void convert_part_pos(const struct engine* e, const struct part* p,
-                      double* ret) {
+                      const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -67,6 +67,49 @@ 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) {
+
+  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->a2_inv;
+  ret[1] *= cosmo->a2_inv;
+  ret[2] *= cosmo->a2_inv;
+}
+
+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
  *
@@ -74,16 +117,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) {
+void hydro_write_particles(const struct part* parts, const struct xpart* xparts,
+                           struct io_props* list, int* num_fields) {
 
   *num_fields = 8;
 
   /* 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[1] =
-      io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v);
+  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,
@@ -92,10 +136,11 @@ void hydro_write_particles(struct part* parts, struct io_props* list,
                                  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("Acceleration", FLOAT, 3,
-                                 UNIT_CONV_ACCELERATION, parts, a_hydro);
-  list[7] =
+  list[6] =
       io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho);
+  list[7] = io_make_output_field_convert_part("Potential", FLOAT, 1,
+                                              UNIT_CONV_POTENTIAL, parts,
+                                              xparts, convert_part_potential);
 }
 
 /**
diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h
index 185a98b6d207bb851512a686be51414341fe7740..bc06a24e2a8245556a1042f2459273b8d750489e 100644
--- a/src/hydro/Gadget2/hydro.h
+++ b/src/hydro/Gadget2/hydro.h
@@ -170,6 +170,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass(
   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.
  *
@@ -513,19 +525,29 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param p The particle to act upon
  * @param xp The particle extended data to act upon
  * @param dt_therm The time-step for this kick (for thermodynamic quantities)
+ * @param 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) {
+    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 entropy by more than a factor of 2 */
   if (dt_therm > 0. && p->entropy_dt * dt_therm < -0.5f * xp->entropy_full) {
-    /* message("Warning! Limiting entropy_dt. Possible cooling error.\n
-     * entropy_full = %g \n entropy_dt * dt =%g \n", */
-    /* 	     xp->entropy_full,p->entropy_dt * dt); */
     p->entropy_dt = -0.5f * xp->entropy_full / dt_therm;
   }
   xp->entropy_full += p->entropy_dt * dt_therm;
 
+  /* Apply the minimal energy limit */
+  const float density = p->rho * cosmo->a3_inv;
+  const float min_energy = hydro_props->minimal_internal_energy;
+  const float min_entropy =
+      gas_entropy_from_internal_energy(density, min_energy);
+  if (xp->entropy_full < min_entropy) {
+    xp->entropy_full = min_entropy;
+    p->entropy_dt = 0.f;
+  }
+
   /* Compute the pressure */
   const float pressure = gas_pressure_from_entropy(p->rho, xp->entropy_full);
 
@@ -545,13 +567,18 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
  *
  * Requires the density to be known
  *
- * @param p The particle to act upon
+ * @param p The particle to act upon.
+ * @param xp The extended data.
+ * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
-    struct part *restrict p, struct xpart *restrict xp) {
+    struct part *restrict p, struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
 
-  /* We read u in the entropy field. We now get S from u */
-  xp->entropy_full = gas_entropy_from_internal_energy(p->rho, p->entropy);
+  /* We read u in the entropy field. We now get (comoving) S from (physical) u
+   * and (physical) rho. Note that comoving S == physical S */
+  xp->entropy_full =
+      gas_entropy_from_internal_energy(p->rho * cosmo->a3_inv, p->entropy);
   p->entropy = xp->entropy_full;
 
   /* Compute the pressure */
@@ -593,4 +620,21 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part(
   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->entropy = u_init;
+}
+
 #endif /* SWIFT_GADGET2_HYDRO_H */
diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h
index 90484c9cee1b384b0540f56e3c845cc4120cdfe0..b7fa7dd4aa00249d009037961888c89abc0d0313 100644
--- a/src/hydro/Gadget2/hydro_iact.h
+++ b/src/hydro/Gadget2/hydro_iact.h
@@ -659,9 +659,9 @@ runner_iact_nonsym_1_vec_force(
     vector viz, vector pirho, vector grad_hi, vector piPOrho2, vector balsara_i,
     vector ci, float *Vjx, float *Vjy, float *Vjz, float *Pjrho, float *Grad_hj,
     float *PjPOrho2, float *Balsara_j, float *Cj, float *Mj, vector hi_inv,
-    vector hj_inv, vector *a_hydro_xSum, vector *a_hydro_ySum,
-    vector *a_hydro_zSum, vector *h_dtSum, vector *v_sigSum,
-    vector *entropy_dtSum, mask_t mask) {
+    vector hj_inv, const float a, const float H, vector *a_hydro_xSum,
+    vector *a_hydro_ySum, vector *a_hydro_zSum, vector *h_dtSum,
+    vector *v_sigSum, vector *entropy_dtSum, mask_t mask) {
 
 #ifdef WITH_VECTORIZATION
 
@@ -687,8 +687,11 @@ runner_iact_nonsym_1_vec_force(
   const vector balsara_j = vector_load(Balsara_j);
   const vector cj = vector_load(Cj);
 
-  const vector fac_mu =
-      vector_set1(1.f); /* Will change with cosmological integration */
+  /* Cosmological terms */
+  const float fac_mu = pow_three_gamma_minus_five_over_two(a);
+  const float a2_Hubble = a * a * H;
+  const vector v_fac_mu = vector_set1(fac_mu);
+  const vector v_a2_Hubble = vector_set1(a2_Hubble);
 
   /* Load stuff. */
   balsara.v = vec_add(balsara_i.v, balsara_j.v);
@@ -718,13 +721,16 @@ runner_iact_nonsym_1_vec_force(
   dvz.v = vec_sub(viz.v, vjz.v);
 
   /* Compute dv dot r. */
-  dvdr.v = vec_fma(dvx.v, dx->v, vec_fma(dvy.v, dy->v, vec_mul(dvz.v, dz->v)));
+  dvdr.v =
+      vec_fma(dvx.v, dx->v,
+              vec_fma(dvy.v, dy->v,
+                      vec_fma(dvz.v, dz->v, vec_mul(v_a2_Hubble.v, r2->v))));
 
   /* Compute the relative velocity. (This is 0 if the particles move away from
    * each other and negative otherwise) */
   omega_ij.v = vec_fmin(dvdr.v, vec_setzero());
-  mu_ij.v =
-      vec_mul(fac_mu.v, vec_mul(ri.v, omega_ij.v)); /* This is 0 or negative */
+  mu_ij.v = vec_mul(v_fac_mu.v,
+                    vec_mul(ri.v, omega_ij.v)); /* This is 0 or negative */
 
   /* Compute signal velocity */
   v_sig.v = vec_fnma(vec_set1(3.f), mu_ij.v, vec_add(ci.v, cj.v));
@@ -787,9 +793,10 @@ runner_iact_nonsym_2_vec_force(
     vector viz, vector pirho, vector grad_hi, vector piPOrho2, vector balsara_i,
     vector ci, float *Vjx, float *Vjy, float *Vjz, float *Pjrho, float *Grad_hj,
     float *PjPOrho2, float *Balsara_j, float *Cj, float *Mj, vector hi_inv,
-    float *Hj_inv, vector *a_hydro_xSum, vector *a_hydro_ySum,
-    vector *a_hydro_zSum, vector *h_dtSum, vector *v_sigSum,
-    vector *entropy_dtSum, mask_t mask, mask_t mask_2, short mask_cond) {
+    float *Hj_inv, const float a, const float H, vector *a_hydro_xSum,
+    vector *a_hydro_ySum, vector *a_hydro_zSum, vector *h_dtSum,
+    vector *v_sigSum, vector *entropy_dtSum, mask_t mask, mask_t mask_2,
+    short mask_cond) {
 
 #ifdef WITH_VECTORIZATION
 
@@ -853,8 +860,11 @@ runner_iact_nonsym_2_vec_force(
   const vector hj_inv = vector_load(Hj_inv);
   const vector hj_inv_2 = vector_load(&Hj_inv[VEC_SIZE]);
 
-  const vector fac_mu =
-      vector_set1(1.f); /* Will change with cosmological integration */
+  /* Cosmological terms */
+  const float fac_mu = pow_three_gamma_minus_five_over_two(a);
+  const float a2_Hubble = a * a * H;
+  const vector v_fac_mu = vector_set1(fac_mu);
+  const vector v_a2_Hubble = vector_set1(a2_Hubble);
 
   /* Find the balsara switch. */
   balsara.v = vec_add(balsara_i.v, balsara_j.v);
@@ -891,18 +901,22 @@ runner_iact_nonsym_2_vec_force(
   dvz_2.v = vec_sub(viz.v, vjz_2.v);
 
   /* Compute dv dot r. */
-  dvdr.v = vec_fma(dvx.v, dx.v, vec_fma(dvy.v, dy.v, vec_mul(dvz.v, dz.v)));
-  dvdr_2.v = vec_fma(dvx_2.v, dx_2.v,
-                     vec_fma(dvy_2.v, dy_2.v, vec_mul(dvz_2.v, dz_2.v)));
+  dvdr.v = vec_fma(
+      dvx.v, dx.v,
+      vec_fma(dvy.v, dy.v, vec_fma(dvz.v, dz.v, vec_mul(v_a2_Hubble.v, r2.v))));
+  dvdr_2.v = vec_fma(
+      dvx_2.v, dx_2.v,
+      vec_fma(dvy_2.v, dy_2.v,
+              vec_fma(dvz_2.v, dz_2.v, vec_mul(v_a2_Hubble.v, r2_2.v))));
 
   /* Compute the relative velocity. (This is 0 if the particles move away from
    * each other and negative otherwise) */
   omega_ij.v = vec_fmin(dvdr.v, vec_setzero());
   omega_ij_2.v = vec_fmin(dvdr_2.v, vec_setzero());
-  mu_ij.v =
-      vec_mul(fac_mu.v, vec_mul(ri.v, omega_ij.v)); /* This is 0 or negative */
+  mu_ij.v = vec_mul(v_fac_mu.v,
+                    vec_mul(ri.v, omega_ij.v)); /* This is 0 or negative */
   mu_ij_2.v = vec_mul(
-      fac_mu.v, vec_mul(ri_2.v, omega_ij_2.v)); /* This is 0 or negative */
+      v_fac_mu.v, vec_mul(ri_2.v, omega_ij_2.v)); /* This is 0 or negative */
 
   /* Compute signal velocity */
   v_sig.v = vec_fnma(vec_set1(3.f), mu_ij.v, vec_add(ci.v, cj.v));
diff --git a/src/hydro/Gadget2/hydro_io.h b/src/hydro/Gadget2/hydro_io.h
index 1d1f5ed453f249191b6853a3a699de847ed501f2..28c0eea4772f51fab35a08d43c0564472694eeeb 100644
--- a/src/hydro/Gadget2/hydro_io.h
+++ b/src/hydro/Gadget2/hydro_io.h
@@ -55,18 +55,20 @@ 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, float* ret) {
+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_P(const struct engine* e, const struct part* p, float* ret) {
+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,
-                      double* ret) {
+                      const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -79,6 +81,49 @@ 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) {
+
+  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->a2_inv;
+  ret[1] *= cosmo->a2_inv;
+  ret[2] *= cosmo->a2_inv;
+}
+
+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
  *
@@ -86,16 +131,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(const struct part* parts, struct io_props* list,
-                           int* num_fields) {
+void hydro_write_particles(const struct part* parts, const struct xpart* xparts,
+                           struct io_props* list, int* num_fields) {
 
   *num_fields = 10;
 
   /* 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[1] =
-      io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v);
+  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,
@@ -104,15 +150,17 @@ void hydro_write_particles(const struct part* parts, struct io_props* list,
       "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy);
   list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
                                  UNIT_CONV_NO_UNITS, parts, id);
-  list[6] = io_make_output_field("Acceleration", FLOAT, 3,
-                                 UNIT_CONV_ACCELERATION, parts, a_hydro);
-  list[7] =
+  list[6] =
       io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho);
-  list[8] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1,
+  list[7] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1,
                                               UNIT_CONV_ENERGY_PER_UNIT_MASS,
-                                              parts, convert_u);
-  list[9] = io_make_output_field_convert_part(
-      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, convert_P);
+                                              parts, xparts, convert_part_u);
+  list[8] = io_make_output_field_convert_part(
+      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_part_P);
+
+  list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1,
+                                              UNIT_CONV_POTENTIAL, parts,
+                                              xparts, convert_part_potential);
 
 #ifdef DEBUG_INTERACTIONS_SPH
 
diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h
index 0faa4bf39ae533ee9942898bdc08118ee4078868..bc90a6790fa8123b19badf63761a6dd1378197f6 100644
--- a/src/hydro/Gizmo/hydro.h
+++ b/src/hydro/Gizmo/hydro.h
@@ -517,7 +517,7 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param p The particle 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
@@ -611,7 +611,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @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) {
+    struct part* p, struct xpart* xp, float dt, const struct cosmology* cosmo,
+    const struct hydro_props* hydro_props) {
 
   float a_grav[3];
 
@@ -628,6 +629,14 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
   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.energy = min_energy;
+    p->conserved.flux.energy = 0.f;
+  }
+
   gizmo_check_physical_quantity("mass", p->conserved.mass);
   gizmo_check_physical_quantity("energy", p->conserved.energy);
 
@@ -812,6 +821,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass(
   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.
  *
@@ -917,4 +938,29 @@ __attribute__((always_inline)) INLINE static void hydro_set_entropy(
   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_HYDRO_H */
diff --git a/src/hydro/Gizmo/hydro_io.h b/src/hydro/Gizmo/hydro_io.h
index b8e2e30646c3aafb73d387010ab7ea0b0a663a77..7aa56b3bde5247f4839b8f4f60cdb3de67253392 100644
--- a/src/hydro/Gizmo/hydro_io.h
+++ b/src/hydro/Gizmo/hydro_io.h
@@ -72,7 +72,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, float* ret) {
+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);
 }
@@ -84,7 +85,8 @@ void convert_u(const struct engine* e, const struct part* p, float* ret) {
  * @param p Particle.
  * @param ret (return) Entropic function of the particle
  */
-void convert_A(const struct engine* e, const struct part* p, float* ret) {
+void convert_A(const struct engine* e, const struct part* p,
+               const struct xpart* xp, float* ret) {
   ret[0] = hydro_get_comoving_entropy(p);
 }
 
@@ -95,7 +97,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) {
+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
@@ -110,7 +113,7 @@ void convert_Etot(const struct engine* e, const struct part* p, float* ret) {
 }
 
 void convert_part_pos(const struct engine* e, const struct part* p,
-                      double* ret) {
+                      const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -123,6 +126,49 @@ 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) {
+
+  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->a2_inv;
+  ret[1] *= cosmo->a2_inv;
+  ret[2] *= cosmo->a2_inv;
+}
+
+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
  *
@@ -130,33 +176,39 @@ 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) {
+void hydro_write_particles(const struct part* parts, const struct xpart* xparts,
+                           struct io_props* list, int* num_fields) {
 
-  *num_fields = 10;
+  *num_fields = 11;
 
   /* 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[1] = io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts,
-                                 primitives.v);
+  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, 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("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts,
                                  primitives.rho);
   list[7] = 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[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, convert_Etot);
+      "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);
 }
 
 /**
diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h
index 97cd5ef577dcfd7b07c5183a92349313096a6412..3f9d99683bde4ed6db64d8aaa5b111e2f67f0969 100644
--- a/src/hydro/Minimal/hydro.h
+++ b/src/hydro/Minimal/hydro.h
@@ -197,6 +197,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass(
   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.
  *
@@ -510,9 +522,12 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param p The particle to act upon.
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
+ * @param 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) {
+    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) {
@@ -520,6 +535,14 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
   }
   xp->u_full += p->u_dt * dt_therm;
 
+  /* Apply the minimal energy limit */
+  const float min_energy =
+      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);
 
@@ -540,9 +563,11 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
  *
  * @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) {
+    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);
@@ -580,4 +605,21 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part(
   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/Minimal/hydro_io.h b/src/hydro/Minimal/hydro_io.h
index 5922834e12f6dd74e951b46c2c93f975d7e8b82e..380d6120e05acf2e015ece6f133df02fad3b761d 100644
--- a/src/hydro/Minimal/hydro_io.h
+++ b/src/hydro/Minimal/hydro_io.h
@@ -69,18 +69,20 @@ 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, float* ret) {
+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, float* ret) {
+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,
-                      double* ret) {
+                      const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -93,23 +95,68 @@ 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) {
+
+  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->a2_inv;
+  ret[1] *= cosmo->a2_inv;
+  ret[2] *= cosmo->a2_inv;
+}
+
+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.
  */
-void hydro_write_particles(struct part* parts, struct io_props* list,
-                           int* num_fields) {
+void hydro_write_particles(const struct part* parts, const struct xpart* xparts,
+                           struct io_props* list, int* num_fields) {
 
   *num_fields = 10;
 
   /* 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[1] =
-      io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v);
+  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,
@@ -118,14 +165,17 @@ void hydro_write_particles(struct part* parts, struct io_props* list,
                                  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("Acceleration", FLOAT, 3,
-                                 UNIT_CONV_ACCELERATION, parts, a_hydro);
-  list[7] =
+  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_convert_part(
-      "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, convert_S);
-  list[9] = io_make_output_field_convert_part(
-      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, convert_P);
+      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P);
+
+  list[9] = io_make_output_field_convert_part("Potential", FLOAT, 1,
+                                              UNIT_CONV_POTENTIAL, parts,
+                                              xparts, convert_part_potential);
 }
 
 /**
diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h
index 37de855b375a5ac11f8a33f3993f7d2101c58522..87d46c6d43f0d4f6de6d18f5400b38f0fc4d0f55 100644
--- a/src/hydro/PressureEntropy/hydro.h
+++ b/src/hydro/PressureEntropy/hydro.h
@@ -170,6 +170,18 @@ __attribute__((always_inline)) INLINE static float hydro_get_mass(
   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.
  *
@@ -524,9 +536,12 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param p The particle to act upon
  * @param xp The particle extended data to act upon
  * @param dt_therm The time-step for this kick (for thermodynamic quantities)
+ * @param 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) {
+    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 entropy (temperature) by more than a factor of 2*/
   if (dt_therm > 0. && p->entropy_dt * dt_therm < -0.5f * xp->entropy_full) {
@@ -534,6 +549,16 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
   }
   xp->entropy_full += p->entropy_dt * dt_therm;
 
+  /* Apply the minimal energy limit */
+  const float density = p->rho_bar * cosmo->a3_inv;
+  const float min_energy = hydro_props->minimal_internal_energy;
+  const float min_entropy =
+      gas_entropy_from_internal_energy(density, min_energy);
+  if (xp->entropy_full < min_entropy) {
+    xp->entropy_full = min_entropy;
+    p->entropy_dt = 0.f;
+  }
+
   /* Compute the pressure */
   const float pressure =
       gas_pressure_from_entropy(p->rho_bar, xp->entropy_full);
@@ -556,12 +581,16 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
  * Requires the density to be known
  *
  * @param p The particle to act upon
+ * @param xp The extended data.
+ * @param cosmo The cosmological model.
  */
 __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
-    struct part *restrict p, struct xpart *restrict xp) {
+    struct part *restrict p, struct xpart *restrict xp,
+    const struct cosmology *cosmo) {
 
   /* We read u in the entropy field. We now get S from u */
-  xp->entropy_full = gas_entropy_from_internal_energy(p->rho_bar, p->entropy);
+  xp->entropy_full =
+      gas_entropy_from_internal_energy(p->rho_bar * cosmo->a3_inv, p->entropy);
   p->entropy = xp->entropy_full;
   p->entropy_one_over_gamma = pow_one_over_gamma(p->entropy);
 
@@ -605,4 +634,21 @@ __attribute__((always_inline)) INLINE static void hydro_first_init_part(
   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->entropy = u_init;
+}
+
 #endif /* SWIFT_PRESSURE_ENTROPY_HYDRO_H */
diff --git a/src/hydro/PressureEntropy/hydro_io.h b/src/hydro/PressureEntropy/hydro_io.h
index 454d999b6dcd604e25da4c513d902854c43c781b..78371c1eb21fafed56e89d46690d0cf1e0f2a0f0 100644
--- a/src/hydro/PressureEntropy/hydro_io.h
+++ b/src/hydro/PressureEntropy/hydro_io.h
@@ -67,18 +67,20 @@ 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, float* ret) {
+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, float* ret) {
+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,
-                      double* ret) {
+                      const struct xpart* xp, double* ret) {
 
   if (e->s->periodic) {
     ret[0] = box_wrap(p->x[0], 0.0, e->s->dim[0]);
@@ -91,6 +93,49 @@ 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) {
+
+  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->a2_inv;
+  ret[1] *= cosmo->a2_inv;
+  ret[2] *= cosmo->a2_inv;
+}
+
+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
  *
@@ -98,16 +143,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) {
+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, convert_part_pos);
-  list[1] =
-      io_make_output_field("Velocities", FLOAT, 3, UNIT_CONV_SPEED, parts, v);
+  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,
@@ -116,17 +162,18 @@ void hydro_write_particles(struct part* parts, struct io_props* list,
       "Entropy", FLOAT, 1, UNIT_CONV_ENTROPY_PER_UNIT_MASS, parts, entropy);
   list[5] = io_make_output_field("ParticleIDs", ULONGLONG, 1,
                                  UNIT_CONV_NO_UNITS, parts, id);
-  list[6] = io_make_output_field("Acceleration", FLOAT, 3,
-                                 UNIT_CONV_ACCELERATION, parts, a_hydro);
-  list[7] =
+  list[6] =
       io_make_output_field("Density", FLOAT, 1, UNIT_CONV_DENSITY, parts, rho);
-  list[8] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1,
+  list[7] = io_make_output_field_convert_part("InternalEnergy", FLOAT, 1,
                                               UNIT_CONV_ENERGY_PER_UNIT_MASS,
-                                              parts, convert_u);
-  list[9] = io_make_output_field_convert_part(
-      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, convert_P);
-  list[10] = io_make_output_field("WeightedDensity", FLOAT, 1,
-                                  UNIT_CONV_DENSITY, parts, rho_bar);
+                                              parts, xparts, convert_u);
+  list[8] = io_make_output_field_convert_part(
+      "Pressure", FLOAT, 1, UNIT_CONV_PRESSURE, parts, xparts, convert_P);
+  list[9] = io_make_output_field("WeightedDensity", FLOAT, 1, UNIT_CONV_DENSITY,
+                                 parts, rho_bar);
+  list[10] = io_make_output_field_convert_part("Potential", FLOAT, 1,
+                                               UNIT_CONV_POTENTIAL, parts,
+                                               xparts, convert_part_potential);
 }
 
 /**
diff --git a/src/hydro_properties.c b/src/hydro_properties.c
index 31e18913b7414110db68c6202c512de5fb90a3c1..bd8328946d4bfd078bd10b439e96b35dce457c49 100644
--- a/src/hydro_properties.c
+++ b/src/hydro_properties.c
@@ -36,8 +36,13 @@
 #define hydro_props_default_volume_change 1.4f
 #define hydro_props_default_h_max FLT_MAX
 #define hydro_props_default_h_tolerance 1e-4
+#define hydro_props_default_init_temp 0.f
+#define hydro_props_default_min_temp 0.f
+#define hydro_props_default_H_fraction 0.76
 
 void hydro_props_init(struct hydro_props *p,
+                      const struct phys_const *phys_const,
+                      const struct unit_system *us,
                       const struct swift_params *params) {
 
   /* Kernel properties */
@@ -73,6 +78,53 @@ void hydro_props_init(struct hydro_props *p,
   const float max_volume_change = parser_get_opt_param_float(
       params, "SPH:max_volume_change", hydro_props_default_volume_change);
   p->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv));
+
+  /* Initial temperature */
+  p->initial_temperature = parser_get_opt_param_float(
+      params, "SPH:initial_temperature", hydro_props_default_init_temp);
+
+  /* Initial temperature */
+  p->minimal_temperature = parser_get_opt_param_float(
+      params, "SPH:minimal_temperature", hydro_props_default_min_temp);
+
+  if ((p->initial_temperature != 0.) &&
+      (p->initial_temperature < p->minimal_temperature))
+    error("Initial temperature lower than minimal allowed temperature!");
+
+  /* Hydrogen mass fraction */
+  p->hydrogen_mass_fraction = parser_get_opt_param_double(
+      params, "SPH:H_mass_fraction", hydro_props_default_H_fraction);
+
+  /* Compute the initial energy (Note the temp. read is in internal units) */
+  double u_init = phys_const->const_boltzmann_k / phys_const->const_proton_mass;
+  u_init *= p->initial_temperature;
+  u_init *= hydro_one_over_gamma_minus_one;
+
+  /* Correct for hydrogen mass fraction */
+  double mean_molecular_weight;
+  if (p->initial_temperature *
+          units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE) >
+      1e4)
+    mean_molecular_weight = 4. / (8. - 5. * (1. - p->hydrogen_mass_fraction));
+  else
+    mean_molecular_weight = 4. / (1. + 3. * p->hydrogen_mass_fraction);
+
+  p->initial_internal_energy = u_init / mean_molecular_weight;
+
+  /* Compute the minimal energy (Note the temp. read is in internal units) */
+  double u_min = phys_const->const_boltzmann_k / phys_const->const_proton_mass;
+  u_min *= p->initial_temperature;
+  u_min *= hydro_one_over_gamma_minus_one;
+
+  /* Correct for hydrogen mass fraction */
+  if (p->minimal_temperature *
+          units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE) >
+      1e4)
+    mean_molecular_weight = 4. / (8. - 5. * (1. - p->hydrogen_mass_fraction));
+  else
+    mean_molecular_weight = 4. / (1. + 3. * p->hydrogen_mass_fraction);
+
+  p->minimal_internal_energy = u_min / mean_molecular_weight;
 }
 
 void hydro_props_print(const struct hydro_props *p) {
@@ -103,6 +155,12 @@ void hydro_props_print(const struct hydro_props *p) {
   if (p->max_smoothing_iterations != hydro_props_default_max_iterations)
     message("Maximal iterations in ghost task set to %d (default is %d)",
             p->max_smoothing_iterations, hydro_props_default_max_iterations);
+
+  if (p->initial_temperature != hydro_props_default_init_temp)
+    message("Initial gas temperature set to %f", p->initial_temperature);
+
+  if (p->minimal_temperature != hydro_props_default_min_temp)
+    message("Minimal gas temperature set to %f", p->minimal_temperature);
 }
 
 #if defined(HAVE_HDF5)
@@ -125,6 +183,12 @@ void hydro_props_print_snapshot(hid_t h_grpsph, const struct hydro_props *p) {
                        pow_dimension(expf(p->log_max_h_change)));
   io_write_attribute_i(h_grpsph, "Max ghost iterations",
                        p->max_smoothing_iterations);
+  io_write_attribute_f(h_grpsph, "Minimal temperature", p->minimal_temperature);
+  io_write_attribute_f(h_grpsph, "Initial temperature", p->initial_temperature);
+  io_write_attribute_f(h_grpsph, "Initial energy per unit mass",
+                       p->initial_internal_energy);
+  io_write_attribute_f(h_grpsph, "Hydrogen mass fraction",
+                       p->hydrogen_mass_fraction);
 }
 #endif
 
diff --git a/src/hydro_properties.h b/src/hydro_properties.h
index 6d325c35d6ae6a9bcddbfff9ccc4601e026fc4e6..2799f6c86eec7f0ebf140643c5ab7fa9b60e6273 100644
--- a/src/hydro_properties.h
+++ b/src/hydro_properties.h
@@ -33,7 +33,9 @@
 
 /* Local includes. */
 #include "parser.h"
+#include "physical_constants.h"
 #include "restart.h"
+#include "units.h"
 
 /**
  * @brief Contains all the constants and parameters of the hydro scheme
@@ -63,10 +65,28 @@ struct hydro_props {
 
   /*! Maximal change of h over one time-step */
   float log_max_h_change;
+
+  /*! Minimal temperature allowed */
+  float minimal_temperature;
+
+  /*! Minimal internal energy per unit mass */
+  float minimal_internal_energy;
+
+  /*! Initial temperature */
+  float initial_temperature;
+
+  /*! Initial internal energy per unit mass */
+  float initial_internal_energy;
+
+  /*! Primoridal hydrogen mass fraction for initial energy conversion */
+  float hydrogen_mass_fraction;
 };
 
 void hydro_props_print(const struct hydro_props *p);
-void hydro_props_init(struct hydro_props *p, const struct swift_params *params);
+void hydro_props_init(struct hydro_props *p,
+                      const struct phys_const *phys_const,
+                      const struct unit_system *us,
+                      const 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/io_properties.h b/src/io_properties.h
index 17db123ba8325ede0c7af4e4b005e359cace7a7f..55e128e890b75db2d01911bd3c3b46388b0c6597 100644
--- a/src/io_properties.h
+++ b/src/io_properties.h
@@ -34,9 +34,11 @@ enum DATA_IMPORTANCE { COMPULSORY = 1, OPTIONAL = 0, UNUSED = -1 };
 
 /* Helper typedefs */
 typedef void (*conversion_func_part_float)(const struct engine*,
-                                           const struct part*, float*);
+                                           const struct part*,
+                                           const struct xpart*, float*);
 typedef void (*conversion_func_part_double)(const struct engine*,
-                                            const struct part*, double*);
+                                            const struct part*,
+                                            const struct xpart*, double*);
 typedef void (*conversion_func_gpart_float)(const struct engine*,
                                             const struct gpart*, float*);
 typedef void (*conversion_func_gpart_double)(const struct engine*,
@@ -78,6 +80,7 @@ struct io_props {
 
   /* The particle arrays */
   const struct part* parts;
+  const struct xpart* xparts;
   const struct gpart* gparts;
 
   /* Are we converting? */
@@ -125,6 +128,7 @@ INLINE static struct io_props io_make_input_field_(
   r.field = field;
   r.partSize = partSize;
   r.parts = NULL;
+  r.xparts = NULL;
   r.gparts = NULL;
   r.conversion = 0;
   r.convert_part_f = NULL;
@@ -179,10 +183,10 @@ INLINE static struct io_props io_make_output_field_(
 /**
  * @brief Constructs an #io_props (with conversion) from its parameters
  */
-#define io_make_output_field_convert_part(name, type, dim, units, part, \
-                                          convert)                      \
-  io_make_output_field_convert_part_##type(name, type, dim, units,      \
-                                           sizeof(part[0]), part, convert)
+#define io_make_output_field_convert_part(name, type, dim, units, part, xpart, \
+                                          convert)                             \
+  io_make_output_field_convert_part_##type(                                    \
+      name, type, dim, units, sizeof(part[0]), part, xpart, convert)
 
 /**
  * @brief Construct an #io_props from its parameters
@@ -193,6 +197,7 @@ INLINE static struct io_props io_make_output_field_(
  * @param units The units of the dataset
  * @param partSize The size in byte of the particle
  * @param parts The particle array
+ * @param xparts The xparticle array
  * @param functionPtr The function used to convert a particle to a float
  *
  * Do not call this function directly. Use the macro defined above.
@@ -200,7 +205,8 @@ INLINE static struct io_props io_make_output_field_(
 INLINE static struct io_props io_make_output_field_convert_part_FLOAT(
     const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension,
     enum unit_conversion_factor units, size_t partSize,
-    const struct part* parts, conversion_func_part_float functionPtr) {
+    const struct part* parts, const struct xpart* xparts,
+    conversion_func_part_float functionPtr) {
 
   struct io_props r;
   strcpy(r.name, name);
@@ -211,6 +217,7 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT(
   r.field = NULL;
   r.partSize = partSize;
   r.parts = parts;
+  r.xparts = xparts;
   r.gparts = NULL;
   r.conversion = 1;
   r.convert_part_f = functionPtr;
@@ -230,6 +237,7 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT(
  * @param units The units of the dataset
  * @param partSize The size in byte of the particle
  * @param parts The particle array
+ * @param xparts The xparticle array
  * @param functionPtr The function used to convert a particle to a float
  *
  * Do not call this function directly. Use the macro defined above.
@@ -237,7 +245,8 @@ INLINE static struct io_props io_make_output_field_convert_part_FLOAT(
 INLINE static struct io_props io_make_output_field_convert_part_DOUBLE(
     const char name[FIELD_BUFFER_SIZE], enum IO_DATA_TYPE type, int dimension,
     enum unit_conversion_factor units, size_t partSize,
-    const struct part* parts, conversion_func_part_double functionPtr) {
+    const struct part* parts, const struct xpart* xparts,
+    conversion_func_part_double functionPtr) {
 
   struct io_props r;
   strcpy(r.name, name);
@@ -248,6 +257,7 @@ INLINE static struct io_props io_make_output_field_convert_part_DOUBLE(
   r.field = NULL;
   r.partSize = partSize;
   r.parts = parts;
+  r.xparts = xparts;
   r.gparts = NULL;
   r.conversion = 1;
   r.convert_part_f = NULL;
@@ -293,6 +303,7 @@ INLINE static struct io_props io_make_output_field_convert_gpart_FLOAT(
   r.field = NULL;
   r.partSize = gpartSize;
   r.parts = NULL;
+  r.xparts = NULL;
   r.gparts = gparts;
   r.conversion = 1;
   r.convert_part_f = NULL;
@@ -330,6 +341,7 @@ INLINE static struct io_props io_make_output_field_convert_gpart_DOUBLE(
   r.field = NULL;
   r.partSize = gpartSize;
   r.parts = NULL;
+  r.xparts = NULL;
   r.gparts = gparts;
   r.conversion = 1;
   r.convert_part_f = NULL;
diff --git a/src/kernel_gravity.h b/src/kernel_gravity.h
index 799bda85b0c69dd2757f47fb0225006adb6d1432..dc9db63f9d59ad0b3e72792b33b38a961ea6c30f 100644
--- a/src/kernel_gravity.h
+++ b/src/kernel_gravity.h
@@ -26,15 +26,40 @@
 #include "inline.h"
 #include "minmax.h"
 
+/*! Conversion factor between Plummer softening and internal softening */
+#define kernel_gravity_softening_plummer_equivalent 3.
+#define kernel_gravity_softening_plummer_equivalent_inv (1. / 3.)
+
+/**
+ * @brief Computes the gravity softening function for potential.
+ *
+ * This functions assumes 0 < u < 1.
+ *
+ * @param u The ratio of the distance to the softening length $u = x/h$.
+ * @param W (return) The value of the kernel function $W(x,h)$.
+ */
+__attribute__((always_inline)) INLINE static void kernel_grav_pot_eval(
+    float u, float *const W) {
+
+  /* W(u) = 3u^7 - 15u^6 + 28u^5 - 21u^4 + 7u^2 - 3 */
+  *W = 3.f * u - 15.f;
+  *W = *W * u + 28.f;
+  *W = *W * u - 21.f;
+  *W = *W * u;
+  *W = *W * u + 7.f;
+  *W = *W * u;
+  *W = *W * u - 3.f;
+}
+
 /**
- * @brief Computes the gravity softening function.
+ * @brief Computes the gravity softening function for forces.
  *
  * This functions assumes 0 < u < 1.
  *
  * @param u The ratio of the distance to the softening length $u = x/h$.
  * @param W (return) The value of the kernel function $W(x,h)$.
  */
-__attribute__((always_inline)) INLINE static void kernel_grav_eval(
+__attribute__((always_inline)) INLINE static void kernel_grav_force_eval(
     float u, float *const W) {
 
   /* W(u) = 21u^5 - 90u^4 + 140u^3 - 84u^2 + 14 */
@@ -48,14 +73,37 @@ __attribute__((always_inline)) INLINE static void kernel_grav_eval(
 #ifdef SWIFT_GRAVITY_FORCE_CHECKS
 
 /**
- * @brief Computes the gravity softening function in double precision.
+ * @brief Computes the gravity softening function for potential in double
+ * precision.
+ *
+ * This functions assumes 0 < u < 1.
+ *
+ * @param u The ratio of the distance to the softening length $u = x/h$.
+ * @param W (return) The value of the kernel function $W(x,h)$.
+ */
+__attribute__((always_inline)) INLINE static void kernel_grav_eval_pot_double(
+    double u, double *const W) {
+
+  /* W(u) = 3u^7 - 15u^6 + 28u^5 - 21u^4 + 7u^2 - 3 */
+  *W = 3. * u - 15.;
+  *W = *W * u + 28.;
+  *W = *W * u - 21.;
+  *W = *W * u;
+  *W = *W * u + 7.;
+  *W = *W * u;
+  *W = *W * u - 3;
+}
+
+/**
+ * @brief Computes the gravity softening function for forces in double
+ * precision.
  *
  * This functions assumes 0 < u < 1.
  *
  * @param u The ratio of the distance to the softening length $u = x/h$.
  * @param W (return) The value of the kernel function $W(x,h)$.
  */
-__attribute__((always_inline)) INLINE static void kernel_grav_eval_double(
+__attribute__((always_inline)) INLINE static void kernel_grav_eval_force_double(
     double u, double *const W) {
 
   /* W(u) = 21u^5 - 90u^4 + 140u^3 - 84u^2 + 14 */
diff --git a/src/kernel_long_gravity.h b/src/kernel_long_gravity.h
index 2e0097ded217769af832af049b6c00b1305ac339..578d7c2154f6801e21c004c01cade8bf6bd728ae 100644
--- a/src/kernel_long_gravity.h
+++ b/src/kernel_long_gravity.h
@@ -31,12 +31,41 @@
 #include <math.h>
 
 /**
- * @brief Computes the long-range correction term for the FFT calculation.
+ * @brief Computes the long-range correction term for the potential calculation
+ * coming from FFT.
  *
  * @param u The ratio of the distance to the FFT cell scale \f$u = r/r_s\f$.
  * @param W (return) The value of the kernel function.
  */
-__attribute__((always_inline)) INLINE static void kernel_long_grav_eval(
+__attribute__((always_inline)) INLINE static void kernel_long_grav_pot_eval(
+    const float u, float *const W) {
+
+#ifdef GADGET2_LONG_RANGE_CORRECTION
+
+  const float arg1 = u * 0.5f;
+  const float term1 = erfcf(arg1);
+
+  *W = term1;
+#else
+
+  const float x = 2.f * u;
+  const float exp_x = good_approx_expf(x);
+  const float alpha = 1.f / (1.f + exp_x);
+
+  /* We want 2 - 2 exp(x) * alpha */
+  *W = 1.f - alpha * exp_x;
+  *W *= 2.f;
+#endif
+}
+
+/**
+ * @brief Computes the long-range correction term for the force calculation
+ * coming from FFT.
+ *
+ * @param u The ratio of the distance to the FFT cell scale \f$u = r/r_s\f$.
+ * @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) {
 
 #ifdef GADGET2_LONG_RANGE_CORRECTION
@@ -82,4 +111,4 @@ __attribute__((always_inline)) INLINE static void fourier_kernel_long_grav_eval(
 #endif
 }
 
-#endif  // SWIFT_KERNEL_LONG_GRAVITY_H
+#endif /* SWIFT_KERNEL_LONG_GRAVITY_H */
diff --git a/src/kick.h b/src/kick.h
index 9b1f4f18112a1cb2affcff70e7773ba4c48681b5..9d10f1e78d3934c4277c14217cbbc46514e87033 100644
--- a/src/kick.h
+++ b/src/kick.h
@@ -68,13 +68,16 @@ __attribute__((always_inline)) INLINE static void kick_gpart(
  * @param dt_kick_hydro The kick time-step for hydro accelerations.
  * @param dt_kick_grav The kick time-step for gravity accelerations.
  * @param dt_kick_therm The kick time-step for changes in thermal state.
+ * @param cosmo The cosmological model.
+ * @param hydro_props The constants used in the scheme
  * @param ti_start The starting (integer) time of the kick (for debugging
  * checks).
  * @param ti_end The ending (integer) time of the kick (for debugging checks).
  */
 __attribute__((always_inline)) INLINE static void kick_part(
     struct part *restrict p, struct xpart *restrict xp, double dt_kick_hydro,
-    double dt_kick_grav, double dt_kick_therm, integertime_t ti_start,
+    double dt_kick_grav, double dt_kick_therm, const struct cosmology *cosmo,
+    const struct hydro_props *hydro_props, integertime_t ti_start,
     integertime_t ti_end) {
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -107,7 +110,7 @@ __attribute__((always_inline)) INLINE static void kick_part(
   }
 
   /* Extra kick work */
-  hydro_kick_extra(p, xp, dt_kick_therm);
+  hydro_kick_extra(p, xp, dt_kick_therm, cosmo, hydro_props);
   if (p->gpart != NULL) gravity_kick_extra(p->gpart, dt_kick_grav);
 }
 
diff --git a/src/logger.c b/src/logger.c
index 38cb220b94dd14680584e9edca75883e1df16907..5fd4145aa1b042ed806dd3fe5487d094600b66c4 100644
--- a/src/logger.c
+++ b/src/logger.c
@@ -218,12 +218,6 @@ void logger_log_gpart(struct gpart *p, unsigned int mask, size_t *offset,
     buff += 3 * sizeof(float);
   }
 
-  /* Particle smoothing length as a single float. */
-  if (mask & logger_mask_h) {
-    memcpy(buff, &p->epsilon, sizeof(float));
-    buff += sizeof(float);
-  }
-
   /* Particle constants, which is a bit more complicated. */
   if (mask & logger_mask_rho) {
     memcpy(buff, &p->mass, sizeof(float));
@@ -385,12 +379,6 @@ int logger_read_gpart(struct gpart *p, size_t *offset, const char *buff) {
     buff += 3 * sizeof(float);
   }
 
-  /* Particle smoothing length as a single float. */
-  if (mask & logger_mask_h) {
-    memcpy(&p->epsilon, buff, sizeof(float));
-    buff += sizeof(float);
-  }
-
   /* Particle constants, which is a bit more complicated. */
   if (mask & logger_mask_rho) {
     memcpy(&p->mass, buff, sizeof(float));
diff --git a/src/multipole.h b/src/multipole.h
index d842081814b6ef7b6f490a1ba47a0152dd84d5a1..dd74c4a40d79cc393e252bfe061591dd868f2a55 100644
--- a/src/multipole.h
+++ b/src/multipole.h
@@ -1524,8 +1524,8 @@ INLINE static void gravity_M2L(struct grav_tensor *l_b,
                                const double dim[3]) {
 
   /* Recover some constants */
-  const float eps = props->epsilon;
-  const float eps_inv = props->epsilon_inv;
+  const float eps = props->epsilon_cur;
+  const float eps_inv = props->epsilon_cur_inv;
 
   /* Compute distance vector */
   float dx = (float)(pos_b[0] - pos_a[0]);
@@ -2263,30 +2263,38 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb,
 
   /* Local accumulator */
   double a_grav[3] = {0., 0., 0.};
+  double pot = 0.;
 
-#if SELF_GRAVITY_MULTIPOLE_ORDER > 0
   /* Distance to the multipole */
   const double dx[3] = {gp->x[0] - loc[0], gp->x[1] - loc[1],
                         gp->x[2] - loc[2]};
+  /* 0th order contributions */
+  pot -= X_000(dx) * lb->F_000;
 
-  /* 0th order interaction */
+#if SELF_GRAVITY_MULTIPOLE_ORDER > 0
+  /* 1st order contributions */
   a_grav[0] += X_000(dx) * lb->F_100;
   a_grav[1] += X_000(dx) * lb->F_010;
   a_grav[2] += X_000(dx) * lb->F_001;
+
+  pot -= X_001(dx) * lb->F_001 + X_010(dx) * lb->F_010 + X_100(dx) * lb->F_100;
 #endif
 #if SELF_GRAVITY_MULTIPOLE_ORDER > 1
 
-  /* 1st order interaction */
+  /* 2nd order contributions */
   a_grav[0] +=
       X_100(dx) * lb->F_200 + X_010(dx) * lb->F_110 + X_001(dx) * lb->F_101;
   a_grav[1] +=
       X_100(dx) * lb->F_110 + X_010(dx) * lb->F_020 + X_001(dx) * lb->F_011;
   a_grav[2] +=
       X_100(dx) * lb->F_101 + X_010(dx) * lb->F_011 + X_001(dx) * lb->F_002;
+
+  pot -= X_002(dx) * lb->F_002 + X_011(dx) * lb->F_011 + X_020(dx) * lb->F_020 +
+         X_101(dx) * lb->F_101 + X_110(dx) * lb->F_110 + X_200(dx) * lb->F_200;
 #endif
 #if SELF_GRAVITY_MULTIPOLE_ORDER > 2
 
-  /* 2nd order interaction */
+  /* 3rd order contributions */
   a_grav[0] +=
       X_200(dx) * lb->F_300 + X_020(dx) * lb->F_120 + X_002(dx) * lb->F_102;
   a_grav[0] +=
@@ -2299,10 +2307,15 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb,
       X_200(dx) * lb->F_201 + X_020(dx) * lb->F_021 + X_002(dx) * lb->F_003;
   a_grav[2] +=
       X_110(dx) * lb->F_111 + X_101(dx) * lb->F_102 + X_011(dx) * lb->F_012;
+
+  pot -= X_003(dx) * lb->F_003 + X_012(dx) * lb->F_012 + X_021(dx) * lb->F_021 +
+         X_030(dx) * lb->F_030 + X_102(dx) * lb->F_102 + X_111(dx) * lb->F_111 +
+         X_120(dx) * lb->F_120 + X_201(dx) * lb->F_201 + X_210(dx) * lb->F_210 +
+         X_300(dx) * lb->F_300;
 #endif
 #if SELF_GRAVITY_MULTIPOLE_ORDER > 3
 
-  /* 3rd order contributions */
+  /* 4th order contributions */
   a_grav[0] += X_003(dx) * lb->F_103 + X_012(dx) * lb->F_112 +
                X_021(dx) * lb->F_121 + X_030(dx) * lb->F_130 +
                X_102(dx) * lb->F_202 + X_111(dx) * lb->F_211 +
@@ -2319,10 +2332,16 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb,
                X_120(dx) * lb->F_121 + X_201(dx) * lb->F_202 +
                X_210(dx) * lb->F_211 + X_300(dx) * lb->F_301;
 
+  pot -= X_004(dx) * lb->F_004 + X_013(dx) * lb->F_013 + X_022(dx) * lb->F_022 +
+         X_031(dx) * lb->F_031 + X_040(dx) * lb->F_040 + X_103(dx) * lb->F_103 +
+         X_112(dx) * lb->F_112 + X_121(dx) * lb->F_121 + X_130(dx) * lb->F_130 +
+         X_202(dx) * lb->F_202 + X_211(dx) * lb->F_211 + X_220(dx) * lb->F_220 +
+         X_301(dx) * lb->F_301 + X_310(dx) * lb->F_310 + X_400(dx) * lb->F_400;
+
 #endif
 #if SELF_GRAVITY_MULTIPOLE_ORDER > 4
 
-  /* 4th order contributions */
+  /* 5th order contributions */
   a_grav[0] +=
       X_004(dx) * lb->F_104 + X_013(dx) * lb->F_113 + X_022(dx) * lb->F_122 +
       X_031(dx) * lb->F_131 + X_040(dx) * lb->F_140 + X_103(dx) * lb->F_203 +
@@ -2342,6 +2361,14 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb,
       X_202(dx) * lb->F_203 + X_211(dx) * lb->F_212 + X_220(dx) * lb->F_221 +
       X_301(dx) * lb->F_302 + X_310(dx) * lb->F_311 + X_400(dx) * lb->F_401;
 
+  pot -= X_005(dx) * lb->F_005 + X_014(dx) * lb->F_014 + X_023(dx) * lb->F_023 +
+         X_032(dx) * lb->F_032 + X_041(dx) * lb->F_041 + X_050(dx) * lb->F_050 +
+         X_104(dx) * lb->F_104 + X_113(dx) * lb->F_113 + X_122(dx) * lb->F_122 +
+         X_131(dx) * lb->F_131 + X_140(dx) * lb->F_140 + X_203(dx) * lb->F_203 +
+         X_212(dx) * lb->F_212 + X_221(dx) * lb->F_221 + X_230(dx) * lb->F_230 +
+         X_302(dx) * lb->F_302 + X_311(dx) * lb->F_311 + X_320(dx) * lb->F_320 +
+         X_401(dx) * lb->F_401 + X_410(dx) * lb->F_410 + X_500(dx) * lb->F_500;
+
 #endif
 #if SELF_GRAVITY_MULTIPOLE_ORDER > 5
 #error "Missing implementation for order >5"
@@ -2351,50 +2378,7 @@ INLINE static void gravity_L2P(const struct grav_tensor *lb,
   gp->a_grav[0] += a_grav[0];
   gp->a_grav[1] += a_grav[1];
   gp->a_grav[2] += a_grav[2];
-}
-
-INLINE static void gravity_M2P(const struct multipole *ma,
-                               const struct gravity_props *props,
-                               const double loc[3], struct gpart *gp) {
-
-#if SELF_GRAVITY_MULTIPOLE_ORDER > 0
-
-  const float eps2 = props->epsilon2;
-  const float eps_inv = props->epsilon_inv;
-  const float eps_inv3 = props->epsilon_inv3;
-
-  /* Distance to the multipole */
-  const float dx = gp->x[0] - loc[0];
-  const float dy = gp->x[1] - loc[1];
-  const float dz = gp->x[2] - loc[2];
-  const float r2 = dx * dx + dy * dy + dz * dz;
-
-  /* Get the inverse distance */
-  const float r_inv = 1.f / sqrtf(r2);
-
-  float f, W;
-
-  if (r2 >= eps2) {
-
-    /* Get Newtonian gravity */
-    f = ma->M_000 * r_inv * r_inv * r_inv;
-
-  } else {
-
-    const float r = r2 * r_inv;
-    const float u = r * eps_inv;
-
-    kernel_grav_eval(u, &W);
-
-    /* Get softened gravity */
-    f = ma->M_000 * eps_inv3 * W;
-  }
-
-  gp->a_grav[0] -= f * dx;
-  gp->a_grav[1] -= f * dy;
-  gp->a_grav[2] -= f * dz;
-
-#endif
+  gp->potential += pot;
 }
 
 /**
diff --git a/src/parallel_io.c b/src/parallel_io.c
index d3f5f8dc36dd63ad1b7cf4d65311751cd15f887b..51c88e95396eb6af1ab7f7eb2d1ed48eada59e0d 100644
--- a/src/parallel_io.c
+++ b/src/parallel_io.c
@@ -69,11 +69,14 @@
  * @param offset Offset in the array where this mpi task starts writing.
  * @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 h The value of the reduced Hubble constant to use for cleaning.
  */
 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) {
+                     const struct unit_system* ic_units, int cleanup_h,
+                     double h) {
 
   const size_t typeSize = io_sizeof_type(props.type);
   const size_t copySize = typeSize * props.dimension;
@@ -136,6 +139,23 @@ 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;
+      for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= h_factor;
+    } else {
+      float* temp_f = (float*)temp;
+      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= h_factor;
+    }
+  }
+
   /* Copy temporary buffer to particle data */
   char* temp_c = (char*)temp;
   for (size_t i = 0; i < N; ++i)
@@ -158,11 +178,13 @@ void readArray_chunk(hid_t h_data, hid_t h_plist_id,
  * @param offset The offset in the array on disk for this rank.
  * @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 h The value of the reduced Hubble constant to use for cleaning.
  */
 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) {
+               const struct unit_system* ic_units, int cleanup_h, double h) {
 
   const size_t typeSize = io_sizeof_type(props.type);
   const size_t copySize = typeSize * props.dimension;
@@ -207,7 +229,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);
+                    internal_units, ic_units, cleanup_h, h);
 
     /* Compute how many items are left */
     if (N > max_chunk_size) {
@@ -309,8 +331,7 @@ void prepareArray(struct engine* e, hid_t grp, char* fileName, FILE* xmfFile,
   io_write_attribute_d(
       h_data, "CGS conversion factor",
       units_cgs_conversion_factor(snapshot_units, props.units));
-  io_write_attribute_f(h_data, "h-scale exponent",
-                       units_h_factor(snapshot_units, props.units));
+  io_write_attribute_f(h_data, "h-scale exponent", 0);
   io_write_attribute_f(h_data, "a-scale exponent",
                        units_a_factor(snapshot_units, props.units));
   io_write_attribute_s(h_data, "Conversion factor", buffer);
@@ -541,6 +562,8 @@ void writeArray(struct engine* e, hid_t grp, char* fileName,
  * @param with_hydro Are we running with hydro ?
  * @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 h The value of the reduced Hubble constant to use for correction.
  * @param mpi_rank The MPI rank of this node
  * @param mpi_size The number of MPI ranks
  * @param comm The MPI communicator
@@ -554,8 +577,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 mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
-                      int n_threads, int dry_run) {
+                      int cleanup_h, double h, 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) */
@@ -626,6 +650,13 @@ void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
   else if (hydro_dimension == 1)
     dim[2] = dim[1] = dim[0];
 
+  /* Convert the box size if we want to clean-up h-factors */
+  if (cleanup_h) {
+    dim[0] /= h;
+    dim[1] /= h;
+    dim[2] /= h;
+  }
+
   /* message("Found %lld particles in a %speriodic box of size [%f %f %f].", */
   /* 	  N_total[0], (periodic ? "": "non-"), dim[0], */
   /* 	  dim[1], dim[2]); */
@@ -771,7 +802,7 @@ 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);
+                  offset[ptype], internal_units, ic_units, cleanup_h, h);
 
     /* Close particle group */
     H5Gclose(h_grp);
@@ -821,9 +852,10 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
                   const struct unit_system* internal_units,
                   const struct unit_system* snapshot_units) {
 
-  struct part* parts = e->s->parts;
-  struct gpart* gparts = e->s->gparts;
-  struct spart* sparts = e->s->sparts;
+  const struct part* parts = e->s->parts;
+  const struct xpart* xparts = e->s->xparts;
+  const struct gpart* gparts = e->s->gparts;
+  const struct spart* sparts = e->s->sparts;
   FILE* xmfFile = 0;
   int periodic = e->s->periodic;
   int numFiles = 1;
@@ -980,7 +1012,7 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
     switch (ptype) {
 
       case swift_type_gas:
-        hydro_write_particles(parts, list, &num_fields);
+        hydro_write_particles(parts, xparts, list, &num_fields);
         num_fields += chemistry_write_particles(parts, list + num_fields);
         break;
 
@@ -1045,10 +1077,11 @@ void write_output_parallel(struct engine* e, const char* baseName,
   const size_t Ngas = e->s->nr_parts;
   const size_t Nstars = e->s->nr_sparts;
   const size_t Ntot = e->s->nr_gparts;
-  struct part* parts = e->s->parts;
-  struct gpart* gparts = e->s->gparts;
+  const struct part* parts = e->s->parts;
+  const struct xpart* xparts = e->s->xparts;
+  const struct gpart* gparts = e->s->gparts;
   struct gpart* dmparts = NULL;
-  struct spart* sparts = e->s->sparts;
+  const struct spart* sparts = e->s->sparts;
 
   /* Number of unassociated gparts */
   const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
@@ -1208,7 +1241,7 @@ void write_output_parallel(struct engine* e, const char* baseName,
 
       case swift_type_gas:
         Nparticles = Ngas;
-        hydro_write_particles(parts, list, &num_fields);
+        hydro_write_particles(parts, xparts, list, &num_fields);
         num_fields += chemistry_write_particles(parts, list + num_fields);
         break;
 
diff --git a/src/parallel_io.h b/src/parallel_io.h
index 207e478c341488deb056761b37fb9c7370744b38..5ad3b34cdc4320d6fe3eb860615db33958ad612f 100644
--- a/src/parallel_io.h
+++ b/src/parallel_io.h
@@ -22,6 +22,8 @@
 /* Config parameters. */
 #include "../config.h"
 
+#if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5)
+
 /* MPI headers. */
 #ifdef WITH_MPI
 #include <mpi.h>
@@ -32,15 +34,14 @@
 #include "part.h"
 #include "units.h"
 
-#if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5)
-
 void read_ic_parallel(char* fileName, const struct unit_system* internal_units,
                       double dim[3], struct part** parts, struct gpart** gparts,
                       struct spart** sparts, size_t* Ngas, size_t* Ngparts,
                       size_t* Nsparts, int* periodic, int* flag_entropy,
                       int with_hydro, int with_gravity, int with_stars,
-                      int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
-                      int nr_threads, int dry_run);
+                      int cleanup_h, double h, 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/part.c b/src/part.c
index 712a13d42a629355290bcf89f31b94fb9670a215..c43ffa4820504282b4fb02e63b5cfc4196f3be77 100644
--- a/src/part.c
+++ b/src/part.c
@@ -119,7 +119,7 @@ void part_verify_links(struct part *parts, struct gpart *gparts,
     if (gparts[k].type == swift_type_dark_matter) {
 
       /* Check that it's not linked */
-      if (gparts[k].id_or_neg_offset < 0)
+      if (gparts[k].id_or_neg_offset <= 0)
         error("DM gpart particle linked to something !");
     }
 
diff --git a/src/partition.c b/src/partition.c
index 8203c255d484672254b249042db22f5952579b4e..8feae3b7a8dce7c78310a3b35762d31b439c69e3 100644
--- a/src/partition.c
+++ b/src/partition.c
@@ -48,6 +48,7 @@
 
 /* Local headers. */
 #include "debug.h"
+#include "engine.h"
 #include "error.h"
 #include "partition.h"
 #include "restart.h"
diff --git a/src/potential.h b/src/potential.h
index bcb3fd284021fba339ba494c90b81c91bd2ce72f..680d4e235fdf7a7666901f34a82f62feda4ae9bb 100644
--- a/src/potential.h
+++ b/src/potential.h
@@ -38,6 +38,10 @@
 #include "./potential/disc_patch/potential.h"
 #elif defined(EXTERNAL_POTENTIAL_SINE_WAVE)
 #include "./potential/sine_wave/potential.h"
+#elif defined(EXTERNAL_POTENTIAL_POINTMASS_RING)
+#include "./potential/point_mass_ring/potential.h"
+#elif defined(EXTERNAL_POTENTIAL_POINTMASS_SOFT)
+#include "./potential/point_mass_softened/potential.h"
 #else
 #error "Invalid choice of external potential"
 #endif
diff --git a/src/potential/point_mass_ring/potential.h b/src/potential/point_mass_ring/potential.h
new file mode 100644
index 0000000000000000000000000000000000000000..ebf047ea7c1f946536300f976713893e66295c59
--- /dev/null
+++ b/src/potential/point_mass_ring/potential.h
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Tom Theuns (tom.theuns@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_POTENTIAL_POINT_MASS_H
+#define SWIFT_POTENTIAL_POINT_MASS_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <float.h>
+#include <math.h>
+
+/* Local includes. */
+#include "error.h"
+#include "parser.h"
+#include "part.h"
+#include "physical_constants.h"
+#include "space.h"
+#include "units.h"
+
+/**
+ * @brief External Potential Properties - Point mass case
+ */
+struct external_potential {
+
+  /*! Position of the point mass */
+  double x, y, z;
+
+  /*! Mass */
+  double mass;
+
+  /*! Time-step condition pre-factor */
+  float timestep_mult;
+};
+
+/**
+ * @brief Computes the time-step due to the acceleration from a point mass
+ *        based on Hopkins' central potential stuff (i.e. using a 'softened'
+ *        gravitational edge).
+ *
+ * We pass in the time for simulations where the potential evolves with time.
+ *
+ * @param time The current time.
+ * @param potential The properties of the external potential.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static float external_gravity_timestep(
+    double time, const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const,
+    const struct gpart* restrict g) {
+
+  const float G_newton = phys_const->const_newton_G;
+  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 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]);
+  float factor;
+
+  if (r < 0.175) {
+    // We need to flatten gravity as in SWIFT the timestepping is different
+    // than in GIZMO. This causes particles to become trapped close to the
+    // central point mass!
+    factor = 0.;
+  } else if (r < 0.35) {
+    factor = (8.16 * r * r) + 1.f - r / 0.35;
+  } else if (r > 2.1) {
+    factor = 1.f + (r - 2.1) / 0.1;
+  } else {
+    // 0.35 > r > 2.1
+    factor = 1.f;
+  }
+
+  const float dota_x = G_newton * potential->mass * rinv3 *
+                       (-g->v_full[0] + 3.f * rinv2 * drdv * dx) * factor;
+  const float dota_y = G_newton * potential->mass * rinv3 *
+                       (-g->v_full[1] + 3.f * rinv2 * drdv * dy) * factor;
+  const float dota_z = G_newton * potential->mass * rinv3 *
+                       (-g->v_full[2] + 3.f * rinv2 * drdv * dz) * factor;
+
+  const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z;
+  const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] +
+                    g->a_grav[2] * g->a_grav[2];
+
+  if (fabsf(dota_2) > 0.f)
+    return potential->timestep_mult * sqrtf(a_2 / dota_2);
+  else
+    return FLT_MAX;
+}
+
+/**
+ * @brief Computes the gravitational acceleration of a particle due to a
+ * point mass
+ *
+ * Note that the accelerations are multiplied by Newton's G constant later
+ * on.
+ *
+ * We pass in the time for simulations where the potential evolves with time.
+ *
+ * @param time The current time.
+ * @param potential The proerties of the external potential.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static void external_gravity_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 r = sqrtf(dx * dx + dy * dy + dz * dz);
+  const float rinv = 1.f / r;
+  const float rinv2 = rinv * rinv;
+  const float rinv3 = rinv * rinv2;
+  float factor = 1.f;
+
+  if (r < 0.175) {
+    // We need to flatten gravity as in SWIFT the timestepping is different
+    // than in GIZMO. This causes particles to become trapped close to the
+    // central point mass!
+    factor = 0.;
+    printf("Help me, I'm trapped! (r = %f id = %lld)\n", r,
+           g->id_or_neg_offset);
+  } else if (r < 0.35) {
+    factor = (8.16 * r * r) + 1.f - r / 0.35;
+    printf("Factor is %f, r is %f \n", factor, r);
+  } else if (r > 2.1) {
+    factor = 1.f + (r - 2.1) / 0.1;
+  } else {
+    // 0.35 > r > 2.1
+    factor = 1.f;
+  }
+
+  g->a_grav[0] += -potential->mass * dx * rinv3 * factor;
+  g->a_grav[1] += -potential->mass * dy * rinv3 * factor;
+  g->a_grav[2] += -potential->mass * dz * rinv3 * factor;
+}
+
+/**
+ * @brief Computes the gravitational potential energy of a particle in a point
+ * mass potential.
+ *
+ * @param time The current time (unused here).
+ * @param potential The #external_potential used in the run.
+ * @param phys_const Physical constants in internal units.
+ * @param g Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+external_gravity_get_potential_energy(
+    double time, const struct external_potential* potential,
+    const struct phys_const* const phys_const, const struct gpart* g) {
+
+  const float dx = g->x[0] - potential->x;
+  const float dy = g->x[1] - potential->y;
+  const float dz = g->x[2] - potential->z;
+  const float rinv = 1. / sqrtf(dx * dx + dy * dy + dz * dz);
+  return -phys_const->const_newton_G * potential->mass * rinv;
+}
+
+/**
+ * @brief Initialises the external potential properties in the internal system
+ * of units.
+ *
+ * @param parameter_file The parsed parameter file
+ * @param phys_const Physical constants in internal units
+ * @param us The current internal system of units
+ * @param s The #space we run in.
+ * @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");
+  potential->mass =
+      parser_get_param_double(parameter_file, "PointMassPotential:mass");
+  potential->timestep_mult = parser_get_param_float(
+      parameter_file, "PointMassPotential:timestep_mult");
+}
+
+/**
+ * @brief Prints the properties of the external potential to stdout.
+ *
+ * @param  potential The external potential properties.
+ */
+static INLINE void potential_print_backend(
+    const struct external_potential* potential) {
+
+  message(
+      "External potential is 'Point mass' with properties (x,y,z) = (%e, %e, "
+      "%e), M = %e timestep multiplier = %e.",
+      potential->x, potential->y, potential->z, potential->mass,
+      potential->timestep_mult);
+}
+
+#endif /* SWIFT_POTENTIAL_POINT_MASS_H */
diff --git a/src/potential/point_mass_softened/potential.h b/src/potential/point_mass_softened/potential.h
new file mode 100644
index 0000000000000000000000000000000000000000..83a79ea3cddbff37fdd70d58d70afcaf46f7bc0e
--- /dev/null
+++ b/src/potential/point_mass_softened/potential.h
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk)
+ *                    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_POTENTIAL_POINT_MASS_H
+#define SWIFT_POTENTIAL_POINT_MASS_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <float.h>
+#include <math.h>
+
+/* Local includes. */
+#include "error.h"
+#include "parser.h"
+#include "part.h"
+#include "physical_constants.h"
+#include "space.h"
+#include "units.h"
+
+/**
+ * @brief External Potential Properties - Point mass case
+ *
+ * This file contains the softened central potential. This has a 'softening
+ * factor' that prevents the potential from becoming singular at the centre
+ * of the potential, i.e. it has the form
+ *
+ * $$ \phi \propto 1/(r^2 + \eta^2) $$
+ *
+ * where $\eta$ is some small value.
+ */
+struct external_potential {
+
+  /*! Position of the point mass */
+  double x, y, z;
+
+  /*! Mass */
+  double mass;
+
+  /*! Time-step condition pre-factor */
+  float timestep_mult;
+
+  /*! Potential softening */
+  float softening;
+};
+
+/**
+ * @brief Computes the time-step due to the acceleration from a point mass
+ *
+ * We pass in the time for simulations where the potential evolves with time.
+ *
+ * @param time The current time.
+ * @param potential The properties of the externa potential.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static float external_gravity_timestep(
+    double time, const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const,
+    const struct gpart* restrict g) {
+
+  const float dx = g->x[0] - potential->x;
+  const float dy = g->x[1] - potential->y;
+  const float dz = g->x[2] - potential->z;
+
+  const float softening2 = potential->softening * potential->softening;
+  const float r2 = dx * dx + dy * dy + dz * dz;
+  const float rinv = 1.f / sqrtf(r2);
+  const float rinv2_softened = 1.f / (r2 + softening2);
+
+  /* G * M / (r^2 + eta^2)^{3/2} */
+  const float GMr32 = phys_const->const_newton_G * potential->mass *
+                      sqrtf(rinv2_softened * rinv2_softened * rinv2_softened);
+
+  /* This is actually dr dot v */
+  const float drdv =
+      (dx) * (g->v_full[0]) + (dy) * (g->v_full[1]) + (dz) * (g->v_full[2]);
+
+  /* We want da/dt */
+  /* da/dt = -GM/(r^2 + \eta^2)^{3/2}  *
+   *         (\vec{v} - 3 \vec{r} \cdot \vec{v} \hat{r} /
+   *         (r^2 + \eta^2)) */
+  const float dota_x =
+      GMr32 * (3.f * drdv * dx * rinv2_softened * rinv - g->v_full[0]);
+  const float dota_y =
+      GMr32 * (3.f * drdv * dy * rinv2_softened * rinv - g->v_full[0]);
+  const float dota_z =
+      GMr32 * (3.f * drdv * dz * rinv2_softened * rinv - g->v_full[0]);
+
+  const float dota_2 = dota_x * dota_x + dota_y * dota_y + dota_z * dota_z;
+  const float a_2 = g->a_grav[0] * g->a_grav[0] + g->a_grav[1] * g->a_grav[1] +
+                    g->a_grav[2] * g->a_grav[2];
+
+  if (fabsf(dota_2) > 0.f)
+    return potential->timestep_mult * sqrtf(a_2 / dota_2);
+  else
+    return FLT_MAX;
+}
+
+/**
+ * @brief Computes the gravitational acceleration of a particle due to a
+ * point mass
+ *
+ * Note that the accelerations are multiplied by Newton's G constant later
+ * on.
+ *
+ * We pass in the time for simulations where the potential evolves with time.
+ *
+ * @param time The current time.
+ * @param potential The proerties of the external potential.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static void external_gravity_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 rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz +
+                                 potential->softening * potential->softening);
+  const float rinv3 = rinv * rinv * rinv;
+
+  g->a_grav[0] += -potential->mass * dx * rinv3;
+  g->a_grav[1] += -potential->mass * dy * rinv3;
+  g->a_grav[2] += -potential->mass * dz * rinv3;
+}
+
+/**
+ * @brief Computes the gravitational potential energy of a particle in a point
+ * mass potential.
+ *
+ * @param time The current time (unused here).
+ * @param potential The #external_potential used in the run.
+ * @param phys_const Physical constants in internal units.
+ * @param g Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+external_gravity_get_potential_energy(
+    double time, const struct external_potential* potential,
+    const struct phys_const* const phys_const, const struct gpart* g) {
+
+  const float dx = g->x[0] - potential->x;
+  const float dy = g->x[1] - potential->y;
+  const float dz = g->x[2] - potential->z;
+  const float rinv = 1. / sqrtf(dx * dx + dy * dy + dz * dz +
+                                potential->softening * potential->softening);
+
+  return -phys_const->const_newton_G * potential->mass * rinv;
+}
+
+/**
+ * @brief Initialises the external potential properties in the internal system
+ * of units.
+ *
+ * @param parameter_file The parsed parameter file
+ * @param phys_const Physical constants in internal units
+ * @param us The current internal system of units
+ * @param s The #space we run in.
+ * @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");
+  potential->mass =
+      parser_get_param_double(parameter_file, "PointMassPotential:mass");
+  potential->timestep_mult = parser_get_param_float(
+      parameter_file, "PointMassPotential:timestep_mult");
+  potential->softening =
+      parser_get_param_float(parameter_file, "PointMassPotential:softening");
+}
+
+/**
+ * @brief Prints the properties of the external potential to stdout.
+ *
+ * @param  potential The external potential properties.
+ */
+static INLINE void potential_print_backend(
+    const struct external_potential* potential) {
+
+  message(
+      "External potential is '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->timestep_mult, potential->softening);
+}
+
+#endif /* SWIFT_POTENTIAL_POINT_MASS_H */
diff --git a/src/runner.c b/src/runner.c
index 1b8f9a2fc84b08c590396f1251a6bf3c5cc3189e..35996a3ba9a760d5f019ede35abc207bbdcbe1b3 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -981,6 +981,7 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) {
 
   const struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
+  const struct hydro_props *hydro_props = e->hydro_properties;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   struct part *restrict parts = c->parts;
   struct xpart *restrict xparts = c->xparts;
@@ -1044,8 +1045,8 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) {
         }
 
         /* do the kick */
-        kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, ti_begin,
-                  ti_begin + ti_step / 2);
+        kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, cosmo,
+                  hydro_props, ti_begin, ti_begin + ti_step / 2);
 
         /* Update the accelerations to be used in the drift for hydro */
         if (p->gpart != NULL) {
@@ -1150,6 +1151,7 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) {
 
   const struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
+  const struct hydro_props *hydro_props = e->hydro_properties;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const int count = c->count;
   const int gcount = c->gcount;
@@ -1209,8 +1211,8 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) {
         }
 
         /* Finish the time-step with a second half-kick */
-        kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm,
-                  ti_begin + ti_step / 2, ti_begin + ti_step);
+        kick_part(p, xp, dt_kick_hydro, dt_kick_grav, dt_kick_therm, cosmo,
+                  hydro_props, ti_begin + ti_step / 2, ti_begin + ti_step);
 
 #ifdef SWIFT_DEBUG_CHECKS
         /* Check that kick and the drift are synchronized */
diff --git a/src/runner_doiact.h b/src/runner_doiact.h
index 2ea4cbf24576e50607379d8659106a57a1f80356..2987d205e5005d4618c087bfe7f18a0b3c12fef5 100644
--- a/src/runner_doiact.h
+++ b/src/runner_doiact.h
@@ -1161,7 +1161,8 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
     if (fabsf(d - sort_i[pid].d) - ci->dx_max_sort >
-        1.0e-4 * max(fabsf(d), ci->dx_max_sort_old))
+            1.0e-4 * max(fabsf(d), ci->dx_max_sort_old) &&
+        fabsf(d - sort_i[pid].d) - ci->dx_max_sort > ci->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell ci. ci->nodeID=%d "
           "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->dx_max_sort=%e "
@@ -1174,8 +1175,9 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
     const float d = p->x[0] * runner_shift[sid][0] +
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
-    if (fabsf(d - sort_j[pjd].d) - cj->dx_max_sort >
-        1.0e-4 * max(fabsf(d), cj->dx_max_sort_old))
+    if ((fabsf(d - sort_j[pjd].d) - cj->dx_max_sort) >
+            1.0e-4 * max(fabsf(d), cj->dx_max_sort_old) &&
+        (fabsf(d - sort_j[pjd].d) - cj->dx_max_sort) > cj->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell cj. cj->nodeID=%d "
           "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->dx_max_sort=%e "
@@ -1672,7 +1674,8 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
     if (fabsf(d - sort_i[pid].d) - ci->dx_max_sort >
-        1.0e-4 * max(fabsf(d), ci->dx_max_sort_old))
+            1.0e-4 * max(fabsf(d), ci->dx_max_sort_old) &&
+        fabsf(d - sort_i[pid].d) - ci->dx_max_sort > ci->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell ci. ci->nodeID=%d "
           "cj->nodeID=%d d=%e sort_i[pid].d=%e ci->dx_max_sort=%e "
@@ -1686,7 +1689,8 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
                     p->x[1] * runner_shift[sid][1] +
                     p->x[2] * runner_shift[sid][2];
     if (fabsf(d - sort_j[pjd].d) - cj->dx_max_sort >
-        1.0e-4 * max(fabsf(d), cj->dx_max_sort_old))
+            1.0e-4 * max(fabsf(d), cj->dx_max_sort_old) &&
+        fabsf(d - sort_j[pjd].d) - cj->dx_max_sort > cj->width[0] * 1.0e-10)
       error(
           "particle shift diff exceeds dx_max_sort in cell cj. cj->nodeID=%d "
           "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->dx_max_sort=%e "
diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h
index 0bc7b851c05e94974ef6337ba3d8df5e5420c9a1..4f353a317e9f312f5ae6627cfaf0c06d176e8fad 100644
--- a/src/runner_doiact_grav.h
+++ b/src/runner_doiact_grav.h
@@ -21,10 +21,12 @@
 #define SWIFT_RUNNER_DOIACT_GRAV_H
 
 /* Includes. */
+#include "active.h"
 #include "cell.h"
 #include "gravity.h"
 #include "inline.h"
 #include "part.h"
+#include "timers.h"
 
 /**
  * @brief Recursively propagate the multipoles down the tree by applying the
@@ -34,7 +36,8 @@
  * @param c The #cell we are working on.
  * @param timer Are we timing this ?
  */
-void runner_do_grav_down(struct runner *r, struct cell *c, int timer) {
+static INLINE void runner_do_grav_down(struct runner *r, struct cell *c,
+                                       int timer) {
 
   /* Some constants */
   const struct engine *e = r->e;
@@ -125,8 +128,9 @@ void runner_do_grav_down(struct runner *r, struct cell *c, int timer) {
  * @param ci The #cell with field tensor to interact.
  * @param cj The #cell with the multipole.
  */
-void runner_dopair_grav_mm(const struct runner *r, struct cell *restrict ci,
-                           struct cell *restrict cj) {
+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;
@@ -204,8 +208,8 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e,
     const float h_inv_i = 1.f / h_i;
     const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i;
 
-    /* Local accumulators for the acceleration */
-    float a_x = 0.f, a_y = 0.f, a_z = 0.f;
+    /* Local accumulators for the acceleration and potential */
+    float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f;
 
     /* Make the compiler understand we are in happy vectorization land */
     swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT);
@@ -240,13 +244,15 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e,
 #endif
 
       /* Interact! */
-      float f_ij;
-      runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij);
+      float f_ij, pot_ij;
+      runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij,
+                               &pot_ij);
 
       /* Store it back */
       a_x -= f_ij * dx;
       a_y -= f_ij * dy;
       a_z -= f_ij * dz;
+      pot += pot_ij;
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Update the interaction counter if it's not a padded gpart */
@@ -258,6 +264,7 @@ static INLINE void runner_dopair_grav_pp_full(const struct engine *e,
     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);
@@ -295,8 +302,8 @@ static INLINE void runner_dopair_grav_pp_truncated(
     const float h_inv_i = 1.f / h_i;
     const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i;
 
-    /* Local accumulators for the acceleration */
-    float a_x = 0.f, a_y = 0.f, a_z = 0.f;
+    /* Local accumulators for the acceleration and potential */
+    float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f;
 
     /* Make the compiler understand we are in happy vectorization land */
     swift_align_information(float, cj_cache->x, SWIFT_CACHE_ALIGNMENT);
@@ -331,14 +338,15 @@ static INLINE void runner_dopair_grav_pp_truncated(
 #endif
 
       /* Interact! */
-      float f_ij;
+      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);
+                                    rlr_inv, &f_ij, &pot_ij);
 
       /* Store it back */
       a_x -= f_ij * dx;
       a_y -= f_ij * dy;
       a_z -= f_ij * dz;
+      pot += pot_ij;
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Update the interaction counter if it's not a padded gpart */
@@ -350,6 +358,7 @@ static INLINE void runner_dopair_grav_pp_truncated(
     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);
@@ -372,6 +381,7 @@ static INLINE void runner_dopair_grav_pm(
   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,
@@ -407,14 +417,15 @@ static INLINE void runner_dopair_grav_pm(
     const float r2 = dx * dx + dy * dy + dz * dz;
 
     /* Interact! */
-    float f_x, f_y, f_z;
-    runner_iact_grav_pm(dx, dy, dz, r2, h_i, h_inv_i, multi_j, &f_x, &f_y,
-                        &f_z);
+    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);
 
     /* 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 */
@@ -434,7 +445,8 @@ static INLINE void runner_dopair_grav_pm(
  * @param ci The first #cell.
  * @param cj The other #cell.
  */
-void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj) {
+static INLINE void runner_dopair_grav_pp(struct runner *r, struct cell *ci,
+                                         struct cell *cj) {
 
   const struct engine *e = r->e;
 
@@ -454,7 +466,6 @@ void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj) {
   struct space *s = e->s;
   const int periodic = s->periodic;
   const double cell_width = s->width[0];
-  const float theta_crit2 = e->gravity_properties->theta_crit2;
   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;
@@ -520,11 +531,11 @@ void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj) {
 
   /* 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, theta_crit2,
-                         ci);
+                         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, theta_crit2,
-                         cj);
+                         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) {
@@ -635,7 +646,8 @@ void runner_dopair_grav_pp(struct runner *r, struct cell *ci, struct cell *cj) {
  *
  * @todo Use a local cache for the particles.
  */
-void runner_doself_grav_pp_full(struct runner *r, struct cell *c) {
+static INLINE void runner_doself_grav_pp_full(struct runner *r,
+                                              struct cell *c) {
 
   /* Some constants */
   const struct engine *const e = r->e;
@@ -662,7 +674,7 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) {
   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);
+                                  gcount_padded, loc, c, e->gravity_properties);
 
   /* Ok... Here we go ! */
 
@@ -683,7 +695,7 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) {
     const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i;
 
     /* Local accumulators for the acceleration */
-    float a_x = 0.f, a_y = 0.f, a_z = 0.f;
+    float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f;
 
     /* Make the compiler understand we are in happy vectorization land */
     swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT);
@@ -721,13 +733,15 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) {
 #endif
 
       /* Interact! */
-      float f_ij;
-      runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij);
+      float f_ij, pot_ij;
+      runner_iact_grav_pp_full(r2, h2_i, h_inv_i, h_inv3_i, mass_j, &f_ij,
+                               &pot_ij);
 
       /* Store it back */
       a_x -= f_ij * dx;
       a_y -= f_ij * dy;
       a_z -= f_ij * dz;
+      pot += pot_ij;
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Update the interaction counter if it's not a padded gpart */
@@ -739,6 +753,7 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) {
     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 */
@@ -754,7 +769,8 @@ void runner_doself_grav_pp_full(struct runner *r, struct cell *c) {
  *
  * @todo Use a local cache for the particles.
  */
-void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) {
+static INLINE void runner_doself_grav_pp_truncated(struct runner *r,
+                                                   struct cell *c) {
 
   /* Some constants */
   const struct engine *const e = r->e;
@@ -788,7 +804,7 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) {
   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);
+                                  gcount_padded, loc, c, e->gravity_properties);
 
   /* Ok... Here we go ! */
 
@@ -808,8 +824,8 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) {
     const float h_inv_i = 1.f / h_i;
     const float h_inv3_i = h_inv_i * h_inv_i * h_inv_i;
 
-    /* Local accumulators for the acceleration */
-    float a_x = 0.f, a_y = 0.f, a_z = 0.f;
+    /* Local accumulators for the acceleration and potential */
+    float a_x = 0.f, a_y = 0.f, a_z = 0.f, pot = 0.f;
 
     /* Make the compiler understand we are in happy vectorization land */
     swift_align_information(float, ci_cache->x, SWIFT_CACHE_ALIGNMENT);
@@ -847,14 +863,15 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) {
 #endif
 
       /* Interact! */
-      float f_ij;
+      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);
+                                    rlr_inv, &f_ij, &pot_ij);
 
       /* Store it back */
       a_x -= f_ij * dx;
       a_y -= f_ij * dy;
       a_z -= f_ij * dz;
+      pot += pot_ij;
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Update the interaction counter if it's not a padded gpart */
@@ -866,6 +883,7 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) {
     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 */
@@ -879,7 +897,7 @@ void runner_doself_grav_pp_truncated(struct runner *r, struct cell *c) {
  * @param r The #runner.
  * @param c The #cell.
  */
-void runner_doself_grav_pp(struct runner *r, struct cell *c) {
+static INLINE void runner_doself_grav_pp(struct runner *r, struct cell *c) {
 
   /* Some properties of the space */
   const struct engine *e = r->e;
@@ -934,8 +952,8 @@ void runner_doself_grav_pp(struct runner *r, struct cell *c) {
  *
  * @todo Use a local cache for the particles.
  */
-void runner_dopair_grav(struct runner *r, struct cell *ci, struct cell *cj,
-                        int gettimer) {
+static INLINE void runner_dopair_grav(struct runner *r, struct cell *ci,
+                                      struct cell *cj, int gettimer) {
 
   /* Some constants */
   const struct engine *e = r->e;
@@ -1086,7 +1104,8 @@ void runner_dopair_grav(struct runner *r, struct cell *ci, struct cell *cj,
  *
  * @todo Use a local cache for the particles.
  */
-void runner_doself_grav(struct runner *r, struct cell *c, int gettimer) {
+static INLINE void runner_doself_grav(struct runner *r, struct cell *c,
+                                      int gettimer) {
 
   /* Some constants */
   const struct engine *e = r->e;
@@ -1137,7 +1156,8 @@ void runner_doself_grav(struct runner *r, struct cell *c, int gettimer) {
  * @param ci The #cell of interest.
  * @param timer Are we timing this ?
  */
-void runner_do_grav_long_range(struct runner *r, struct cell *ci, int timer) {
+static INLINE void runner_do_grav_long_range(struct runner *r, struct cell *ci,
+                                             int timer) {
 
   /* Some constants */
   const struct engine *e = r->e;
diff --git a/src/runner_doiact_vec.c b/src/runner_doiact_vec.c
index aa6e6dc3c15f1846462b900076cf131e726cd883..2e86280d64491ee1750f41c2cd22ab01c08e30b8 100644
--- a/src/runner_doiact_vec.c
+++ b/src/runner_doiact_vec.c
@@ -1111,6 +1111,7 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) {
 #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH)
 
   const struct engine *e = r->e;
+  const struct cosmology *restrict cosmo = e->cosmology;
   const timebin_t max_active_bin = e->max_active_bin;
   struct part *restrict parts = c->parts;
   const int count = c->count;
@@ -1139,6 +1140,10 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) {
   /* Read the particles from the cell and store them locally in the cache. */
   cache_read_force_particles(c, cell_cache);
 
+  /* Cosmological terms */
+  const float a = cosmo->a;
+  const float H = cosmo->H;
+
   /* Loop over the particles in the cell. */
   for (int pid = 0; pid < count; pid++) {
 
@@ -1262,7 +1267,7 @@ void runner_doself2_force_vec(struct runner *r, struct cell *restrict c) {
             &cell_cache->vy[pjd], &cell_cache->vz[pjd], &cell_cache->rho[pjd],
             &cell_cache->grad_h[pjd], &cell_cache->pOrho2[pjd],
             &cell_cache->balsara[pjd], &cell_cache->soundspeed[pjd],
-            &cell_cache->m[pjd], v_hi_inv, v_hj_inv, &v_a_hydro_xSum,
+            &cell_cache->m[pjd], v_hi_inv, v_hj_inv, a, H, &v_a_hydro_xSum,
             &v_a_hydro_ySum, &v_a_hydro_zSum, &v_h_dtSum, &v_sigSum,
             &v_entropy_dtSum, v_doi_mask);
       }
@@ -1684,7 +1689,7 @@ void runner_dopair_subset_density_vec(struct runner *r,
                                       struct cell *restrict cj, const int sid,
                                       const int flipped, const double *shift) {
 
-#ifdef WITH_VECTORIZATION
+#if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH)
 
   TIMER_TIC;
 
@@ -1988,6 +1993,7 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci,
 #if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH)
 
   const struct engine *restrict e = r->e;
+  const struct cosmology *restrict cosmo = e->cosmology;
   const timebin_t max_active_bin = e->max_active_bin;
 
   TIMER_TIC;
@@ -2019,6 +2025,10 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci,
   const int active_ci = cell_is_active_hydro(ci, e) && ci_local;
   const int active_cj = cell_is_active_hydro(cj, e) && cj_local;
 
+  /* Cosmological terms */
+  const float a = cosmo->a;
+  const float H = cosmo->H;
+
 #ifdef SWIFT_DEBUG_CHECKS
   /* Check that particles have been drifted to the current time */
   for (int pid = 0; pid < count_i; pid++)
@@ -2213,7 +2223,7 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci,
               &cj_cache->grad_h[cj_cache_idx], &cj_cache->pOrho2[cj_cache_idx],
               &cj_cache->balsara[cj_cache_idx],
               &cj_cache->soundspeed[cj_cache_idx], &cj_cache->m[cj_cache_idx],
-              v_hi_inv, v_hj_inv, &v_a_hydro_xSum, &v_a_hydro_ySum,
+              v_hi_inv, v_hj_inv, a, H, &v_a_hydro_xSum, &v_a_hydro_ySum,
               &v_a_hydro_zSum, &v_h_dtSum, &v_sigSum, &v_entropy_dtSum,
               v_doi_mask);
         }
@@ -2349,7 +2359,7 @@ void runner_dopair2_force_vec(struct runner *r, struct cell *ci,
               &ci_cache->grad_h[ci_cache_idx], &ci_cache->pOrho2[ci_cache_idx],
               &ci_cache->balsara[ci_cache_idx],
               &ci_cache->soundspeed[ci_cache_idx], &ci_cache->m[ci_cache_idx],
-              v_hj_inv, v_hi_inv, &v_a_hydro_xSum, &v_a_hydro_ySum,
+              v_hj_inv, v_hi_inv, a, H, &v_a_hydro_xSum, &v_a_hydro_ySum,
               &v_a_hydro_zSum, &v_h_dtSum, &v_sigSum, &v_entropy_dtSum,
               v_doj_mask);
         }
diff --git a/src/scheduler.c b/src/scheduler.c
index f9cfff5645c571a6bae1d0dc111766e2b2be1120..151304293749a29abe9bd9680d8f8f81bc845884 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -1435,7 +1435,8 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
     switch (t->type) {
       case task_type_self:
       case task_type_sub_self:
-        if (t->subtype == task_subtype_grav)
+        if (t->subtype == task_subtype_grav ||
+            t->subtype == task_subtype_external_grav)
           qid = t->ci->super_gravity->owner;
         else
           qid = t->ci->super_hydro->owner;
diff --git a/src/serial_io.c b/src/serial_io.c
index 3e62bd7fda99ed7703a4fe67340d5a03e5dc45b8..dd623f946ce6ec32415586e5048979de3adb58fa 100644
--- a/src/serial_io.c
+++ b/src/serial_io.c
@@ -67,6 +67,8 @@
  * @param offset The offset position where this rank starts reading.
  * @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 h The value of the reduced Hubble constant to use for cleaning.
  *
  * @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.
@@ -74,7 +76,7 @@
 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) {
+               const struct unit_system* ic_units, int cleanup_h, double h) {
 
   const size_t typeSize = io_sizeof_type(props.type);
   const size_t copySize = typeSize * props.dimension;
@@ -161,6 +163,23 @@ 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;
+      for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= h_factor;
+    } else {
+      float* temp_f = (float*)temp;
+      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= h_factor;
+    }
+  }
+
   /* Copy temporary buffer to particle data */
   char* temp_c = temp;
   for (size_t i = 0; i < N; ++i)
@@ -197,13 +216,13 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName,
     rank = 2;
     shape[0] = N_total;
     shape[1] = props.dimension;
-    chunk_shape[0] = 1 << 16; /* Just a guess...*/
+    chunk_shape[0] = 1 << 20; /* Just a guess...*/
     chunk_shape[1] = props.dimension;
   } else {
     rank = 1;
     shape[0] = N_total;
     shape[1] = 0;
-    chunk_shape[0] = 1 << 16; /* Just a guess...*/
+    chunk_shape[0] = 1 << 20; /* Just a guess...*/
     chunk_shape[1] = 0;
   }
 
@@ -211,7 +230,7 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName,
   if (chunk_shape[0] > N_total) chunk_shape[0] = N_total;
 
   /* Change shape of data space */
-  hid_t h_err = H5Sset_extent_simple(h_space, rank, shape, NULL);
+  hid_t h_err = H5Sset_extent_simple(h_space, rank, shape, shape);
   if (h_err < 0) {
     error("Error while changing data space shape for field '%s'.", props.name);
   }
@@ -228,11 +247,15 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName,
 
   /* Impose data compression */
   if (e->snapshotCompression > 0) {
+    h_err = H5Pset_shuffle(h_prop);
+    if (h_err < 0)
+      error("Error while setting shuffling options for field '%s'.",
+            props.name);
+
     h_err = H5Pset_deflate(h_prop, e->snapshotCompression);
-    if (h_err < 0) {
+    if (h_err < 0)
       error("Error while setting compression options for field '%s'.",
             props.name);
-    }
   }
 
   /* Create dataset */
@@ -252,8 +275,7 @@ void prepareArray(const struct engine* e, hid_t grp, char* fileName,
   io_write_attribute_d(
       h_data, "CGS conversion factor",
       units_cgs_conversion_factor(snapshot_units, props.units));
-  io_write_attribute_f(h_data, "h-scale exponent",
-                       units_h_factor(snapshot_units, props.units));
+  io_write_attribute_f(h_data, "h-scale exponent", 0);
   io_write_attribute_f(h_data, "a-scale exponent",
                        units_a_factor(snapshot_units, props.units));
   io_write_attribute_s(h_data, "Conversion factor", buffer);
@@ -378,6 +400,8 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
  * @param with_hydro Are we reading gas particles ?
  * @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 h The value of the reduced Hubble constant to use for correction.
  * @param mpi_rank The MPI rank of this node
  * @param mpi_size The number of MPI ranks
  * @param comm The MPI communicator
@@ -398,8 +422,8 @@ 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 mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
-                    int n_threads, int dry_run) {
+                    int cleanup_h, double h, 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) */
@@ -472,6 +496,13 @@ void read_ic_serial(char* fileName, const struct unit_system* internal_units,
     else if (hydro_dimension == 1)
       dim[2] = dim[1] = dim[0];
 
+    /* Convert the box size if we want to clean-up h-factors */
+    if (cleanup_h) {
+      dim[0] /= h;
+      dim[1] /= h;
+      dim[2] /= h;
+    }
+
     /* message("Found %lld particles in a %speriodic box of size [%f %f %f].",
      */
     /* 	    N_total, (periodic ? "": "non-"), dim[0], dim[1], dim[2]); */
@@ -637,7 +668,7 @@ 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);
+                      internal_units, ic_units, cleanup_h, h);
 
         /* Close particle group */
         H5Gclose(h_grp);
@@ -708,10 +739,11 @@ void write_output_serial(struct engine* e, const char* baseName,
   const size_t Ntot = e->s->nr_gparts;
   int periodic = e->s->periodic;
   int numFiles = 1;
-  struct part* parts = e->s->parts;
-  struct gpart* gparts = e->s->gparts;
+  const struct part* parts = e->s->parts;
+  const struct xpart* xparts = e->s->xparts;
+  const struct gpart* gparts = e->s->gparts;
   struct gpart* dmparts = NULL;
-  struct spart* sparts = e->s->sparts;
+  const struct spart* sparts = e->s->sparts;
   FILE* xmfFile = 0;
 
   /* Number of unassociated gparts */
@@ -958,7 +990,7 @@ void write_output_serial(struct engine* e, const char* baseName,
 
           case swift_type_gas:
             Nparticles = Ngas;
-            hydro_write_particles(parts, list, &num_fields);
+            hydro_write_particles(parts, xparts, list, &num_fields);
             num_fields += chemistry_write_particles(parts, list + num_fields);
             break;
 
diff --git a/src/serial_io.h b/src/serial_io.h
index 025faffc0087a8b26d4149914e621a978e63c9be..cad428916f400bc9b144dcf4c23f9d38c75c1e9d 100644
--- a/src/serial_io.h
+++ b/src/serial_io.h
@@ -22,6 +22,8 @@
 /* Config parameters. */
 #include "../config.h"
 
+#if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5)
+
 /* MPI headers. */
 #ifdef WITH_MPI
 #include <mpi.h>
@@ -32,15 +34,13 @@
 #include "part.h"
 #include "units.h"
 
-#if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5)
-
 void read_ic_serial(char* fileName, const struct unit_system* internal_units,
                     double dim[3], struct part** parts, struct gpart** gparts,
                     struct spart** sparts, size_t* Ngas, size_t* Ngparts,
                     size_t* Nstars, int* periodic, int* flag_entropy,
                     int with_hydro, int with_gravity, int with_stars,
-                    int mpi_rank, int mpi_size, MPI_Comm comm, MPI_Info info,
-                    int nr_threads, int dry_run);
+                    int cleanup_h, double h, 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/single_io.c b/src/single_io.c
index caa39e4a7cf583b210d3e9322abcd53c332cfe70..2170bcffc4ce3ab21f1edd168d1dc37b2b4af963 100644
--- a/src/single_io.c
+++ b/src/single_io.c
@@ -64,13 +64,15 @@
  * @param N The number of particles.
  * @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.
  *
  * @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,
                const struct unit_system* internal_units,
-               const struct unit_system* ic_units) {
+               const struct unit_system* ic_units, int cleanup_h, double h) {
 
   const size_t typeSize = io_sizeof_type(prop.type);
   const size_t copySize = typeSize * prop.dimension;
@@ -124,18 +126,35 @@ void readArray(hid_t h_grp, const struct io_props prop, size_t N,
   }
 
   /* Unit conversion if necessary */
-  const double factor =
+  const double unit_factor =
       units_conversion_factor(ic_units, internal_units, prop.units);
-  if (factor != 1. && exist != 0) {
+  if (unit_factor != 1. && exist != 0) {
 
     /* message("Converting ! factor=%e", factor); */
 
     if (io_is_double_precision(prop.type)) {
       double* temp_d = (double*)temp;
-      for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= factor;
+      for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= unit_factor;
     } else {
       float* temp_f = (float*)temp;
-      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= factor;
+      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= unit_factor;
+    }
+  }
+
+  /* Clean-up h if necessary */
+  const float h_factor_exp = units_h_factor(internal_units, prop.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,
+     * h_factor); */
+
+    if (io_is_double_precision(prop.type)) {
+      double* temp_d = (double*)temp;
+      for (size_t i = 0; i < num_elements; ++i) temp_d[i] *= h_factor;
+    } else {
+      float* temp_f = (float*)temp;
+      for (size_t i = 0; i < num_elements; ++i) temp_f[i] *= h_factor;
     }
   }
 
@@ -204,13 +223,13 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
     rank = 2;
     shape[0] = N;
     shape[1] = props.dimension;
-    chunk_shape[0] = 1 << 16; /* Just a guess...*/
+    chunk_shape[0] = 1 << 20; /* Just a guess...*/
     chunk_shape[1] = props.dimension;
   } else {
     rank = 1;
     shape[0] = N;
     shape[1] = 0;
-    chunk_shape[0] = 1 << 16; /* Just a guess...*/
+    chunk_shape[0] = 1 << 20; /* Just a guess...*/
     chunk_shape[1] = 0;
   }
 
@@ -218,7 +237,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
   if (chunk_shape[0] > N) chunk_shape[0] = N;
 
   /* Change shape of data space */
-  hid_t h_err = H5Sset_extent_simple(h_space, rank, shape, NULL);
+  hid_t h_err = H5Sset_extent_simple(h_space, rank, shape, shape);
   if (h_err < 0) {
     error("Error while changing data space shape for field '%s'.", props.name);
   }
@@ -235,11 +254,15 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
 
   /* Impose data compression */
   if (e->snapshotCompression > 0) {
+    h_err = H5Pset_shuffle(h_prop);
+    if (h_err < 0)
+      error("Error while setting shuffling options for field '%s'.",
+            props.name);
+
     h_err = H5Pset_deflate(h_prop, e->snapshotCompression);
-    if (h_err < 0) {
+    if (h_err < 0)
       error("Error while setting compression options for field '%s'.",
             props.name);
-    }
   }
 
   /* Create dataset */
@@ -266,8 +289,7 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
   io_write_attribute_d(
       h_data, "CGS conversion factor",
       units_cgs_conversion_factor(snapshot_units, props.units));
-  io_write_attribute_f(h_data, "h-scale exponent",
-                       units_h_factor(snapshot_units, props.units));
+  io_write_attribute_f(h_data, "h-scale exponent", 0);
   io_write_attribute_f(h_data, "a-scale exponent",
                        units_a_factor(snapshot_units, props.units));
   io_write_attribute_s(h_data, "Conversion factor", buffer);
@@ -297,6 +319,9 @@ void writeArray(const struct engine* e, hid_t grp, char* fileName,
  * @param with_hydro Are we reading gas particles ?
  * @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 h The value of the reduced Hubble constant to use for correction.
+ * @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.
  *
  * Opens the HDF5 file fileName and reads the particles contained
@@ -312,7 +337,7 @@ void read_ic_single(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 n_threads, int dry_run) {
+                    int cleanup_h, double h, int n_threads, int dry_run) {
 
   hid_t h_file = 0, h_grp = 0;
   /* GADGET has only cubic boxes (in cosmological mode) */
@@ -380,6 +405,13 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units,
   else if (hydro_dimension == 1)
     dim[2] = dim[1] = dim[0];
 
+  /* Convert the box size if we want to clean-up h-factors */
+  if (cleanup_h) {
+    dim[0] /= h;
+    dim[1] /= h;
+    dim[2] /= h;
+  }
+
   /* message("Found %d particles in a %speriodic box of size [%f %f %f].",  */
   /* 	  *N, (periodic ? "": "non-"), dim[0], dim[1], dim[2]);  */
 
@@ -513,7 +545,8 @@ void read_ic_single(char* fileName, const struct unit_system* internal_units,
     /* Read everything */
     if (!dry_run)
       for (int i = 0; i < num_fields; ++i)
-        readArray(h_grp, list[i], Nparticles, internal_units, ic_units);
+        readArray(h_grp, list[i], Nparticles, internal_units, ic_units,
+                  cleanup_h, h);
 
     /* Close particle group */
     H5Gclose(h_grp);
@@ -574,10 +607,11 @@ void write_output_single(struct engine* e, const char* baseName,
   const size_t Ntot = e->s->nr_gparts;
   int periodic = e->s->periodic;
   int numFiles = 1;
-  struct part* parts = e->s->parts;
-  struct gpart* gparts = e->s->gparts;
+  const struct part* parts = e->s->parts;
+  const struct xpart* xparts = e->s->xparts;
+  const struct gpart* gparts = e->s->gparts;
   struct gpart* dmparts = NULL;
-  struct spart* sparts = e->s->sparts;
+  const struct spart* sparts = e->s->sparts;
 
   /* Number of unassociated gparts */
   const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
@@ -775,7 +809,7 @@ void write_output_single(struct engine* e, const char* baseName,
 
       case swift_type_gas:
         N = Ngas;
-        hydro_write_particles(parts, list, &num_fields);
+        hydro_write_particles(parts, xparts, list, &num_fields);
         num_fields += chemistry_write_particles(parts, list + num_fields);
         break;
 
diff --git a/src/single_io.h b/src/single_io.h
index efed4ef27ade61f7726777d71a2fdc586ea26030..26b849716e3e018d9a10c5c5c513ad26c7ccb274 100644
--- a/src/single_io.h
+++ b/src/single_io.h
@@ -22,24 +22,24 @@
 /* Config parameters. */
 #include "../config.h"
 
+#if defined(HAVE_HDF5) && !defined(WITH_MPI)
+
 /* Includes. */
 #include "engine.h"
 #include "part.h"
 #include "units.h"
 
-#if defined(HAVE_HDF5) && !defined(WITH_MPI)
-
 void read_ic_single(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 nr_threads, int dry_run);
+                    int cleanup_h, double h, int nr_threads, int dry_run);
 
 void write_output_single(struct engine* e, const char* baseName,
                          const struct unit_system* internal_units,
                          const struct unit_system* snapshot_units);
 
-#endif
+#endif /* HAVE_HDF5 && !WITH_MPI */
 
 #endif /* SWIFT_SINGLE_IO_H */
diff --git a/src/space.c b/src/space.c
index 329526fe14e38220750903058bc5201ef7486f63..3da35b64a3f77b4de183c24188d3237d7680f621 100644
--- a/src/space.c
+++ b/src/space.c
@@ -2647,8 +2647,20 @@ void space_first_init_parts(struct space *s,
   struct part *restrict p = s->parts;
   struct xpart *restrict xp = s->xparts;
 
+  const struct cosmology *cosmo = s->e->cosmology;
+  const float a_factor_vel = cosmo->a * cosmo->a;
+
+  const struct hydro_props *hydro_props = s->e->hydro_properties;
+  const float u_init = hydro_props->initial_internal_energy;
+  const float u_min = hydro_props->minimal_internal_energy;
+
   for (size_t i = 0; i < nr_parts; ++i) {
 
+    /* Convert velocities to internal units */
+    p[i].v[0] *= a_factor_vel;
+    p[i].v[1] *= a_factor_vel;
+    p[i].v[2] *= a_factor_vel;
+
 #ifdef HYDRO_DIMENSION_2D
     p[i].x[2] = 0.f;
     p[i].v[2] = 0.f;
@@ -2661,6 +2673,10 @@ void space_first_init_parts(struct space *s,
 
     hydro_first_init_part(&p[i], &xp[i]);
 
+    /* Overwrite the internal energy? */
+    if (u_init > 0.f) hydro_set_init_internal_energy(&p[i], u_init);
+    if (u_min > 0.f) hydro_set_init_internal_energy(&p[i], u_min);
+
     /* Also initialise the chemistry */
     chemistry_first_init_part(&p[i], &xp[i], chemistry);
 
@@ -2684,9 +2700,16 @@ void space_first_init_gparts(struct space *s,
 
   const size_t nr_gparts = s->nr_gparts;
   struct gpart *restrict gp = s->gparts;
+  const struct cosmology *cosmo = s->e->cosmology;
+  const float a_factor_vel = cosmo->a * cosmo->a;
 
   for (size_t i = 0; i < nr_gparts; ++i) {
 
+    /* Convert velocities to internal units */
+    gp[i].v_full[0] *= a_factor_vel;
+    gp[i].v_full[1] *= a_factor_vel;
+    gp[i].v_full[2] *= a_factor_vel;
+
 #ifdef HYDRO_DIMENSION_2D
     gp[i].x[2] = 0.f;
     gp[i].v_full[2] = 0.f;
@@ -2715,9 +2738,16 @@ void space_first_init_sparts(struct space *s) {
 
   const size_t nr_sparts = s->nr_sparts;
   struct spart *restrict sp = s->sparts;
+  const struct cosmology *cosmo = s->e->cosmology;
+  const float a_factor_vel = cosmo->a * cosmo->a;
 
   for (size_t i = 0; i < nr_sparts; ++i) {
 
+    /* Convert velocities to internal units */
+    sp[i].v[0] *= a_factor_vel;
+    sp[i].v[1] *= a_factor_vel;
+    sp[i].v[2] *= a_factor_vel;
+
 #ifdef HYDRO_DIMENSION_2D
     sp[i].x[2] = 0.f;
     sp[i].v[2] = 0.f;
@@ -2792,11 +2822,12 @@ void space_init_gparts(struct space *s, int verbose) {
 void space_convert_quantities_mapper(void *restrict map_data, int count,
                                      void *restrict extra_data) {
   struct space *s = (struct space *)extra_data;
+  const struct cosmology *cosmo = s->e->cosmology;
   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]);
+    hydro_convert_quantities(&parts[k], &xparts[k], cosmo);
 }
 
 /**
@@ -2824,6 +2855,7 @@ void space_convert_quantities(struct space *s, int verbose) {
  *
  * @param s The #space to initialize.
  * @param params The parsed parameter file.
+ * @param cosmo The current cosmological model.
  * @param dim Spatial dimensions of the domain.
  * @param parts Array of Gas particles.
  * @param gparts Array of Gravity particles.
@@ -2832,8 +2864,9 @@ void space_convert_quantities(struct space *s, int verbose) {
  * @param Ngpart The number of Gravity particles in the space.
  * @param Nspart The number of star particles in the space.
  * @param periodic flag whether the domain is periodic or not.
- * @param replicate How many replications along each direction do we want ?
- * @param gravity flag whether we are doing gravity or not.
+ * @param replicate How many replications along each direction do we want?
+ * @param generate_gas_in_ics Are we generating gas particles from the gparts?
+ * @param self_gravity flag whether we are doing gravity or not?
  * @param verbose Print messages to stdout or not.
  * @param dry_run If 1, just initialise stuff, don't do anything with the parts.
  *
@@ -2843,9 +2876,10 @@ void space_convert_quantities(struct space *s, int verbose) {
  * recursively.
  */
 void space_init(struct space *s, const struct swift_params *params,
-                double dim[3], struct part *parts, struct gpart *gparts,
-                struct spart *sparts, size_t Npart, size_t Ngpart,
-                size_t Nspart, int periodic, int replicate, int gravity,
+                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,
+                int replicate, int generate_gas_in_ics, int self_gravity,
                 int verbose, int dry_run) {
 
   /* Clean-up everything */
@@ -2856,7 +2890,7 @@ void space_init(struct space *s, const struct swift_params *params,
   s->dim[1] = dim[1];
   s->dim[2] = dim[2];
   s->periodic = periodic;
-  s->gravity = gravity;
+  s->gravity = self_gravity;
   s->nr_parts = Npart;
   s->size_parts = Npart;
   s->parts = parts;
@@ -2868,6 +2902,19 @@ void space_init(struct space *s, const struct swift_params *params,
   s->sparts = sparts;
   s->nr_queues = 1; /* Temporary value until engine construction */
 
+  /* Are we generating gas from the DM-only ICs? */
+  if (generate_gas_in_ics) {
+    space_generate_gas(s, cosmo, verbose);
+    parts = s->parts;
+    gparts = s->gparts;
+    Npart = s->nr_parts;
+    Ngpart = s->nr_gparts;
+
+#ifdef SWIFT_DEBUG_CHECKS
+    part_verify_links(parts, gparts, sparts, Npart, Ngpart, Nspart, 1);
+#endif
+  }
+
   /* Are we replicating the space ? */
   if (replicate < 1)
     error("Value of 'InitialConditions:replicate' (%d) is too small",
@@ -2880,6 +2927,10 @@ void space_init(struct space *s, const struct swift_params *params,
     Npart = s->nr_parts;
     Ngpart = s->nr_gparts;
     Nspart = s->nr_sparts;
+
+#ifdef SWIFT_DEBUG_CHECKS
+    part_verify_links(parts, gparts, sparts, Npart, Ngpart, Nspart, 1);
+#endif
   }
 
   /* Decide on the minimal top-level cell size */
@@ -2926,8 +2977,8 @@ void space_init(struct space *s, const struct swift_params *params,
   }
 
   /* Apply h scaling */
-  const double scaling =
-      parser_get_opt_param_double(params, "InitialConditions:h_scaling", 1.0);
+  const double scaling = parser_get_opt_param_double(
+      params, "InitialConditions:smoothing_length_scaling", 1.0);
   if (scaling != 1.0 && !dry_run) {
     message("Re-scaling smoothing lengths by a factor %e", scaling);
     for (size_t k = 0; k < Npart; k++) parts[k].h *= scaling;
@@ -3144,6 +3195,152 @@ void space_replicate(struct space *s, int replicate, int verbose) {
 #endif
 }
 
+void space_generate_gas(struct space *s, const struct cosmology *cosmo,
+                        int verbose) {
+
+  if (verbose) message("Generating gas particles from gparts");
+
+  /* Store the current values */
+  const size_t nr_parts = s->nr_parts;
+  const size_t nr_gparts = s->nr_gparts;
+
+  if (nr_parts != 0)
+    error("Generating gas particles from DM but gas already exists!");
+
+  s->size_parts = s->nr_parts = nr_gparts;
+  s->size_gparts = s->nr_gparts = 2 * nr_gparts;
+
+  /* Allocate space for new particles */
+  struct part *parts = NULL;
+  struct gpart *gparts = NULL;
+
+  if (posix_memalign((void **)&parts, part_align,
+                     s->nr_parts * sizeof(struct part)) != 0)
+    error("Failed to allocate new part array.");
+
+  if (posix_memalign((void **)&gparts, gpart_align,
+                     s->nr_gparts * sizeof(struct gpart)) != 0)
+    error("Failed to allocate new gpart array.");
+
+  /* Start by copying the gparts */
+  memcpy(gparts, s->gparts, nr_gparts * sizeof(struct gpart));
+  memcpy(gparts + nr_gparts, s->gparts, nr_gparts * sizeof(struct gpart));
+
+  /* And zero the parts */
+  bzero(parts, s->nr_parts * sizeof(struct part));
+
+  /* Compute some constants */
+  const double mass_ratio = cosmo->Omega_b / cosmo->Omega_m;
+  const double bg_density = cosmo->Omega_m * cosmo->critical_density;
+  const double bg_density_inv = 1. / bg_density;
+
+  /* Update the particle properties */
+  for (size_t i = 0; i < nr_gparts; ++i) {
+
+    struct part *p = &parts[i];
+    struct gpart *gp_gas = &gparts[i];
+    struct gpart *gp_dm = &gparts[nr_gparts + i];
+
+    /* Set the IDs */
+    p->id = gp_gas->id_or_neg_offset * 2 + 1;
+    gp_dm->id_or_neg_offset *= 2;
+
+    if (gp_dm->id_or_neg_offset <= 0) error("DM particle ID overflowd");
+
+    if (p->id <= 0) error("gas particle ID overflowd");
+
+    /* Set the links correctly */
+    p->gpart = gp_gas;
+    gp_gas->id_or_neg_offset = -i;
+    gp_gas->type = swift_type_gas;
+
+    /* Compute positions shift */
+    const double d = cbrt(gp_dm->mass * bg_density_inv);
+    const double shift_dm = d * mass_ratio;
+    const double shift_gas = d * (1. - mass_ratio);
+
+    /* Set the masses */
+    gp_dm->mass *= (1. - mass_ratio);
+    gp_gas->mass *= mass_ratio;
+    hydro_set_mass(p, gp_gas->mass);
+
+    /* Set the new positions */
+    gp_dm->x[0] += shift_dm;
+    gp_dm->x[1] += shift_dm;
+    gp_dm->x[2] += shift_dm;
+    gp_gas->x[0] += shift_gas;
+    gp_gas->x[1] += shift_gas;
+    gp_gas->x[2] += shift_gas;
+    p->x[0] = gp_gas->x[0];
+    p->x[1] = gp_gas->x[1];
+    p->x[2] = gp_gas->x[2];
+
+    /* Also copy the velocities */
+    p->v[0] = gp_gas->v_full[0];
+    p->v[1] = gp_gas->v_full[1];
+    p->v[2] = gp_gas->v_full[2];
+
+    /* Set the smoothing length to the mean inter-particle separation */
+    p->h = 30. * d;
+  }
+
+  /* Replace the content of the space */
+  free(s->gparts);
+  s->parts = parts;
+  s->gparts = gparts;
+}
+
+/**
+ * @brief Verify that the matter content matches the cosmology model.
+ *
+ * @param s The #space.
+ * @param cosmo The current cosmology model.
+ * @param rank The MPI rank of this #space.
+ */
+void space_check_cosmology(struct space *s, const struct cosmology *cosmo,
+                           int rank) {
+
+  struct gpart *gparts = s->gparts;
+  const size_t nr_gparts = s->nr_gparts;
+
+  /* Sum up the mass in this space */
+  double mass = 0.;
+  for (size_t i = 0; i < nr_gparts; ++i) {
+    mass += gparts[i].mass;
+  }
+
+/* Reduce the total mass */
+#ifdef WITH_MPI
+  double total_mass;
+  MPI_Reduce(&mass, &total_mass, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
+#else
+  double total_mass = mass;
+#endif
+
+  if (rank == 0) {
+
+    const double volume = s->dim[0] * s->dim[1] * s->dim[2];
+
+    /* Current Hubble constant */
+    const double H = cosmo->H;
+
+    /* z=0 Hubble parameter */
+    const double H0 = cosmo->H0;
+
+    /* Critical density at z=0 */
+    const double rho_crit0 = cosmo->critical_density * H0 * H0 / (H * H);
+
+    /* Compute the mass density */
+    const double Omega_m = (total_mass / volume) / rho_crit0;
+
+    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",
+          cosmo->Omega_m, Omega_m);
+  }
+}
+
 /**
  * @brief Cleans-up all the cell links in the space
  *
diff --git a/src/space.h b/src/space.h
index 11cbaabdc9fc3ae17024c042ae868d464954d501..76d9369db22740440831fe13eb4d57672e4f9951 100644
--- a/src/space.h
+++ b/src/space.h
@@ -38,6 +38,7 @@
 
 /* Avoid cyclic inclusions */
 struct cell;
+struct cosmology;
 
 /* Some constants. */
 #define space_cellallocchunk 1000
@@ -182,9 +183,10 @@ 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,
-                double dim[3], struct part *parts, struct gpart *gparts,
-                struct spart *sparts, size_t Npart, size_t Ngpart,
-                size_t Nspart, int periodic, int replicate, int gravity,
+                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,
+                int replicate, int generate_gas_in_ics, int gravity,
                 int verbose, int dry_run);
 void space_sanitize(struct space *s);
 void space_map_cells_pre(struct space *s, int full,
@@ -239,6 +241,10 @@ void space_check_top_multipoles_drift_point(struct space *s,
                                             integertime_t ti_drift);
 void space_check_timesteps(struct space *s);
 void space_replicate(struct space *s, int replicate, int verbose);
+void space_generate_gas(struct space *s, const struct cosmology *cosmo,
+                        int verbose);
+void space_check_cosmology(struct space *s, const struct cosmology *cosmo,
+                           int rank);
 void space_reset_task_counters(struct space *s);
 void space_clean(struct space *s);
 void space_free_cells(struct space *s);
diff --git a/src/stars/Default/star_io.h b/src/stars/Default/star_io.h
index 96bbdce6d83dc241d05e7dd1754f476dc0b8e5f9..c3dc31096383533e1e15fa65615d2c9aac0f43e3 100644
--- a/src/stars/Default/star_io.h
+++ b/src/stars/Default/star_io.h
@@ -52,7 +52,7 @@ 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(struct spart* sparts, struct io_props* list,
+void star_write_particles(const struct spart* sparts, struct io_props* list,
                           int* num_fields) {
 
   /* Say how much we want to read */
diff --git a/src/statistics.c b/src/statistics.c
index ed3197fbd0538394bb1c40d834c16e174a410068..8d8afae0d1441927e001d7d58a3ee5ff0399f5ed 100644
--- a/src/statistics.c
+++ b/src/statistics.c
@@ -191,7 +191,7 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) {
     stats.E_int += m * u_inter;
     stats.E_rad += cooling_get_radiated_energy(xp);
     if (gp != NULL) {
-      stats.E_pot_self += m * gravity_get_potential(gp) * a_inv;
+      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);
     }
@@ -292,7 +292,7 @@ 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 += m * gravity_get_potential(gp) * a_inv;
+    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);
   }
diff --git a/src/units.c b/src/units.c
index 5c50bb063b9411f47357407678458e6094f69e2b..4b632e735b7c6e1c12afe8aebc16aa44abc5b597 100644
--- a/src/units.c
+++ b/src/units.c
@@ -220,6 +220,11 @@ void units_get_base_unit_exponants_array(float baseUnitsExp[5],
       baseUnitsExp[UNIT_TIME] = -2.f;
       break;
 
+    case UNIT_CONV_POTENTIAL:
+      baseUnitsExp[UNIT_LENGTH] = 2.f;
+      baseUnitsExp[UNIT_TIME] = -2.f;
+      break;
+
     case UNIT_CONV_FORCE:
       baseUnitsExp[UNIT_MASS] = 1.f;
       baseUnitsExp[UNIT_LENGTH] = 1.f;
diff --git a/src/units.h b/src/units.h
index 5ac70a909a77146ba5f7a441d7747acfc80c3dfa..87c44cc6eb4934980027a60642dd135f03029f7c 100644
--- a/src/units.h
+++ b/src/units.h
@@ -73,6 +73,7 @@ enum unit_conversion_factor {
   UNIT_CONV_DENSITY,
   UNIT_CONV_SPEED,
   UNIT_CONV_ACCELERATION,
+  UNIT_CONV_POTENTIAL,
   UNIT_CONV_FORCE,
   UNIT_CONV_ENERGY,
   UNIT_CONV_ENERGY_PER_UNIT_MASS,
diff --git a/src/vector_power.h b/src/vector_power.h
index 6e8377586fdbc072e8f894c0805f43c122f8c54f..ffaa3b3f6cea69626462a9b5f26f94827c6af556 100644
--- a/src/vector_power.h
+++ b/src/vector_power.h
@@ -415,273 +415,273 @@ __attribute__((always_inline)) INLINE static double X_112(const double v[3]) {
 /***************************/
 
 /**
-* @brief Compute \f$ \frac{1}{(0,0,5)!}\vec{v}^{(0,0,5)} \f$.
-*
-* Note \f$ \vec{v}^{(0,0,5)} = v_z^5 \f$
-* and \f$ \frac{1}{(0,0,5)!} = 1/(0!*0!*5!) = 1/120 = 8.333333e-03 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(0,0,5)!}\vec{v}^{(0,0,5)} \f$.
+ *
+ * Note \f$ \vec{v}^{(0,0,5)} = v_z^5 \f$
+ * and \f$ \frac{1}{(0,0,5)!} = 1/(0!*0!*5!) = 1/120 = 8.333333e-03 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_005(const double v[3]) {
 
   return 8.333333333333333e-03 * v[2] * v[2] * v[2] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(0,1,4)!}\vec{v}^{(0,1,4)} \f$.
-*
-* Note \f$ \vec{v}^{(0,1,4)} = v_y^1 v_z^4 \f$
-* and \f$ \frac{1}{(0,1,4)!} = 1/(0!*1!*4!) = 1/24 = 4.166667e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(0,1,4)!}\vec{v}^{(0,1,4)} \f$.
+ *
+ * Note \f$ \vec{v}^{(0,1,4)} = v_y^1 v_z^4 \f$
+ * and \f$ \frac{1}{(0,1,4)!} = 1/(0!*1!*4!) = 1/24 = 4.166667e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_014(const double v[3]) {
 
   return 4.166666666666666e-02 * v[1] * v[2] * v[2] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(0,2,3)!}\vec{v}^{(0,2,3)} \f$.
-*
-* Note \f$ \vec{v}^{(0,2,3)} = v_y^2 v_z^3 \f$
-* and \f$ \frac{1}{(0,2,3)!} = 1/(0!*2!*3!) = 1/12 = 8.333333e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(0,2,3)!}\vec{v}^{(0,2,3)} \f$.
+ *
+ * Note \f$ \vec{v}^{(0,2,3)} = v_y^2 v_z^3 \f$
+ * and \f$ \frac{1}{(0,2,3)!} = 1/(0!*2!*3!) = 1/12 = 8.333333e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_023(const double v[3]) {
 
   return 8.333333333333333e-02 * v[1] * v[1] * v[2] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(0,3,2)!}\vec{v}^{(0,3,2)} \f$.
-*
-* Note \f$ \vec{v}^{(0,3,2)} = v_y^3 v_z^2 \f$
-* and \f$ \frac{1}{(0,3,2)!} = 1/(0!*3!*2!) = 1/12 = 8.333333e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(0,3,2)!}\vec{v}^{(0,3,2)} \f$.
+ *
+ * Note \f$ \vec{v}^{(0,3,2)} = v_y^3 v_z^2 \f$
+ * and \f$ \frac{1}{(0,3,2)!} = 1/(0!*3!*2!) = 1/12 = 8.333333e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_032(const double v[3]) {
 
   return 8.333333333333333e-02 * v[1] * v[1] * v[1] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(0,4,1)!}\vec{v}^{(0,4,1)} \f$.
-*
-* Note \f$ \vec{v}^{(0,4,1)} = v_y^4 v_z^1 \f$
-* and \f$ \frac{1}{(0,4,1)!} = 1/(0!*4!*1!) = 1/24 = 4.166667e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(0,4,1)!}\vec{v}^{(0,4,1)} \f$.
+ *
+ * Note \f$ \vec{v}^{(0,4,1)} = v_y^4 v_z^1 \f$
+ * and \f$ \frac{1}{(0,4,1)!} = 1/(0!*4!*1!) = 1/24 = 4.166667e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_041(const double v[3]) {
 
   return 4.166666666666666e-02 * v[1] * v[1] * v[1] * v[1] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(0,5,0)!}\vec{v}^{(0,5,0)} \f$.
-*
-* Note \f$ \vec{v}^{(0,5,0)} = v_y^5 \f$
-* and \f$ \frac{1}{(0,5,0)!} = 1/(0!*5!*0!) = 1/120 = 8.333333e-03 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(0,5,0)!}\vec{v}^{(0,5,0)} \f$.
+ *
+ * Note \f$ \vec{v}^{(0,5,0)} = v_y^5 \f$
+ * and \f$ \frac{1}{(0,5,0)!} = 1/(0!*5!*0!) = 1/120 = 8.333333e-03 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_050(const double v[3]) {
 
   return 8.333333333333333e-03 * v[1] * v[1] * v[1] * v[1] * v[1];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(1,0,4)!}\vec{v}^{(1,0,4)} \f$.
-*
-* Note \f$ \vec{v}^{(1,0,4)} = v_x^1 v_z^4 \f$
-* and \f$ \frac{1}{(1,0,4)!} = 1/(1!*0!*4!) = 1/24 = 4.166667e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(1,0,4)!}\vec{v}^{(1,0,4)} \f$.
+ *
+ * Note \f$ \vec{v}^{(1,0,4)} = v_x^1 v_z^4 \f$
+ * and \f$ \frac{1}{(1,0,4)!} = 1/(1!*0!*4!) = 1/24 = 4.166667e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_104(const double v[3]) {
 
   return 4.166666666666666e-02 * v[0] * v[2] * v[2] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(1,1,3)!}\vec{v}^{(1,1,3)} \f$.
-*
-* Note \f$ \vec{v}^{(1,1,3)} = v_x^1 v_y^1 v_z^3 \f$
-* and \f$ \frac{1}{(1,1,3)!} = 1/(1!*1!*3!) = 1/6 = 1.666667e-01 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(1,1,3)!}\vec{v}^{(1,1,3)} \f$.
+ *
+ * Note \f$ \vec{v}^{(1,1,3)} = v_x^1 v_y^1 v_z^3 \f$
+ * and \f$ \frac{1}{(1,1,3)!} = 1/(1!*1!*3!) = 1/6 = 1.666667e-01 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_113(const double v[3]) {
 
   return 1.666666666666667e-01 * v[0] * v[1] * v[2] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(1,2,2)!}\vec{v}^{(1,2,2)} \f$.
-*
-* Note \f$ \vec{v}^{(1,2,2)} = v_x^1 v_y^2 v_z^2 \f$
-* and \f$ \frac{1}{(1,2,2)!} = 1/(1!*2!*2!) = 1/4 = 2.500000e-01 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(1,2,2)!}\vec{v}^{(1,2,2)} \f$.
+ *
+ * Note \f$ \vec{v}^{(1,2,2)} = v_x^1 v_y^2 v_z^2 \f$
+ * and \f$ \frac{1}{(1,2,2)!} = 1/(1!*2!*2!) = 1/4 = 2.500000e-01 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_122(const double v[3]) {
 
   return 2.500000000000000e-01 * v[0] * v[1] * v[1] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(1,3,1)!}\vec{v}^{(1,3,1)} \f$.
-*
-* Note \f$ \vec{v}^{(1,3,1)} = v_x^1 v_y^3 v_z^1 \f$
-* and \f$ \frac{1}{(1,3,1)!} = 1/(1!*3!*1!) = 1/6 = 1.666667e-01 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(1,3,1)!}\vec{v}^{(1,3,1)} \f$.
+ *
+ * Note \f$ \vec{v}^{(1,3,1)} = v_x^1 v_y^3 v_z^1 \f$
+ * and \f$ \frac{1}{(1,3,1)!} = 1/(1!*3!*1!) = 1/6 = 1.666667e-01 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_131(const double v[3]) {
 
   return 1.666666666666667e-01 * v[0] * v[1] * v[1] * v[1] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(1,4,0)!}\vec{v}^{(1,4,0)} \f$.
-*
-* Note \f$ \vec{v}^{(1,4,0)} = v_x^1 v_y^4 \f$
-* and \f$ \frac{1}{(1,4,0)!} = 1/(1!*4!*0!) = 1/24 = 4.166667e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(1,4,0)!}\vec{v}^{(1,4,0)} \f$.
+ *
+ * Note \f$ \vec{v}^{(1,4,0)} = v_x^1 v_y^4 \f$
+ * and \f$ \frac{1}{(1,4,0)!} = 1/(1!*4!*0!) = 1/24 = 4.166667e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_140(const double v[3]) {
 
   return 4.166666666666666e-02 * v[0] * v[1] * v[1] * v[1] * v[1];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(2,0,3)!}\vec{v}^{(2,0,3)} \f$.
-*
-* Note \f$ \vec{v}^{(2,0,3)} = v_x^2 v_z^3 \f$
-* and \f$ \frac{1}{(2,0,3)!} = 1/(2!*0!*3!) = 1/12 = 8.333333e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(2,0,3)!}\vec{v}^{(2,0,3)} \f$.
+ *
+ * Note \f$ \vec{v}^{(2,0,3)} = v_x^2 v_z^3 \f$
+ * and \f$ \frac{1}{(2,0,3)!} = 1/(2!*0!*3!) = 1/12 = 8.333333e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_203(const double v[3]) {
 
   return 8.333333333333333e-02 * v[0] * v[0] * v[2] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(2,1,2)!}\vec{v}^{(2,1,2)} \f$.
-*
-* Note \f$ \vec{v}^{(2,1,2)} = v_x^2 v_y^1 v_z^2 \f$
-* and \f$ \frac{1}{(2,1,2)!} = 1/(2!*1!*2!) = 1/4 = 2.500000e-01 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(2,1,2)!}\vec{v}^{(2,1,2)} \f$.
+ *
+ * Note \f$ \vec{v}^{(2,1,2)} = v_x^2 v_y^1 v_z^2 \f$
+ * and \f$ \frac{1}{(2,1,2)!} = 1/(2!*1!*2!) = 1/4 = 2.500000e-01 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_212(const double v[3]) {
 
   return 2.500000000000000e-01 * v[0] * v[0] * v[1] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(2,2,1)!}\vec{v}^{(2,2,1)} \f$.
-*
-* Note \f$ \vec{v}^{(2,2,1)} = v_x^2 v_y^2 v_z^1 \f$
-* and \f$ \frac{1}{(2,2,1)!} = 1/(2!*2!*1!) = 1/4 = 2.500000e-01 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(2,2,1)!}\vec{v}^{(2,2,1)} \f$.
+ *
+ * Note \f$ \vec{v}^{(2,2,1)} = v_x^2 v_y^2 v_z^1 \f$
+ * and \f$ \frac{1}{(2,2,1)!} = 1/(2!*2!*1!) = 1/4 = 2.500000e-01 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_221(const double v[3]) {
 
   return 2.500000000000000e-01 * v[0] * v[0] * v[1] * v[1] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(2,3,0)!}\vec{v}^{(2,3,0)} \f$.
-*
-* Note \f$ \vec{v}^{(2,3,0)} = v_x^2 v_y^3 \f$
-* and \f$ \frac{1}{(2,3,0)!} = 1/(2!*3!*0!) = 1/12 = 8.333333e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(2,3,0)!}\vec{v}^{(2,3,0)} \f$.
+ *
+ * Note \f$ \vec{v}^{(2,3,0)} = v_x^2 v_y^3 \f$
+ * and \f$ \frac{1}{(2,3,0)!} = 1/(2!*3!*0!) = 1/12 = 8.333333e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_230(const double v[3]) {
 
   return 8.333333333333333e-02 * v[0] * v[0] * v[1] * v[1] * v[1];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(3,0,2)!}\vec{v}^{(3,0,2)} \f$.
-*
-* Note \f$ \vec{v}^{(3,0,2)} = v_x^3 v_z^2 \f$
-* and \f$ \frac{1}{(3,0,2)!} = 1/(3!*0!*2!) = 1/12 = 8.333333e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(3,0,2)!}\vec{v}^{(3,0,2)} \f$.
+ *
+ * Note \f$ \vec{v}^{(3,0,2)} = v_x^3 v_z^2 \f$
+ * and \f$ \frac{1}{(3,0,2)!} = 1/(3!*0!*2!) = 1/12 = 8.333333e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_302(const double v[3]) {
 
   return 8.333333333333333e-02 * v[0] * v[0] * v[0] * v[2] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(3,1,1)!}\vec{v}^{(3,1,1)} \f$.
-*
-* Note \f$ \vec{v}^{(3,1,1)} = v_x^3 v_y^1 v_z^1 \f$
-* and \f$ \frac{1}{(3,1,1)!} = 1/(3!*1!*1!) = 1/6 = 1.666667e-01 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(3,1,1)!}\vec{v}^{(3,1,1)} \f$.
+ *
+ * Note \f$ \vec{v}^{(3,1,1)} = v_x^3 v_y^1 v_z^1 \f$
+ * and \f$ \frac{1}{(3,1,1)!} = 1/(3!*1!*1!) = 1/6 = 1.666667e-01 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_311(const double v[3]) {
 
   return 1.666666666666667e-01 * v[0] * v[0] * v[0] * v[1] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(3,2,0)!}\vec{v}^{(3,2,0)} \f$.
-*
-* Note \f$ \vec{v}^{(3,2,0)} = v_x^3 v_y^2 \f$
-* and \f$ \frac{1}{(3,2,0)!} = 1/(3!*2!*0!) = 1/12 = 8.333333e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(3,2,0)!}\vec{v}^{(3,2,0)} \f$.
+ *
+ * Note \f$ \vec{v}^{(3,2,0)} = v_x^3 v_y^2 \f$
+ * and \f$ \frac{1}{(3,2,0)!} = 1/(3!*2!*0!) = 1/12 = 8.333333e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_320(const double v[3]) {
 
   return 8.333333333333333e-02 * v[0] * v[0] * v[0] * v[1] * v[1];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(4,0,1)!}\vec{v}^{(4,0,1)} \f$.
-*
-* Note \f$ \vec{v}^{(4,0,1)} = v_x^4 v_z^1 \f$
-* and \f$ \frac{1}{(4,0,1)!} = 1/(4!*0!*1!) = 1/24 = 4.166667e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(4,0,1)!}\vec{v}^{(4,0,1)} \f$.
+ *
+ * Note \f$ \vec{v}^{(4,0,1)} = v_x^4 v_z^1 \f$
+ * and \f$ \frac{1}{(4,0,1)!} = 1/(4!*0!*1!) = 1/24 = 4.166667e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_401(const double v[3]) {
 
   return 4.166666666666666e-02 * v[0] * v[0] * v[0] * v[0] * v[2];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(4,1,0)!}\vec{v}^{(4,1,0)} \f$.
-*
-* Note \f$ \vec{v}^{(4,1,0)} = v_x^4 v_y^1 \f$
-* and \f$ \frac{1}{(4,1,0)!} = 1/(4!*1!*0!) = 1/24 = 4.166667e-02 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(4,1,0)!}\vec{v}^{(4,1,0)} \f$.
+ *
+ * Note \f$ \vec{v}^{(4,1,0)} = v_x^4 v_y^1 \f$
+ * and \f$ \frac{1}{(4,1,0)!} = 1/(4!*1!*0!) = 1/24 = 4.166667e-02 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_410(const double v[3]) {
 
   return 4.166666666666666e-02 * v[0] * v[0] * v[0] * v[0] * v[1];
 }
 
 /**
-* @brief Compute \f$ \frac{1}{(5,0,0)!}\vec{v}^{(5,0,0)} \f$.
-*
-* Note \f$ \vec{v}^{(5,0,0)} = v_x^5 \f$
-* and \f$ \frac{1}{(5,0,0)!} = 1/(5!*0!*0!) = 1/120 = 8.333333e-03 \f$
-*
-* @param v vector (\f$ v \f$).
-*/
+ * @brief Compute \f$ \frac{1}{(5,0,0)!}\vec{v}^{(5,0,0)} \f$.
+ *
+ * Note \f$ \vec{v}^{(5,0,0)} = v_x^5 \f$
+ * and \f$ \frac{1}{(5,0,0)!} = 1/(5!*0!*0!) = 1/120 = 8.333333e-03 \f$
+ *
+ * @param v vector (\f$ v \f$).
+ */
 __attribute__((always_inline)) INLINE static double X_500(const double v[3]) {
 
   return 8.333333333333333e-03 * v[0] * v[0] * v[0] * v[0] * v[0];
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e830aad453653dd2ffa265627953af1c28d8cf59..8a757e0b87ffae4d6daa1a6f2b67cf9548b868d0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -15,9 +15,9 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # Add the source directory and debug to CFLAGS
-AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS)
+AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS)
 
-AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(GRACKLE_LIBS)
+AM_LDFLAGS = ../src/.libs/libswiftsim.a $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS)
 
 # List of programs and scripts to run in the test suite
 TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetry \
@@ -26,7 +26,8 @@ TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetr
         testAdiabaticIndex testRiemannExact testRiemannTRRS testRiemannHLLC \
         testMatrixInversion testThreadpool testDump testLogger testInteractions.sh \
         testVoronoi1D testVoronoi2D testVoronoi3D testGravityDerivatives \
-	testPeriodicBC.sh testPeriodicBCPerturbed.sh
+	testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \
+	testPotentialPair 
 
 # List of test programs to compile
 check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \
@@ -36,7 +37,7 @@ check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \
                  testAdiabaticIndex testRiemannExact testRiemannTRRS \
                  testRiemannHLLC testMatrixInversion testDump testLogger \
 		 testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \
-		 testGravityDerivatives
+		 testGravityDerivatives testPotentialSelf testPotentialPair
 
 # Rebuild tests when SWIFT is updated.
 $(check_PROGRAMS): ../src/.libs/libswiftsim.a
@@ -100,6 +101,10 @@ testLogger_SOURCES = testLogger.c
 
 testGravityDerivatives_SOURCES = testGravityDerivatives.c
 
+testPotentialSelf_SOURCES = testPotentialSelf.c
+
+testPotentialPair_SOURCES = testPotentialPair.c
+
 # Files necessary for distribution
 EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \
 	     test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \
diff --git a/tests/fft_params.yml b/tests/fft_params.yml
index 05d6d8f0b0578d11645fc1d78c1a6322160ae87a..6938e3658148874e58f50ab768a5e1fbc41d9573 100644
--- a/tests/fft_params.yml
+++ b/tests/fft_params.yml
@@ -3,8 +3,9 @@ Scheduler:
   
 # Parameters for the self-gravity scheme
 Gravity:
-  eta:                   0.025    # Constant dimensionless multiplier for time integration. 
-  theta:                 0.7      # Opening angle (Multipole acceptance criterion)
-  epsilon:               0.00001  # Softening length (in internal units).
-  a_smooth:              0.
-  r_cut:                 0.
+  eta:                    0.025      # Constant dimensionless multiplier for time integration.
+  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.
+  r_cut:                  0.
diff --git a/tests/testFFT.c b/tests/testFFT.c
index ba737935afc4568a1509d2c43a3534b60a6ef867..7b67181ebd3e29bffbf564d00f702e6c15669fab 100644
--- a/tests/testFFT.c
+++ b/tests/testFFT.c
@@ -57,15 +57,18 @@ int main() {
   struct swift_params *params = malloc(sizeof(struct swift_params));
   parser_read_file("fft_params.yml", params);
 
+  struct cosmology cosmo;
+  cosmology_init_no_cosmo(&cosmo);
+
   /* Initialise the gravity properties */
   struct gravity_props gravity_properties;
-  gravity_props_init(&gravity_properties, params);
+  gravity_props_init(&gravity_properties, params, &cosmo);
 
   /* Build the infrastructure */
   struct space space;
   double dim[3] = {1., 1., 1.};
-  space_init(&space, params, dim, NULL, gparts, NULL, 0, nr_gparts, 0, 1, 1, 1,
-             0, 0);
+  space_init(&space, params, &cosmo, dim, NULL, gparts, NULL, 0, nr_gparts, 0,
+             1, 1, 0, 1, 0, 0);
 
   struct engine engine;
   engine.s = &space;
diff --git a/tests/testInteractions.c b/tests/testInteractions.c
index 31b7eb431da6fc12e767bc9053a21dc9ef010598..9c5dd36415970ff2a53220aa56cecba6fc5fe193 100644
--- a/tests/testInteractions.c
+++ b/tests/testInteractions.c
@@ -610,8 +610,9 @@ void test_force_interactions(struct part test_part, struct part *parts,
             (viz_vec), rhoi_vec, grad_hi_vec, pOrhoi2_vec, balsara_i_vec,
             ci_vec, &(vjxq[i]), &(vjyq[i]), &(vjzq[i]), &(rhojq[i]),
             &(grad_hjq[i]), &(pOrhoj2q[i]), &(balsarajq[i]), &(cjq[i]),
-            &(mjq[i]), hi_inv_vec, &(hj_invq[i]), &a_hydro_xSum, &a_hydro_ySum,
-            &a_hydro_zSum, &h_dtSum, &v_sigSum, &entropy_dtSum, mask, mask2, 0);
+            &(mjq[i]), hi_inv_vec, &(hj_invq[i]), a, H, &a_hydro_xSum,
+            &a_hydro_ySum, &a_hydro_zSum, &h_dtSum, &v_sigSum, &entropy_dtSum,
+            mask, mask2, 0);
       } else { /* Only use one vector for interaction. */
 
         vector my_r2, my_dx, my_dy, my_dz, hj, hj_inv;
@@ -626,7 +627,7 @@ void test_force_interactions(struct part test_part, struct part *parts,
             &my_r2, &my_dx, &my_dy, &my_dz, vix_vec, viy_vec, viz_vec, rhoi_vec,
             grad_hi_vec, pOrhoi2_vec, balsara_i_vec, ci_vec, &(vjxq[i]),
             &(vjyq[i]), &(vjzq[i]), &(rhojq[i]), &(grad_hjq[i]), &(pOrhoj2q[i]),
-            &(balsarajq[i]), &(cjq[i]), &(mjq[i]), hi_inv_vec, hj_inv,
+            &(balsarajq[i]), &(cjq[i]), &(mjq[i]), hi_inv_vec, hj_inv, a, H,
             &a_hydro_xSum, &a_hydro_ySum, &a_hydro_zSum, &h_dtSum, &v_sigSum,
             &entropy_dtSum, mask);
       }
diff --git a/tests/testPotentialPair.c b/tests/testPotentialPair.c
new file mode 100644
index 0000000000000000000000000000000000000000..53fc54ccdd63a9a9150b6701c1a76ac20af91d4c
--- /dev/null
+++ b/tests/testPotentialPair.c
@@ -0,0 +1,367 @@
+/*******************************************************************************
+ * 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/>.
+ *
+ ******************************************************************************/
+#include "../config.h"
+
+/* Some standard headers. */
+#include <fenv.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Local headers. */
+#include "runner_doiact_grav.h"
+#include "swift.h"
+
+const int num_tests = 100;
+const double eps = 0.1;
+
+/**
+ * @brief Check that a and b are consistent (up to some relative error)
+ *
+ * @param a First value
+ * @param b Second value
+ * @param s String used to identify this check in messages
+ */
+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);
+}
+
+/* Definitions of the potential and force that match
+   exactly the theory document */
+double S(double x) { return exp(x) / (1. + exp(x)); }
+
+double S_prime(double x) { return exp(x) / ((1. + exp(x)) * (1. + exp(x))); }
+
+double potential(double mass, double r, double H, double rlr) {
+
+  const double u = r / H;
+  const double x = r / rlr;
+  double pot;
+  if (u > 1.)
+    pot = -mass / r;
+  else
+    pot = -mass *
+          (-3. * u * u * u * u * u * u * u + 15. * u * u * u * u * u * u -
+           28. * u * u * u * u * u + 21. * u * u * u * u - 7. * u * u + 3.) /
+          H;
+
+  return pot * (2. - 2. * S(2. * x));
+}
+
+double acceleration(double mass, double r, double H, double rlr) {
+
+  const double u = r / H;
+  const double x = r / rlr;
+  double acc;
+  if (u > 1.)
+    acc = -mass / (r * r * r);
+  else
+    acc = -mass * (21. * u * u * u * u * u - 90. * u * u * u * u +
+                   140. * u * u * u - 84. * u * u + 14.) /
+          (H * H * H);
+
+  return r * acc * (4. * x * S_prime(2 * x) - 2. * S(2. * x) + 2.);
+}
+
+int main() {
+
+  /* Initialize CPU frequency, this also starts time. */
+  unsigned long long cpufreq = 0;
+  clocks_set_cpufreq(cpufreq);
+
+  /* Initialise a few things to get us going */
+  struct engine e;
+  e.max_active_bin = num_time_bins;
+  e.time = 0.1f;
+  e.ti_current = 8;
+  e.time_base = 1e-10;
+
+  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 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;
+
+  struct runner r;
+  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);
+
+  /* Let's create one cell with a massive particle and a bunch of test particles
+   */
+  struct cell ci, cj;
+  bzero(&ci, sizeof(struct cell));
+  bzero(&cj, sizeof(struct cell));
+
+  ci.width[0] = 1.;
+  ci.width[1] = 1.;
+  ci.width[2] = 1.;
+  ci.loc[0] = 0.;
+  ci.loc[1] = 0.;
+  ci.loc[2] = 0.;
+  ci.gcount = 1;
+  ci.ti_old_gpart = 8;
+  ci.ti_old_multipole = 8;
+  ci.ti_gravity_end_min = 8;
+  ci.ti_gravity_end_max = 8;
+
+  cj.width[0] = 1.;
+  cj.width[1] = 1.;
+  cj.width[2] = 1.;
+  cj.loc[0] = 1.;
+  cj.loc[1] = 0.;
+  cj.loc[2] = 0.;
+  cj.gcount = num_tests;
+  cj.ti_old_gpart = 8;
+  cj.ti_old_multipole = 8;
+  cj.ti_gravity_end_min = 8;
+  cj.ti_gravity_end_max = 8;
+
+  /* Allocate multipoles */
+  ci.multipole = malloc(sizeof(struct gravity_tensors));
+  cj.multipole = malloc(sizeof(struct gravity_tensors));
+  bzero(ci.multipole, sizeof(struct gravity_tensors));
+  bzero(cj.multipole, sizeof(struct gravity_tensors));
+
+  /* Set the multipoles */
+  ci.multipole->r_max = 0.1;
+  cj.multipole->r_max = 0.1;
+
+  /* Allocate the particles */
+  if (posix_memalign((void **)&ci.gparts, gpart_align,
+                     ci.gcount * sizeof(struct gpart)) != 0)
+    error("Error allocating gparts for cell ci");
+  bzero(ci.gparts, ci.gcount * sizeof(struct gpart));
+
+  if (posix_memalign((void **)&cj.gparts, gpart_align,
+                     cj.gcount * sizeof(struct gpart)) != 0)
+    error("Error allocating gparts for cell ci");
+  bzero(cj.gparts, cj.gcount * sizeof(struct gpart));
+
+  /* Create the mass-less test particles */
+  for (int n = 0; n < num_tests; ++n) {
+
+    struct gpart *gp = &cj.gparts[n];
+
+    gp->x[0] = 1. + (n + 1) / ((double)num_tests);
+    gp->x[1] = 0.5;
+    gp->x[2] = 0.5;
+    gp->mass = 0.;
+    gp->time_bin = 1;
+    gp->type = swift_type_dark_matter;
+    gp->id_or_neg_offset = n + 1;
+#ifdef SWIFT_DEBUG_CHECKS
+    gp->ti_drift = 8;
+#endif
+  }
+
+  /***********************************************/
+  /* Let's start by testing the P-P interactions */
+  /***********************************************/
+
+  /* Create the massive particle */
+  ci.gparts[0].x[0] = 0.;
+  ci.gparts[0].x[1] = 0.5;
+  ci.gparts[0].x[2] = 0.5;
+  ci.gparts[0].mass = 1.;
+  ci.gparts[0].time_bin = 1;
+  ci.gparts[0].type = swift_type_dark_matter;
+  ci.gparts[0].id_or_neg_offset = 1;
+#ifdef SWIFT_DEBUG_CHECKS
+  ci.gparts[0].ti_drift = 8;
+#endif
+
+  /* Now compute the forces */
+  runner_dopair_grav_pp(&r, &ci, &cj);
+
+  /* Verify everything */
+  for (int n = 0; n < num_tests; ++n) {
+    const struct gpart *gp = &cj.gparts[n];
+    const struct gpart *gp2 = &ci.gparts[0];
+    const double epsilon = gravity_get_softening(gp, &props);
+
+    double pot_true =
+        potential(ci.gparts[0].mass, gp->x[0] - gp2->x[0], epsilon, rlr);
+    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);
+
+    check_value(gp->potential, pot_true, "potential");
+    check_value(gp->a_grav[0], acc_true, "acceleration");
+  }
+
+  message("\n\t\t P-P interactions all good\n");
+
+  /* Reset the accelerations */
+  for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.gparts[n]);
+
+  /**********************************/
+  /* Test the basic PM interactions */
+  /**********************************/
+
+  /* Set an opening angle that allows P-M interactions */
+  props.theta_crit2 = 1.;
+
+  ci.gparts[0].mass = 0.;
+  ci.multipole->CoM[0] = 0.;
+  ci.multipole->CoM[1] = 0.5;
+  ci.multipole->CoM[2] = 0.5;
+
+  bzero(&ci.multipole->m_pole, sizeof(struct multipole));
+  bzero(&cj.multipole->m_pole, sizeof(struct multipole));
+  ci.multipole->m_pole.M_000 = 1.;
+
+  /* Now compute the forces */
+  runner_dopair_grav_pp(&r, &ci, &cj);
+
+  /* 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 * FLT_MAX);
+    double acc_true = acceleration(
+        mpole->m_pole.M_000, gp->x[0] - mpole->CoM[0], epsilon, rlr * FLT_MAX);
+
+    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 basic P-M interactions all good\n");
+
+  /* Reset the accelerations */
+  for (int n = 0; n < num_tests; ++n) gravity_init_gpart(&cj.gparts[n]);
+
+/***************************************/
+/* Test the high-order PM interactions */
+/***************************************/
+
+#if SELF_GRAVITY_MULTIPOLE_ORDER >= 3
+
+  /* Let's make ci more interesting */
+  free(ci.gparts);
+  ci.gcount = 8;
+  if (posix_memalign((void **)&ci.gparts, gpart_align,
+                     ci.gcount * sizeof(struct gpart)) != 0)
+    error("Error allocating gparts for cell ci");
+  bzero(ci.gparts, ci.gcount * sizeof(struct gpart));
+
+  /* Place particles on a simple cube of side-length 0.2 */
+  for (int n = 0; n < 8; ++n) {
+    if (n & 1)
+      ci.gparts[n].x[0] = 0.0 - 0.1;
+    else
+      ci.gparts[n].x[0] = 0.0 + 0.1;
+
+    if (n & 2)
+      ci.gparts[n].x[1] = 0.5 - 0.1;
+    else
+      ci.gparts[n].x[1] = 0.5 + 0.1;
+
+    if (n & 2)
+      ci.gparts[n].x[2] = 0.5 - 0.1;
+    else
+      ci.gparts[n].x[2] = 0.5 + 0.1;
+
+    ci.gparts[n].mass = 1. / 8.;
+
+    ci.gparts[n].time_bin = 1;
+    ci.gparts[n].type = swift_type_dark_matter;
+    ci.gparts[n].id_or_neg_offset = 1;
+#ifdef SWIFT_DEBUG_CHECKS
+    ci.gparts[n].ti_drift = 8;
+#endif
+  }
+
+  /* Now let's make a multipole out of it. */
+  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);
+
+  /* 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.};
+
+    for (int i = 0; i < 8; ++i) {
+      const struct gpart *gp2 = &ci.gparts[i];
+      const double epsilon = gravity_get_softening(gp, &props);
+
+      const double dx[3] = {gp2->x[0] - gp->x[0], gp2->x[1] - gp->x[1],
+                            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;
+    }
+
+    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");
+  }
+
+  message("\n\t\t high-order P-M interactions all good\n");
+
+#endif
+
+  free(ci.multipole);
+  free(cj.multipole);
+  free(ci.gparts);
+  free(cj.gparts);
+  return 0;
+}
diff --git a/tests/testPotentialSelf.c b/tests/testPotentialSelf.c
new file mode 100644
index 0000000000000000000000000000000000000000..6d31f079fa79f7463637ec71dc2c75f37a10b129
--- /dev/null
+++ b/tests/testPotentialSelf.c
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * 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/>.
+ *
+ ******************************************************************************/
+#include "../config.h"
+
+/* Some standard headers. */
+#include <fenv.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Local headers. */
+#include "runner_doiact_grav.h"
+#include "swift.h"
+
+const int num_tests = 100;
+const double eps = 0.02;
+
+/**
+ * @brief Check that a and b are consistent (up to some relative error)
+ *
+ * @param a First value
+ * @param b Second value
+ * @param s String used to identify this check in messages
+ */
+void check_value(double a, double b, const char *s) {
+  if (fabs(a - b) / fabs(a + b) > 1e-6 && fabs(a - b) > 1.e-6)
+    error("Values are inconsistent: %12.15e %12.15e (%s)!", a, b, s);
+}
+
+/* Definitions of the potential and force that match
+   exactly the theory document */
+double S(double x) { return good_approx_exp(x) / (1. + good_approx_exp(x)); }
+
+double S_prime(double x) {
+  return good_approx_exp(x) /
+         ((1. + good_approx_exp(x)) * (1. + good_approx_exp(x)));
+}
+
+double potential(double mass, double r, double H, double rlr) {
+
+  const double u = r / H;
+  const double x = r / rlr;
+  double pot;
+  if (u > 1.)
+    pot = -mass / r;
+  else
+    pot = -mass *
+          (-3. * u * u * u * u * u * u * u + 15. * u * u * u * u * u * u -
+           28. * u * u * u * u * u + 21. * u * u * u * u - 7. * u * u + 3.) /
+          H;
+
+  return pot * (2. - 2. * S(2. * x));
+}
+
+double acceleration(double mass, double r, double H, double rlr) {
+
+  const double u = r / H;
+  const double x = r / rlr;
+  double acc;
+  if (u > 1.)
+    acc = -mass / (r * r * r);
+  else
+    acc = -mass * (21. * u * u * u * u * u - 90. * u * u * u * u +
+                   140. * u * u * u - 84. * u * u + 14.) /
+          (H * H * H);
+
+  return r * acc * (4. * x * S_prime(2 * x) - 2. * S(2. * x) + 2.);
+}
+
+int main() {
+
+  /* Initialize CPU frequency, this also starts time. */
+  unsigned long long cpufreq = 0;
+  clocks_set_cpufreq(cpufreq);
+
+  /* Initialise a few things to get us going */
+  struct engine e;
+  e.max_active_bin = num_time_bins;
+  e.time = 0.1f;
+  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 gravity_props props;
+  props.a_smooth = 1.25;
+  props.epsilon_cur = eps;
+  e.gravity_properties = &props;
+
+  struct runner r;
+  bzero(&r, sizeof(struct runner));
+  r.e = &e;
+
+  const double rlr = props.a_smooth * s.width[0];
+
+  /* Init the cache for gravity interaction */
+  gravity_cache_init(&r.ci_gravity_cache, num_tests * 2);
+
+  /* Let's create one cell with a massive particle and a bunch of test particles
+   */
+  struct cell c;
+  bzero(&c, sizeof(struct cell));
+  c.width[0] = 1.;
+  c.width[1] = 1.;
+  c.width[2] = 1.;
+  c.loc[0] = 0.;
+  c.loc[1] = 0.;
+  c.loc[2] = 0.;
+  c.gcount = 1 + num_tests;
+  c.ti_old_gpart = 8;
+  c.ti_gravity_end_min = 8;
+  c.ti_gravity_end_max = 8;
+
+  if (posix_memalign((void **)&c.gparts, gpart_align,
+                     c.gcount * sizeof(struct gpart)) != 0)
+    error("Impossible to allocate memory for the gparts.");
+  bzero(c.gparts, c.gcount * sizeof(struct gpart));
+
+  /* Create the massive particle */
+  c.gparts[0].x[0] = 0.;
+  c.gparts[0].x[1] = 0.5;
+  c.gparts[0].x[2] = 0.5;
+  c.gparts[0].mass = 1.;
+  c.gparts[0].time_bin = 1;
+  c.gparts[0].type = swift_type_dark_matter;
+  c.gparts[0].id_or_neg_offset = 1;
+#ifdef SWIFT_DEBUG_CHECKS
+  c.gparts[0].ti_drift = 8;
+#endif
+
+  /* Create the mass-less particles */
+  for (int n = 1; n < num_tests + 1; ++n) {
+
+    struct gpart *gp = &c.gparts[n];
+
+    gp->x[0] = n / ((double)num_tests);
+    gp->x[1] = 0.5;
+    gp->x[2] = 0.5;
+    gp->mass = 0.;
+    gp->time_bin = 1;
+    gp->type = swift_type_dark_matter;
+    gp->id_or_neg_offset = n + 1;
+#ifdef SWIFT_DEBUG_CHECKS
+    gp->ti_drift = 8;
+#endif
+  }
+
+  /* Now compute the forces */
+  runner_doself_grav_pp_truncated(&r, &c);
+
+  /* Verify everything */
+  for (int n = 1; n < num_tests + 1; ++n) {
+    const struct gpart *gp = &c.gparts[n];
+
+    const double epsilon = gravity_get_softening(gp, &props);
+
+    double pot_true = potential(c.gparts[0].mass, gp->x[0], epsilon, rlr);
+    double acc_true = acceleration(c.gparts[0].mass, gp->x[0], epsilon, rlr);
+
+    // message("x=%e f=%e f_true=%e", gp->x[0], gp->a_grav[0], acc_true);
+
+    check_value(gp->potential, pot_true, "potential");
+    check_value(gp->a_grav[0], acc_true, "acceleration");
+  }
+
+  free(c.gparts);
+  return 0;
+}
diff --git a/tests/testReading.c b/tests/testReading.c
index f5e2757c2fe3bd507e877f134e8c768aba0ae645..ca1e0ef69078c5e384a9cd4eab1098923ce9f279 100644
--- a/tests/testReading.c
+++ b/tests/testReading.c
@@ -48,7 +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, 1, 0);
+                 &Ngpart, &Nspart, &periodic, &flag_entropy_ICs, 1, 1, 0, 0, 1.,
+                 1, 0);
 
   /* Check global properties read are correct */
   assert(dim[0] == boxSize);
diff --git a/tests/tolerance_27_perturbed_h2.dat b/tests/tolerance_27_perturbed_h2.dat
index 781531d6a0d180d58ab74b9ef12efb927ccc733d..882554741734aeb270427b62580d6077907056ad 100644
--- a/tests/tolerance_27_perturbed_h2.dat
+++ b/tests/tolerance_27_perturbed_h2.dat
@@ -1,4 +1,4 @@
 #   ID      pos_x      pos_y      pos_z        v_x        v_y        v_z           rho        rho_dh        wcount     wcount_dh         div_v       curl_vx       curl_vy       curl_vz
     0	      1e-6       1e-6	      1e-6 	       1e-6 	    1e-6	     1e-6	         3e-6       1e-4	        5e-4       1.5e-2		         1.4e-5	       3e-6	         3e-6		       1e-5
-    0	      1e-6       1e-6	      1e-6 	       1e-6 	    1e-6	     1e-6	         1.5e-6	    2.5e-2	      1e-5       5.86e-3		       4.96e-4	     3e-3	         4.5e-3	 	       3e-3
+    0	      1e-6       1e-6	      1e-6 	       1e-6 	    1e-6	     1e-6	         1.5e-6	    2.5e-2	      1e-5       5.86e-3		       1.17e-3	     3e-3	         5.65e-3	 	       3e-3
     0	      1e-6       1e-6	      1e-6 	       1e-6 	    1e-6	     1e-6	         1e-6	      1e-6	        1e-6       1e0		           1e-6	         4e-6	         4e-6		       4e-6