diff --git a/.gitignore b/.gitignore
index 4f81f60dd7d2fd85074759e4fcc4f3e0ee21b187..80e8eafa6133ca7d183b3503f6171713a6df1b36 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,8 +22,6 @@ doc/Doxyfile
 
 examples/swift
 examples/swift_mpi
-examples/swift_fixdt
-examples/swift_fixdt_mpi
 examples/*.xmf
 examples/used_parameters.yml
 examples/energy.txt
diff --git a/README b/README
index 562a54f3104884b1bf4c5e607700279161fad4c9..9ef773cd85b408ff822b3652c3fd5507e6d95d01 100644
--- a/README
+++ b/README
@@ -13,8 +13,6 @@ See INSTALL.swift for install instructions.
 
 Usage: swift [OPTION]... PARAMFILE
        swift_mpi [OPTION]... PARAMFILE
-       swift_fixdt [OPTION]... PARAMFILE
-       swift_fixdt_mpi [OPTION]... PARAMFILE
 
 Valid options are:
   -a          Pin runners using processor affinity
diff --git a/configure.ac b/configure.ac
index f6e2ea0db8e9829b719a0190eea7a1d891bfbbd6..8cb7fd1b3f09819b7c8b57ce5d70e3b38e32a637 100644
--- a/configure.ac
+++ b/configure.ac
@@ -170,6 +170,18 @@ if test "x$enable_debug" = "xyes"; then
    fi
 fi
 
+# Check if task debugging is on.
+AC_ARG_ENABLE([task-debugging],
+   [AS_HELP_STRING([--enable-task-debugging],
+     [Store task timing information and generate task dump files @<:@yes/no@:>@]
+   )],
+   [enable_task_debugging="$enableval"],
+   [enable_task_debugging="no"]
+)
+if test "$enable_task_debugging" = "yes"; then
+   AC_DEFINE([SWIFT_DEBUG_TASKS],1,[Enable task debugging])
+fi
+
 # Define HAVE_POSIX_MEMALIGN if it works.
 AX_FUNC_POSIX_MEMALIGN
 
@@ -533,6 +545,7 @@ AC_MSG_RESULT([
    libNUMA enabled : $have_numa
    Using tcmalloc  : $have_tcmalloc
    CPU profiler    : $have_profiler
+   Task debugging  : $enable_task_debugging
 ])
 
 # Generate output.
diff --git a/examples/CoolingBox/coolingBox.yml b/examples/CoolingBox/coolingBox.yml
index e13de6095066836853d9e9068330938f6260f38e..b90ae61e5c862753227b82ebcec4cbf8f3083fab 100644
--- a/examples/CoolingBox/coolingBox.yml
+++ b/examples/CoolingBox/coolingBox.yml
@@ -1,7 +1,7 @@
 # Define the system of units to use internally. 
 InternalUnitSystem:
   UnitMass_in_cgs:     2.0e33   # Solar masses
-  UnitLength_in_cgs:   3.01e21   # Kilparsecs
+  UnitLength_in_cgs:   3.0857e21   # Kiloparsecs
   UnitVelocity_in_cgs: 1.0e5   # Time unit is cooling time
   UnitCurrent_in_cgs:  1   # Amperes
   UnitTemp_in_cgs:     1   # Kelvin
@@ -9,9 +9,9 @@ InternalUnitSystem:
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   1.0    # The end time of the simulation (in internal units).
-  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+  time_end:   4.    # The end time of the simulation (in internal units).
+  dt_min:     1e-4  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
 
 # Parameters governing the snapshots
 Snapshots:
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   
 # Parameters related to the initial conditions
@@ -36,9 +35,8 @@ InitialConditions:
 
 # Dimensionless pre-factor for the time-step condition
 LambdaCooling:
-  lambda:                      0.0    # Cooling rate (in cgs units)
+  lambda_cgs:                 1.0e-22    # Cooling rate (in cgs units)
   minimum_temperature:         1.0e4  # Minimal temperature (Kelvin)
   mean_molecular_weight:       0.59   # Mean molecular weight
   hydrogen_mass_abundance:     0.75   # Hydrogen mass abundance (dimensionless)
   cooling_tstep_mult:          1.0    # Dimensionless pre-factor for the time-step condition
-  
diff --git a/examples/CoolingBox/energy_plot.py b/examples/CoolingBox/energy_plot.py
index 28cf9ab64decb5b56e98118a407221fac2bd4f16..00e6fd1dfa0ee9bfbb9b5147282776f635b060f5 100644
--- a/examples/CoolingBox/energy_plot.py
+++ b/examples/CoolingBox/energy_plot.py
@@ -13,7 +13,7 @@ m_p = 1.67e-24 #proton mass
 #initial conditions set in makeIC.py
 rho = 3.2e3
 P = 4.5e6
-n_H_cgs = 0.0001
+#n_H_cgs = 0.0001
 gamma = 5./3.
 T_init = 1.0e5
 
@@ -24,7 +24,7 @@ unit_mass = units.attrs["Unit mass in cgs (U_M)"]
 unit_length = units.attrs["Unit length in cgs (U_L)"]
 unit_time = units.attrs["Unit time in cgs (U_t)"]
 parameters = f["Parameters"]
-cooling_lambda = float(parameters.attrs["LambdaCooling:lambda"])
+cooling_lambda = float(parameters.attrs["LambdaCooling:lambda_cgs"])
 min_T = float(parameters.attrs["LambdaCooling:minimum_temperature"])
 mu = float(parameters.attrs["LambdaCooling:mean_molecular_weight"])
 X_H = float(parameters.attrs["LambdaCooling:hydrogen_mass_abundance"])
@@ -32,50 +32,65 @@ X_H = float(parameters.attrs["LambdaCooling:hydrogen_mass_abundance"])
 #get number of particles
 header = f["Header"]
 n_particles = header.attrs["NumPart_ThisFile"][0]
+
 #read energy and time arrays
 array = np.genfromtxt(stats_filename,skip_header = 1)
 time = array[:,0]
-total_energy = array[:,2]
+kin_plus_therm = array[:,2]
+radiated = array[:,6]
 total_mass = array[:,1]
 
+#ignore first row where there are just zeros
 time = time[1:]
-total_energy = total_energy[1:]
+kin_plus_therm = kin_plus_therm[1:]
+radiated = radiated[1:]
 total_mass = total_mass[1:]
 
+total_energy = kin_plus_therm + radiated
+initial_energy = total_energy[0]
 #conversions to cgs
 rho_cgs = rho * unit_mass / (unit_length)**3
 time_cgs = time * unit_time
-u_init_cgs = total_energy[0]/(total_mass[0]) * unit_length**2 / (unit_time)**2 
+initial_energy_cgs = initial_energy/total_mass[0] * unit_length**2 / (unit_time)**2 
+n_H_cgs = X_H * rho_cgs / m_p
 
 #find the energy floor
-print min_T
 u_floor_cgs = k_b * min_T / (mu * m_p * (gamma - 1.))
+
 #find analytic solution
-analytic_time = np.linspace(time_cgs[0],time_cgs[-1],1000)
-print time_cgs[1]
-print analytic_time[1]
+analytic_time_cgs = np.linspace(0,time_cgs[-1],1000)
 du_dt_cgs = -cooling_lambda * n_H_cgs**2 / rho_cgs
-u_analytic = du_dt_cgs*(analytic_time - analytic_time[0]) + u_init_cgs
-cooling_time = u_init_cgs/(-du_dt_cgs)
-#rescale energy to initial energy
-total_energy /= total_energy[0]
-u_analytic /= u_init_cgs
-u_floor_cgs /= u_init_cgs
-# plot_title = r"$\Lambda \, = \, %1.1g \mathrm{erg}\mathrm{cm^3}\mathrm{s^{-1}} \, \, T_{init} = %1.1g\mathrm{K} \, \, T_{floor} = %1.1g\mathrm{K} \, \, n_H = %1.1g\mathrm{cm^{-3}}$" %(cooling_lambda,T_init,T_floor,n_H)
-# plot_filename = "energy_plot_creasey_no_cooling_T_init_1p0e5_n_H_0p1.png"
-#analytic_solution = np.zeros(n_snaps-1)
-for i in range(u_analytic.size):
-    if u_analytic[i]<u_floor_cgs:
-        u_analytic[i] = u_floor_cgs
-plt.plot(time_cgs,total_energy,'k',label = "Numerical solution")
-plt.plot(analytic_time,u_analytic,'--r',lw = 2.0,label = "Analytic Solution")
-plt.plot((cooling_time,cooling_time),(0,1),'b',label = "Cooling time")
-plt.plot((time_cgs[0],time_cgs[0]),(0,1),'m',label = "First output")
-plt.title(r"$n_H = %1.1e \, \mathrm{cm}^{-3}$" %n_H_cgs)
-plt.xlabel("Time (seconds)")
-plt.ylabel("Energy/Initial energy")
+u_analytic_cgs = du_dt_cgs*analytic_time_cgs + initial_energy_cgs
+cooling_time_cgs = initial_energy_cgs/(-du_dt_cgs)
+
+for i in range(u_analytic_cgs.size):
+    if u_analytic_cgs[i]<u_floor_cgs:
+        u_analytic_cgs[i] = u_floor_cgs
+
+#rescale analytic solution
+u_analytic = u_analytic_cgs/initial_energy_cgs
+
+#put time in units of cooling_time
+time=time_cgs/cooling_time_cgs
+analytic_time = analytic_time_cgs/cooling_time_cgs
+
+#rescale (numerical) energy by initial energy
+radiated /= initial_energy
+kin_plus_therm /= initial_energy
+total_energy = kin_plus_therm + radiated
+plt.plot(time,kin_plus_therm,'kd',label = "Kinetic + thermal energy")
+plt.plot(time,radiated,'bo',label = "Radiated energy")
+plt.plot(time,total_energy,'g',label = "Total energy")
+plt.plot(analytic_time,u_analytic,'r',lw = 2.0,label = "Analytic Solution")
+#plt.plot(analytic_time,1-u_analytic,'k',lw = 2.0)
+#plt.plot((cooling_time,cooling_time),(0,1),'b',label = "Cooling time")
+#plt.plot((time[1]-time_cgs[0],time_cgs[1]-time_cgs[0]),(0,1),'m',label = "First output")
+#plt.title(r"$n_H = %1.1e \, \mathrm{cm}^{-3}$" %n_H_cgs)
+plt.xlabel("Time / cooling time")
+plt.ylabel("Energy / Initial energy")
+#plt.ylim(0,1.1)
 plt.ylim(0.999,1.001)
-plt.xlim(0,min(10.0*cooling_time,time_cgs[-1]))
+#plt.xlim(0,min(10,time[-1]))
 plt.legend(loc = "upper right")    
 if (int(sys.argv[1])==0):
     plt.show()
diff --git a/examples/CoolingBox/makeIC.py b/examples/CoolingBox/makeIC.py
index f35c9243d4fa71f872fd27520de14a23073c4b9d..5de012a17af4eef71e56548602e7956faef529f5 100644
--- a/examples/CoolingBox/makeIC.py
+++ b/examples/CoolingBox/makeIC.py
@@ -27,10 +27,10 @@ from numpy import *
 
 # Parameters
 periodic= 1           # 1 For periodic box
-boxSize = 1           #1 kiloparsec    
+boxSize = 1           # 1 kiloparsec    
 L = int(sys.argv[1])  # Number of particles along one axis
-rho = 3.2e3          # Density in code units (0.01 hydrogen atoms per cm^3)
-P = 4.5e6          # Pressure in code units (at 10^5K)
+rho = 3.2e3           # Density in code units (3.2e6 is 0.1 hydrogen atoms per cm^3)
+P = 4.5e6             # Pressure in code units (at 10^5K)
 gamma = 5./3.         # Gas adiabatic index
 eta = 1.2349          # 48 ngbs with cubic spline kernel
 fileName = "coolingBox.hdf5" 
@@ -63,9 +63,9 @@ grp.attrs["PeriodicBoundariesOn"] = periodic
 
 #Units
 grp = file.create_group("/Units")
-grp.attrs["Unit length in cgs (U_L)"] = 3.08e21 
+grp.attrs["Unit length in cgs (U_L)"] = 3.0857e21 
 grp.attrs["Unit mass in cgs (U_M)"] = 2.0e33 
-grp.attrs["Unit time in cgs (U_t)"] = 3.08e16 
+grp.attrs["Unit time in cgs (U_t)"] = 3.0857e16 
 grp.attrs["Unit current in cgs (U_I)"] = 1.
 grp.attrs["Unit temperature in cgs (U_T)"] = 1.
 
diff --git a/examples/CoolingBox/run.sh b/examples/CoolingBox/run.sh
index c78eec9da6c486bc31a60ab7a8521ce6a6a63165..cb3264808d57b435c9f65bf5a684a94ff9f878fd 100755
--- a/examples/CoolingBox/run.sh
+++ b/examples/CoolingBox/run.sh
@@ -5,6 +5,10 @@ echo "Generating initial conditions for the cooling box example..."
 
 python makeIC.py 10
 
-../swift -s -t 1 coolingBox.yml -C 2>&1 | tee output.log
+../swift -s -C -t 16 coolingBox.yml 
+
+#-C 2>&1 | tee output.log
 
 python energy_plot.py 0
+
+#python test_energy_conservation.py 0
diff --git a/examples/CoolingBox/test_energy_conservation.py b/examples/CoolingBox/test_energy_conservation.py
new file mode 100644
index 0000000000000000000000000000000000000000..bb15071c0668d71580015351ce75ce18390c8cf0
--- /dev/null
+++ b/examples/CoolingBox/test_energy_conservation.py
@@ -0,0 +1,116 @@
+import numpy as np
+import matplotlib.pyplot as plt
+import h5py as h5
+import sys
+
+stats_filename = "./energy.txt"
+snap_filename = "coolingBox_000.hdf5"
+#plot_dir = "./"
+n_snaps = 41
+time_end = 4.0
+dt_snap = 0.1
+#some constants in cgs units
+k_b = 1.38E-16 #boltzmann
+m_p = 1.67e-24 #proton mass
+#initial conditions set in makeIC.py
+rho = 4.8e3
+P = 4.5e6
+#n_H_cgs = 0.0001
+gamma = 5./3.
+T_init = 1.0e5
+
+#find the sound speed
+
+#Read the units parameters from the snapshot
+f = h5.File(snap_filename,'r')
+units = f["InternalCodeUnits"]
+unit_mass = units.attrs["Unit mass in cgs (U_M)"]
+unit_length = units.attrs["Unit length in cgs (U_L)"]
+unit_time = units.attrs["Unit time in cgs (U_t)"]
+parameters = f["Parameters"]
+cooling_lambda = float(parameters.attrs["LambdaCooling:lambda_cgs"])
+min_T = float(parameters.attrs["LambdaCooling:minimum_temperature"])
+mu = float(parameters.attrs["LambdaCooling:mean_molecular_weight"])
+X_H = float(parameters.attrs["LambdaCooling:hydrogen_mass_abundance"])
+
+#get number of particles
+header = f["Header"]
+n_particles = header.attrs["NumPart_ThisFile"][0]
+#read energy and time arrays
+array = np.genfromtxt(stats_filename,skip_header = 1)
+time = array[:,0]
+total_energy = array[:,2]
+total_mass = array[:,1]
+
+time = time[1:]
+total_energy = total_energy[1:]
+total_mass = total_mass[1:]
+
+#conversions to cgs
+rho_cgs = rho * unit_mass / (unit_length)**3
+time_cgs = time * unit_time
+u_init_cgs = total_energy[0]/(total_mass[0]) * unit_length**2 / (unit_time)**2 
+n_H_cgs = X_H * rho_cgs / m_p
+
+#find the sound speed in cgs
+c_s = np.sqrt((gamma - 1.)*k_b*T_init/(mu*m_p))
+#assume box size is unit length
+sound_crossing_time = unit_length/c_s
+
+print "Sound speed = %g cm/s" %c_s
+print "Sound crossing time = %g s" %sound_crossing_time 
+#find the energy floor
+u_floor_cgs = k_b * min_T / (mu * m_p * (gamma - 1.))
+#find analytic solution
+analytic_time_cgs = np.linspace(time_cgs[0],time_cgs[-1],1000)
+du_dt_cgs = -cooling_lambda * n_H_cgs**2 / rho_cgs
+u_analytic = du_dt_cgs*(analytic_time_cgs - analytic_time_cgs[0]) + u_init_cgs
+cooling_time = u_init_cgs/(-du_dt_cgs)
+
+#put time in units of sound crossing time
+time=time_cgs/sound_crossing_time
+analytic_time = analytic_time_cgs/sound_crossing_time
+#rescale energy to initial energy
+total_energy /= total_energy[0]
+u_analytic /= u_init_cgs
+u_floor_cgs /= u_init_cgs
+# plot_title = r"$\Lambda \, = \, %1.1g \mathrm{erg}\mathrm{cm^3}\mathrm{s^{-1}} \, \, T_{init} = %1.1g\mathrm{K} \, \, T_{floor} = %1.1g\mathrm{K} \, \, n_H = %1.1g\mathrm{cm^{-3}}$" %(cooling_lambda,T_init,T_floor,n_H)
+# plot_filename = "energy_plot_creasey_no_cooling_T_init_1p0e5_n_H_0p1.png"
+#analytic_solution = np.zeros(n_snaps-1)
+for i in range(u_analytic.size):
+    if u_analytic[i]<u_floor_cgs:
+        u_analytic[i] = u_floor_cgs
+plt.plot(time-time[0],total_energy,'k',label = "Numerical solution from energy.txt")
+plt.plot(analytic_time-analytic_time[0],u_analytic,'r',lw = 2.0,label = "Analytic Solution")
+
+#now get energies from the snapshots
+snapshot_time = np.linspace(0,time_end,num = n_snaps)
+snapshot_time = snapshot_time[1:]
+snapshot_time_cgs = snapshot_time * unit_time
+snapshot_time = snapshot_time_cgs/ sound_crossing_time
+snapshot_time -= snapshot_time[0]
+snapshot_energy = np.zeros(n_snaps)
+for i in range(0,n_snaps):
+    snap_filename = "coolingBox_%03d.hdf5" %i
+    f = h5.File(snap_filename,'r')
+    snapshot_internal_energy_array = np.array(f["PartType0/InternalEnergy"])
+    total_internal_energy = np.sum(snapshot_internal_energy_array)
+    velocity_array = np.array(f["PartType0/Velocities"])
+    total_kinetic_energy = 0.5*np.sum(velocity_array**2)
+    snapshot_energy[i] = total_internal_energy + total_kinetic_energy
+snapshot_energy/=snapshot_energy[0]
+snapshot_energy = snapshot_energy[1:]
+
+plt.plot(snapshot_time,snapshot_energy,'bd',label = "Numerical solution from snapshots")
+
+#plt.title(r"$n_H = %1.1e \, \mathrm{cm}^{-3}$" %n_H_cgs)
+plt.xlabel("Time (sound crossing time)")
+plt.ylabel("Energy/Initial energy")
+plt.ylim(0.99,1.01)
+#plt.xlim(0,min(10,time[-1]))
+plt.legend(loc = "upper right")    
+if (int(sys.argv[1])==0):
+    plt.show()
+else:
+    plt.savefig(full_plot_filename,format = "png")
+    plt.close()
diff --git a/examples/CoolingHalo/README b/examples/CoolingHalo/README
new file mode 100644
index 0000000000000000000000000000000000000000..7ef3c5e0283a500856582b386300aad630c0a55a
--- /dev/null
+++ b/examples/CoolingHalo/README
@@ -0,0 +1,25 @@
+
+To make the initial conditions we distribute gas particles randomly in
+a cube with a side length twice that of the virial radius. The density
+profile of the gas is proportional to r^(-2) where r is the distance
+from the centre of the cube.
+
+The parameter v_rot (in makeIC.py and cooling.yml) sets the circular
+velocity of the halo, and by extension, the viral radius, viral mass,
+and the internal energy of the gas such that hydrostatic equilibrium
+is achieved.
+
+While the system is initially in hydrostatic equilibrium, the cooling
+of the gas means that the halo will collapse.
+
+To run this example, make such that the code is compiled with either
+the isothermal potential or softened isothermal potential, and
+'const_lambda' cooling, set in src/const.h. In the latter case, a
+(small) value of epsilon needs to be set in cooling.yml.  0.1 kpc
+should work well.
+
+The plotting scripts produce a plot of the density, internal energy
+and radial velocity profile for each
+snapshot. test_energy_conservation.py shows the evolution of energy
+with time. These can be used to check if the example has run properly.
+
diff --git a/examples/CoolingHalo/cooling_halo.yml b/examples/CoolingHalo/cooling_halo.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c06b099eb0dd06d39040e0ecc8e8f1320a89ac6b
--- /dev/null
+++ b/examples/CoolingHalo/cooling_halo.yml
@@ -0,0 +1,54 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.9885e39     # 10^6 solar masses
+  UnitLength_in_cgs:   3.0856776e21  # Kiloparsecs
+  UnitVelocity_in_cgs: 1e5           # Kilometres per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   10.0    # The end time of the simulation (in internal units).
+  dt_min:     1e-5  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-2 # Time between statistics output
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            CoolingHalo  # 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)
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2349   # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel).
+  delta_neighbours:      1.       # The tolerance for the targetted number of neighbours.
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  CoolingHalo.hdf5       # The file to read
+  shift_x:    0.                  # A shift to apply to all particles read from the ICs (in internal units).
+  shift_y:    0.
+  shift_z:    0.
+ 
+# External potential parameters
+SoftenedIsothermalPotential:
+  position_x:      0.     # location of centre of isothermal potential in internal units
+  position_y:      0.
+  position_z:      0.	
+  vrot:            200.     # rotation speed of isothermal potential in internal units
+  timestep_mult:   0.03     # controls time step
+  epsilon:         0.1    #softening for the isothermal potential
+
+# Cooling parameters
+LambdaCooling:
+  lambda_cgs:                  1.0e-22    # Cooling rate (in cgs units)
+  minimum_temperature:         1.0e4  # Minimal temperature (Kelvin)
+  mean_molecular_weight:       0.59   # Mean molecular weight
+  hydrogen_mass_abundance:     0.75   # Hydrogen mass abundance (dimensionless)
+  cooling_tstep_mult:          1.0    # Dimensionless pre-factor for the time-step condition
diff --git a/examples/CoolingHalo/density_profile.py b/examples/CoolingHalo/density_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..335f7089b6835b65cf37e1bcd312a17966c295a7
--- /dev/null
+++ b/examples/CoolingHalo/density_profile.py
@@ -0,0 +1,101 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+n_snaps = 11
+
+#for the plotting
+#n_radial_bins = int(sys.argv[1])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "Hydrostatic_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["IsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+for i in range(n_snaps):
+
+    filename = "Hydrostatic_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+    r = radius_over_virial_radius
+
+    # bin_width = 1./n_radial_bins
+#     hist = np.histogram(r,bins = n_radial_bins)[0] # number of particles in each bin
+
+# #find the mass in each radial bin
+
+#     mass_dset = f["PartType0/Masses"]
+# #mass of each particles should be equal
+#     part_mass = np.array(mass_dset)[0]
+#     part_mass_cgs = part_mass * unit_mass_cgs
+#     part_mass_over_virial_mass = part_mass_cgs / M_vir_cgs 
+
+#     mass_hist = hist * part_mass_over_virial_mass
+#     radial_bin_mids = np.linspace(bin_width/2.,1 - bin_width/2.,n_radial_bins)
+# #volume in each radial bin
+#     volume = 4.*np.pi * radial_bin_mids**2 * bin_width
+
+# #now divide hist by the volume so we have a density in each bin
+
+#     density = mass_hist / volume
+
+    # read the densities
+
+    density_dset = f["PartType0/Density"]
+    density = np.array(density_dset)
+    density_cgs = density * unit_mass_cgs / unit_length_cgs**3
+    rho = density_cgs * r_vir_cgs**3 / M_vir_cgs
+
+    t = np.linspace(0.01,2.0,1000)
+    rho_analytic = t**(-2)/(4.*np.pi)
+
+    plt.plot(r,rho,'x',label = "Numerical solution")
+    plt.plot(t,rho_analytic,label = "Analytic Solution")
+    plt.legend(loc = "upper right")
+    plt.xlabel(r"$r / r_{vir}$")
+    plt.ylabel(r"$\rho / (M_{vir} / r_{vir}^3)$")
+    plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c))
+    #plt.ylim((0.1,40))
+    plt.xscale('log')
+    plt.yscale('log')
+    plot_filename = "density_profile_%03d.png" %i
+    plt.savefig(plot_filename,format = "png")
+    plt.close()
+
diff --git a/examples/CoolingHalo/internal_energy_profile.py b/examples/CoolingHalo/internal_energy_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..854bdf223cfae75203a1924b4af6136b4b7aa6cd
--- /dev/null
+++ b/examples/CoolingHalo/internal_energy_profile.py
@@ -0,0 +1,104 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+def do_binning(x,y,x_bin_edges):
+
+    #x and y are arrays, where y = f(x)
+    #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values
+
+    n_bins = x_bin_edges.size - 1
+    count = np.zeros(n_bins)
+    y_totals = np.zeros(n_bins)
+    
+    for i in range(n_bins):
+        ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0])
+        count[i] = ind.size
+        binned_y = y[ind]
+        y_totals[i] = np.sum(binned_y)
+
+    return(count,y_totals)
+
+
+n_snaps = 100
+
+#for the plotting
+n_radial_bins = int(sys.argv[1])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "Hydrostatic_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["IsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+for i in range(n_snaps):
+
+    filename = "Hydrostatic_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+#get the internal energies
+    u_dset = f["PartType0/InternalEnergy"]
+    u = np.array(u_dset)
+
+#make dimensionless
+    u /= v_c**2/(2. * (gamma - 1.))
+    r = radius_over_virial_radius
+
+    bin_edges = np.linspace(0,1,n_radial_bins + 1)
+    (hist,u_totals) = do_binning(r,u,bin_edges)
+    
+    bin_widths = 1. / n_radial_bins
+    radial_bin_mids = np.linspace(bin_widths / 2. , 1. - bin_widths / 2. , n_radial_bins) 
+    binned_u = u_totals / hist
+
+
+    plt.plot(radial_bin_mids,binned_u,'ko',label = "Numerical solution")
+    plt.plot((0,1),(1,1),label = "Analytic Solution")
+    plt.legend(loc = "lower right")
+    plt.xlabel(r"$r / r_{vir}$")
+    plt.ylabel(r"$u / (v_c^2 / (2(\gamma - 1)) $")
+    plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c))
+    plt.ylim((0,1))
+    plot_filename = "internal_energy_profile_%03d.png" %i
+    plt.savefig(plot_filename,format = "png")
+    plt.close()
+
+
+        
+    
diff --git a/examples/CoolingHalo/makeIC.py b/examples/CoolingHalo/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b542e200da709e2cc7f668ab8b62b94e0bf95ee
--- /dev/null
+++ b/examples/CoolingHalo/makeIC.py
@@ -0,0 +1,234 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Stefan Arridge (stefan.arridge@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+import h5py
+import sys
+import numpy as np
+import math
+import random
+
+# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2)
+# usage: python makeIC.py 1000: generate 1000 particles
+
+# Some constants
+
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+
+# First set unit velocity and then the circular velocity parameter for the isothermal potential
+const_unit_velocity_in_cgs = 1.e5 #kms^-1
+
+v_c = 200.
+v_c_cgs = v_c * const_unit_velocity_in_cgs
+
+# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius
+
+# Find H_0, the inverse Hubble time, in cgs
+
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+# From this we can find the virial radius, the radius within which the average density of the halo is
+# 200. * the mean matter density
+
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+
+# Now get the virial mass
+
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+# Now set the unit length and mass
+
+const_unit_mass_in_cgs = M_vir_cgs
+const_unit_length_in_cgs = r_vir_cgs
+
+print "UnitMass_in_cgs:     ", const_unit_mass_in_cgs 
+print "UnitLength_in_cgs:   ", const_unit_length_in_cgs
+print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs
+
+#derived quantities
+const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs)
+print "UnitTime_in_cgs:     ", const_unit_time_in_cgs
+const_G                = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs)))
+print 'G=', const_G
+
+# Parameters
+periodic= 1            # 1 For periodic box
+boxSize = 4.          
+G       = const_G 
+N       = int(sys.argv[1])  # Number of particles
+
+# Create the file
+filename = "CoolingHalo.hdf5"
+file = h5py.File(filename, 'w')
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs 
+grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+
+# Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = periodic
+
+# set seed for random number
+np.random.seed(1234)
+
+
+# Positions
+# r^(-2) distribution corresponds to uniform distribution in radius
+radius = boxSize  * np.sqrt(3.) / 2.* np.random.rand(N) #the diagonal extent of the cube
+ctheta = -1. + 2 * np.random.rand(N)
+stheta = np.sqrt(1.-ctheta**2)
+phi    =  2 * math.pi * np.random.rand(N)
+coords      = np.zeros((N, 3))
+coords[:,0] = radius * stheta * np.cos(phi)
+coords[:,1] = radius * stheta * np.sin(phi)
+coords[:,2] = radius * ctheta
+
+#shift to centre of box
+coords += np.full((N,3),boxSize/2.)
+print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0]))
+print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1]))
+print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2]))
+
+print np.mean(coords[:,0])
+print np.mean(coords[:,1])
+print np.mean(coords[:,2])
+
+#now find the particles which are within the box
+
+x_coords = coords[:,0]
+y_coords = coords[:,1]
+z_coords = coords[:,2]
+
+ind = np.where(x_coords < boxSize)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(x_coords > 0.)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(y_coords < boxSize)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(y_coords > 0.)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(z_coords < boxSize)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(z_coords > 0.)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+#count number of particles
+
+N = x_coords.size
+
+print "Number of particles in the box = " , N
+
+#make the coords and radius arrays again
+coords_2 = np.zeros((N,3))
+coords_2[:,0] = x_coords
+coords_2[:,1] = y_coords
+coords_2[:,2] = z_coords
+
+radius = np.sqrt(coords_2[:,0]**2 + coords_2[:,1]**2 + coords_2[:,2]**2)
+
+#test we've done it right
+
+print "x range = (%f,%f)" %(np.min(coords_2[:,0]),np.max(coords_2[:,0]))
+print "y range = (%f,%f)" %(np.min(coords_2[:,1]),np.max(coords_2[:,1]))
+print "z range = (%f,%f)" %(np.min(coords_2[:,2]),np.max(coords_2[:,2]))
+
+print np.mean(coords_2[:,0])
+print np.mean(coords_2[:,1])
+print np.mean(coords_2[:,2])
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] =  [N ,0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# Particle group
+grp = file.create_group("/PartType0")
+
+ds = grp.create_dataset('Coordinates', (N, 3), 'd')
+ds[()] = coords_2
+coords_2 = np.zeros(1)
+
+# All velocities set to zero
+v = np.zeros((N,3))
+ds = grp.create_dataset('Velocities', (N, 3), 'f')
+ds[()] = v
+v = np.zeros(1)
+
+# All particles of equal mass
+mass = 1. / N
+m = np.full((N,),mass)
+ds = grp.create_dataset('Masses', (N, ), 'f')
+ds[()] = m
+m = np.zeros(1)
+
+# Smoothing lengths
+l = (4. * np.pi * radius**2 / N)**(1./3.) #local mean inter-particle separation
+h = np.full((N, ), eta * l)
+ds = grp.create_dataset('SmoothingLength', (N,), 'f')
+ds[()] = h
+h = np.zeros(1)
+
+# Internal energies
+u = v_c**2 / (2. * (gamma - 1.)) 
+u = np.full((N, ), u)
+ds = grp.create_dataset('InternalEnergy', (N,), 'f')
+ds[()] = u
+u = np.zeros(1)
+
+# Particle IDs
+ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L')
+ds = grp.create_dataset('ParticleIDs', (N, ), 'L')
+ds[()] = ids
+
+file.close()
diff --git a/examples/CoolingHalo/makeIC_random_box.py b/examples/CoolingHalo/makeIC_random_box.py
new file mode 100644
index 0000000000000000000000000000000000000000..4295cb135233f2d5a59405b44e6d8e9c80a1f6c0
--- /dev/null
+++ b/examples/CoolingHalo/makeIC_random_box.py
@@ -0,0 +1,168 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Stefan Arridge (stefan.arridge@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+import h5py
+import sys
+import numpy as np
+import math
+import random
+
+# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2)
+# usage: python makeIC.py 1000: generate 1000 particles
+
+# Some constants
+
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+
+# First set unit velocity and then the circular velocity parameter for the isothermal potential
+const_unit_velocity_in_cgs = 1.e5 #kms^-1
+
+v_c = 200.
+v_c_cgs = v_c * const_unit_velocity_in_cgs
+
+# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius
+
+# Find H_0, the inverse Hubble time, in cgs
+
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+# From this we can find the virial radius, the radius within which the average density of the halo is
+# 200. * the mean matter density
+
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+
+# Now get the virial mass
+
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+# Now set the unit length and mass
+
+const_unit_mass_in_cgs = M_vir_cgs
+const_unit_length_in_cgs = r_vir_cgs
+
+print "UnitMass_in_cgs:     ", const_unit_mass_in_cgs 
+print "UnitLength_in_cgs:   ", const_unit_length_in_cgs
+print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs
+
+#derived quantities
+const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs)
+print "UnitTime_in_cgs:     ", const_unit_time_in_cgs
+const_G                = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs)))
+print 'G=', const_G
+
+# Parameters
+periodic= 1            # 1 For periodic box
+boxSize = 4.          
+G       = const_G 
+N       = int(sys.argv[1])  # Number of particles
+
+# Create the file
+filename = "random_box.hdf5"
+file = h5py.File(filename, 'w')
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs 
+grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] =  [N ,0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = periodic
+
+# set seed for random number
+np.random.seed(1234)
+
+# Particle group
+grp = file.create_group("/PartType0")
+
+# Positions
+
+# distribute particles randomly in the box
+coords      = np.zeros((N, 3))
+coords[:,0] = boxSize * np.random.rand(N)
+coords[:,1] = boxSize * np.random.rand(N)
+coords[:,2] = boxSize * np.random.rand(N)
+#shift to centre of box
+#coords += np.full((N,3),boxSize/2.)
+print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0]))
+print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1]))
+print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2]))
+
+print np.mean(coords[:,0])
+print np.mean(coords[:,1])
+print np.mean(coords[:,2])
+
+ds = grp.create_dataset('Coordinates', (N, 3), 'd')
+ds[()] = coords
+coords = np.zeros(1)
+
+# All velocities set to zero
+v = np.zeros((N,3))
+ds = grp.create_dataset('Velocities', (N, 3), 'f')
+ds[()] = v
+v = np.zeros(1)
+
+# All particles of equal mass
+mass = 1. / N
+m = np.full((N,),mass)
+ds = grp.create_dataset('Masses', (N, ), 'f')
+ds[()] = m
+m = np.zeros(1)
+
+# Smoothing lengths
+l = (boxSize**3 / N)**(1./3.) #local mean inter-particle separation
+h = np.full((N, ), eta * l)
+ds = grp.create_dataset('SmoothingLength', (N,), 'f')
+ds[()] = h
+h = np.zeros(1)
+
+# Internal energies
+u = v_c**2 / (2. * (gamma - 1.)) 
+u = np.full((N, ), u)
+ds = grp.create_dataset('InternalEnergy', (N,), 'f')
+ds[()] = u
+u = np.zeros(1)
+
+# Particle IDs
+ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L')
+ds = grp.create_dataset('ParticleIDs', (N, ), 'L')
+ds[()] = ids
+
+file.close()
diff --git a/examples/CoolingHalo/run.sh b/examples/CoolingHalo/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..60ceae649d183dce3a7e5019a1ff94ce7bc4f08d
--- /dev/null
+++ b/examples/CoolingHalo/run.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+echo "Generating initial conditions for the isothermal potential box example..."
+python makeIC.py 10000 
+
+../swift -g -s -C -t 16 cooling_halo.yml 2>&1 | tee output.log
+
+python radial_profile.py 2. 200 100
+
+python internal_energy_profile.py 2. 200 100
+
+python test_energy_conservation.py 2. 200 100
diff --git a/examples/CoolingHalo/test_energy_conservation.py b/examples/CoolingHalo/test_energy_conservation.py
new file mode 100644
index 0000000000000000000000000000000000000000..00374e905e8eeb66bfe8c7360ab37522bc93af32
--- /dev/null
+++ b/examples/CoolingHalo/test_energy_conservation.py
@@ -0,0 +1,96 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+n_snaps = 41
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "CoolingHalo_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+potential_energy_array = []
+internal_energy_array = []
+kinetic_energy_array = []
+time_array_cgs = []
+
+for i in range(n_snaps):
+
+    filename = "CoolingHalo_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    time_array_cgs = np.append(time_array_cgs,snap_time_cgs)
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+    r = radius_over_virial_radius
+    total_potential_energy = np.sum(v_c**2*np.log(r))
+    potential_energy_array = np.append(potential_energy_array,total_potential_energy)
+
+    vels_dset = f["PartType0/Velocities"]
+    vels = np.array(vels_dset)
+    speed_squared = vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2
+    total_kinetic_energy = 0.5 * np.sum(speed_squared)
+    kinetic_energy_array = np.append(kinetic_energy_array,total_kinetic_energy)
+
+    u_dset = f["PartType0/InternalEnergy"]
+    u = np.array(u_dset)
+    total_internal_energy = np.sum(u)
+    internal_energy_array = np.append(internal_energy_array,total_internal_energy)
+
+#put energies in units of v_c^2 and rescale by number of particles
+
+pe = potential_energy_array / (N*v_c**2)
+ke = kinetic_energy_array / (N*v_c**2)
+ie = internal_energy_array / (N*v_c**2)
+te = pe + ke + ie
+
+dyn_time_cgs = r_vir_cgs / v_c_cgs
+time_array = time_array_cgs / dyn_time_cgs
+
+plt.plot(time_array,ke,label = "Kinetic Energy")
+plt.plot(time_array,pe,label = "Potential Energy")
+plt.plot(time_array,ie,label = "Internal Energy")
+plt.plot(time_array,te,label = "Total Energy")
+plt.legend(loc = "upper right")
+plt.xlabel(r"$t / t_{dyn}$")
+plt.ylabel(r"$E / v_c^2$")
+plt.title(r"$%d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(N,v_c))
+plt.ylim((-4,2))
+#plot_filename = "density_profile_%03d.png" %i
+plt.show()
+
diff --git a/examples/CoolingHalo/velocity_profile.py b/examples/CoolingHalo/velocity_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..d64d255b18482bc26578f21f46199aa3540ae7b5
--- /dev/null
+++ b/examples/CoolingHalo/velocity_profile.py
@@ -0,0 +1,111 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+def do_binning(x,y,x_bin_edges):
+
+    #x and y are arrays, where y = f(x)
+    #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values
+
+    n_bins = x_bin_edges.size - 1
+    count = np.zeros(n_bins)
+    y_totals = np.zeros(n_bins)
+    
+    for i in range(n_bins):
+        ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0])
+        count[i] = ind.size
+        binned_y = y[ind]
+        y_totals[i] = np.sum(binned_y)
+
+    return(count,y_totals)
+
+
+#for the plotting
+max_r = float(sys.argv[1])
+n_radial_bins = int(sys.argv[2])
+n_snaps = int(sys.argv[3])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+CONST_m_H_CGS = 1.67e-24
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "CoolingHalo_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+for i in range(n_snaps):
+
+    filename = "CoolingHalo_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+#get the internal energies
+    vel_dset = f["PartType0/Velocities"]
+    vel = np.array(vel_dset)
+
+#make dimensionless
+    vel /= v_c
+    r = radius_over_virial_radius
+
+    #find radial component of velocity
+
+    v_r = np.zeros(r.size)
+    for j in range(r.size):
+        v_r[j] = -np.dot(coords[j,:],vel[j,:])/radius[j]
+
+    bin_edges = np.linspace(0,max_r,n_radial_bins + 1)
+    (hist,v_r_totals) = do_binning(r,v_r,bin_edges)
+    
+    bin_widths = bin_edges[1] - bin_edges[0]
+    radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) 
+    binned_v_r = v_r_totals / hist
+
+    #calculate cooling radius
+
+    #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs)
+
+    plt.plot(radial_bin_mids,binned_v_r,'ko',label = "Average radial velocity in shell")
+    #plt.plot((0,1),(1,1),label = "Analytic Solution")
+    #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius")
+    plt.legend(loc = "upper right")
+    plt.xlabel(r"$r / r_{vir}$")
+    plt.ylabel(r"$v_r / v_c$")
+    plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c))
+    plt.ylim((0,2))
+    plot_filename = "./plots/radial_velocity_profile/velocity_profile_%03d.png" %i
+    plt.savefig(plot_filename,format = "png")
+    plt.close()
diff --git a/examples/CoolingHaloWithSpin/README b/examples/CoolingHaloWithSpin/README
new file mode 100644
index 0000000000000000000000000000000000000000..53564036ca35dc0c4396a801cc4fa768d41875b5
--- /dev/null
+++ b/examples/CoolingHaloWithSpin/README
@@ -0,0 +1,29 @@
+
+To make the initial conditions we distribute gas particles randomly in
+a cube with a side length twice that of the virial radius. The density
+profile of the gas is proportional to r^(-2) where r is the distance
+from the centre of the cube.
+
+The parameter v_rot (in makeIC.py and cooling.yml) sets the circular
+velocity of the halo, and by extension, the viral radius, viral mass,
+and the internal energy of the gas such that hydrostatic equilibrium
+is achieved.
+
+The gas is given some angular momentum about the z-axis. This is
+defined by the 'spin_lambda' variable in makeIC.py
+
+While the system is initially in hydrostatic equilibrium, the cooling
+of the gas and the non-zero angular momentum means that the halo will
+collapse into a spinning disc.
+
+To run this example, make such that the code is compiled with either
+the isothermal potential or softened isothermal potential, and
+'const_lambda' cooling, set in src/const.h. In the latter case, a
+(small) value of epsilon needs to be set in cooling.yml.  0.1 kpc
+should work well.
+
+The plotting scripts produce a plot of the density, internal energy
+and radial velocity profile for each
+snapshot. test_energy_conservation.py shows the evolution of energy
+with time. These can be used to check if the example has run properly.
+
diff --git a/examples/CoolingHaloWithSpin/cooling_halo.yml b/examples/CoolingHaloWithSpin/cooling_halo.yml
new file mode 100644
index 0000000000000000000000000000000000000000..684dd11fcf7adc9477d199e599dfb5b76faa91f6
--- /dev/null
+++ b/examples/CoolingHaloWithSpin/cooling_halo.yml
@@ -0,0 +1,51 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.9885e39     # 10^6 solar masses
+  UnitLength_in_cgs:   3.0856776e21  # Kiloparsecs
+  UnitVelocity_in_cgs: 1e5           # Kilometres per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   10.    # The end time of the simulation (in internal units).
+  dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-1  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-2 # Time between statistics output
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            CoolingHalo  # 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)
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2349   # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel).
+  delta_neighbours:      1.       # The tolerance for the targetted number of neighbours.
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  CoolingHalo.hdf5       # The file to read
+ 
+# External potential parameters
+SoftenedIsothermalPotential:
+  position_x:      0.     # location of centre of isothermal potential in internal units
+  position_y:      0.
+  position_z:      0.	
+  vrot:            200.     # rotation speed of isothermal potential in internal units
+  timestep_mult:   0.03     # controls time step
+  epsilon:         1.0      #softening for the isothermal potential
+
+# Cooling parameters
+LambdaCooling:
+  lambda_cgs:                  1.0e-22   # Cooling rate (in cgs units)
+  minimum_temperature:         1.0e4  # Minimal temperature (Kelvin)
+  mean_molecular_weight:       0.59   # Mean molecular weight
+  hydrogen_mass_abundance:     0.75   # Hydrogen mass abundance (dimensionless)
+  cooling_tstep_mult:          0.1    # Dimensionless pre-factor for the time-step condition
diff --git a/examples/CoolingHaloWithSpin/density_profile.py b/examples/CoolingHaloWithSpin/density_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea282328e5b75530a128eab2dec5f065e46cf819
--- /dev/null
+++ b/examples/CoolingHaloWithSpin/density_profile.py
@@ -0,0 +1,120 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+#for the plotting
+max_r = float(sys.argv[1]) #in units of the virial radius 
+n_radial_bins = int(sys.argv[2])
+n_snaps = int(sys.argv[3])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+CONST_m_H_CGS = 1.67e-24
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "CoolingHalo_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"])
+X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"])
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+for i in range(n_snaps):
+
+    filename = "CoolingHalo_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+    r = radius_over_virial_radius
+
+    bin_edges = np.linspace(0.,max_r,n_radial_bins+1)
+    bin_width = bin_edges[1] - bin_edges[0]
+    hist = np.histogram(r,bins = bin_edges)[0] # number of particles in each bin
+
+#find the mass in each radial bin
+
+    mass_dset = f["PartType0/Masses"]
+#mass of each particles should be equal
+    part_mass = np.array(mass_dset)[0]
+    part_mass_cgs = part_mass * unit_mass_cgs
+    part_mass_over_virial_mass = part_mass_cgs / M_vir_cgs 
+
+    mass_hist = hist * part_mass_over_virial_mass
+    radial_bin_mids = np.linspace(bin_width/2.,max_r - bin_width/2.,n_radial_bins)
+#volume in each radial bin
+    volume = 4.*np.pi * radial_bin_mids**2 * bin_width
+
+#now divide hist by the volume so we have a density in each bin
+
+    density = mass_hist / volume
+
+    ##read the densities
+
+    # density_dset = f["PartType0/Density"]
+    # density = np.array(density_dset)
+    # density_cgs = density * unit_mass_cgs / unit_length_cgs**3
+    # rho = density_cgs * r_vir_cgs**3 / M_vir_cgs
+
+    t = np.linspace(10./n_radial_bins,10.0,1000)
+    rho_analytic = t**(-2)/(4.*np.pi)
+
+    #calculate cooling radius
+
+    r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs)
+
+    #initial analytic density profile
+    
+    if (i == 0):
+        r_0 = radial_bin_mids[0]
+        rho_0 = density[0]
+
+        rho_analytic_init = rho_0 * (radial_bin_mids/r_0)**(-2)
+    plt.plot(radial_bin_mids,density/rho_analytic_init,'ko',label = "Average density of shell")
+    #plt.plot(t,rho_analytic,label = "Initial analytic density profile"
+    plt.xlabel(r"$r / r_{vir}$")
+    plt.ylabel(r"$\rho / \rho_{init})$")
+    plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c))
+    #plt.ylim((1.e-2,1.e1))
+    plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,20),'r',label = "Cooling radius")
+    plt.xlim((radial_bin_mids[0],max_r))
+    plt.ylim((0,20))
+    plt.plot((0,max_r),(1,1))
+    #plt.xscale('log')
+    #plt.yscale('log')
+    plt.legend(loc = "upper right")
+    plot_filename = "density_profile_%03d.png" %i
+    plt.savefig(plot_filename,format = "png")
+    plt.close()
+
diff --git a/examples/CoolingHaloWithSpin/internal_energy_profile.py b/examples/CoolingHaloWithSpin/internal_energy_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3e470cc24a939c9bc915371e927d9bd39196bff
--- /dev/null
+++ b/examples/CoolingHaloWithSpin/internal_energy_profile.py
@@ -0,0 +1,111 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+def do_binning(x,y,x_bin_edges):
+
+    #x and y are arrays, where y = f(x)
+    #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values
+
+    n_bins = x_bin_edges.size - 1
+    count = np.zeros(n_bins)
+    y_totals = np.zeros(n_bins)
+    
+    for i in range(n_bins):
+        ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0])
+        count[i] = ind.size
+        binned_y = y[ind]
+        y_totals[i] = np.sum(binned_y)
+
+    return(count,y_totals)
+
+
+#for the plotting
+max_r = float(sys.argv[1])
+n_radial_bins = int(sys.argv[2])
+n_snaps = int(sys.argv[3])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+CONST_m_H_CGS = 1.67e-24
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "CoolingHalo_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"])
+X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"])
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+for i in range(n_snaps):
+
+    filename = "CoolingHalo_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+#get the internal energies
+    u_dset = f["PartType0/InternalEnergy"]
+    u = np.array(u_dset)
+
+#make dimensionless
+    u /= v_c**2/(2. * (gamma - 1.))
+    r = radius_over_virial_radius
+
+    bin_edges = np.linspace(0,max_r,n_radial_bins + 1)
+    (hist,u_totals) = do_binning(r,u,bin_edges)
+    
+    bin_widths = bin_edges[1] - bin_edges[0]
+    radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) 
+    binned_u = u_totals / hist
+
+    #calculate cooling radius
+
+    r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs)
+
+    plt.plot(radial_bin_mids,binned_u,'ko',label = "Numerical solution")
+    #plt.plot((0,1),(1,1),label = "Analytic Solution")
+    plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius")
+    plt.legend(loc = "lower right")
+    plt.xlabel(r"$r / r_{vir}$")
+    plt.ylabel(r"$u / (v_c^2 / (2(\gamma - 1)) $")
+    plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c))
+    plt.ylim((0,2))
+    plot_filename = "internal_energy_profile_%03d.png" %i
+    plt.savefig(plot_filename,format = "png")
+    plt.close()
+
+
+        
+    
diff --git a/examples/CoolingHaloWithSpin/makeIC.py b/examples/CoolingHaloWithSpin/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..8970fbaa70578532a4f41bab7a096d8fa3565d26
--- /dev/null
+++ b/examples/CoolingHaloWithSpin/makeIC.py
@@ -0,0 +1,238 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Stefan Arridge (stefan.arridge@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+import h5py
+import sys
+import numpy as np
+import math
+import random
+
+# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2)
+# usage: python makeIC.py 1000: generate 1000 particles
+
+# Some constants
+
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+spin_lambda = 0.05 #spin parameter
+
+# First set unit velocity and then the circular velocity parameter for the isothermal potential
+const_unit_velocity_in_cgs = 1.e5 #kms^-1
+
+v_c = 200.
+v_c_cgs = v_c * const_unit_velocity_in_cgs
+
+# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius
+
+# Find H_0, the inverse Hubble time, in cgs
+
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+# From this we can find the virial radius, the radius within which the average density of the halo is
+# 200. * the mean matter density
+
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+
+# Now get the virial mass
+
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+# Now set the unit length and mass
+
+const_unit_mass_in_cgs = M_vir_cgs
+const_unit_length_in_cgs = r_vir_cgs
+
+print "UnitMass_in_cgs:     ", const_unit_mass_in_cgs 
+print "UnitLength_in_cgs:   ", const_unit_length_in_cgs
+print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs
+
+#derived quantities
+const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs)
+print "UnitTime_in_cgs:     ", const_unit_time_in_cgs
+const_G                = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs)))
+print 'G=', const_G
+
+# Parameters
+periodic= 1            # 1 For periodic box
+boxSize = 4.          
+G       = const_G 
+N       = int(sys.argv[1])  # Number of particles
+
+# Create the file
+filename = "CoolingHalo.hdf5"
+file = h5py.File(filename, 'w')
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs 
+grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+
+# Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = periodic
+
+# set seed for random number
+np.random.seed(1234)
+
+
+# Positions
+# r^(-2) distribution corresponds to uniform distribution in radius
+radius = boxSize  * np.sqrt(3.) / 2.* np.random.rand(N) #the diagonal extent of the cube
+ctheta = -1. + 2 * np.random.rand(N)
+stheta = np.sqrt(1.-ctheta**2)
+phi    =  2 * math.pi * np.random.rand(N)
+coords      = np.zeros((N, 3))
+coords[:,0] = radius * stheta * np.cos(phi)
+coords[:,1] = radius * stheta * np.sin(phi)
+coords[:,2] = radius * ctheta
+
+#shift to centre of box
+coords += np.full((N,3),boxSize/2.)
+print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0]))
+print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1]))
+print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2]))
+
+print np.mean(coords[:,0])
+print np.mean(coords[:,1])
+print np.mean(coords[:,2])
+
+#now find the particles which are within the box
+
+x_coords = coords[:,0]
+y_coords = coords[:,1]
+z_coords = coords[:,2]
+
+ind = np.where(x_coords < boxSize)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(x_coords > 0.)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(y_coords < boxSize)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(y_coords > 0.)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(z_coords < boxSize)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(z_coords > 0.)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+#count number of particles
+
+N = x_coords.size
+
+print "Number of particles in the box = " , N
+
+#make the coords and radius arrays again
+coords_2 = np.zeros((N,3))
+coords_2[:,0] = x_coords
+coords_2[:,1] = y_coords
+coords_2[:,2] = z_coords
+
+radius = np.sqrt((coords_2[:,0]-boxSize/2.)**2 + (coords_2[:,1]-boxSize/2.)**2 + (coords_2[:,2]-boxSize/2.)**2)
+
+#now give particle's velocities
+v = np.zeros((N,3))
+
+#first work out total angular momentum of the halo within the virial radius
+#we work in units where r_vir = 1 and M_vir = 1
+Total_E = v_c**2 / 2.0
+J = spin_lambda * const_G / np.sqrt(Total_E)
+print "J =", J
+#all particles within the virial radius have omega parallel to the z-axis, magnitude 
+#is proportional to 1 over the radius
+omega = np.zeros((N,3))
+for i in range(N):
+    omega[i,2] = 3.*J / radius[i]
+    v[i,:] = np.cross(omega[i,:],(coords_2[i,:]-boxSize/2.))
+        
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] =  [N ,0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# Particle group
+grp = file.create_group("/PartType0")
+
+ds = grp.create_dataset('Coordinates', (N, 3), 'd')
+ds[()] = coords_2
+coords_2 = np.zeros(1)
+
+ds = grp.create_dataset('Velocities', (N, 3), 'f')
+ds[()] = v
+v = np.zeros(1)
+
+# All particles of equal mass
+mass = 1. / N
+m = np.full((N,),mass)
+ds = grp.create_dataset('Masses', (N, ), 'f')
+ds[()] = m
+m = np.zeros(1)
+
+# Smoothing lengths
+l = (4. * np.pi * radius**2 / N)**(1./3.) #local mean inter-particle separation
+h = np.full((N, ), eta * l)
+ds = grp.create_dataset('SmoothingLength', (N,), 'f')
+ds[()] = h
+h = np.zeros(1)
+
+# Internal energies
+u = v_c**2 / (2. * (gamma - 1.)) 
+u = np.full((N, ), u)
+ds = grp.create_dataset('InternalEnergy', (N,), 'f')
+ds[()] = u
+u = np.zeros(1)
+
+# Particle IDs
+ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L')
+ds = grp.create_dataset('ParticleIDs', (N, ), 'L')
+ds[()] = ids
+
+file.close()
diff --git a/examples/CoolingHaloWithSpin/makeIC_random_box.py b/examples/CoolingHaloWithSpin/makeIC_random_box.py
new file mode 100644
index 0000000000000000000000000000000000000000..4295cb135233f2d5a59405b44e6d8e9c80a1f6c0
--- /dev/null
+++ b/examples/CoolingHaloWithSpin/makeIC_random_box.py
@@ -0,0 +1,168 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Stefan Arridge (stefan.arridge@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+import h5py
+import sys
+import numpy as np
+import math
+import random
+
+# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2)
+# usage: python makeIC.py 1000: generate 1000 particles
+
+# Some constants
+
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+
+# First set unit velocity and then the circular velocity parameter for the isothermal potential
+const_unit_velocity_in_cgs = 1.e5 #kms^-1
+
+v_c = 200.
+v_c_cgs = v_c * const_unit_velocity_in_cgs
+
+# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius
+
+# Find H_0, the inverse Hubble time, in cgs
+
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+# From this we can find the virial radius, the radius within which the average density of the halo is
+# 200. * the mean matter density
+
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+
+# Now get the virial mass
+
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+# Now set the unit length and mass
+
+const_unit_mass_in_cgs = M_vir_cgs
+const_unit_length_in_cgs = r_vir_cgs
+
+print "UnitMass_in_cgs:     ", const_unit_mass_in_cgs 
+print "UnitLength_in_cgs:   ", const_unit_length_in_cgs
+print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs
+
+#derived quantities
+const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs)
+print "UnitTime_in_cgs:     ", const_unit_time_in_cgs
+const_G                = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs)))
+print 'G=', const_G
+
+# Parameters
+periodic= 1            # 1 For periodic box
+boxSize = 4.          
+G       = const_G 
+N       = int(sys.argv[1])  # Number of particles
+
+# Create the file
+filename = "random_box.hdf5"
+file = h5py.File(filename, 'w')
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs 
+grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] =  [N ,0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = periodic
+
+# set seed for random number
+np.random.seed(1234)
+
+# Particle group
+grp = file.create_group("/PartType0")
+
+# Positions
+
+# distribute particles randomly in the box
+coords      = np.zeros((N, 3))
+coords[:,0] = boxSize * np.random.rand(N)
+coords[:,1] = boxSize * np.random.rand(N)
+coords[:,2] = boxSize * np.random.rand(N)
+#shift to centre of box
+#coords += np.full((N,3),boxSize/2.)
+print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0]))
+print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1]))
+print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2]))
+
+print np.mean(coords[:,0])
+print np.mean(coords[:,1])
+print np.mean(coords[:,2])
+
+ds = grp.create_dataset('Coordinates', (N, 3), 'd')
+ds[()] = coords
+coords = np.zeros(1)
+
+# All velocities set to zero
+v = np.zeros((N,3))
+ds = grp.create_dataset('Velocities', (N, 3), 'f')
+ds[()] = v
+v = np.zeros(1)
+
+# All particles of equal mass
+mass = 1. / N
+m = np.full((N,),mass)
+ds = grp.create_dataset('Masses', (N, ), 'f')
+ds[()] = m
+m = np.zeros(1)
+
+# Smoothing lengths
+l = (boxSize**3 / N)**(1./3.) #local mean inter-particle separation
+h = np.full((N, ), eta * l)
+ds = grp.create_dataset('SmoothingLength', (N,), 'f')
+ds[()] = h
+h = np.zeros(1)
+
+# Internal energies
+u = v_c**2 / (2. * (gamma - 1.)) 
+u = np.full((N, ), u)
+ds = grp.create_dataset('InternalEnergy', (N,), 'f')
+ds[()] = u
+u = np.zeros(1)
+
+# Particle IDs
+ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L')
+ds = grp.create_dataset('ParticleIDs', (N, ), 'L')
+ds[()] = ids
+
+file.close()
diff --git a/examples/CoolingHaloWithSpin/run.sh b/examples/CoolingHaloWithSpin/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3a0d9c02000e760b030a96107038d3c6163f3227
--- /dev/null
+++ b/examples/CoolingHaloWithSpin/run.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+echo "Generating initial conditions for the isothermal potential box example..."
+python makeIC.py 10000 
+
+../swift -g -s -C -t 16 cooling_halo.yml 2>&1 | tee output.log
+
+# python radial_profile.py 10
+
+# python internal_energy_profile.py 10
+
+# python test_energy_conservation.py
diff --git a/examples/CoolingHaloWithSpin/test_energy_conservation.py b/examples/CoolingHaloWithSpin/test_energy_conservation.py
new file mode 100644
index 0000000000000000000000000000000000000000..42a049f30ac1928b89bfff355ee6ff7c8185bbb7
--- /dev/null
+++ b/examples/CoolingHaloWithSpin/test_energy_conservation.py
@@ -0,0 +1,114 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+n_snaps = 101
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "CoolingHalo_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+potential_energy_array = []
+internal_energy_array = []
+kinetic_energy_array = []
+time_array_cgs = []
+
+for i in range(n_snaps):
+
+    filename = "CoolingHalo_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    time_array_cgs = np.append(time_array_cgs,snap_time_cgs)
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+    r = radius_over_virial_radius
+    total_potential_energy = np.sum(v_c**2*np.log(r))
+    potential_energy_array = np.append(potential_energy_array,total_potential_energy)
+
+    vels_dset = f["PartType0/Velocities"]
+    vels = np.array(vels_dset)
+    speed_squared = vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2
+    total_kinetic_energy = 0.5 * np.sum(speed_squared)
+    kinetic_energy_array = np.append(kinetic_energy_array,total_kinetic_energy)
+
+    u_dset = f["PartType0/InternalEnergy"]
+    u = np.array(u_dset)
+    total_internal_energy = np.sum(u)
+    internal_energy_array = np.append(internal_energy_array,total_internal_energy)
+
+#get the radiated energy
+
+energy_array = np.genfromtxt("energy.txt",skip_header = 1)
+#rad_energy_time = energy_array[:,0]
+#rad_energy_time_cgs = rad_energy_time * unit_time_cgs
+rad_energy_array = energy_array[:,6]
+
+#only use every 10th term in the rad_energy_array
+rad_energy_array = rad_energy_array[0::10]
+
+#put energies in units of v_c^2 and rescale by number of particles
+
+pe = potential_energy_array / (N*v_c**2)
+ke = kinetic_energy_array / (N*v_c**2)
+ie = internal_energy_array / (N*v_c**2)
+re = rad_energy_array / (N*v_c**2)
+te = pe + ke + ie #+ re
+
+print pe
+print ke
+print ie
+#print re
+print te
+
+dyn_time_cgs = r_vir_cgs / v_c_cgs
+time_array = time_array_cgs / dyn_time_cgs
+#rad_time_array = rad_energy_time_cgs / dyn_time_cgs
+plt.plot(time_array,ke,label = "Kinetic Energy")
+plt.plot(time_array,pe,label = "Potential Energy")
+plt.plot(time_array,ie,label = "Internal Energy")
+#plt.plot(time_array,re,label = "Radiated Energy")
+plt.plot(time_array,te,label = "Total Energy")
+plt.legend(loc = "lower left")
+plt.xlabel(r"$t / t_{dyn}$")
+plt.ylabel(r"$E / v_c^2$")
+plt.title(r"$%d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(N,v_c))
+#plt.ylim((-4,2))
+#plot_filename = "density_profile_%03d.png" %i
+plt.show()
+
diff --git a/examples/CoolingHaloWithSpin/velocity_profile.py b/examples/CoolingHaloWithSpin/velocity_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..d64d255b18482bc26578f21f46199aa3540ae7b5
--- /dev/null
+++ b/examples/CoolingHaloWithSpin/velocity_profile.py
@@ -0,0 +1,111 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+def do_binning(x,y,x_bin_edges):
+
+    #x and y are arrays, where y = f(x)
+    #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values
+
+    n_bins = x_bin_edges.size - 1
+    count = np.zeros(n_bins)
+    y_totals = np.zeros(n_bins)
+    
+    for i in range(n_bins):
+        ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0])
+        count[i] = ind.size
+        binned_y = y[ind]
+        y_totals[i] = np.sum(binned_y)
+
+    return(count,y_totals)
+
+
+#for the plotting
+max_r = float(sys.argv[1])
+n_radial_bins = int(sys.argv[2])
+n_snaps = int(sys.argv[3])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+CONST_m_H_CGS = 1.67e-24
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "CoolingHalo_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+for i in range(n_snaps):
+
+    filename = "CoolingHalo_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+#get the internal energies
+    vel_dset = f["PartType0/Velocities"]
+    vel = np.array(vel_dset)
+
+#make dimensionless
+    vel /= v_c
+    r = radius_over_virial_radius
+
+    #find radial component of velocity
+
+    v_r = np.zeros(r.size)
+    for j in range(r.size):
+        v_r[j] = -np.dot(coords[j,:],vel[j,:])/radius[j]
+
+    bin_edges = np.linspace(0,max_r,n_radial_bins + 1)
+    (hist,v_r_totals) = do_binning(r,v_r,bin_edges)
+    
+    bin_widths = bin_edges[1] - bin_edges[0]
+    radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) 
+    binned_v_r = v_r_totals / hist
+
+    #calculate cooling radius
+
+    #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs)
+
+    plt.plot(radial_bin_mids,binned_v_r,'ko',label = "Average radial velocity in shell")
+    #plt.plot((0,1),(1,1),label = "Analytic Solution")
+    #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius")
+    plt.legend(loc = "upper right")
+    plt.xlabel(r"$r / r_{vir}$")
+    plt.ylabel(r"$v_r / v_c$")
+    plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c))
+    plt.ylim((0,2))
+    plot_filename = "./plots/radial_velocity_profile/velocity_profile_%03d.png" %i
+    plt.savefig(plot_filename,format = "png")
+    plt.close()
diff --git a/examples/CosmoVolume/cosmoVolume.yml b/examples/CosmoVolume/cosmoVolume.yml
index 13cea318144d296183d630a53d78c69d050c1abe..46189bf25f26d46cbd1e5321b00298cd5553eb59 100644
--- a/examples/CosmoVolume/cosmoVolume.yml
+++ b/examples/CosmoVolume/cosmoVolume.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.705    # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/EAGLE_12/eagle_12.yml b/examples/EAGLE_12/eagle_12.yml
index 80714d87f4afa7d7e4d41ce7bf56faed856208ef..bb5f97f029e1d50d81bbdccae9ac620e9e0e6f08 100644
--- a/examples/EAGLE_12/eagle_12.yml
+++ b/examples/EAGLE_12/eagle_12.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1    # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/EAGLE_25/eagle_25.yml b/examples/EAGLE_25/eagle_25.yml
index 6afb737677040ba0605d4e3116800f079a059be4..12a413b7e2c45443601c0b9753383b90942298b0 100644
--- a/examples/EAGLE_25/eagle_25.yml
+++ b/examples/EAGLE_25/eagle_25.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1    # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/EAGLE_50/eagle_50.yml b/examples/EAGLE_50/eagle_50.yml
index d9e5d46326780fe5b2abde025b50a7ec667b19b1..b84b1eb7c362f85d8cd6a08ff2a15f72d1337396 100644
--- a/examples/EAGLE_50/eagle_50.yml
+++ b/examples/EAGLE_50/eagle_50.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1    # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/ExternalPointMass/externalPointMass.yml b/examples/ExternalPointMass/externalPointMass.yml
index ce300b32157361e7860d201c186823471a179c0a..621a66bbc39838ac8d3d8a8a3992b2a7be3157a8 100644
--- a/examples/ExternalPointMass/externalPointMass.yml
+++ b/examples/ExternalPointMass/externalPointMass.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  10.      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/Feedback/feedback.yml b/examples/Feedback/feedback.yml
index 9ff7eea325b43fe3c670d16bdbd4ccc66f33333e..de4f7abef1ef538a97a5e38c72b4db5ce2647976 100644
--- a/examples/Feedback/feedback.yml
+++ b/examples/Feedback/feedback.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1       # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
@@ -37,8 +36,8 @@ InitialConditions:
 # Parameters for feedback
 
 SN:
-	time:    0.001 # time the SN explodes (internal units)
-	energy:  1.0   # energy of the explosion (internal units)
-	x:       0.5   # x-position of explostion (internal units)
-	y:       0.5   # y-position of explostion (internal units)
-	z:       0.5   # z-position of explostion (internal units)
+  time:    0.001 # time the SN explodes (internal units)
+  energy:  1.0   # energy of the explosion (internal units)
+  x:       0.5   # x-position of explostion (internal units)
+  y:       0.5   # y-position of explostion (internal units)
+  z:       0.5   # z-position of explostion (internal units)
diff --git a/examples/Gradients/gradientsCartesian.yml b/examples/Gradients/gradientsCartesian.yml
index 917a4803004c2ce89984beb857cb1691d9a1ec1b..61192e52393d88501408ac3982afeff2dc58f727 100644
--- a/examples/Gradients/gradientsCartesian.yml
+++ b/examples/Gradients/gradientsCartesian.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.01     # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/Gradients/gradientsRandom.yml b/examples/Gradients/gradientsRandom.yml
index 209f30060f031f7d50a15ffbf8ad0e7fe5b013b8..75e6e65c92b79f6883616b91a9345a56ca7330c8 100644
--- a/examples/Gradients/gradientsRandom.yml
+++ b/examples/Gradients/gradientsRandom.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.01     # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/Gradients/gradientsStretched.yml b/examples/Gradients/gradientsStretched.yml
index 592a70762988fca764c3ec7dcbc9bfcc9a8f2751..71c75533d13ff35b1f5bd707917fff116764187f 100644
--- a/examples/Gradients/gradientsStretched.yml
+++ b/examples/Gradients/gradientsStretched.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.01     # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/GreshoVortex_2D/gresho.yml b/examples/GreshoVortex_2D/gresho.yml
index b4de5bde517556cacb94d996f5a11cbe05188bf9..746bad295448f6513c31431e9e206143e9565328 100644
--- a/examples/GreshoVortex_2D/gresho.yml
+++ b/examples/GreshoVortex_2D/gresho.yml
@@ -6,6 +6,9 @@ InternalUnitSystem:
   UnitCurrent_in_cgs:  1   # Amperes
   UnitTemp_in_cgs:     1   # Kelvin
 
+Scheduler:
+  max_top_level_cells: 15
+
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
@@ -27,7 +30,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.02     # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   
 # Parameters related to the initial conditions
diff --git a/examples/HydrostaticHalo/README b/examples/HydrostaticHalo/README
new file mode 100644
index 0000000000000000000000000000000000000000..797c533926b7a131db9a08424b48b58ac3d3b8f4
--- /dev/null
+++ b/examples/HydrostaticHalo/README
@@ -0,0 +1,25 @@
+Hydrostatic halo in equilibrium in an isothermal potential. Running
+for 10 dynamical times.
+
+To make the initial conditions we distribute gas particles randomly in
+a cube with a side length twice that of the virial radius. The density
+profile of the gas is proportional to r^(-2) where r is the distance
+from the centre of the cube.
+
+The parameter v_rot (in makeIC.py and hydrostatic.yml) sets the circular
+velocity of the halo, and by extension, the viral radius, viral mass,
+and the internal energy of the gas such that hydrostatic equilibrium
+is achieved.
+
+To run this example, make such that the code is compiled with either
+the isothermal potential or softened isothermal potential set in
+src/const.h. In the latter case, a (small) value of epsilon needs to
+be set in hydrostatic.yml.  ~1 kpc should work well.
+
+The plotting scripts produce a plot of the density, internal energy
+and radial velocity profile for each snapshot and divides the profile
+by the expected profile. 
+
+The script test_energy_conservation.py shows the evolution of energy
+with time. These can be used to check if the example has run properly.
+
diff --git a/examples/HydrostaticHalo/density_profile.py b/examples/HydrostaticHalo/density_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..52bebb9ffefa77dae66af155fb31fed539dcde13
--- /dev/null
+++ b/examples/HydrostaticHalo/density_profile.py
@@ -0,0 +1,120 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+#for the plotting
+max_r = float(sys.argv[1]) #in units of the virial radius 
+n_radial_bins = int(sys.argv[2])
+n_snaps = int(sys.argv[3])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+CONST_m_H_CGS = 1.67e-24
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "Hydrostatic_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+#lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"])
+#X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"])
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+for i in range(n_snaps):
+
+    filename = "Hydrostatic_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+    r = radius_over_virial_radius
+
+    bin_edges = np.linspace(0.,max_r,n_radial_bins+1)
+    bin_width = bin_edges[1] - bin_edges[0]
+    hist = np.histogram(r,bins = bin_edges)[0] # number of particles in each bin
+
+#find the mass in each radial bin
+
+    mass_dset = f["PartType0/Masses"]
+#mass of each particles should be equal
+    part_mass = np.array(mass_dset)[0]
+    part_mass_cgs = part_mass * unit_mass_cgs
+    part_mass_over_virial_mass = part_mass_cgs / M_vir_cgs 
+
+    mass_hist = hist * part_mass_over_virial_mass
+    radial_bin_mids = np.linspace(bin_width/2.,max_r - bin_width/2.,n_radial_bins)
+#volume in each radial bin
+    volume = 4.*np.pi * radial_bin_mids**2 * bin_width
+
+#now divide hist by the volume so we have a density in each bin
+
+    density = mass_hist / volume
+
+    ##read the densities
+
+    # density_dset = f["PartType0/Density"]
+    # density = np.array(density_dset)
+    # density_cgs = density * unit_mass_cgs / unit_length_cgs**3
+    # rho = density_cgs * r_vir_cgs**3 / M_vir_cgs
+
+    t = np.linspace(10./n_radial_bins,10.0,1000)
+    rho_analytic = t**(-2)/(4.*np.pi)
+
+    #calculate cooling radius
+
+    #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs)
+
+    #initial analytic density profile
+    
+    if (i == 0):
+        r_0 = radial_bin_mids[0]
+        rho_0 = density[0]
+
+        rho_analytic_init = rho_0 * (radial_bin_mids/r_0)**(-2)
+    plt.plot(radial_bin_mids,density/rho_analytic_init,'ko',label = "Average density of shell")
+    #plt.plot(t,rho_analytic,label = "Initial analytic density profile"
+    plt.xlabel(r"$r / r_{vir}$")
+    plt.ylabel(r"$\rho / \rho_{init})$")
+    plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c))
+    #plt.ylim((1.e-2,1.e1))
+    #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,20),'r',label = "Cooling radius")
+    plt.xlim((radial_bin_mids[0],max_r))
+    plt.ylim((0,20))
+    plt.plot((0,max_r),(1,1))
+    #plt.xscale('log')
+    #plt.yscale('log')
+    plt.legend(loc = "upper right")
+    plot_filename = "./plots/density_profile/density_profile_%03d.png" %i
+    plt.savefig(plot_filename,format = "png")
+    plt.close()
+
diff --git a/examples/HydrostaticHalo/hydrostatic.yml b/examples/HydrostaticHalo/hydrostatic.yml
new file mode 100644
index 0000000000000000000000000000000000000000..39a91a4ec475a70ef4e61b9cdc59b8221a74093e
--- /dev/null
+++ b/examples/HydrostaticHalo/hydrostatic.yml
@@ -0,0 +1,44 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.9885e39     # 10^6 solar masses
+  UnitLength_in_cgs:   3.0856776e21  # Kiloparsecs
+  UnitVelocity_in_cgs: 1e5           # Kilometres 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:   30.   # The end time of the simulation (in internal units).
+  dt_min:     1e-6  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-2 # Time between statistics output
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            Hydrostatic  # 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)
+
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:        1.2349   # Target smoothing length in units of the mean inter-particle separation (1.2349 == 48Ngbs with the cubic spline kernel).
+  delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
+  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  Hydrostatic.hdf5       # The file to read
+ 
+# External potential parameters
+SoftenedIsothermalPotential:
+  position_x:      0.     # location of centre of isothermal potential in internal units
+  position_y:      0.
+  position_z:      0.	
+  vrot:            200.     # rotation speed of isothermal potential in internal units
+  epsilon:         1.0
+  timestep_mult:   0.03     # controls time step
+
diff --git a/examples/HydrostaticHalo/internal_energy_profile.py b/examples/HydrostaticHalo/internal_energy_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..a1b2bda314a66eb965974d34519f66c544ee8aed
--- /dev/null
+++ b/examples/HydrostaticHalo/internal_energy_profile.py
@@ -0,0 +1,111 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+def do_binning(x,y,x_bin_edges):
+
+    #x and y are arrays, where y = f(x)
+    #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values
+
+    n_bins = x_bin_edges.size - 1
+    count = np.zeros(n_bins)
+    y_totals = np.zeros(n_bins)
+    
+    for i in range(n_bins):
+        ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0])
+        count[i] = ind.size
+        binned_y = y[ind]
+        y_totals[i] = np.sum(binned_y)
+
+    return(count,y_totals)
+
+
+#for the plotting
+max_r = float(sys.argv[1])
+n_radial_bins = int(sys.argv[2])
+n_snaps = int(sys.argv[3])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+CONST_m_H_CGS = 1.67e-24
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "Hydrostatic_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+#lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"])
+#X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"])
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+for i in range(n_snaps):
+
+    filename = "Hydrostatic_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+#get the internal energies
+    u_dset = f["PartType0/InternalEnergy"]
+    u = np.array(u_dset)
+
+#make dimensionless
+    u /= v_c**2/(2. * (gamma - 1.))
+    r = radius_over_virial_radius
+
+    bin_edges = np.linspace(0,max_r,n_radial_bins + 1)
+    (hist,u_totals) = do_binning(r,u,bin_edges)
+    
+    bin_widths = bin_edges[1] - bin_edges[0]
+    radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) 
+    binned_u = u_totals / hist
+
+    #calculate cooling radius
+
+    #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs)
+
+    plt.plot(radial_bin_mids,binned_u,'ko',label = "Numerical solution")
+    #plt.plot((0,1),(1,1),label = "Analytic Solution")
+    #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius")
+    plt.legend(loc = "lower right")
+    plt.xlabel(r"$r / r_{vir}$")
+    plt.ylabel(r"$u / (v_c^2 / (2(\gamma - 1)) $")
+    plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c))
+    plt.ylim((0,2))
+    plot_filename = "./plots/internal_energy/internal_energy_profile_%03d.png" %i
+    plt.savefig(plot_filename,format = "png")
+    plt.close()
+
+
+        
+    
diff --git a/examples/HydrostaticHalo/makeIC.py b/examples/HydrostaticHalo/makeIC.py
new file mode 100644
index 0000000000000000000000000000000000000000..f33387e18dd0ab523684227f5c745b5c8b807b7f
--- /dev/null
+++ b/examples/HydrostaticHalo/makeIC.py
@@ -0,0 +1,234 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Stefan Arridge (stefan.arridge@durham.ac.uk)
+ # 
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU Lesser General Public License as published
+ # by the Free Software Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ # 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ # 
+ # You should have received a copy of the GNU Lesser General Public License
+ # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ # 
+ ##############################################################################
+
+import h5py
+import sys
+import numpy as np
+import math
+import random
+
+# Generates N particles in a spherically symmetric distribution with density profile ~r^(-2)
+# usage: python makeIC.py 1000: generate 1000 particles
+
+# Some constants
+
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+
+# First set unit velocity and then the circular velocity parameter for the isothermal potential
+const_unit_velocity_in_cgs = 1.e5 #kms^-1
+
+v_c = 200.
+v_c_cgs = v_c * const_unit_velocity_in_cgs
+
+# Now we use this to get the virial mass and virial radius, which we will set to be the unit mass and radius
+
+# Find H_0, the inverse Hubble time, in cgs
+
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+# From this we can find the virial radius, the radius within which the average density of the halo is
+# 200. * the mean matter density
+
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+
+# Now get the virial mass
+
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+# Now set the unit length and mass
+
+const_unit_mass_in_cgs = M_vir_cgs
+const_unit_length_in_cgs = r_vir_cgs
+
+print "UnitMass_in_cgs:     ", const_unit_mass_in_cgs 
+print "UnitLength_in_cgs:   ", const_unit_length_in_cgs
+print "UnitVelocity_in_cgs: ", const_unit_velocity_in_cgs
+
+#derived quantities
+const_unit_time_in_cgs = (const_unit_length_in_cgs / const_unit_velocity_in_cgs)
+print "UnitTime_in_cgs:     ", const_unit_time_in_cgs
+const_G                = ((CONST_G_CGS*const_unit_mass_in_cgs*const_unit_time_in_cgs*const_unit_time_in_cgs/(const_unit_length_in_cgs*const_unit_length_in_cgs*const_unit_length_in_cgs)))
+print 'G=', const_G
+
+# Parameters
+periodic= 1            # 1 For periodic box
+boxSize = 4.          
+G       = const_G 
+N       = int(sys.argv[1])  # Number of particles
+
+# Create the file
+filename = "Hydrostatic.hdf5"
+file = h5py.File(filename, 'w')
+
+#Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs 
+grp.attrs["Unit time in cgs (U_t)"] = const_unit_length_in_cgs / const_unit_velocity_in_cgs
+grp.attrs["Unit current in cgs (U_I)"] = 1.
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.
+
+
+# Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = periodic
+
+# set seed for random number
+np.random.seed(1234)
+
+
+# Positions
+# r^(-2) distribution corresponds to uniform distribution in radius
+radius = boxSize  * np.sqrt(3.) / 2.* np.random.rand(N) #the diagonal extent of the cube
+ctheta = -1. + 2 * np.random.rand(N)
+stheta = np.sqrt(1.-ctheta**2)
+phi    =  2 * math.pi * np.random.rand(N)
+coords      = np.zeros((N, 3))
+coords[:,0] = radius * stheta * np.cos(phi)
+coords[:,1] = radius * stheta * np.sin(phi)
+coords[:,2] = radius * ctheta
+
+#shift to centre of box
+coords += np.full((N,3),boxSize/2.)
+print "x range = (%f,%f)" %(np.min(coords[:,0]),np.max(coords[:,0]))
+print "y range = (%f,%f)" %(np.min(coords[:,1]),np.max(coords[:,1]))
+print "z range = (%f,%f)" %(np.min(coords[:,2]),np.max(coords[:,2]))
+
+#print np.mean(coords[:,0])
+#print np.mean(coords[:,1])
+#print np.mean(coords[:,2])
+
+#now find the particles which are within the box
+
+x_coords = coords[:,0]
+y_coords = coords[:,1]
+z_coords = coords[:,2]
+
+ind = np.where(x_coords < boxSize)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(x_coords > 0.)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(y_coords < boxSize)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(y_coords > 0.)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(z_coords < boxSize)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+ind = np.where(z_coords > 0.)[0]
+x_coords = x_coords[ind]
+y_coords = y_coords[ind]
+z_coords = z_coords[ind]
+
+#count number of particles
+
+N = x_coords.size
+
+print "Number of particles in the box = " , N
+
+#make the coords and radius arrays again
+coords_2 = np.zeros((N,3))
+coords_2[:,0] = x_coords
+coords_2[:,1] = y_coords
+coords_2[:,2] = z_coords
+
+radius = np.sqrt(coords_2[:,0]**2 + coords_2[:,1]**2 + coords_2[:,2]**2)
+
+#test we've done it right
+
+print "x range = (%f,%f)" %(np.min(coords_2[:,0]),np.max(coords_2[:,0]))
+print "y range = (%f,%f)" %(np.min(coords_2[:,1]),np.max(coords_2[:,1]))
+print "z range = (%f,%f)" %(np.min(coords_2[:,2]),np.max(coords_2[:,2]))
+
+print np.mean(coords_2[:,0])
+print np.mean(coords_2[:,1])
+print np.mean(coords_2[:,2])
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] =  [N ,0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [N, 0, 0, 0, 0, 0]
+grp.attrs["Time"] = 0.0
+grp.attrs["NumFilesPerSnapshot"] = 1
+grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+grp.attrs["Flag_Entropy_ICs"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# Particle group
+grp = file.create_group("/PartType0")
+
+ds = grp.create_dataset('Coordinates', (N, 3), 'd')
+ds[()] = coords_2
+coords_2 = np.zeros(1)
+
+# All velocities set to zero
+v = np.zeros((N,3))
+ds = grp.create_dataset('Velocities', (N, 3), 'f')
+ds[()] = v
+v = np.zeros(1)
+
+# All particles of equal mass
+mass = 1. / N
+m = np.full((N,),mass)
+ds = grp.create_dataset('Masses', (N, ), 'f')
+ds[()] = m
+m = np.zeros(1)
+
+# Smoothing lengths
+l = (4. * np.pi * radius**2 / N)**(1./3.) #local mean inter-particle separation
+h = np.full((N, ), eta * l)
+ds = grp.create_dataset('SmoothingLength', (N,), 'f')
+ds[()] = h
+h = np.zeros(1)
+
+# Internal energies
+u = v_c**2 / (2. * (gamma - 1.)) 
+u = np.full((N, ), u)
+ds = grp.create_dataset('InternalEnergy', (N,), 'f')
+ds[()] = u
+u = np.zeros(1)
+
+# Particle IDs
+ids = 1 + np.linspace(0, N, N, endpoint=False, dtype='L')
+ds = grp.create_dataset('ParticleIDs', (N, ), 'L')
+ds[()] = ids
+
+file.close()
diff --git a/examples/HydrostaticHalo/run.sh b/examples/HydrostaticHalo/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d23ead6a67f43c9d19d76a797e72d050a3978d61
--- /dev/null
+++ b/examples/HydrostaticHalo/run.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+echo "Generating initial conditions for the isothermal potential box example..."
+python makeIC.py 100000
+
+# Run for 10 dynamical times
+../swift -g -s -t 2 hydrostatic.yml 2>&1 | tee output.log
+
+echo "Plotting density profiles"
+mkdir plots
+mkdir plots/density_profile
+python density_profile.py 2. 200 300
+
+echo "Plotting internal energy profiles"
+mkdir plots/internal_energy
+python internal_energy_profile.py 2. 200 300
+
+echo "Plotting radial velocity profiles"
+mkdir plots/radial_velocity_profile
+python velocity_profile.py 2. 200 300
+
+echo "Plotting energy as a function of time"
+python test_energy_conservation.py 300
diff --git a/examples/HydrostaticHalo/test_energy_conservation.py b/examples/HydrostaticHalo/test_energy_conservation.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca091050c4127d11a37a2cc7504e42d244031e25
--- /dev/null
+++ b/examples/HydrostaticHalo/test_energy_conservation.py
@@ -0,0 +1,95 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+n_snaps = int(sys.argv[1])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "Hydrostatic_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+potential_energy_array = []
+internal_energy_array = []
+kinetic_energy_array = []
+time_array_cgs = []
+
+for i in range(n_snaps):
+
+    filename = "Hydrostatic_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    time_array_cgs = np.append(time_array_cgs,snap_time_cgs)
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+    r = radius_over_virial_radius
+    total_potential_energy = np.sum(v_c**2*np.log(r))
+    potential_energy_array = np.append(potential_energy_array,total_potential_energy)
+
+    vels_dset = f["PartType0/Velocities"]
+    vels = np.array(vels_dset)
+    speed_squared = vels[:,0]**2 + vels[:,1]**2 + vels[:,2]**2
+    total_kinetic_energy = 0.5 * np.sum(speed_squared)
+    kinetic_energy_array = np.append(kinetic_energy_array,total_kinetic_energy)
+
+    u_dset = f["PartType0/InternalEnergy"]
+    u = np.array(u_dset)
+    total_internal_energy = np.sum(u)
+    internal_energy_array = np.append(internal_energy_array,total_internal_energy)
+
+#put energies in units of v_c^2 and rescale by number of particles
+
+pe = potential_energy_array / (N*v_c**2)
+ke = kinetic_energy_array / (N*v_c**2)
+ie = internal_energy_array / (N*v_c**2)
+te = pe + ke + ie
+
+dyn_time_cgs = r_vir_cgs / v_c_cgs
+time_array = time_array_cgs / dyn_time_cgs
+
+plt.plot(time_array,ke,label = "Kinetic Energy")
+plt.plot(time_array,pe,label = "Potential Energy")
+plt.plot(time_array,ie,label = "Internal Energy")
+plt.plot(time_array,te,label = "Total Energy")
+plt.legend(loc = "lower right")
+plt.xlabel(r"$t / t_{dyn}$")
+plt.ylabel(r"$E / v_c^2$")
+plt.title(r"$%d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(N,v_c))
+plt.ylim((-2,2))
+plt.savefig("energy_conservation.png",format = 'png')
+
diff --git a/examples/HydrostaticHalo/velocity_profile.py b/examples/HydrostaticHalo/velocity_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..f6a7350b9731d660b2092266d4d6ad3730bab48c
--- /dev/null
+++ b/examples/HydrostaticHalo/velocity_profile.py
@@ -0,0 +1,111 @@
+import numpy as np
+import h5py as h5
+import matplotlib.pyplot as plt
+import sys
+
+def do_binning(x,y,x_bin_edges):
+
+    #x and y are arrays, where y = f(x)
+    #returns number of elements of x in each bin, and the total of the y elements corresponding to those x values
+
+    n_bins = x_bin_edges.size - 1
+    count = np.zeros(n_bins)
+    y_totals = np.zeros(n_bins)
+    
+    for i in range(n_bins):
+        ind = np.intersect1d(np.where(x > bin_edges[i])[0],np.where(x <= bin_edges[i+1])[0])
+        count[i] = ind.size
+        binned_y = y[ind]
+        y_totals[i] = np.sum(binned_y)
+
+    return(count,y_totals)
+
+
+#for the plotting
+max_r = float(sys.argv[1])
+n_radial_bins = int(sys.argv[2])
+n_snaps = int(sys.argv[3])
+
+#some constants
+OMEGA = 0.3 # Cosmological matter fraction at z = 0
+PARSEC_IN_CGS = 3.0856776e18
+KM_PER_SEC_IN_CGS = 1.0e5
+CONST_G_CGS = 6.672e-8
+CONST_m_H_CGS = 1.67e-24
+h = 0.67777 # hubble parameter
+gamma = 5./3.
+eta = 1.2349
+H_0_cgs = 100. * h * KM_PER_SEC_IN_CGS / (1.0e6 * PARSEC_IN_CGS)
+
+#read some header/parameter information from the first snapshot
+
+filename = "Hydrostatic_000.hdf5"
+f = h5.File(filename,'r')
+params = f["Parameters"]
+unit_mass_cgs = float(params.attrs["InternalUnitSystem:UnitMass_in_cgs"])
+unit_length_cgs = float(params.attrs["InternalUnitSystem:UnitLength_in_cgs"])
+unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"])
+unit_time_cgs = unit_length_cgs / unit_velocity_cgs
+v_c = float(params.attrs["SoftenedIsothermalPotential:vrot"])
+v_c_cgs = v_c * unit_velocity_cgs
+header = f["Header"]
+N = header.attrs["NumPart_Total"][0]
+box_centre = np.array(header.attrs["BoxSize"])
+
+#calculate r_vir and M_vir from v_c
+r_vir_cgs = v_c_cgs / (10. * H_0_cgs * np.sqrt(OMEGA))
+M_vir_cgs = r_vir_cgs * v_c_cgs**2 / CONST_G_CGS
+
+for i in range(n_snaps):
+
+    filename = "Hydrostatic_%03d.hdf5" %i
+    f = h5.File(filename,'r')
+    coords_dset = f["PartType0/Coordinates"]
+    coords = np.array(coords_dset)
+#translate coords by centre of box
+    header = f["Header"]
+    snap_time = header.attrs["Time"]
+    snap_time_cgs = snap_time * unit_time_cgs
+    coords[:,0] -= box_centre[0]/2.
+    coords[:,1] -= box_centre[1]/2.
+    coords[:,2] -= box_centre[2]/2.
+    radius = np.sqrt(coords[:,0]**2 + coords[:,1]**2 + coords[:,2]**2)
+    radius_cgs = radius*unit_length_cgs
+    radius_over_virial_radius = radius_cgs / r_vir_cgs
+
+#get the internal energies
+    vel_dset = f["PartType0/Velocities"]
+    vel = np.array(vel_dset)
+
+#make dimensionless
+    vel /= v_c
+    r = radius_over_virial_radius
+
+    #find radial component of velocity
+
+    v_r = np.zeros(r.size)
+    for j in range(r.size):
+        v_r[j] = -np.dot(coords[j,:],vel[j,:])/radius[j]
+
+    bin_edges = np.linspace(0,max_r,n_radial_bins + 1)
+    (hist,v_r_totals) = do_binning(r,v_r,bin_edges)
+    
+    bin_widths = bin_edges[1] - bin_edges[0]
+    radial_bin_mids = np.linspace(bin_widths / 2. , max_r - bin_widths / 2. , n_radial_bins) 
+    binned_v_r = v_r_totals / hist
+
+    #calculate cooling radius
+
+    #r_cool_over_r_vir = np.sqrt((2.*(gamma - 1.)*lambda_cgs*M_vir_cgs*X_H**2)/(4.*np.pi*CONST_m_H_CGS**2*v_c_cgs**2*r_vir_cgs**3))*np.sqrt(snap_time_cgs)
+
+    plt.plot(radial_bin_mids,binned_v_r,'ko',label = "Average radial velocity in shell")
+    #plt.plot((0,1),(1,1),label = "Analytic Solution")
+    #plt.plot((r_cool_over_r_vir,r_cool_over_r_vir),(0,2),'r',label = "Cooling radius")
+    plt.legend(loc = "upper right")
+    plt.xlabel(r"$r / r_{vir}$")
+    plt.ylabel(r"$v_r / v_c$")
+    plt.title(r"$\mathrm{Time}= %.3g \, s \, , \, %d \, \, \mathrm{particles} \,,\, v_c = %.1f \, \mathrm{km / s}$" %(snap_time_cgs,N,v_c))
+    plt.ylim((0,2))
+    plot_filename = "./plots/radial_velocity_profile/velocity_profile_%03d.png" %i
+    plt.savefig(plot_filename,format = "png")
+    plt.close()
diff --git a/examples/IsothermalPotential/GravityOnly/README b/examples/IsothermalPotential/README
similarity index 100%
rename from examples/IsothermalPotential/GravityOnly/README
rename to examples/IsothermalPotential/README
diff --git a/examples/IsothermalPotential/GravityOnly/isothermal.yml b/examples/IsothermalPotential/isothermal.yml
similarity index 100%
rename from examples/IsothermalPotential/GravityOnly/isothermal.yml
rename to examples/IsothermalPotential/isothermal.yml
diff --git a/examples/IsothermalPotential/GravityOnly/makeIC.py b/examples/IsothermalPotential/makeIC.py
similarity index 97%
rename from examples/IsothermalPotential/GravityOnly/makeIC.py
rename to examples/IsothermalPotential/makeIC.py
index 07993f19d40a9a3b9a4b86c9dd8c44f7e6fa3d7e..976119f0a312c5acc81fab943ba3cf5769102269 100644
--- a/examples/IsothermalPotential/GravityOnly/makeIC.py
+++ b/examples/IsothermalPotential/makeIC.py
@@ -141,17 +141,17 @@ ds = grp1.create_dataset('Velocities', (numPart, 3), 'f')
 ds[()] = v
 v = numpy.zeros(1)
 
-m = numpy.full((numPart, ), mass)
+m = numpy.full((numPart, ), mass, dtype='f')
 ds = grp1.create_dataset('Masses', (numPart,), 'f')
 ds[()] = m
 m = numpy.zeros(1)
 
-h = numpy.full((numPart, ), 1.1255 * boxSize / L)
+h = numpy.full((numPart, ), 1.1255 * boxSize / L,  dtype='f')
 ds = grp1.create_dataset('SmoothingLength', (numPart,), 'f')
 ds[()] = h
 h = numpy.zeros(1)
 
-u = numpy.full((numPart, ), internalEnergy)
+u = numpy.full((numPart, ), internalEnergy,  dtype='f')
 ds = grp1.create_dataset('InternalEnergy', (numPart,), 'f')
 ds[()] = u
 u = numpy.zeros(1)
diff --git a/examples/IsothermalPotential/GravityOnly/run.sh b/examples/IsothermalPotential/run.sh
similarity index 79%
rename from examples/IsothermalPotential/GravityOnly/run.sh
rename to examples/IsothermalPotential/run.sh
index f6adfcececf4923485c0deabd97e9af9a6f64b05..28a3cc0910f986f84bcd603091543643356f1c4a 100755
--- a/examples/IsothermalPotential/GravityOnly/run.sh
+++ b/examples/IsothermalPotential/run.sh
@@ -7,4 +7,4 @@ then
     python makeIC.py 1000 1
 fi
 
-../../swift -g -t 2 isothermal.yml 2>&1 | tee output.log
+../swift -g -t 1 isothermal.yml 2>&1 | tee output.log
diff --git a/examples/IsothermalPotential/GravityOnly/test.pro b/examples/IsothermalPotential/test.pro
similarity index 100%
rename from examples/IsothermalPotential/GravityOnly/test.pro
rename to examples/IsothermalPotential/test.pro
diff --git a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
index 38dd16880a209b885f7ad9c30c024988f4d8228f..a229ecbdedba00d334ad9d3b80dce187b4ac0224 100644
--- a/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
+++ b/examples/KelvinHelmholtz_2D/kelvinHelmholtz.yml
@@ -6,6 +6,9 @@ InternalUnitSystem:
   UnitCurrent_in_cgs:  1   # Amperes
   UnitTemp_in_cgs:     1   # Kelvin
 
+Scheduler:
+  max_top_level_cells: 30
+  
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
@@ -27,7 +30,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.01      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   
 # Parameters related to the initial conditions
diff --git a/examples/Makefile.am b/examples/Makefile.am
index d9e1f2fe741098fe2051155fc1ff2d66d4751cee..4da84788a485dacd2103fe85ad3e729ade6b582a 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -30,13 +30,12 @@ EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS)
 MPI_LIBS = $(METIS_LIBS) $(MPI_THREAD_LIBS)
 MPI_FLAGS = -DWITH_MPI $(METIS_INCS)
 
-
 # Programs.
-bin_PROGRAMS = swift swift_fixdt
+bin_PROGRAMS = swift
 
 # Build MPI versions as well?
 if HAVEMPI
-bin_PROGRAMS += swift_mpi swift_fixdt_mpi
+bin_PROGRAMS += swift_mpi
 endif
 
 # engine_policy_setaffinity is available?
@@ -51,19 +50,11 @@ swift_SOURCES = main.c
 swift_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)"
 swift_LDADD =  ../src/.libs/libswiftsim.a $(EXTRA_LIBS)
 
-swift_fixdt_SOURCES = main.c
-swift_fixdt_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) -DENGINE_POLICY="engine_policy_fixdt | engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)"
-swift_fixdt_LDADD =  ../src/.libs/libswiftsim.a $(EXTRA_LIBS)
-
 # Sources for swift_mpi, do we need an affinity policy for MPI?
 swift_mpi_SOURCES = main.c
 swift_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)"
 swift_mpi_LDADD =  ../src/.libs/libswiftsim_mpi.a $(MPI_LIBS) $(EXTRA_LIBS)
 
-swift_fixdt_mpi_SOURCES = main.c
-swift_fixdt_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_policy_fixdt | engine_policy_keep $(ENGINE_POLICY_SETAFFINITY)"
-swift_fixdt_mpi_LDADD =  ../src/.libs/libswiftsim_mpi.a $(MPI_LIBS) $(EXTRA_LIBS)
-
 # Scripts to generate ICs
 EXTRA_DIST = BigCosmoVolume/makeIC.py \
 	     BigPerturbedBox/makeIC_fcc.py \
@@ -74,6 +65,9 @@ EXTRA_DIST = BigCosmoVolume/makeIC.py \
 	     EAGLE_50/eagle_50.yml EAGLE_50/getIC.sh EAGLE_50/README EAGLE_50/run.sh \
 	     ExternalPointMass/externalPointMass.yml ExternalPointMass/makeIC.py ExternalPointMass/run.sh ExternalPointMass/test.pro \
 	     GreshoVortex_2D/getGlass.sh GreshoVortex_2D/gresho.yml GreshoVortex_2D/makeIC.py GreshoVortex_2D/plotSolution.py GreshoVortex_2D/run.sh \
+	     HydrostaticHalo/README HydrostaticHalo/hydrostatic.yml HydrostaticHalo/makeIC.py HydrostaticHalo/run.sh \
+	     HydrostaticHalo/density_profile.py HydrostaticHalo/velocity_profile.py HydrostaticHalo/internal_energy_profile.py HydrostaticHalo/test_energy_conservation.py \
+	     IsothermalPotential/README IsothermalPotential/run.sh IsothermalPotential/test.pro IsothermalPotential/isothermal.yml IsothermalPotential/makeIC.py \
 	     KelvinHelmholtz_2D/kelvinHelmholtz.yml KelvinHelmholtz_2D/makeIC.py KelvinHelmholtz_2D/plotSolution.py KelvinHelmholtz_2D/run.sh \
 	     MultiTypes/makeIC.py  MultiTypes/multiTypes.yml MultiTypes/run.sh \
 	     PerturbedBox_2D/makeIC.py PerturbedBox_2D/perturbedPlane.yml \
diff --git a/examples/MultiTypes/multiTypes.yml b/examples/MultiTypes/multiTypes.yml
index 51a6d2b478681e2e1c61e199f758e35c507ec195..4d54f95fcdd09464b03d0f9987398cd2710b2e44 100644
--- a/examples/MultiTypes/multiTypes.yml
+++ b/examples/MultiTypes/multiTypes.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   
 # Parameters related to the initial conditions
diff --git a/examples/PerturbedBox_2D/perturbedPlane.yml b/examples/PerturbedBox_2D/perturbedPlane.yml
index e810131cc4b1c66b46b483b1605f9d84bcf203b3..b92e29f620edc6f72399111fbe73ba6bd1485e92 100644
--- a/examples/PerturbedBox_2D/perturbedPlane.yml
+++ b/examples/PerturbedBox_2D/perturbedPlane.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   
 # Parameters related to the initial conditions
diff --git a/examples/PerturbedBox_3D/perturbedBox.yml b/examples/PerturbedBox_3D/perturbedBox.yml
index 3a445e27e74fb83fb8deb56fde6003c22f7dedf1..71c8dece4df5505eb44511ee92291feedd7ffab1 100644
--- a/examples/PerturbedBox_3D/perturbedBox.yml
+++ b/examples/PerturbedBox_3D/perturbedBox.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   
 # Parameters related to the initial conditions
diff --git a/examples/SedovBlast_1D/sedov.yml b/examples/SedovBlast_1D/sedov.yml
index 1ecfeb32452d05f299b98124c4fdfc79126f7504..2a15d6ed22e71735c04274cee3f719b9d21f170e 100644
--- a/examples/SedovBlast_1D/sedov.yml
+++ b/examples/SedovBlast_1D/sedov.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1       # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/SedovBlast_2D/sedov.yml b/examples/SedovBlast_2D/sedov.yml
index 6f519835d26ff5aa851ffb8999e650815c522cd3..1cc4aced9af3314cadde44f768016225426addf6 100644
--- a/examples/SedovBlast_2D/sedov.yml
+++ b/examples/SedovBlast_2D/sedov.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1       # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/SedovBlast_3D/sedov.yml b/examples/SedovBlast_3D/sedov.yml
index 6f519835d26ff5aa851ffb8999e650815c522cd3..1cc4aced9af3314cadde44f768016225426addf6 100644
--- a/examples/SedovBlast_3D/sedov.yml
+++ b/examples/SedovBlast_3D/sedov.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1       # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/SodShock_1D/sodShock.yml b/examples/SodShock_1D/sodShock.yml
index d5c4d0b034ff5351222d2162e37e3e40ceab834f..a5759109e48b7e7eb9cbd15957cf438edd909f1f 100644
--- a/examples/SodShock_1D/sodShock.yml
+++ b/examples/SodShock_1D/sodShock.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.4      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/SodShock_2D/sodShock.yml b/examples/SodShock_2D/sodShock.yml
index 6805724ff58defebc41f3fb5b636d0003b0d6680..7ee61adf38b41012ed4d77a882640b826f15dec1 100644
--- a/examples/SodShock_2D/sodShock.yml
+++ b/examples/SodShock_2D/sodShock.yml
@@ -6,6 +6,9 @@ InternalUnitSystem:
   UnitCurrent_in_cgs:  1   # Amperes
   UnitTemp_in_cgs:     1   # Kelvin
 
+Scheduler:
+  max_top_level_cells: 60
+  
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
@@ -27,7 +30,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.02     # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/SodShock_3D/sodShock.yml b/examples/SodShock_3D/sodShock.yml
index 1ab6eb626db09678f66322e8f0e8674c0931ddb6..e7e01fa36bf93105371aa97336e18535e4078853 100644
--- a/examples/SodShock_3D/sodShock.yml
+++ b/examples/SodShock_3D/sodShock.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.05     # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
 
 # Parameters related to the initial conditions
diff --git a/examples/SquareTest_2D/square.yml b/examples/SquareTest_2D/square.yml
index 4f39c6490899cfaafeda17fb0c28281cbadcbbea..40ccf41cec99f82f9c208f9f719323d32caea19b 100644
--- a/examples/SquareTest_2D/square.yml
+++ b/examples/SquareTest_2D/square.yml
@@ -6,6 +6,9 @@ InternalUnitSystem:
   UnitCurrent_in_cgs:  1   # Amperes
   UnitTemp_in_cgs:     1   # Kelvin
 
+Scheduler:
+  max_top_level_cells: 15
+  
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
@@ -27,7 +30,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.02     # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   
 # Parameters related to the initial conditions
diff --git a/examples/UniformBox_2D/uniformPlane.yml b/examples/UniformBox_2D/uniformPlane.yml
index a3e2d275e50fb20f66ea6494c1202319e462dbed..0354f8e78eea17a41b3ed02e615cd3fb216f59c0 100644
--- a/examples/UniformBox_2D/uniformPlane.yml
+++ b/examples/UniformBox_2D/uniformPlane.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   
 # Parameters related to the initial conditions
diff --git a/examples/UniformBox_3D/uniformBox.yml b/examples/UniformBox_3D/uniformBox.yml
index 8aaa802b64de46244f7066bce00f342cad8c5ef0..e75c878389be83304dbb9670e20a6aaf03c9c6e0 100644
--- a/examples/UniformBox_3D/uniformBox.yml
+++ b/examples/UniformBox_3D/uniformBox.yml
@@ -27,7 +27,6 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_smoothing_length:  0.1      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   
 # Parameters related to the initial conditions
diff --git a/examples/main.c b/examples/main.c
index 06efe75b32f790b10d1125fb5010bc3d1b4f9e24..dcc113ab6af6a06e7c20ac1aac7c2d3b715f7ef3 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -52,8 +52,6 @@ void print_help_message() {
 
   printf("\nUsage: swift [OPTION]... PARAMFILE\n");
   printf("       swift_mpi [OPTION]... PARAMFILE\n");
-  printf("       swift_fixdt [OPTION]... PARAMFILE\n");
-  printf("       swift_fixdt_mpi [OPTION]... PARAMFILE\n\n");
 
   printf("Valid options are:\n");
   printf("  %2s %8s %s\n", "-a", "", "Pin runners using processor affinity");
@@ -114,8 +112,8 @@ int main(int argc, char *argv[]) {
     error("Call to MPI_Init failed with error %i.", res);
   if (prov != MPI_THREAD_MULTIPLE)
     error(
-        "MPI does not provide the level of threading required "
-        "(MPI_THREAD_MULTIPLE).");
+        "MPI does not provide the level of threading"
+        " required (MPI_THREAD_MULTIPLE).");
   if ((res = MPI_Comm_size(MPI_COMM_WORLD, &nr_nodes)) != MPI_SUCCESS)
     error("MPI_Comm_size failed with error %i.", res);
   if ((res = MPI_Comm_rank(MPI_COMM_WORLD, &myrank)) != MPI_SUCCESS)
@@ -127,9 +125,7 @@ int main(int argc, char *argv[]) {
   if ((res = MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN)) !=
       MPI_SUCCESS)
     error("Call to MPI_Comm_set_errhandler failed with error %i.", res);
-  if (myrank == 0)
-    printf("[0000][00000.0] MPI is up and running with %i node(s).\n",
-           nr_nodes);
+  if (myrank == 0) message("MPI is up and running with %i node(s).", nr_nodes);
   if (nr_nodes == 1) {
     message("WARNING: you are running with one MPI rank.");
     message("WARNING: you should use the non-MPI version of this program.");
@@ -235,6 +231,13 @@ int main(int argc, char *argv[]) {
           if (myrank == 0) print_help_message();
           return 1;
         }
+#ifndef SWIFT_DEBUG_TASKS
+        if (dump_tasks) {
+          error(
+              "Task dumping is only possible if SWIFT was configured with the "
+              "--enable-task-debugging option.");
+        }
+#endif
         break;
       case '?':
         if (myrank == 0) print_help_message();
@@ -282,6 +285,11 @@ int main(int argc, char *argv[]) {
   message("Running on: %s", hostname());
 #endif
 
+/* Do we have debugging checks ? */
+#ifdef SWIFT_DEBUG_CHECKS
+  message("WARNING: Debugging checks activated. Code will be slower !");
+#endif
+
   /* Do we choke on FP-exceptions ? */
   if (with_fp_exceptions) {
     feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
@@ -454,7 +462,7 @@ int main(int argc, char *argv[]) {
   /* Initialise the external potential properties */
   struct external_potential potential;
   if (with_external_gravity)
-    potential_init(params, &prog_const, &us, &potential);
+    potential_init(params, &prog_const, &us, &s, &potential);
   if (with_external_gravity && myrank == 0) potential_print(&potential);
 
   /* Initialise the cooling function properties */
@@ -549,6 +557,7 @@ int main(int argc, char *argv[]) {
     /* Take a step. */
     engine_step(&e);
 
+#ifdef SWIFT_DEBUG_TASKS
     /* Dump the task data using the given frequency. */
     if (dump_tasks && (dump_tasks == 1 || j % dump_tasks == 1)) {
 #ifdef WITH_MPI
@@ -626,8 +635,9 @@ int main(int argc, char *argv[]) {
         }
       }
       fclose(file_thread);
-#endif
+#endif  // WITH_MPI
     }
+#endif  // SWIFT_DEBUG_TASKS
   }
 
 /* Print the values of the runner histogram. */
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index fb193a81baf410360172793763875891f654211a..899bfb02243c69d3c0d0a94cb05c4f63cb62df32 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -8,13 +8,14 @@ InternalUnitSystem:
 
 # Parameters for the task scheduling
 Scheduler:
-  nr_queues:        0        # (Optional) The number of task queues to use. Use 0  to let the system decide.
-  cell_max_size:    8000000  # (Optional) Maximal number of interactions per task if we force the split (this is the default value).
-  cell_sub_size:    64000000 # (Optional) Maximal number of interactions per sub-task  (this is the default value).
-  cell_split_size:  400      # (Optional) Maximal number of particles per cell (this is the default value).
-  cell_max_count:   10000    # (Optional) Maximal number of particles per cell allowed before triggering a sanitizing (this is the default value).
+  nr_queues:             0        # (Optional) The number of task queues to use. Use 0  to let the system decide.
+  cell_max_size:         8000000  # (Optional) Maximal number of interactions per task if we force the split (this is the default value).
+  cell_sub_size:         64000000 # (Optional) Maximal number of interactions per sub-task  (this is the default value).
+  cell_split_size:       400      # (Optional) Maximal number of particles per cell (this is the default value).
+  cell_max_count:        10000    # (Optional) Maximal number of particles per cell allowed before triggering a sanitizing (this is the default value).
+  max_top_level_cells:   12       # (Optional) Maximal number of top-level cells in any dimension. The number of top-level cells will be the cube of this (this is the default value).
 
-# Parameters governing the time integration
+# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.)
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   1.    # The end time of the simulation (in internal units).
@@ -43,9 +44,8 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   delta_neighbours:      0.1      # The tolerance for the targetted number of neighbours.
-  max_ghost_iterations:  30       # (Optional) Maximal number of iterations allowed to converge towards the smoothing length.
-  max_smoothing_length:  0.1      # Maximal smoothing length allowed (in internal units).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
+  max_ghost_iterations:  30       # (Optional) Maximal number of iterations allowed to converge towards the smoothing length.
   max_volume_change:     2.       # (Optional) Maximal allowed change of kernel volume over one time-step
 
 # Parameters related to the initial conditions
@@ -76,12 +76,21 @@ PointMassPotential:
 
 # Isothermal potential parameters
 IsothermalPotential:
-  position_x:      100.     # Location of centre of isothermal potential (internal units)
+  position_x:      100.     # Location of centre of isothermal potential with respect to centre of the box (internal units)
   position_y:      100.
   position_z:      100.
   vrot:            200.     # Rotation speed of isothermal potential (internal units)
   timestep_mult:   0.03     # Dimensionless pre-factor for the time-step condition
 
+# External potential parameters
+SoftenedIsothermalPotential:
+  position_x:      0.       # Location of centre of isothermal potential with respect to centre of the box (internal units)
+  position_y:      0.
+  position_z:      0.	
+  vrot:            200.     # rotation speed of isothermal potential (internal units)
+  epsilon:         0.1      # Softening size (internal units)
+  timestep_mult:   0.03     # controls time step
+  
 # Disk-patch potential parameters
 DiscPatchPotential:
   surface_density: 10.      # Surface density of the disc (internal units)
diff --git a/examples/plot_scaling_results.py b/examples/plot_scaling_results.py
index 938d204bea8a9da81a93cf2d6d7334ac99d10e3a..26864fe675f502b025f0bf10d73bbba6f8162957 100755
--- a/examples/plot_scaling_results.py
+++ b/examples/plot_scaling_results.py
@@ -49,7 +49,7 @@ hexcols = ['#332288', '#88CCEE', '#44AA99', '#117733', '#999933', '#DDCC77',
            '#CC6677', '#882255', '#AA4499', '#661100', '#6699CC', '#AA4466',
            '#4477AA']
 linestyle = (hexcols[0],hexcols[1],hexcols[3],hexcols[5],hexcols[6],hexcols[8])
-#cmdLine = './swift_fixdt -s -t 16 cosmoVolume.yml'
+#cmdLine = './swift -s -t 16 cosmoVolume.yml'
 #platform = 'KNL'
 
 # Work out how many data series there are
diff --git a/examples/plot_tasks.py b/examples/plot_tasks.py
index 7b4683422725f206a3f582e00d82712c7e3c3f59..6295c81a5f2fdb1e726cdf0a8fb43713004800f1 100755
--- a/examples/plot_tasks.py
+++ b/examples/plot_tasks.py
@@ -56,7 +56,7 @@ pl.rcParams.update(PLOT_PARAMS)
 
 #  Tasks and subtypes. Indexed as in tasks.h.
 TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init", "ghost",
-             "extra_ghost", "kick", "kick_fixdt", "send", "recv",
+             "extra_ghost", "kick", "send", "recv",
              "grav_gather_m", "grav_fft", "grav_mm", "grav_up",
              "grav_external", "cooling", "count"]
 
@@ -70,7 +70,6 @@ TASKCOLOURS = {"none": "black",
                "ghost": "cyan",
                "extra_ghost": "cyan",
                "kick": "green",
-               "kick_fixdt": "green",
                "send": "yellow",
                "recv": "magenta",
                "grav_gather_m": "mediumorchid",
diff --git a/examples/plot_tasks_MPI.py b/examples/plot_tasks_MPI.py
index 02bc4a03510afce396429316fb4489926bc41b12..734918b8cbf388ef8f1a064e014cfd28775edde2 100755
--- a/examples/plot_tasks_MPI.py
+++ b/examples/plot_tasks_MPI.py
@@ -64,7 +64,7 @@ pl.rcParams.update(PLOT_PARAMS)
 
 #  Tasks and subtypes. Indexed as in tasks.h.
 TASKTYPES = ["none", "sort", "self", "pair", "sub_self", "sub_pair", "init",
-             "ghost", "extra_ghost", "kick", "kick_fixdt", "send", "recv",
+             "ghost", "extra_ghost", "kick", "send", "recv",
              "grav_gather_m", "grav_fft", "grav_mm", "grav_up",
              "grav_external", "cooling", "count"]
 
@@ -78,7 +78,6 @@ TASKCOLOURS = {"none": "black",
                "ghost": "cyan",
                "extra_ghost": "cyan",
                "kick": "green",
-               "kick_fixdt": "green",
                "send": "yellow",
                "recv": "magenta",
                "grav_gather_m": "mediumorchid",
diff --git a/m4/ax_cc_maxopt.m4 b/m4/ax_cc_maxopt.m4
index d5b87c903c5af46b767d7b49e40963345c8128af..93d5d6dcd78ff77c934f77ad0e1e02ef37873a37 100644
--- a/m4/ax_cc_maxopt.m4
+++ b/m4/ax_cc_maxopt.m4
@@ -160,6 +160,9 @@ if test "$ac_test_CFLAGS" != "set"; then
      # note that we enable "unsafe" fp optimization with other compilers, too
      AX_CHECK_COMPILE_FLAG(-ffast-math, CFLAGS="$CFLAGS -ffast-math")
 
+     # not all codes will benefit from this.
+     AX_CHECK_COMPILE_FLAG(-funroll-loops, CFLAGS="$CFLAGS -funroll-loops")
+
      AX_GCC_ARCHFLAG($acx_maxopt_portable)
      ;;
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 34e6e0ea2617474bb3c29482c1779a7af81de2e0..b37d330dbce96f578f88bc95755abdbea3c08e75 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,7 +60,7 @@ AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \
 nobase_noinst_HEADERS = align.h approx_math.h atomic.h cbrt.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \
 		 kernel_long_gravity.h vector.h runner_doiact.h runner_doiact_grav.h runner_doiact_fft.h \
                  units.h intrinsics.h minmax.h kick.h timestep.h drift.h adiabatic_index.h io_properties.h \
-		 dimension.h equation_of_state.h \
+		 dimension.h equation_of_state.h active.h \
 		 gravity.h gravity_io.h \
 		 gravity/Default/gravity.h gravity/Default/gravity_iact.h gravity/Default/gravity_io.h \
 		 gravity/Default/gravity_debug.h gravity/Default/gravity_part.h  \
@@ -80,6 +80,7 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h cbrt.h cycle.h error.h in
 		 riemann/riemann_exact.h riemann/riemann_vacuum.h \
 	         potential/none/potential.h potential/point_mass/potential.h \
                  potential/isothermal/potential.h potential/disc_patch/potential.h \
+		 potential/softened_isothermal/potential.h \
 		 cooling/none/cooling.h cooling/none/cooling_struct.h \
 	         cooling/const_du/cooling.h cooling/const_du/cooling_struct.h \
                  cooling/const_lambda/cooling.h cooling/const_lambda/cooling_struct.h 
diff --git a/src/active.h b/src/active.h
new file mode 100644
index 0000000000000000000000000000000000000000..17adfd07d2bfe0519f0b432217d5253de13c4b78
--- /dev/null
+++ b/src/active.h
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_ACTIVE_H
+#define SWIFT_ACTIVE_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Local includes. */
+#include "cell.h"
+#include "const.h"
+#include "engine.h"
+#include "part.h"
+
+/**
+ * @brief Does a cell contain any active particle ?
+ *
+ * @param c The #cell.
+ * @param e The #engine containing information about the current time.
+ */
+__attribute__((always_inline)) INLINE static int cell_is_active(
+    const struct cell *c, const struct engine *e) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (c->ti_end_min < e->ti_current)
+    error("cell in an impossible time-zone! c->ti_end_min=%d e->ti_current=%d",
+          c->ti_end_min, e->ti_current);
+#endif
+
+  return (c->ti_end_min == e->ti_current);
+}
+
+/**
+ * @brief Are *all* particles in a cell active ?
+ *
+ * @param c The #cell.
+ * @param e The #engine containing information about the current time.
+ */
+__attribute__((always_inline)) INLINE static int cell_is_all_active(
+    const struct cell *c, const struct engine *e) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (c->ti_end_max < e->ti_current)
+    error("cell in an impossible time-zone! c->ti_end_max=%d e->ti_current=%d",
+          c->ti_end_max, e->ti_current);
+#endif
+
+  return (c->ti_end_max == e->ti_current);
+}
+
+/**
+ * @brief Is this particle active ?
+ *
+ * @param p The #part.
+ * @param e The #engine containing information about the current time.
+ */
+__attribute__((always_inline)) INLINE static int part_is_active(
+    const struct part *p, const struct engine *e) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (p->ti_end < e->ti_current)
+    error("particle in an impossible time-zone! p->ti_end=%d e->ti_current=%d",
+          p->ti_end, e->ti_current);
+#endif
+
+  return (p->ti_end == e->ti_current);
+}
+
+/**
+ * @brief Is this g-particle active ?
+ *
+ * @param gp The #gpart.
+ * @param e The #engine containing information about the current time.
+ */
+__attribute__((always_inline)) INLINE static int gpart_is_active(
+    const struct gpart *gp, const struct engine *e) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (gp->ti_end < e->ti_current)
+    error(
+        "g-particle in an impossible time-zone! gp->ti_end=%d e->ti_current=%d",
+        gp->ti_end, e->ti_current);
+#endif
+
+  return (gp->ti_end == e->ti_current);
+}
+
+#endif /* SWIFT_ACTIVE_H */
diff --git a/src/cell.c b/src/cell.c
index a54906d667dd312fd35a7584047113a45183b46c..878401d770ca4a7787056a25da6d3b60d4313f2f 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -47,6 +47,7 @@
 #include "cell.h"
 
 /* Local headers. */
+#include "active.h"
 #include "atomic.h"
 #include "error.h"
 #include "gravity.h"
@@ -757,16 +758,26 @@ void cell_convert_hydro(struct cell *c, void *data) {
  */
 void cell_clean_links(struct cell *c, void *data) {
   c->density = NULL;
-  c->nr_density = 0;
-
   c->gradient = NULL;
-  c->nr_gradient = 0;
-
   c->force = NULL;
-  c->nr_force = 0;
-
   c->grav = NULL;
-  c->nr_grav = 0;
+}
+
+/**
+ * @brief Checks that a cell is at the current point in time
+ *
+ * Calls error() if the cell is not at the current time.
+ *
+ * @param c Cell to act upon
+ * @param data The current time on the integer time-line
+ */
+void cell_check_drift_point(struct cell *c, void *data) {
+
+  const int ti_current = *(int *)data;
+
+  if (c->ti_old != ti_current)
+    error("Cell in an incorrect time-zone! c->ti_old=%d ti_current=%d",
+          c->ti_old, ti_current);
 }
 
 /**
@@ -793,7 +804,7 @@ int cell_are_neighbours(const struct cell *restrict ci,
   for (int k = 0; k < 3; k++) {
     const double center_i = ci->loc[k];
     const double center_j = cj->loc[k];
-    if (fabsf(center_i - center_j) > min_dist) return 0;
+    if (fabs(center_i - center_j) > min_dist) return 0;
   }
 
   return 1;
@@ -823,7 +834,7 @@ void cell_check_multipole(struct cell *c, void *data) {
             mb.mass);
 
     for (int k = 0; k < 3; ++k)
-      if (fabsf(ma.CoM[k] - mb.CoM[k]) / fabsf(ma.CoM[k] + mb.CoM[k]) > 1e-5)
+      if (fabs(ma.CoM[k] - mb.CoM[k]) / fabs(ma.CoM[k] + mb.CoM[k]) > 1e-5)
         error("Multipole CoM are different (%12.15e vs. %12.15e", ma.CoM[k],
               mb.CoM[k]);
 
@@ -874,14 +885,14 @@ void cell_clean(struct cell *c) {
  * @brief Checks whether a given cell needs drifting or not.
  *
  * @param c the #cell.
- * @param ti_current The current time on the integer time-line.
+ * @param e The #engine (holding current time information).
  *
  * @return 1 If the cell needs drifting, 0 otherwise.
  */
-int cell_is_drift_needed(struct cell *c, int ti_current) {
+int cell_is_drift_needed(struct cell *c, const struct engine *e) {
 
   /* Do we have at least one active particle in the cell ?*/
-  if (c->ti_end_min == ti_current) return 1;
+  if (cell_is_active(c, e)) return 1;
 
   /* Loop over the pair tasks that involve this cell */
   for (struct link *l = c->density; l != NULL; l = l->next) {
@@ -889,9 +900,9 @@ int cell_is_drift_needed(struct cell *c, int ti_current) {
     if (l->t->type != task_type_pair && l->t->type != task_type_sub_pair)
       continue;
 
-    /* Does the other cell in the pair have an active particle ? */
-    if ((l->t->ci == c && l->t->cj->ti_end_min == ti_current) ||
-        (l->t->cj == c && l->t->ci->ti_end_min == ti_current))
+    /* Is the other cell in the pair active ? */
+    if ((l->t->ci == c && cell_is_active(l->t->cj, e)) ||
+        (l->t->cj == c && cell_is_active(l->t->ci, e)))
       return 1;
   }
 
diff --git a/src/cell.h b/src/cell.h
index ae22de0cb268bb5240c480b2eed9435ec218adf8..eefb2114cde1705ab4e4c59f4b837bddeb093e2b 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -37,6 +37,7 @@
 #include "task.h"
 
 /* Avoid cyclic inclusions */
+struct engine;
 struct space;
 struct scheduler;
 
@@ -79,127 +80,191 @@ struct pcell {
 
 } SWIFT_STRUCT_ALIGN;
 
-/* Structure to store the data of a single cell. */
+/**
+ * @brief Cell within the tree structure.
+ *
+ * Contains particles, links to tasks, a multipole object and counters.
+ */
 struct cell {
 
-  /* The cell location on the grid. */
+  /*! This cell's multipole. */
+  struct multipole multipole;
+
+  /*! The cell location on the grid. */
   double loc[3];
 
-  /* The cell dimensions. */
+  /*! The cell dimensions. */
   double width[3];
 
-  /* Max smoothing length in this cell. */
+  /*! Max smoothing length in this cell. */
   double h_max;
 
-  /* Minimum and maximum end of time step in this cell. */
-  int ti_end_min, ti_end_max;
-
-  /* Last time the cell's content was drifted forward in time. */
-  int ti_old;
-
-  /* Minimum dimension, i.e. smallest edge of this cell. */
-  float dmin;
-
-  /* Maximum slack allowed for particle movement. */
-  float slack;
-
-  /* Maximum particle movement in this cell since last construction. */
-  float dx_max;
-
-  /* The depth of this cell in the tree. */
-  int depth, split, maxdepth;
-
-  /* Nr of parts. */
-  int count, gcount;
+  /*! Linking pointer for "memory management". */
+  struct cell *next;
 
-  /* Pointers to the particle data. */
+  /*! Pointer to the #part data. */
   struct part *parts;
 
-  /* Pointers to the extra particle data. */
+  /*! Pointer to the #xpart data. */
   struct xpart *xparts;
 
-  /* Pointers to the gravity particle data. */
+  /*! Pointer to the #gpart data. */
   struct gpart *gparts;
 
-  /* Pointers for the sorted indices. */
+  /*! Pointer for the sorted indices. */
   struct entry *sort;
-  unsigned int sorted;
 
-  /* Pointers to the next level of cells. */
+  /*! Pointers to the next level of cells. */
   struct cell *progeny[8];
 
-  /* Parent cell. */
+  /*! Parent cell. */
   struct cell *parent;
 
-  /* Super cell, i.e. the highest-level supercell that has pair/self tasks */
+  /*! Super cell, i.e. the highest-level parent cell that has pair/self tasks */
   struct cell *super;
 
-  /* The task computing this cell's sorts. */
+  /*! The task computing this cell's sorts. */
   struct task *sorts;
-  int sortsize;
 
-  /* The tasks computing this cell's density. */
-  struct link *density, *gradient, *force, *grav;
-  int nr_density, nr_gradient, nr_force, nr_grav;
+  /*! Linked list of the tasks computing this cell's hydro density. */
+  struct link *density;
 
-  /* The hierarchical tasks. */
-  struct task *extra_ghost, *ghost, *init, *kick;
+  /* Linked list of the tasks computing this cell's hydro gradients. */
+  struct link *gradient;
 
-#ifdef WITH_MPI
+  /*! Linked list of the tasks computing this cell's hydro forces. */
+  struct link *force;
 
-  /* Task receiving data. */
-  struct task *recv_xv, *recv_rho, *recv_gradient, *recv_ti;
+  /*! Linked list of the tasks computing this cell's gravity forces. */
+  struct link *grav;
 
-  /* Task send data. */
-  struct link *send_xv, *send_rho, *send_gradient, *send_ti;
+  /*! The initialistation task */
+  struct task *init;
 
-#endif
+  /*! The ghost task */
+  struct task *ghost;
+
+  /*! The extra ghost task for complex hydro schemes */
+  struct task *extra_ghost;
+
+  /*! The kick task */
+  struct task *kick;
+
+  /*! Task constructing the multipole from the particles */
+  struct task *grav_up;
 
-  /* Tasks for gravity tree. */
-  struct task *grav_up, *grav_down;
+  /*! Task propagating the multipole to the particles */
+  struct task *grav_down;
 
-  /* Task for cooling */
+  /*! Task for cooling */
   struct task *cooling;
 
-  /* Task for source terms */
+  /*! Task for source terms */
   struct task *sourceterms;
 
-  /* Number of tasks that are associated with this cell. */
-  int nr_tasks;
+#ifdef WITH_MPI
 
-  /* Is the data of this cell being used in a sub-cell? */
-  int hold, ghold;
+  /* Task receiving data (positions). */
+  struct task *recv_xv;
 
-  /* Spin lock for various uses. */
-  swift_lock_type lock, glock;
+  /* Task receiving data (density). */
+  struct task *recv_rho;
 
-  /* ID of the previous owner, e.g. runner. */
-  int owner;
+  /* Task receiving data (gradient). */
+  struct task *recv_gradient;
 
-  /* Number of particles updated in this cell. */
-  int updated, g_updated;
+  /* Task receiving data (time-step). */
+  struct task *recv_ti;
 
-  /* Linking pointer for "memory management". */
-  struct cell *next;
+  /* Linked list for sending data (positions). */
+  struct link *send_xv;
 
-  /* This cell's multipole. */
-  struct multipole multipole;
+  /* Linked list for sending data (density). */
+  struct link *send_rho;
 
-  /* ID of the node this cell lives on. */
-  int nodeID;
+  /* Linked list for sending data (gradient). */
+  struct link *send_gradient;
 
-#ifdef WITH_MPI
+  /* Linked list for sending data (time-step). */
+  struct link *send_ti;
 
-  /* Bit mask of the proxies this cell is registered with. */
+  /*! Bit mask of the proxies this cell is registered with. */
   unsigned long long int sendto;
 
-  /* Pointer to this cell's packed representation. */
+  /*! Pointer to this cell's packed representation. */
   struct pcell *pcell;
+
+  /*! Size of the packed representation */
   int pcell_size;
+
+  /*! MPI tag associated with this cell */
   int tag;
 
 #endif
 
+  /*! Minimum end of (integer) time step in this cell. */
+  int ti_end_min;
+
+  /*! Maximum end of (integer) time step in this cell. */
+  int ti_end_max;
+
+  /*! Last (integer) time the cell's content was drifted forward in time. */
+  int ti_old;
+
+  /*! Minimum dimension, i.e. smallest edge of this cell (min(width)). */
+  float dmin;
+
+  /*! Maximum particle movement in this cell since last construction. */
+  float dx_max;
+
+  /*! Nr of #part in this cell. */
+  int count;
+
+  /*! Nr of #gpart in this cell. */
+  int gcount;
+
+  /*! The size of the sort array */
+  int sortsize;
+
+  /*! Bit-mask indicating the sorted directions */
+  unsigned int sorted;
+
+  /*! Spin lock for various uses (#part case). */
+  swift_lock_type lock;
+
+  /*! Spin lock for various uses (#gpart case). */
+  swift_lock_type glock;
+
+  /*! ID of the previous owner, e.g. runner. */
+  int owner;
+
+  /*! Number of #part updated in this cell. */
+  int updated;
+
+  /*! Number of #gpart updated in this cell. */
+  int g_updated;
+
+  /*! ID of the node this cell lives on. */
+  int nodeID;
+
+  /*! Is the #part data of this cell being used in a sub-cell? */
+  int hold;
+
+  /*! Is the #gpart data of this cell being used in a sub-cell? */
+  int ghold;
+
+  /*! Number of tasks that are associated with this cell. */
+  short int nr_tasks;
+
+  /*! The depth of this cell in the tree. */
+  char depth;
+
+  /*! Is this cell split ? */
+  char split;
+
+  /*! The maximal depth of this cell and its progenies */
+  char maxdepth;
+
 } SWIFT_STRUCT_ALIGN;
 
 /* Convert cell location to ID. */
@@ -226,7 +291,8 @@ int cell_are_neighbours(const struct cell *restrict ci,
                         const struct cell *restrict cj);
 void cell_check_multipole(struct cell *c, void *data);
 void cell_clean(struct cell *c);
-int cell_is_drift_needed(struct cell *c, int ti_current);
+void cell_check_drift_point(struct cell *c, void *data);
+int cell_is_drift_needed(struct cell *c, const struct engine *e);
 int cell_unskip_tasks(struct cell *c, struct scheduler *s);
 void cell_set_super(struct cell *c, struct cell *super);
 
diff --git a/src/const.h b/src/const.h
index 11ee65bfdfabbf43962c721b8599928c25fef7cc..740122547fe38372e33219735115edec11b6c413 100644
--- a/src/const.h
+++ b/src/const.h
@@ -94,6 +94,7 @@
 #define EXTERNAL_POTENTIAL_NONE
 //#define EXTERNAL_POTENTIAL_POINTMASS
 //#define EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL
+//#define EXTERNAL_POTENTIAL_SOFTENED_ISOTHERMAL_POTENTIAL
 //#define EXTERNAL_POTENTIAL_DISC_PATCH
 
 /* Source terms */
diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h
index b25980ff2269ca9ea176edcc2a3c771647819133..448af9c3765e3bb6d4cbf4cc94e245a3976d5314 100644
--- a/src/cooling/const_du/cooling.h
+++ b/src/cooling/const_du/cooling.h
@@ -103,7 +103,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
 
   const float cooling_rate = cooling->cooling_rate;
   const float internal_energy = hydro_get_internal_energy(p, 0);
-  return cooling->cooling_tstep_mult * internal_energy / cooling_rate;
+  return cooling->cooling_tstep_mult * internal_energy / fabsf(cooling_rate);
 }
 
 /**
diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h
index 11cf2cae51f1ab2646d1391d2164c399c77a7bba..cb9db2dc34a6014ea15a24d368a006fee3838d67 100644
--- a/src/cooling/const_lambda/cooling.h
+++ b/src/cooling/const_lambda/cooling.h
@@ -24,6 +24,7 @@
 #define SWIFT_COOLING_CONST_LAMBDA_H
 
 /* Some standard headers. */
+#include <float.h>
 #include <math.h>
 
 /* Local includes. */
@@ -47,29 +48,16 @@ __attribute__((always_inline)) INLINE static float cooling_rate(
     const struct phys_const* const phys_const, const struct UnitSystem* us,
     const struct cooling_function_data* cooling, const struct part* p) {
 
-  /* Get particle properties */
-  /* Density */
+  /* Get particle density */
   const float rho = hydro_get_density(p);
+
   /* Get cooling function properties */
   const float X_H = cooling->hydrogen_mass_abundance;
-  /* lambda should always be set in cgs units */
-  const float lambda_cgs = cooling->lambda;
-
-  /*convert from internal code units to cgs*/
-  const float rho_cgs =
-      rho * units_cgs_conversion_factor(us, UNIT_CONV_DENSITY);
-  const float m_p_cgs = phys_const->const_proton_mass *
-                        units_cgs_conversion_factor(us, UNIT_CONV_MASS);
-  const float n_H_cgs = X_H * rho_cgs / m_p_cgs;
 
   /* Calculate du_dt */
-  const float du_dt_cgs = -lambda_cgs * n_H_cgs * n_H_cgs / rho_cgs;
-
-  /* Convert du/dt back to internal code units */
-  const float du_dt =
-      du_dt_cgs * units_cgs_conversion_factor(us, UNIT_CONV_TIME) /
-      units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS);
-
+  const float du_dt = -cooling->lambda *
+                      (X_H * rho / phys_const->const_proton_mass) *
+                      (X_H * rho / phys_const->const_proton_mass) / rho;
   return du_dt;
 }
 
@@ -97,7 +85,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
   /* Calculate du_dt */
   const float du_dt = cooling_rate(phys_const, us, cooling, p);
 
-  /* Intergrate cooling equation, but enforce energy floor */
+  /* Integrate cooling equation, but enforce energy floor */
   float u_new;
   if (u_old + du_dt * dt > u_floor) {
     u_new = u_old + du_dt * dt;
@@ -105,16 +93,12 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
     u_new = u_floor;
   }
 
+  /* Don't allow particle to cool too much in one timestep */
+  if (u_new < 0.5f * u_old) u_new = 0.5f * u_old;
+
   /* Update the internal energy */
   hydro_set_internal_energy(p, u_new);
 
-  /* if (-(u_new_test - u_old) / u_old > 1.0e-6) */
-  /*   error( */
-  /*       "Particle has not successfully cooled: u_old = %g , du_dt = %g , dt =
-   * " */
-  /*       "%g, du_dt*dt = %g, u_old + du_dt*dt = %g, u_new = %g\n", */
-  /*       u_old, du_dt, dt, du_dt * dt, u_new, u_new_test); */
-
   /* Store the radiated energy */
   xp->cooling_data.radiated_energy += hydro_get_mass(p) * (u_old - u_new);
 }
@@ -132,13 +116,16 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
     const struct phys_const* restrict phys_const,
     const struct UnitSystem* restrict us, const struct part* restrict p) {
 
-  /* Get du_dt */
-  const float du_dt = cooling_rate(phys_const, us, cooling, p);
-
   /* Get current internal energy (dt=0) */
   const float u = hydro_get_internal_energy(p, 0.f);
+  const float du_dt = cooling_rate(phys_const, us, cooling, p);
 
-  return u / du_dt;
+  /* If we are close to (or below) the energy floor, we ignore cooling timestep
+   */
+  if (u < 1.01f * cooling->min_energy)
+    return FLT_MAX;
+  else
+    return cooling->cooling_tstep_mult * u / fabsf(du_dt);
 }
 
 /**
@@ -178,9 +165,9 @@ static INLINE void cooling_init_backend(
     const struct phys_const* phys_const,
     struct cooling_function_data* cooling) {
 
-  cooling->lambda =
-      parser_get_param_double(parameter_file, "LambdaCooling:lambda");
-  cooling->min_temperature = parser_get_param_double(
+  const double lambda_cgs =
+      parser_get_param_double(parameter_file, "LambdaCooling:lambda_cgs");
+  const float min_temperature = parser_get_param_double(
       parameter_file, "LambdaCooling:minimum_temperature");
   cooling->hydrogen_mass_abundance = parser_get_param_double(
       parameter_file, "LambdaCooling:hydrogen_mass_abundance");
@@ -189,16 +176,19 @@ static INLINE void cooling_init_backend(
   cooling->cooling_tstep_mult = parser_get_param_double(
       parameter_file, "LambdaCooling:cooling_tstep_mult");
 
-  /*convert minimum temperature into minimum internal energy*/
+  /* convert minimum temperature into minimum internal energy */
   const float u_floor =
-      phys_const->const_boltzmann_k * cooling->min_temperature /
+      phys_const->const_boltzmann_k * min_temperature /
       (hydro_gamma_minus_one * cooling->mean_molecular_weight *
        phys_const->const_proton_mass);
-  const float u_floor_cgs =
-      u_floor * units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_PER_UNIT_MASS);
 
   cooling->min_energy = u_floor;
-  cooling->min_energy_cgs = u_floor_cgs;
+
+  /* convert lambda to code units */
+  cooling->lambda = lambda_cgs *
+                    units_cgs_conversion_factor(us, UNIT_CONV_TIME) /
+                    (units_cgs_conversion_factor(us, UNIT_CONV_ENERGY) *
+                     units_cgs_conversion_factor(us, UNIT_CONV_VOLUME));
 }
 
 /**
@@ -211,10 +201,10 @@ static INLINE void cooling_print_backend(
 
   message(
       "Cooling function is 'Constant lambda' with "
-      "(lambda,min_temperature,hydrogen_mass_abundance,mean_molecular_weight) "
+      "(lambda,min_energy,hydrogen_mass_abundance,mean_molecular_weight) "
       "=  (%g,%g,%g,%g)",
-      cooling->lambda, cooling->min_temperature,
-      cooling->hydrogen_mass_abundance, cooling->mean_molecular_weight);
+      cooling->lambda, cooling->min_energy, cooling->hydrogen_mass_abundance,
+      cooling->mean_molecular_weight);
 }
 
 #endif /* SWIFT_COOLING_CONST_LAMBDA_H */
diff --git a/src/cooling/const_lambda/cooling_struct.h b/src/cooling/const_lambda/cooling_struct.h
index 27c5df16bffbe7d165237d201ca68ea4ba89dd73..30d4e5e4af9c7bd139337709897d8111f88d2aa8 100644
--- a/src/cooling/const_lambda/cooling_struct.h
+++ b/src/cooling/const_lambda/cooling_struct.h
@@ -28,21 +28,17 @@
  */
 struct cooling_function_data {
 
-  /*! Cooling rate in cgs units. Defined by 'rho * du/dt = -lambda * n_H^2'*/
-  float lambda;
+  /*! Cooling rate in internal units */
+  double lambda;
 
-  /*! Minimum temperature (in Kelvin) for all gas particles*/
-  float min_temperature;
-
-  /*! Fraction of gas mass that is Hydrogen. Used to calculate n_H*/
+  /*! Fraction of gas mass that is Hydrogen. Used to calculate n_H */
   float hydrogen_mass_abundance;
 
-  /* 'mu', used to convert min_temperature to min_internal energy*/
+  /*! 'mu', used to convert min_temperature to min_internal energy */
   float mean_molecular_weight;
 
-  /*! Minimally allowed internal energy of the particles */
+  /*! Minimally allowed internal energy of all the particles */
   float min_energy;
-  float min_energy_cgs;
 
   /*! Constant multiplication factor for time-step criterion */
   float cooling_tstep_mult;
diff --git a/src/debug.c b/src/debug.c
index be42485a38ea8d560797e8f1ccc5936456febcd8..7a50368f069deef0c7d77b4ac3407ab059888d68 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -171,7 +171,7 @@ int checkSpacehmax(struct space *s) {
   }
 
   /*  If within some epsilon we are OK. */
-  if (abs(cell_h_max - part_h_max) <= FLT_EPSILON) return 1;
+  if (fabsf(cell_h_max - part_h_max) <= FLT_EPSILON) return 1;
 
   /* There is a problem. Hunt it down. */
   for (int k = 0; k < s->nr_cells; k++) {
@@ -193,6 +193,60 @@ int checkSpacehmax(struct space *s) {
   return 0;
 }
 
+/**
+ * @brief Check if the h_max and dx_max values of a cell's hierarchy are
+ * consistent with the particles. Report verbosely if not.
+ *
+ * @param c the top cell of the hierarchy.
+ * @param depth the recursion depth for use in messages. Set to 0 initially.
+ * @result 1 or 0
+ */
+int checkCellhdxmax(const struct cell *c, int *depth) {
+
+  *depth = *depth + 1;
+
+  float h_max = 0.0f;
+  float dx_max = 0.0f;
+  if (!c->split) {
+    const size_t nr_parts = c->count;
+    struct part *parts = c->parts;
+    for (size_t k = 0; k < nr_parts; k++) {
+      h_max = (h_max > parts[k].h) ? h_max : parts[k].h;
+    }
+  } else {
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL) {
+        struct cell *cp = c->progeny[k];
+        checkCellhdxmax(cp, depth);
+        dx_max = max(dx_max, cp->dx_max);
+        h_max = max(h_max, cp->h_max);
+      }
+  }
+
+  /* Check. */
+  int result = 1;
+  if (c->h_max != h_max) {
+    message("%d Inconsistent h_max: cell %f != parts %f", *depth, c->h_max,
+            h_max);
+    message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]);
+    result = 0;
+  }
+  if (c->dx_max != dx_max) {
+    message("%d Inconsistent dx_max: %f != %f", *depth, c->dx_max, dx_max);
+    message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]);
+    result = 0;
+  }
+
+  /* Check rebuild criterion. */
+  if (h_max > c->dmin) {
+    message("%d Inconsistent c->dmin: %f > %f", *depth, h_max, c->dmin);
+    message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]);
+    result = 0;
+  }
+
+  return result;
+}
+
 #ifdef HAVE_METIS
 
 /**
@@ -331,4 +385,47 @@ void dumpMETISGraph(const char *prefix, idx_t nvertices, idx_t nvertexweights,
   }
 }
 
-#endif
+#endif /* HAVE_METIS */
+
+#ifdef HAVE_MPI
+/**
+ * @brief Dump the positions and MPI ranks of the given top-level cells
+ *        to a simple text file.
+ *
+ * Can be used to visualise the partitioning of an MPI run. Note should
+ * be used immediately after repartitioning when the top-level cells
+ * have been assigned their nodes. Each time this is called a new file
+ * with the given prefix, a unique integer and type of .dat is created.
+ *
+ * @param prefix base output filename
+ * @param cells_top the top-level cells.
+ * @param nr_cells the number of cells.
+ */
+void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells) {
+
+  FILE *file = NULL;
+
+  /* Name of output file. */
+  static int nseq = 0;
+  char fname[200];
+  sprintf(fname, "%s_%03d.dat", prefix, nseq);
+  nseq++;
+
+  file = fopen(fname, "w");
+
+  /* Header. */
+  fprintf(file, "# %6s %6s %6s %6s %6s %6s %6s\n", "x", "y", "z", "xw", "yw",
+          "zw", "rank");
+
+  /* Output */
+  for (int i = 0; i < nr_cells; i++) {
+    struct cell *c = &cells_top[i];
+    fprintf(file, "  %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %6d\n", c->loc[0],
+            c->loc[1], c->loc[2], c->width[0], c->width[1], c->width[2],
+            c->nodeID);
+  }
+
+  fclose(file);
+}
+
+#endif /* HAVE_MPI */
diff --git a/src/debug.h b/src/debug.h
index 2142a22eca91338580d8f50197a57de0cf248bee..7422a6f7f9815490966f08415e0312876ce0123f 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -32,11 +32,16 @@ void printParticle_single(const struct part *p, const struct xpart *xp);
 void printgParticle_single(struct gpart *gp);
 
 int checkSpacehmax(struct space *s);
+int checkCellhdxmax(const struct cell *c, int *depth);
 
 #ifdef HAVE_METIS
 #include "metis.h"
 void dumpMETISGraph(const char *prefix, idx_t nvtxs, idx_t ncon, idx_t *xadj,
                     idx_t *adjncy, idx_t *vwgt, idx_t *vsize, idx_t *adjwgt);
+#endif
 
+#ifdef HAVE_MPI
+void dumpCellRanks(const char *prefix, struct cell *cells_top, int nr_cells);
 #endif
+
 #endif /* SWIFT_DEBUG_H */
diff --git a/src/engine.c b/src/engine.c
index 31927bdbe3ab4501631b2155bb51d4a33b646a7b..e989aefd5343eb7f377c6c1e8bc41f9875f2f42e 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -74,7 +74,6 @@ const char *engine_policy_names[16] = {"none",
                                        "steal",
                                        "keep",
                                        "block",
-                                       "fix_dt",
                                        "cpu_tight",
                                        "mpi",
                                        "numa_affinity",
@@ -126,7 +125,6 @@ void engine_addlink(struct engine *e, struct link **l, struct task *t) {
 void engine_make_hierarchical_tasks(struct engine *e, struct cell *c) {
 
   struct scheduler *s = &e->sched;
-  const int is_fixdt = (e->policy & engine_policy_fixdt);
   const int is_hydro = (e->policy & engine_policy_hydro);
   const int is_with_cooling = (e->policy & engine_policy_cooling);
   const int is_with_sourceterms = (e->policy & engine_policy_sourceterms);
@@ -141,14 +139,8 @@ void engine_make_hierarchical_tasks(struct engine *e, struct cell *c) {
       c->init = scheduler_addtask(s, task_type_init, task_subtype_none, 0, 0, c,
                                   NULL, 0);
 
-      /* Add the kick task that matches the policy. */
-      if (is_fixdt) {
-        c->kick = scheduler_addtask(s, task_type_kick_fixdt, task_subtype_none,
-                                    0, 0, c, NULL, 0);
-      } else {
-        c->kick = scheduler_addtask(s, task_type_kick, task_subtype_none, 0, 0,
-                                    c, NULL, 0);
-      }
+      c->kick = scheduler_addtask(s, task_type_kick, task_subtype_none, 0, 0, c,
+                                  NULL, 0);
 
       /* Generate the ghost task. */
       if (is_hydro)
@@ -565,6 +557,11 @@ void engine_repartition(struct engine *e) {
 
   ticks tic = getticks();
 
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Check that all cells have been drifted to the current time */
+  space_check_drift_point(e->s, e->ti_current);
+#endif
+
   /* Clear the repartition flag. */
   enum repartition_type reparttype = e->forcerepart;
   e->forcerepart = REPART_NONE;
@@ -657,9 +654,8 @@ void engine_addtasks_send(struct engine *e, struct cell *ci, struct cell *cj,
                                4 * ci->tag, 0, ci, cj, 0);
       t_rho = scheduler_addtask(s, task_type_send, task_subtype_none,
                                 4 * ci->tag + 1, 0, ci, cj, 0);
-      if (!(e->policy & engine_policy_fixdt))
-        t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend,
-                                 4 * ci->tag + 2, 0, ci, cj, 0);
+      t_ti = scheduler_addtask(s, task_type_send, task_subtype_tend,
+                               4 * ci->tag + 2, 0, ci, cj, 0);
 #ifdef EXTRA_HYDRO_LOOP
       t_gradient = scheduler_addtask(s, task_type_send, task_subtype_none,
                                      4 * ci->tag + 3, 0, ci, cj, 0);
@@ -743,9 +739,8 @@ void engine_addtasks_recv(struct engine *e, struct cell *c, struct task *t_xv,
                              0, c, NULL, 0);
     t_rho = scheduler_addtask(s, task_type_recv, task_subtype_none,
                               4 * c->tag + 1, 0, c, NULL, 0);
-    if (!(e->policy & engine_policy_fixdt))
-      t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend,
-                               4 * c->tag + 2, 0, c, NULL, 0);
+    t_ti = scheduler_addtask(s, task_type_recv, task_subtype_tend,
+                             4 * c->tag + 2, 0, c, NULL, 0);
 #ifdef EXTRA_HYDRO_LOOP
     t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_none,
                                    4 * c->tag + 3, 0, c, NULL, 0);
@@ -1359,15 +1354,12 @@ void engine_count_and_link_tasks(struct engine *e) {
       atomic_inc(&ci->nr_tasks);
       if (t->subtype == task_subtype_density) {
         engine_addlink(e, &ci->density, t);
-        atomic_inc(&ci->nr_density);
       }
       if (t->subtype == task_subtype_grav) {
         engine_addlink(e, &ci->grav, t);
-        atomic_inc(&ci->nr_grav);
       }
       if (t->subtype == task_subtype_external_grav) {
         engine_addlink(e, &ci->grav, t);
-        atomic_inc(&ci->nr_grav);
       }
 
       /* Link pair tasks to cells. */
@@ -1376,15 +1368,14 @@ void engine_count_and_link_tasks(struct engine *e) {
       atomic_inc(&cj->nr_tasks);
       if (t->subtype == task_subtype_density) {
         engine_addlink(e, &ci->density, t);
-        atomic_inc(&ci->nr_density);
         engine_addlink(e, &cj->density, t);
-        atomic_inc(&cj->nr_density);
       }
       if (t->subtype == task_subtype_grav) {
         engine_addlink(e, &ci->grav, t);
-        atomic_inc(&ci->nr_grav);
         engine_addlink(e, &cj->grav, t);
-        atomic_inc(&cj->nr_grav);
+      }
+      if (t->subtype == task_subtype_external_grav) {
+        error("Found a pair/external-gravity task...");
       }
 
       /* Link sub-self tasks to cells. */
@@ -1392,15 +1383,12 @@ void engine_count_and_link_tasks(struct engine *e) {
       atomic_inc(&ci->nr_tasks);
       if (t->subtype == task_subtype_density) {
         engine_addlink(e, &ci->density, t);
-        atomic_inc(&ci->nr_density);
       }
       if (t->subtype == task_subtype_grav) {
         engine_addlink(e, &ci->grav, t);
-        atomic_inc(&ci->nr_grav);
       }
       if (t->subtype == task_subtype_external_grav) {
         engine_addlink(e, &ci->grav, t);
-        atomic_inc(&ci->nr_grav);
       }
 
       /* Link sub-pair tasks to cells. */
@@ -1409,22 +1397,14 @@ void engine_count_and_link_tasks(struct engine *e) {
       atomic_inc(&cj->nr_tasks);
       if (t->subtype == task_subtype_density) {
         engine_addlink(e, &ci->density, t);
-        atomic_inc(&ci->nr_density);
         engine_addlink(e, &cj->density, t);
-        atomic_inc(&cj->nr_density);
       }
       if (t->subtype == task_subtype_grav) {
         engine_addlink(e, &ci->grav, t);
-        atomic_inc(&ci->nr_grav);
         engine_addlink(e, &cj->grav, t);
-        atomic_inc(&cj->nr_grav);
       }
       if (t->subtype == task_subtype_external_grav) {
         error("Found a sub-pair/external-gravity task...");
-        engine_addlink(e, &ci->grav, t);
-        atomic_inc(&ci->nr_grav);
-        engine_addlink(e, &cj->grav, t);
-        atomic_inc(&cj->nr_grav);
       }
     }
   }
@@ -1650,9 +1630,7 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
       /* Add the link between the new loops and the cell */
       engine_addlink(e, &t->ci->gradient, t2);
-      atomic_inc(&t->ci->nr_gradient);
       engine_addlink(e, &t->ci->force, t3);
-      atomic_inc(&t->ci->nr_force);
 
       /* Now, build all the dependencies for the hydro */
       engine_make_hydro_loops_dependencies(sched, t, t2, t3, t->ci);
@@ -1665,7 +1643,6 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
       /* Add the link between the new loop and the cell */
       engine_addlink(e, &t->ci->force, t2);
-      atomic_inc(&t->ci->nr_force);
 
       /* Now, build all the dependencies for the hydro */
       engine_make_hydro_loops_dependencies(sched, t, t2, t->ci);
@@ -1684,13 +1661,9 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
       /* Add the link between the new loop and both cells */
       engine_addlink(e, &t->ci->gradient, t2);
-      atomic_inc(&t->ci->nr_gradient);
       engine_addlink(e, &t->cj->gradient, t2);
-      atomic_inc(&t->cj->nr_gradient);
       engine_addlink(e, &t->ci->force, t3);
-      atomic_inc(&t->ci->nr_force);
       engine_addlink(e, &t->cj->force, t3);
-      atomic_inc(&t->cj->nr_force);
 
       /* Now, build all the dependencies for the hydro for the cells */
       /* that are local and are not descendant of the same super-cells */
@@ -1709,9 +1682,7 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
       /* Add the link between the new loop and both cells */
       engine_addlink(e, &t->ci->force, t2);
-      atomic_inc(&t->ci->nr_force);
       engine_addlink(e, &t->cj->force, t2);
-      atomic_inc(&t->cj->nr_force);
 
       /* Now, build all the dependencies for the hydro for the cells */
       /* that are local and are not descendant of the same super-cells */
@@ -1742,9 +1713,7 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
       /* Add the link between the new loop and the cell */
       engine_addlink(e, &t->ci->gradient, t2);
-      atomic_inc(&t->ci->nr_gradient);
       engine_addlink(e, &t->ci->force, t3);
-      atomic_inc(&t->ci->nr_force);
 
       /* Now, build all the dependencies for the hydro for the cells */
       /* that are local and are not descendant of the same super-cells */
@@ -1760,7 +1729,6 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
       /* Add the link between the new loop and the cell */
       engine_addlink(e, &t->ci->force, t2);
-      atomic_inc(&t->ci->nr_force);
 
       /* Now, build all the dependencies for the hydro for the cells */
       /* that are local and are not descendant of the same super-cells */
@@ -1786,13 +1754,9 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
       /* Add the link between the new loop and both cells */
       engine_addlink(e, &t->ci->gradient, t2);
-      atomic_inc(&t->ci->nr_gradient);
       engine_addlink(e, &t->cj->gradient, t2);
-      atomic_inc(&t->cj->nr_gradient);
       engine_addlink(e, &t->ci->force, t3);
-      atomic_inc(&t->ci->nr_force);
       engine_addlink(e, &t->cj->force, t3);
-      atomic_inc(&t->cj->nr_force);
 
       /* Now, build all the dependencies for the hydro for the cells */
       /* that are local and are not descendant of the same super-cells */
@@ -1811,9 +1775,7 @@ void engine_make_extra_hydroloop_tasks(struct engine *e) {
 
       /* Add the link between the new loop and both cells */
       engine_addlink(e, &t->ci->force, t2);
-      atomic_inc(&t->ci->nr_force);
       engine_addlink(e, &t->cj->force, t2);
-      atomic_inc(&t->cj->nr_force);
 
       /* Now, build all the dependencies for the hydro for the cells */
       /* that are local and are not descendant of the same super-cells */
@@ -1993,52 +1955,6 @@ void engine_maketasks(struct engine *e) {
             clocks_from_ticks(getticks() - tic), clocks_getunit());
 }
 
-/**
- * @brief Mark tasks to be un-skipped and set the sort flags accordingly.
- *        Threadpool mapper function for fixdt version.
- *
- * @param map_data pointer to the tasks
- * @param num_elements number of tasks
- * @param extra_data pointer to int that will define if a rebuild is needed.
- */
-void engine_marktasks_fixdt_mapper(void *map_data, int num_elements,
-                                   void *extra_data) {
-  /* Unpack the arguments. */
-  struct task *tasks = (struct task *)map_data;
-  size_t *rebuild_space = &((size_t *)extra_data)[0];
-  struct scheduler *s = (struct scheduler *)(((size_t *)extra_data)[1]);
-
-  for (int ind = 0; ind < num_elements; ind++) {
-    struct task *t = &tasks[ind];
-
-    /* All tasks are unskipped (we skip by default). */
-    scheduler_activate(s, t);
-
-    /* Pair? */
-    if (t->type == task_type_pair || t->type == task_type_sub_pair) {
-
-      /* Local pointers. */
-      const struct cell *ci = t->ci;
-      const struct cell *cj = t->cj;
-
-      /* Too much particle movement? */
-      if (t->tight &&
-          (max(ci->h_max, cj->h_max) + ci->dx_max + cj->dx_max > cj->dmin ||
-           ci->dx_max > space_maxreldx * ci->h_max ||
-           cj->dx_max > space_maxreldx * cj->h_max))
-        *rebuild_space = 1;
-
-    }
-
-    /* Sort? */
-    else if (t->type == task_type_sort) {
-
-      /* If all the sorts have been done, make this task implicit. */
-      if (!(t->flags & (t->flags ^ t->ci->sorted))) t->implicit = 1;
-    }
-  }
-}
-
 /**
  * @brief Mark tasks to be un-skipped and set the sort flags accordingly.
  *        Threadpool mapper function.
@@ -2194,24 +2110,11 @@ int engine_marktasks(struct engine *e) {
   const ticks tic = getticks();
   int rebuild_space = 0;
 
-  /* Much less to do here if we're on a fixed time-step. */
-  if (e->policy & engine_policy_fixdt) {
-
-    /* Run through the tasks and mark as skip or not. */
-    size_t extra_data[2] = {rebuild_space, (size_t)&e->sched};
-    threadpool_map(&e->threadpool, engine_marktasks_fixdt_mapper, s->tasks,
-                   s->nr_tasks, sizeof(struct task), 1000, extra_data);
-    return rebuild_space;
-
-    /* Multiple-timestep case */
-  } else {
-
-    /* Run through the tasks and mark as skip or not. */
-    size_t extra_data[3] = {e->ti_current, rebuild_space, (size_t)&e->sched};
-    threadpool_map(&e->threadpool, engine_marktasks_mapper, s->tasks,
-                   s->nr_tasks, sizeof(struct task), 10000, extra_data);
-    rebuild_space = extra_data[1];
-  }
+  /* Run through the tasks and mark as skip or not. */
+  size_t extra_data[3] = {e->ti_current, rebuild_space, (size_t)&e->sched};
+  threadpool_map(&e->threadpool, engine_marktasks_mapper, s->tasks, s->nr_tasks,
+                 sizeof(struct task), 10000, extra_data);
+  rebuild_space = extra_data[1];
 
   if (e->verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
@@ -2275,9 +2178,10 @@ void engine_rebuild(struct engine *e) {
   e->forcerebuild = 0;
 
   /* Re-build the space. */
-  space_rebuild(e->s, 0.0, e->verbose);
+  space_rebuild(e->s, e->verbose);
 
-  if (e->ti_current == 0) space_sanitize(e->s);
+  /* Initial cleaning up session ? */
+  if (e->s->sanitized == 0) space_sanitize(e->s);
 
 /* If in parallel, exchange the cell structure. */
 #ifdef WITH_MPI
@@ -2335,6 +2239,11 @@ void engine_prepare(struct engine *e, int nodrift) {
       e->drift_all = (e->policy & engine_policy_drift_all);
     }
 
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Check that all cells have been drifted to the current time */
+    space_check_drift_point(e->s, e->ti_current);
+#endif
+
     engine_rebuild(e);
   }
 
@@ -2347,7 +2256,7 @@ void engine_prepare(struct engine *e, int nodrift) {
   TIMER_TOC(timer_prepare);
 
   if (e->verbose)
-    message("took %.3f %s (including marktask, rebuild and reweight).",
+    message("took %.3f %s (including drift all, rebuild and reweight).",
             clocks_from_ticks(getticks() - tic), clocks_getunit());
 }
 
@@ -2535,16 +2444,34 @@ void engine_print_stats(struct engine *e) {
             clocks_getunit());
 }
 
+/**
+ * @brief Sets all the force and kick tasks to be skipped.
+ *
+ * @param e The #engine to act on.
+ */
+void engine_skip_force_and_kick(struct engine *e) {
+
+  struct task *tasks = e->sched.tasks;
+  const int nr_tasks = e->sched.nr_tasks;
+
+  for (int i = 0; i < nr_tasks; ++i) {
+
+    struct task *t = &tasks[i];
+
+    /* Skip everything that updates the particles */
+    if (t->subtype == task_subtype_force || t->type == task_type_kick ||
+        t->type == task_type_cooling || t->type == task_type_sourceterms)
+      t->skip = 1;
+  }
+}
+
 /**
  * @brief Launch the runners.
  *
  * @param e The #engine.
  * @param nr_runners The number of #runner to let loose.
- * @param mask The task mask to launch.
- * @param submask The sub-task mask to launch.
  */
-void engine_launch(struct engine *e, int nr_runners, unsigned int mask,
-                   unsigned int submask) {
+void engine_launch(struct engine *e, int nr_runners) {
 
   const ticks tic = getticks();
 
@@ -2559,7 +2486,7 @@ void engine_launch(struct engine *e, int nr_runners, unsigned int mask,
 
   /* Load the tasks. */
   pthread_mutex_unlock(&e->barrier_mutex);
-  scheduler_start(&e->sched, mask, submask);
+  scheduler_start(&e->sched);
   pthread_mutex_lock(&e->barrier_mutex);
 
   /* Remove the safeguard. */
@@ -2599,61 +2526,12 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) {
 
   engine_marktasks(e);
 
-  /* Build the masks corresponding to the policy */
-  unsigned int mask = 0;
-  unsigned int submask = 0;
-
-  /* We always have sort tasks */
-  mask |= 1 << task_type_sort;
-  mask |= 1 << task_type_init;
-
-  /* Add the tasks corresponding to hydro operations to the masks */
-  if (e->policy & engine_policy_hydro) {
-
-    mask |= 1 << task_type_self;
-    mask |= 1 << task_type_pair;
-    mask |= 1 << task_type_sub_self;
-    mask |= 1 << task_type_sub_pair;
-    mask |= 1 << task_type_ghost;
-
-    submask |= 1 << task_subtype_density;
-  }
-
-  /* Add the tasks corresponding to self-gravity to the masks */
-  if (e->policy & engine_policy_self_gravity) {
-
-    mask |= 1 << task_type_grav_up;
-    mask |= 1 << task_type_grav_mm;
-    mask |= 1 << task_type_grav_gather_m;
-    mask |= 1 << task_type_grav_fft;
-    mask |= 1 << task_type_self;
-    mask |= 1 << task_type_pair;
-    mask |= 1 << task_type_sub_self;
-    mask |= 1 << task_type_sub_pair;
-
-    submask |= 1 << task_subtype_grav;
-  }
-
-  /* Add the tasks corresponding to external gravity to the masks */
-  if (e->policy & engine_policy_external_gravity) {
-
-    mask |= 1 << task_type_self;
-    mask |= 1 << task_type_sub_self;
-
-    submask |= 1 << task_subtype_external_grav;
-  }
-
-  /* Add MPI tasks if need be */
-  if (e->policy & engine_policy_mpi) {
-
-    mask |= 1 << task_type_send;
-    mask |= 1 << task_type_recv;
-    submask |= 1 << task_subtype_tend;
-  }
+  /* No time integration. We just want the density and ghosts */
+  engine_skip_force_and_kick(e);
 
   /* Now, launch the calculation */
   TIMER_TIC;
-  engine_launch(e, e->nr_threads, mask, submask);
+  engine_launch(e, e->nr_threads);
   TIMER_TOC(timer_runners);
 
   /* Apply some conversions (e.g. internal energy -> entropy) */
@@ -2663,14 +2541,18 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs) {
     space_map_cells_pre(s, 0, cell_convert_hydro, NULL);
 
     /* Correct what we did (e.g. in PE-SPH, need to recompute rho_bar) */
-    if (hydro_need_extra_init_loop)
-      engine_launch(e, e->nr_threads, mask, submask);
+    if (hydro_need_extra_init_loop) {
+      engine_marktasks(e);
+      engine_skip_force_and_kick(e);
+      engine_launch(e, e->nr_threads);
+    }
   }
 
   clocks_gettime(&time2);
 
   /* Ready to go */
   e->step = -1;
+  e->forcerebuild = 1;
   e->wallclock_time = (float)clocks_diff(&time1, &time2);
 
   if (e->verbose) message("took %.3f %s.", e->wallclock_time, clocks_getunit());
@@ -2741,7 +2623,7 @@ void engine_step(struct engine *e) {
 
   /* Drift only the necessary particles, that means all particles
    * if we are about to repartition. */
-  int repart = (e->forcerepart != REPART_NONE);
+  const int repart = (e->forcerepart != REPART_NONE);
   e->drift_all = repart || e->drift_all;
   engine_drift(e);
 
@@ -2754,85 +2636,11 @@ void engine_step(struct engine *e) {
   /* Restore the default drifting policy */
   e->drift_all = (e->policy & engine_policy_drift_all);
 
-  /* Build the masks corresponding to the policy */
-  unsigned int mask = 0, submask = 0;
-
-  /* We always have sort tasks and init tasks */
-  mask |= 1 << task_type_sort;
-  mask |= 1 << task_type_init;
-
-  /* Add the correct kick task */
-  if (e->policy & engine_policy_fixdt) {
-    mask |= 1 << task_type_kick_fixdt;
-  } else {
-    mask |= 1 << task_type_kick;
-  }
-
-  /* Add the tasks corresponding to hydro operations to the masks */
-  if (e->policy & engine_policy_hydro) {
-
-    mask |= 1 << task_type_self;
-    mask |= 1 << task_type_pair;
-    mask |= 1 << task_type_sub_self;
-    mask |= 1 << task_type_sub_pair;
-    mask |= 1 << task_type_ghost;
-
-    submask |= 1 << task_subtype_density;
-    submask |= 1 << task_subtype_force;
-
-#ifdef EXTRA_HYDRO_LOOP
-    mask |= 1 << task_type_extra_ghost;
-    submask |= 1 << task_subtype_gradient;
-#endif
-  }
-
-  /* Add the tasks corresponding to self-gravity to the masks */
-  if (e->policy & engine_policy_self_gravity) {
-
-    mask |= 1 << task_type_grav_up;
-    mask |= 1 << task_type_grav_mm;
-    mask |= 1 << task_type_grav_gather_m;
-    mask |= 1 << task_type_grav_fft;
-    mask |= 1 << task_type_self;
-    mask |= 1 << task_type_pair;
-    mask |= 1 << task_type_sub_self;
-    mask |= 1 << task_type_sub_pair;
-
-    submask |= 1 << task_subtype_grav;
-  }
-
-  /* Add the tasks corresponding to external gravity to the masks */
-  if (e->policy & engine_policy_external_gravity) {
-
-    mask |= 1 << task_type_self;
-    mask |= 1 << task_type_sub_self;
-
-    submask |= 1 << task_subtype_external_grav;
-  }
-
-  /* Add the tasks corresponding to cooling to the masks */
-  if (e->policy & engine_policy_cooling) {
-    mask |= 1 << task_type_cooling;
-  }
-
-  /* Add the tasks corresponding to sourceterms to the masks */
-  if (e->policy & engine_policy_sourceterms) {
-    mask |= 1 << task_type_sourceterms;
-  }
-
-  /* Add MPI tasks if need be */
-  if (e->policy & engine_policy_mpi) {
-
-    mask |= 1 << task_type_send;
-    mask |= 1 << task_type_recv;
-    submask |= 1 << task_subtype_tend;
-  }
-
   if (e->verbose) engine_print_task_counts(e);
 
   /* Send off the runners. */
   TIMER_TIC;
-  engine_launch(e, e->nr_threads, mask, submask);
+  engine_launch(e, e->nr_threads);
   TIMER_TOC(timer_runners);
 
   /* Save some statistics */
@@ -3386,9 +3194,10 @@ void engine_init(struct engine *e, struct space *s,
     e->file_stats = fopen(energyfileName, "w");
     fprintf(e->file_stats,
             "#%14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s %14s "
-            "%14s\n",
-            "Time", "Mass", "E_tot", "E_kin", "E_int", "E_pot", "E_radcool",
-            "Entropy", "p_x", "p_y", "p_z", "ang_x", "ang_y", "ang_z");
+            "%14s %14s %14s\n",
+            "Time", "Mass", "E_tot", "E_kin", "E_int", "E_pot", "E_pot_self",
+            "E_pot_ext", "E_radcool", "Entropy", "p_x", "p_y", "p_z", "ang_x",
+            "ang_y", "ang_z");
     fflush(e->file_stats);
 
     char timestepsfileName[200] = "";
@@ -3443,32 +3252,19 @@ void engine_init(struct engine *e, struct space *s,
   e->timeBase_inv = 1.0 / e->timeBase;
   e->ti_current = 0;
 
-  /* Fixed time-step case */
-  if (e->policy & engine_policy_fixdt) {
-    e->dt_min = e->dt_max;
-
-    /* Find timestep on the timeline */
-    int dti_timeline = max_nr_timesteps;
-    while (e->dt_min < dti_timeline * e->timeBase) dti_timeline /= 2;
-
-    e->dt_min = e->dt_max = dti_timeline * e->timeBase;
-
-    if (e->nodeID == 0) message("Timestep set to %e", e->dt_max);
-  } else {
-
-    if (e->nodeID == 0) {
-      message("Absolute minimal timestep size: %e", e->timeBase);
+  /* Info about time-steps */
+  if (e->nodeID == 0) {
+    message("Absolute minimal timestep size: %e", e->timeBase);
 
-      float dt_min = e->timeEnd - e->timeBegin;
-      while (dt_min > e->dt_min) dt_min /= 2.f;
+    float dt_min = e->timeEnd - e->timeBegin;
+    while (dt_min > e->dt_min) dt_min /= 2.f;
 
-      message("Minimal timestep size (on time-line): %e", dt_min);
+    message("Minimal timestep size (on time-line): %e", dt_min);
 
-      float dt_max = e->timeEnd - e->timeBegin;
-      while (dt_max > e->dt_max) dt_max /= 2.f;
+    float dt_max = e->timeEnd - e->timeBegin;
+    while (dt_max > e->dt_max) dt_max /= 2.f;
 
-      message("Maximal timestep size (on time-line): %e", dt_max);
-    }
+    message("Maximal timestep size (on time-line): %e", dt_max);
   }
 
   if (e->dt_min < e->timeBase && e->nodeID == 0)
diff --git a/src/engine.h b/src/engine.h
index ec9e16553c3172f22e9bfe60971163488947a47e..861e321627032eb1f773992a9c123249c013daa0 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -56,17 +56,16 @@ enum engine_policy {
   engine_policy_steal = (1 << 1),
   engine_policy_keep = (1 << 2),
   engine_policy_block = (1 << 3),
-  engine_policy_fixdt = (1 << 4),
-  engine_policy_cputight = (1 << 5),
-  engine_policy_mpi = (1 << 6),
-  engine_policy_setaffinity = (1 << 7),
-  engine_policy_hydro = (1 << 8),
-  engine_policy_self_gravity = (1 << 9),
-  engine_policy_external_gravity = (1 << 10),
-  engine_policy_cosmology = (1 << 11),
-  engine_policy_drift_all = (1 << 12),
-  engine_policy_cooling = (1 << 13),
-  engine_policy_sourceterms = (1 << 14)
+  engine_policy_cputight = (1 << 4),
+  engine_policy_mpi = (1 << 5),
+  engine_policy_setaffinity = (1 << 6),
+  engine_policy_hydro = (1 << 7),
+  engine_policy_self_gravity = (1 << 8),
+  engine_policy_external_gravity = (1 << 9),
+  engine_policy_cosmology = (1 << 10),
+  engine_policy_drift_all = (1 << 11),
+  engine_policy_cooling = (1 << 12),
+  engine_policy_sourceterms = (1 << 13)
 };
 
 extern const char *engine_policy_names[];
@@ -230,8 +229,7 @@ void engine_init(struct engine *e, struct space *s,
                  const struct external_potential *potential,
                  const struct cooling_function_data *cooling,
                  struct sourceterms *sourceterms);
-void engine_launch(struct engine *e, int nr_runners, unsigned int mask,
-                   unsigned int submask);
+void engine_launch(struct engine *e, int nr_runners);
 void engine_prepare(struct engine *e, int nodrift);
 void engine_print(struct engine *e);
 void engine_init_particles(struct engine *e, int flag_entropy_ICs);
diff --git a/src/error.h b/src/error.h
index b131cb124feedc48e83427122f3a0edcb2ec81d4..92a9ce71a3d939cb2e18267848b0e2c66dd4741a 100644
--- a/src/error.h
+++ b/src/error.h
@@ -38,19 +38,19 @@
 #ifdef WITH_MPI
 extern int engine_rank;
 #define error(s, ...)                                                      \
-  {                                                                        \
+  ({                                                                       \
     fprintf(stderr, "[%04i] %s %s:%s():%i: " s "\n", engine_rank,          \
             clocks_get_timesincestart(), __FILE__, __FUNCTION__, __LINE__, \
             ##__VA_ARGS__);                                                \
     MPI_Abort(MPI_COMM_WORLD, -1);                                         \
-  }
+  })
 #else
 #define error(s, ...)                                                      \
-  {                                                                        \
+  ({                                                                       \
     fprintf(stderr, "%s %s:%s():%i: " s "\n", clocks_get_timesincestart(), \
             __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__);              \
     abort();                                                               \
-  }
+  })
 #endif
 
 #ifdef WITH_MPI
@@ -60,7 +60,7 @@ extern int engine_rank;
  *
  */
 #define mpi_error(res, s, ...)                                             \
-  {                                                                        \
+  ({                                                                       \
     fprintf(stderr, "[%04i] %s %s:%s():%i: " s "\n", engine_rank,          \
             clocks_get_timesincestart(), __FILE__, __FUNCTION__, __LINE__, \
             ##__VA_ARGS__);                                                \
@@ -69,10 +69,10 @@ extern int engine_rank;
     MPI_Error_string(res, buf, &len);                                      \
     fprintf(stderr, "%s\n\n", buf);                                        \
     MPI_Abort(MPI_COMM_WORLD, -1);                                         \
-  }
+  })
 
 #define mpi_error_string(res, s, ...)                                      \
-  {                                                                        \
+  ({                                                                       \
     fprintf(stderr, "[%04i] %s %s:%s():%i: " s "\n", engine_rank,          \
             clocks_get_timesincestart(), __FILE__, __FUNCTION__, __LINE__, \
             ##__VA_ARGS__);                                                \
@@ -80,7 +80,7 @@ extern int engine_rank;
     char buf[len];                                                         \
     MPI_Error_string(res, buf, &len);                                      \
     fprintf(stderr, "%s\n\n", buf);                                        \
-  }
+  })
 #endif
 
 /**
@@ -89,13 +89,17 @@ extern int engine_rank;
  */
 #ifdef WITH_MPI
 extern int engine_rank;
-#define message(s, ...)                                                     \
-  printf("[%04i] %s %s: " s "\n", engine_rank, clocks_get_timesincestart(), \
-         __FUNCTION__, ##__VA_ARGS__)
+#define message(s, ...)                                                       \
+  ({                                                                          \
+    printf("[%04i] %s %s: " s "\n", engine_rank, clocks_get_timesincestart(), \
+           __FUNCTION__, ##__VA_ARGS__);                                      \
+  })
 #else
-#define message(s, ...)                                               \
-  printf("%s %s: " s "\n", clocks_get_timesincestart(), __FUNCTION__, \
-         ##__VA_ARGS__)
+#define message(s, ...)                                                 \
+  ({                                                                    \
+    printf("%s %s: " s "\n", clocks_get_timesincestart(), __FUNCTION__, \
+           ##__VA_ARGS__);                                              \
+  })
 #endif
 
 /**
@@ -105,7 +109,7 @@ extern int engine_rank;
 #ifdef WITH_MPI
 extern int engine_rank;
 #define assert(expr)                                                          \
-  {                                                                           \
+  ({                                                                          \
     if (!(expr)) {                                                            \
       fprintf(stderr, "[%04i] %s %s:%s():%i: FAILED ASSERTION: " #expr " \n", \
               engine_rank, clocks_get_timesincestart(), __FILE__,             \
@@ -113,17 +117,17 @@ extern int engine_rank;
       fflush(stderr);                                                         \
       MPI_Abort(MPI_COMM_WORLD, -1);                                          \
     }                                                                         \
-  }
+  })
 #else
 #define assert(expr)                                                          \
-  {                                                                           \
+  ({                                                                          \
     if (!(expr)) {                                                            \
       fprintf(stderr, "%s %s:%s():%i: FAILED ASSERTION: " #expr " \n",        \
               clocks_get_timesincestart(), __FILE__, __FUNCTION__, __LINE__); \
       fflush(stderr);                                                         \
       abort();                                                                \
     }                                                                         \
-  }
+  })
 #endif
 
 #endif /* SWIFT_ERROR_H */
diff --git a/src/hydro/Default/hydro.h b/src/hydro/Default/hydro.h
index ccdd0cee32b9386eff54da655b75285b8e08a598..3fd357a2d8778f5ca8b014935d538350eccb99c6 100644
--- a/src/hydro/Default/hydro.h
+++ b/src/hydro/Default/hydro.h
@@ -199,10 +199,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part(
  * and add the self-contribution term.
  *
  * @param p The particle to act upon
- * @param time The current time
  */
 __attribute__((always_inline)) INLINE static void hydro_end_density(
-    struct part *restrict p, float time) {
+    struct part *restrict p) {
 
   /* Some smoothing length multiples. */
   const float h = p->h;
diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h
index c999e20d401570ac6518291df8cf315569fe78bd..157893bc9e27806d2b97ac5f5a81d0f6fbb1c589 100644
--- a/src/hydro/Gadget2/hydro.h
+++ b/src/hydro/Gadget2/hydro.h
@@ -246,10 +246,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part(
  * and add the self-contribution term.
  *
  * @param p The particle to act upon
- * @param ti_current The current time (on the integer timeline)
  */
 __attribute__((always_inline)) INLINE static void hydro_end_density(
-    struct part *restrict p, int ti_current) {
+    struct part *restrict p) {
 
   /* Some smoothing length multiples. */
   const float h = p->h;
diff --git a/src/hydro/Gadget2/hydro_iact.h b/src/hydro/Gadget2/hydro_iact.h
index fca1bcff91a71fb2a26adc117382630a576f9090..08fb2b37db566e191bd74d82488b5d68e764573b 100644
--- a/src/hydro/Gadget2/hydro_iact.h
+++ b/src/hydro/Gadget2/hydro_iact.h
@@ -226,7 +226,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_density(
 
   error(
       "The Gadget2 serial version of runner_iact_density was called when the "
-      "vectorised version should have been used.")
+      "vectorised version should have been used.");
 
 #endif
 }
@@ -377,7 +377,7 @@ runner_iact_nonsym_vec_density(float *R2, float *Dx, float *Hi, float *Hj,
 
   error(
       "The Gadget2 serial version of runner_iact_nonsym_density was called "
-      "when the vectorised version should have been used.")
+      "when the vectorised version should have been used.");
 
 #endif
 }
@@ -656,7 +656,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_vec_force(
 
   error(
       "The Gadget2 serial version of runner_iact_nonsym_force was called when "
-      "the vectorised version should have been used.")
+      "the vectorised version should have been used.");
 
 #endif
 }
@@ -917,7 +917,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_vec_force(
 
   error(
       "The Gadget2 serial version of runner_iact_nonsym_force was called when "
-      "the vectorised version should have been used.")
+      "the vectorised version should have been used.");
 
 #endif
 }
diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h
index bd970795bdf070a7bd7915cc4f493218dbf319d1..1c64291ee64dd770b1f1a76371f67a34230365c7 100644
--- a/src/hydro/Gizmo/hydro.h
+++ b/src/hydro/Gizmo/hydro.h
@@ -109,10 +109,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part(
  * passive particles.
  *
  * @param p The particle to act upon.
- * @param The current physical time.
  */
 __attribute__((always_inline)) INLINE static void hydro_end_density(
-    struct part* restrict p, float time) {
+    struct part* restrict p) {
 
   /* Some smoothing length multiples. */
   const float h = p->h;
diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h
index 3015f26c6bad7f006e8cda16695c750ff40d74df..3b3454f1bb348b178ac57899da4f7611802a69cd 100644
--- a/src/hydro/Minimal/hydro.h
+++ b/src/hydro/Minimal/hydro.h
@@ -256,10 +256,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part(
  * added to them here.
  *
  * @param p The particle to act upon
- * @param time The current time
  */
 __attribute__((always_inline)) INLINE static void hydro_end_density(
-    struct part *restrict p, float time) {
+    struct part *restrict p) {
 
   /* Some smoothing length multiples. */
   const float h = p->h;
diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h
index 0c69728a9ce6317a8d7ddab945e7aab6e94ee247..8c063596efd3be97ebb4da6b6879ac06122bd357 100644
--- a/src/hydro/PressureEntropy/hydro.h
+++ b/src/hydro/PressureEntropy/hydro.h
@@ -254,10 +254,9 @@ __attribute__((always_inline)) INLINE static void hydro_init_part(
  * and add the self-contribution term.
  *
  * @param p The particle to act upon
- * @param ti_current The current time (on the integer timeline)
  */
 __attribute__((always_inline)) INLINE static void hydro_end_density(
-    struct part *restrict p, int ti_current) {
+    struct part *restrict p) {
 
   /* Some smoothing length multiples. */
   const float h = p->h;
diff --git a/src/partition.c b/src/partition.c
index 8d17bedf0aaeadc64044b12ffe1bb8887b02d83e..3f5386154497d6901a5330b828007f86d87033a4 100644
--- a/src/partition.c
+++ b/src/partition.c
@@ -370,7 +370,7 @@ static void pick_metis(struct space *s, int nregions, int *vertexw, int *edgew,
 
   /* Dump graph in METIS format */
   /* dumpMETISGraph("metis_graph", idx_ncells, one, xadj, adjncy,
-   *                weights_v, weights_e, NULL);
+   *                weights_v, NULL, weights_e);
    */
 
   if (METIS_PartGraphKway(&idx_ncells, &one, xadj, adjncy, weights_v, weights_e,
@@ -420,7 +420,7 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID,
    * assume the same graph structure as used in the part_ calls). */
   int nr_cells = s->nr_cells;
   struct cell *cells = s->cells_top;
-  float wscale = 1e-3, vscale = 1e-3, wscale_buff = 0.0;
+  float wscale = 1.f, wscale_buff = 0.0;
   int wtot = 0;
   int wmax = 1e9 / nr_nodes;
   int wmin;
@@ -459,15 +459,8 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID,
         t->type != task_type_init)
       continue;
 
-    /* Get the task weight. This can be slightly negative on multiple board
-     * computers when the runners are not pinned to cores, don't stress just
-     * make a report and ignore these tasks. */
-    int w = (t->toc - t->tic) * wscale;
-    if (w < 0) {
-      message("Task toc before tic: -%.3f %s, (try using processor affinity).",
-              clocks_from_ticks(t->tic - t->toc), clocks_getunit());
-      w = 0;
-    }
+    /* Get the task weight. */
+    int w = t->cost * wscale;
 
     /* Do we need to re-scale? */
     wtot += w;
@@ -616,7 +609,7 @@ static void repart_edge_metis(int partweights, int bothweights, int nodeID,
       if (weights_e[k] == 0) weights_e[k] = 1;
     if (bothweights)
       for (int k = 0; k < nr_cells; k++)
-        if ((weights_v[k] *= vscale) == 0) weights_v[k] = 1;
+        if (weights_v[k] == 0) weights_v[k] = 1;
 
     /* And partition, use both weights or not as requested. */
     if (bothweights)
diff --git a/src/potential.c b/src/potential.c
index 5433a05e3e7886ad88021d3916cae26adfe8b954..6ee80900952c032d019ad5d20ec086d05d34ef29 100644
--- a/src/potential.c
+++ b/src/potential.c
@@ -31,14 +31,15 @@
  * @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
  */
 void potential_init(const struct swift_params* parameter_file,
                     const struct phys_const* phys_const,
-                    const struct UnitSystem* us,
+                    const struct UnitSystem* us, const struct space* s,
                     struct external_potential* potential) {
 
-  potential_init_backend(parameter_file, phys_const, us, potential);
+  potential_init_backend(parameter_file, phys_const, us, s, potential);
 }
 
 /**
diff --git a/src/potential.h b/src/potential.h
index 77bd41794a3a8cd244405493898d63b3f80ff3a6..f7fdd0072c502641d36f229337f4616b9033cbda 100644
--- a/src/potential.h
+++ b/src/potential.h
@@ -37,6 +37,8 @@
 #include "./potential/point_mass/potential.h"
 #elif defined(EXTERNAL_POTENTIAL_ISOTHERMALPOTENTIAL)
 #include "./potential/isothermal/potential.h"
+#elif defined(EXTERNAL_POTENTIAL_SOFTENED_ISOTHERMAL_POTENTIAL)
+#include "./potential/softened_isothermal/potential.h"
 #elif defined(EXTERNAL_POTENTIAL_DISC_PATCH)
 #include "./potential/disc_patch/potential.h"
 #else
@@ -46,7 +48,7 @@
 /* Now, some generic functions, defined in the source file */
 void potential_init(const struct swift_params* parameter_file,
                     const struct phys_const* phys_const,
-                    const struct UnitSystem* us,
+                    const struct UnitSystem* us, const struct space* s,
                     struct external_potential* potential);
 
 void potential_print(const struct external_potential* potential);
diff --git a/src/potential/disc_patch/potential.h b/src/potential/disc_patch/potential.h
index 21d168818e164ad3b3e18076ba824285e40956aa..fe1df8796f046edded0c5b1779859a1c6fffffc0 100644
--- a/src/potential/disc_patch/potential.h
+++ b/src/potential/disc_patch/potential.h
@@ -33,6 +33,7 @@
 #include "parser.h"
 #include "part.h"
 #include "physical_constants.h"
+#include "space.h"
 #include "units.h"
 
 /**
@@ -149,6 +150,25 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
   if (dz < 0) g->a_grav[2] += z_accel;
 }
 
+/**
+ * @brief Computes the gravitational potential energy of a particle in the
+ * disc patch potential.
+ * Time evolving system so not sure how to do this
+ * Placeholder for now- just returns 0
+ *
+ * @param potential The #external_potential used in the run.
+ * @param phys_const Physical constants in internal units.
+ * @param p Pointer to the particle data.
+ */
+
+__attribute__((always_inline)) INLINE static float
+external_gravity_get_potential_energy(
+    const struct external_potential* potential,
+    const struct phys_const* const phys_const, const struct gpart* p) {
+
+  return 0.f;
+}
+
 /**
  * @brief Initialises the external potential properties in the internal system
  * of units.
@@ -161,7 +181,7 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
 static INLINE void potential_init_backend(
     const struct swift_params* parameter_file,
     const struct phys_const* phys_const, const struct UnitSystem* us,
-    struct external_potential* potential) {
+    const struct space* s, struct external_potential* potential) {
 
   potential->surface_density = parser_get_param_double(
       parameter_file, "DiscPatchPotential:surface_density");
diff --git a/src/potential/isothermal/potential.h b/src/potential/isothermal/potential.h
index a993c09a978ca3692ec3359f7633df14760f263d..a582dce17daba0ac9705ef4ae1fc6be9db19315a 100644
--- a/src/potential/isothermal/potential.h
+++ b/src/potential/isothermal/potential.h
@@ -31,6 +31,7 @@
 #include "parser.h"
 #include "part.h"
 #include "physical_constants.h"
+#include "space.h"
 #include "units.h"
 
 /**
@@ -105,6 +106,7 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
   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 rinv2 = 1.f / (dx * dx + dy * dy + dz * dz);
 
   const double term = -potential->vrot2_over_G * rinv2;
@@ -114,6 +116,27 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
   g->a_grav[2] += term * dz;
 }
 
+/**
+ * @brief Computes the gravitational potential energy of a particle in an
+ * isothermal potential.
+ *
+ * @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(
+    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;
+
+  return 0.5f * potential->vrot * potential->vrot *
+         logf(dx * dx + dy * dy * dz * dz);
+}
+
 /**
  * @brief Initialises the external potential properties in the internal system
  * of units.
@@ -126,13 +149,16 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
 static INLINE void potential_init_backend(
     const struct swift_params* parameter_file,
     const struct phys_const* phys_const, const struct UnitSystem* us,
-    struct external_potential* potential) {
+    const struct space* s, struct external_potential* potential) {
 
   potential->x =
+      s->dim[0] / 2. +
       parser_get_param_double(parameter_file, "IsothermalPotential:position_x");
   potential->y =
+      s->dim[1] / 2. +
       parser_get_param_double(parameter_file, "IsothermalPotential:position_y");
   potential->z =
+      s->dim[2] / 2. +
       parser_get_param_double(parameter_file, "IsothermalPotential:position_z");
   potential->vrot =
       parser_get_param_double(parameter_file, "IsothermalPotential:vrot");
diff --git a/src/potential/none/potential.h b/src/potential/none/potential.h
index 8b1c3e841521f3fb42fbdf5c8922cead2ea7cbcb..8248b64678e28e06b9df4aab375cde0b5ed5281b 100644
--- a/src/potential/none/potential.h
+++ b/src/potential/none/potential.h
@@ -30,6 +30,7 @@
 #include "parser.h"
 #include "part.h"
 #include "physical_constants.h"
+#include "space.h"
 #include "units.h"
 
 /**
@@ -67,6 +68,21 @@ __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) {}
 
+/**
+ * @brief Computes the gravitational potential energy due to nothing.
+ *
+ * @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(
+    const struct external_potential* potential,
+    const struct phys_const* const phys_const, const struct gpart* g) {
+
+  return 0.f;
+}
+
 /**
  * @brief Initialises the external potential properties in the internal system
  * of units.
@@ -81,7 +97,7 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
 static INLINE void potential_init_backend(
     const struct swift_params* parameter_file,
     const struct phys_const* phys_const, const struct UnitSystem* us,
-    struct external_potential* potential) {}
+    const struct space* s, struct external_potential* potential) {}
 
 /**
  * @brief Prints the properties of the external potential to stdout.
diff --git a/src/potential/point_mass/potential.h b/src/potential/point_mass/potential.h
index f718af2e2c4ff91540e1834cb2072d321ce38705..5f3d1c27b85c4f1353481e6351fba47aff62d66f 100644
--- a/src/potential/point_mass/potential.h
+++ b/src/potential/point_mass/potential.h
@@ -31,6 +31,7 @@
 #include "parser.h"
 #include "part.h"
 #include "physical_constants.h"
+#include "space.h"
 #include "units.h"
 
 /**
@@ -115,6 +116,26 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
   g->a_grav[2] += -potential->mass * dz * rinv3;
 }
 
+/**
+ * @brief Computes the gravitational potential energy of a particle in a point
+ * mass potential.
+ *
+ * @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(
+    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.
@@ -122,12 +143,13 @@ __attribute__((always_inline)) INLINE static void external_gravity_acceleration(
  * @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 UnitSystem* us,
-    struct external_potential* potential) {
+    const struct space* s, struct external_potential* potential) {
 
   potential->x =
       parser_get_param_double(parameter_file, "PointMassPotential:position_x");
diff --git a/src/potential/softened_isothermal/potential.h b/src/potential/softened_isothermal/potential.h
new file mode 100644
index 0000000000000000000000000000000000000000..24e59b12a5745728fb1189fbbfbc7cc3c06fbfa6
--- /dev/null
+++ b/src/potential/softened_isothermal/potential.h
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016  Stefan Arridge (stefan.arridge@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_SOFTENED_ISOTHERMAL_H
+#define SWIFT_POTENTIAL_SOFTENED_ISOTHERMAL_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#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 - Softened Isothermal sphere case
+ */
+struct external_potential {
+
+  /*! Position of the centre of potential */
+  double x, y, z;
+
+  /*! Rotation velocity */
+  double vrot;
+
+  /*! Square of vrot, the circular velocity which defines the isothermal
+   * potential */
+  double vrot2_over_G;
+
+  /*! Square of the softening length. Acceleration tends to zero within this
+   * distance from the origin */
+  double epsilon2;
+
+  /*! Time-step condition pre-factor */
+  double timestep_mult;
+};
+
+/**
+ * @brief Computes the time-step due to the acceleration from an isothermal
+ * potential.
+ *
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @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 r2_plus_epsilon2_inv =
+      1.f / (dx * dx + dy * dy + dz * dz + potential->epsilon2);
+  const float drdv =
+      dx * (g->v_full[0]) + dy * (g->v_full[1]) + dz * (g->v_full[2]);
+  const double vrot = potential->vrot;
+
+  const float dota_x = vrot * vrot * r2_plus_epsilon2_inv *
+                       (g->v_full[0] - 2.f * drdv * dx * r2_plus_epsilon2_inv);
+  const float dota_y = vrot * vrot * r2_plus_epsilon2_inv *
+                       (g->v_full[1] - 2.f * drdv * dy * r2_plus_epsilon2_inv);
+  const float dota_z = vrot * vrot * r2_plus_epsilon2_inv *
+                       (g->v_full[2] - 2.f * drdv * dz * r2_plus_epsilon2_inv);
+  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];
+
+  return potential->timestep_mult * sqrtf(a_2 / dota_2);
+}
+
+/**
+ * @brief Computes the gravitational acceleration from an isothermal potential.
+ *
+ * Note that the accelerations are multiplied by Newton's G constant
+ * later on.
+ *
+ * a = v_rot^2 * (x,y,z) / (r^2 + epsilon^2)
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @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* potential,
+    const struct phys_const* const phys_const, struct gpart* g) {
+
+  const float dx = g->x[0] - potential->x;
+  const float dy = g->x[1] - potential->y;
+  const float dz = g->x[2] - potential->z;
+  const float r2_plus_epsilon2_inv =
+      1.f / (dx * dx + dy * dy + dz * dz + potential->epsilon2);
+
+  const double term = -potential->vrot2_over_G * r2_plus_epsilon2_inv;
+
+  g->a_grav[0] += term * dx;
+  g->a_grav[1] += term * dy;
+  g->a_grav[2] += term * dz;
+}
+
+/**
+ * @brief Computes the gravitational potential energy of a particle in an
+ * isothermal potential.
+ *
+ * @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(
+    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;
+
+  return 0.5f * potential->vrot * potential->vrot *
+         logf(dx * dx + dy * dy * dz * dz + potential->epsilon2);
+}
+/**
+ * @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 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 UnitSystem* us,
+    const struct space* s, struct external_potential* potential) {
+
+  potential->x = s->dim[0] / 2. +
+                 parser_get_param_double(
+                     parameter_file, "SoftenedIsothermalPotential:position_x");
+  potential->y = s->dim[1] / 2. +
+                 parser_get_param_double(
+                     parameter_file, "SoftenedIsothermalPotential:position_y");
+  potential->z = s->dim[2] / 2. +
+                 parser_get_param_double(
+                     parameter_file, "SoftenedIsothermalPotential:position_z");
+  potential->vrot = parser_get_param_double(parameter_file,
+                                            "SoftenedIsothermalPotential:vrot");
+  potential->timestep_mult = parser_get_param_float(
+      parameter_file, "SoftenedIsothermalPotential:timestep_mult");
+  const double epsilon = parser_get_param_float(
+      parameter_file, "SoftenedIsothermalPotential:epsilon");
+  potential->vrot2_over_G =
+      potential->vrot * potential->vrot / phys_const->const_newton_G;
+  potential->epsilon2 = epsilon * epsilon;
+}
+
+/**
+ * @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 'Isothermal' with properties are (x,y,z) = (%e, "
+      "%e, %e), vrot = %e "
+      "timestep multiplier = %e, epsilon = %e",
+      potential->x, potential->y, potential->z, potential->vrot,
+      potential->timestep_mult, sqrtf(potential->epsilon2));
+}
+
+#endif /* SWIFT_POTENTIAL_ISOTHERMAL_H */
diff --git a/src/runner.c b/src/runner.c
index f5efc99d492be837509e50bd2674ab6923404446..2d6da4e4aedc9c40d1dade243e605e9aeda86dbe 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -38,6 +38,7 @@
 #include "runner.h"
 
 /* Local headers. */
+#include "active.h"
 #include "approx_math.h"
 #include "atomic.h"
 #include "cell.h"
@@ -132,16 +133,16 @@ void runner_do_sourceterms(struct runner *r, struct cell *c, int timer) {
   if (c->split) {
     for (int k = 0; k < 8; k++)
       if (c->progeny[k] != NULL) runner_do_sourceterms(r, c->progeny[k], 0);
-    return;
-  }
+  } else {
 
-  if (count > 0) {
+    if (count > 0) {
 
-    /* do sourceterms in this cell? */
-    const int incell =
-        sourceterms_test_cell(cell_min, cell_width, sourceterms, dimen);
-    if (incell == 1) {
-      sourceterms_apply(r, sourceterms, c);
+      /* do sourceterms in this cell? */
+      const int incell =
+          sourceterms_test_cell(cell_min, cell_width, sourceterms, dimen);
+      if (incell == 1) {
+        sourceterms_apply(r, sourceterms, c);
+      }
     }
   }
 
@@ -159,37 +160,32 @@ void runner_do_grav_external(struct runner *r, struct cell *c, int timer) {
 
   struct gpart *restrict gparts = c->gparts;
   const int gcount = c->gcount;
-  const int ti_current = r->e->ti_current;
-  const struct external_potential *potential = r->e->external_potential;
-  const struct phys_const *constants = r->e->physical_constants;
+  const struct engine *e = r->e;
+  const struct external_potential *potential = e->external_potential;
+  const struct phys_const *constants = e->physical_constants;
   const double time = r->e->time;
 
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (c->ti_end_min > ti_current) return;
+  if (!cell_is_active(c, e)) return;
 
   /* Recurse? */
   if (c->split) {
     for (int k = 0; k < 8; k++)
       if (c->progeny[k] != NULL) runner_do_grav_external(r, c->progeny[k], 0);
-    return;
-  }
-
-#ifdef TASK_VERBOSE
-  OUT;
-#endif
-
-  /* Loop over the gparts in this cell. */
-  for (int i = 0; i < gcount; i++) {
+  } else {
 
-    /* Get a direct pointer on the part. */
-    struct gpart *restrict gp = &gparts[i];
+    /* Loop over the gparts in this cell. */
+    for (int i = 0; i < gcount; i++) {
 
-    /* Is this part within the time step? */
-    if (gp->ti_end <= ti_current) {
+      /* Get a direct pointer on the part. */
+      struct gpart *restrict gp = &gparts[i];
 
-      external_gravity_acceleration(time, potential, constants, gp);
+      /* Is this part within the time step? */
+      if (gpart_is_active(gp, e)) {
+        external_gravity_acceleration(time, potential, constants, gp);
+      }
     }
   }
 
@@ -221,26 +217,22 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) {
   if (c->split) {
     for (int k = 0; k < 8; k++)
       if (c->progeny[k] != NULL) runner_do_cooling(r, c->progeny[k], 0);
-    return;
-  }
-
-#ifdef TASK_VERBOSE
-  OUT;
-#endif
+  } else {
 
-  /* Loop over the parts in this cell. */
-  for (int i = 0; i < count; i++) {
+    /* Loop over the parts in this cell. */
+    for (int i = 0; i < count; i++) {
 
-    /* Get a direct pointer on the part. */
-    struct part *restrict p = &parts[i];
-    struct xpart *restrict xp = &xparts[i];
+      /* Get a direct pointer on the part. */
+      struct part *restrict p = &parts[i];
+      struct xpart *restrict xp = &xparts[i];
 
-    /* Kick has already updated ti_end, so need to check ti_begin */
-    if (p->ti_begin == ti_current) {
+      /* Kick has already updated ti_end, so need to check ti_begin */
+      if (p->ti_begin == ti_current) {
 
-      const double dt = (p->ti_end - p->ti_begin) * timeBase;
+        const double dt = (p->ti_end - p->ti_begin) * timeBase;
 
-      cooling_cool_part(constants, us, cooling_func, p, xp, dt);
+        cooling_cool_part(constants, us, cooling_func, p, xp, dt);
+      }
     }
   }
 
@@ -492,18 +484,17 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) {
   struct gpart *restrict gparts = c->gparts;
   const int count = c->count;
   const int gcount = c->gcount;
-  const int ti_current = r->e->ti_current;
+  const struct engine *e = r->e;
 
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (c->ti_end_min > ti_current) return;
+  if (!cell_is_active(c, e)) return;
 
   /* Recurse? */
   if (c->split) {
     for (int k = 0; k < 8; k++)
       if (c->progeny[k] != NULL) runner_do_init(r, c->progeny[k], 0);
-    return;
   } else {
 
     /* Loop over the parts in this cell. */
@@ -512,7 +503,7 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) {
       /* Get a direct pointer on the part. */
       struct part *restrict p = &parts[i];
 
-      if (p->ti_end <= ti_current) {
+      if (part_is_active(p, e)) {
 
         /* Get ready for a density calculation */
         hydro_init_part(p);
@@ -525,7 +516,7 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) {
       /* Get a direct pointer on the part. */
       struct gpart *restrict gp = &gparts[i];
 
-      if (gp->ti_end <= ti_current) {
+      if (gpart_is_active(gp, e)) {
 
         /* Get ready for a density calculation */
         gravity_init_gpart(gp);
@@ -542,23 +533,25 @@ void runner_do_init(struct runner *r, struct cell *c, int timer) {
  *
  * @param r The runner thread.
  * @param c The cell.
+ * @param timer Are we timing this ?
  */
-void runner_do_extra_ghost(struct runner *r, struct cell *c) {
+void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) {
 
 #ifdef EXTRA_HYDRO_LOOP
 
   struct part *restrict parts = c->parts;
   const int count = c->count;
-  const int ti_current = r->e->ti_current;
+  const struct engine *e = r->e;
+
+  TIMER_TIC;
 
   /* Anything to do here? */
-  if (c->ti_end_min > ti_current) return;
+  if (!cell_is_active(c, e)) return;
 
   /* Recurse? */
   if (c->split) {
     for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL) runner_do_extra_ghost(r, c->progeny[k]);
-    return;
+      if (c->progeny[k] != NULL) runner_do_extra_ghost(r, c->progeny[k], 0);
   } else {
 
     /* Loop over the parts in this cell. */
@@ -567,7 +560,7 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c) {
       /* Get a direct pointer on the part. */
       struct part *restrict p = &parts[i];
 
-      if (p->ti_end <= ti_current) {
+      if (part_is_active(p, e)) {
 
         /* Get ready for a force calculation */
         hydro_end_gradient(p);
@@ -575,6 +568,8 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c) {
     }
   }
 
+  if (timer) TIMER_TOC(timer_do_extra_ghost);
+
 #else
   error("SWIFT was not compiled with the extra hydro loop activated.");
 #endif
@@ -586,164 +581,166 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c) {
  *
  * @param r The runner thread.
  * @param c The cell.
+ * @param timer Are we timing this ?
  */
-void runner_do_ghost(struct runner *r, struct cell *c) {
+void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
   struct part *restrict parts = c->parts;
   struct xpart *restrict xparts = c->xparts;
   int redo, count = c->count;
-  const int ti_current = r->e->ti_current;
-  const double timeBase = r->e->timeBase;
-  const float target_wcount = r->e->hydro_properties->target_neighbours;
+  const struct engine *e = r->e;
+  const int ti_current = e->ti_current;
+  const double timeBase = e->timeBase;
+  const float target_wcount = e->hydro_properties->target_neighbours;
   const float max_wcount =
-      target_wcount + r->e->hydro_properties->delta_neighbours;
+      target_wcount + e->hydro_properties->delta_neighbours;
   const float min_wcount =
-      target_wcount - r->e->hydro_properties->delta_neighbours;
-  const int max_smoothing_iter =
-      r->e->hydro_properties->max_smoothing_iterations;
+      target_wcount - e->hydro_properties->delta_neighbours;
+  const int max_smoothing_iter = e->hydro_properties->max_smoothing_iterations;
 
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (c->ti_end_min > ti_current) return;
+  if (!cell_is_active(c, e)) return;
 
   /* Recurse? */
   if (c->split) {
     for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL) runner_do_ghost(r, c->progeny[k]);
-    return;
-  }
+      if (c->progeny[k] != NULL) runner_do_ghost(r, c->progeny[k], 0);
+  } else {
 
-  /* Init the IDs that have to be updated. */
-  int *pid = NULL;
-  if ((pid = malloc(sizeof(int) * count)) == NULL)
-    error("Can't allocate memory for pid.");
-  for (int k = 0; k < count; k++) pid[k] = k;
+    /* Init the IDs that have to be updated. */
+    int *pid = NULL;
+    if ((pid = malloc(sizeof(int) * count)) == NULL)
+      error("Can't allocate memory for pid.");
+    for (int k = 0; k < count; k++) pid[k] = k;
 
-  /* While there are particles that need to be updated... */
-  for (int num_reruns = 0; count > 0 && num_reruns < max_smoothing_iter;
-       num_reruns++) {
+    /* While there are particles that need to be updated... */
+    for (int num_reruns = 0; count > 0 && num_reruns < max_smoothing_iter;
+         num_reruns++) {
 
-    /* Reset the redo-count. */
-    redo = 0;
+      /* Reset the redo-count. */
+      redo = 0;
 
-    /* Loop over the parts in this cell. */
-    for (int i = 0; i < count; i++) {
+      /* Loop over the parts in this cell. */
+      for (int i = 0; i < count; i++) {
 
-      /* Get a direct pointer on the part. */
-      struct part *restrict p = &parts[pid[i]];
-      struct xpart *restrict xp = &xparts[pid[i]];
+        /* Get a direct pointer on the part. */
+        struct part *restrict p = &parts[pid[i]];
+        struct xpart *restrict xp = &xparts[pid[i]];
 
-      /* Is this part within the timestep? */
-      if (p->ti_end <= ti_current) {
+        /* Is this part within the timestep? */
+        if (part_is_active(p, e)) {
 
-        /* Finish the density calculation */
-        hydro_end_density(p, ti_current);
+          /* Finish the density calculation */
+          hydro_end_density(p);
 
-        float h_corr = 0.f;
+          float h_corr = 0.f;
 
-        /* If no derivative, double the smoothing length. */
-        if (p->density.wcount_dh == 0.0f) h_corr = p->h;
+          /* If no derivative, double the smoothing length. */
+          if (p->density.wcount_dh == 0.0f) h_corr = p->h;
 
-        /* Otherwise, compute the smoothing length update (Newton step). */
-        else {
-          h_corr = (target_wcount - p->density.wcount) / p->density.wcount_dh;
+          /* Otherwise, compute the smoothing length update (Newton step). */
+          else {
+            h_corr = (target_wcount - p->density.wcount) / p->density.wcount_dh;
 
-          /* Truncate to the range [ -p->h/2 , p->h ]. */
-          h_corr = (h_corr < p->h) ? h_corr : p->h;
-          h_corr = (h_corr > -0.5f * p->h) ? h_corr : -0.5f * p->h;
-        }
+            /* Truncate to the range [ -p->h/2 , p->h ]. */
+            h_corr = (h_corr < p->h) ? h_corr : p->h;
+            h_corr = (h_corr > -0.5f * p->h) ? h_corr : -0.5f * p->h;
+          }
 
-        /* Did we get the right number density? */
-        if (p->density.wcount > max_wcount || p->density.wcount < min_wcount) {
+          /* Did we get the right number density? */
+          if (p->density.wcount > max_wcount ||
+              p->density.wcount < min_wcount) {
 
-          /* Ok, correct then */
-          p->h += h_corr;
+            /* Ok, correct then */
+            p->h += h_corr;
 
-          /* Flag for another round of fun */
-          pid[redo] = pid[i];
-          redo += 1;
+            /* Flag for another round of fun */
+            pid[redo] = pid[i];
+            redo += 1;
 
-          /* Re-initialise everything */
-          hydro_init_part(p);
+            /* Re-initialise everything */
+            hydro_init_part(p);
 
-          /* Off we go ! */
-          continue;
-        }
+            /* Off we go ! */
+            continue;
+          }
 
-        /* We now have a particle whose smoothing length has converged */
+          /* We now have a particle whose smoothing length has converged */
 
-        /* As of here, particle force variables will be set. */
+          /* As of here, particle force variables will be set. */
 
-        /* Compute variables required for the force loop */
-        hydro_prepare_force(p, xp, ti_current, timeBase);
+          /* Compute variables required for the force loop */
+          hydro_prepare_force(p, xp, ti_current, timeBase);
 
-        /* The particle force values are now set.  Do _NOT_
-           try to read any particle density variables! */
+          /* The particle force values are now set.  Do _NOT_
+             try to read any particle density variables! */
 
-        /* Prepare the particle for the force loop over neighbours */
-        hydro_reset_acceleration(p);
+          /* Prepare the particle for the force loop over neighbours */
+          hydro_reset_acceleration(p);
+        }
       }
-    }
 
-    /* We now need to treat the particles whose smoothing length had not
-     * converged again */
+      /* We now need to treat the particles whose smoothing length had not
+       * converged again */
 
-    /* Re-set the counter for the next loop (potentially). */
-    count = redo;
-    if (count > 0) {
+      /* Re-set the counter for the next loop (potentially). */
+      count = redo;
+      if (count > 0) {
 
-      /* Climb up the cell hierarchy. */
-      for (struct cell *finger = c; finger != NULL; finger = finger->parent) {
+        /* Climb up the cell hierarchy. */
+        for (struct cell *finger = c; finger != NULL; finger = finger->parent) {
 
-        /* Run through this cell's density interactions. */
-        for (struct link *l = finger->density; l != NULL; l = l->next) {
+          /* Run through this cell's density interactions. */
+          for (struct link *l = finger->density; l != NULL; l = l->next) {
 
-          /* Self-interaction? */
-          if (l->t->type == task_type_self)
-            runner_doself_subset_density(r, finger, parts, pid, count);
+            /* Self-interaction? */
+            if (l->t->type == task_type_self)
+              runner_doself_subset_density(r, finger, parts, pid, count);
 
-          /* Otherwise, pair interaction? */
-          else if (l->t->type == task_type_pair) {
+            /* Otherwise, pair interaction? */
+            else if (l->t->type == task_type_pair) {
 
-            /* Left or right? */
-            if (l->t->ci == finger)
-              runner_dopair_subset_density(r, finger, parts, pid, count,
-                                           l->t->cj);
-            else
-              runner_dopair_subset_density(r, finger, parts, pid, count,
-                                           l->t->ci);
+              /* Left or right? */
+              if (l->t->ci == finger)
+                runner_dopair_subset_density(r, finger, parts, pid, count,
+                                             l->t->cj);
+              else
+                runner_dopair_subset_density(r, finger, parts, pid, count,
+                                             l->t->ci);
 
-          }
+            }
+
+            /* Otherwise, sub-self interaction? */
+            else if (l->t->type == task_type_sub_self)
+              runner_dosub_subset_density(r, finger, parts, pid, count, NULL,
+                                          -1, 1);
 
-          /* Otherwise, sub-self interaction? */
-          else if (l->t->type == task_type_sub_self)
-            runner_dosub_subset_density(r, finger, parts, pid, count, NULL, -1,
-                                        1);
-
-          /* Otherwise, sub-pair interaction? */
-          else if (l->t->type == task_type_sub_pair) {
-
-            /* Left or right? */
-            if (l->t->ci == finger)
-              runner_dosub_subset_density(r, finger, parts, pid, count,
-                                          l->t->cj, -1, 1);
-            else
-              runner_dosub_subset_density(r, finger, parts, pid, count,
-                                          l->t->ci, -1, 1);
+            /* Otherwise, sub-pair interaction? */
+            else if (l->t->type == task_type_sub_pair) {
+
+              /* Left or right? */
+              if (l->t->ci == finger)
+                runner_dosub_subset_density(r, finger, parts, pid, count,
+                                            l->t->cj, -1, 1);
+              else
+                runner_dosub_subset_density(r, finger, parts, pid, count,
+                                            l->t->ci, -1, 1);
+            }
           }
         }
       }
     }
-  }
 
-  if (count)
-    message("Smoothing length failed to converge on %i particles.", count);
+    if (count)
+      message("Smoothing length failed to converge on %i particles.", count);
 
-  /* Be clean */
-  free(pid);
+    /* Be clean */
+    free(pid);
+  }
 
-  TIMER_TOC(timer_do_ghost);
+  if (timer) TIMER_TOC(timer_do_ghost);
 }
 
 /**
@@ -757,20 +754,18 @@ void runner_do_ghost(struct runner *r, struct cell *c) {
  */
 static void runner_do_drift(struct cell *c, struct engine *e, int drift) {
 
-  const int ti_current = e->ti_current;
-
   /* Unskip any active tasks. */
-  if (c->ti_end_min == e->ti_current) {
+  if (cell_is_active(c, e)) {
     const int forcerebuild = cell_unskip_tasks(c, &e->sched);
     if (forcerebuild) atomic_inc(&e->forcerebuild);
   }
 
   /* Do we really need to drift? */
   if (drift) {
-    if (!e->drift_all && !cell_is_drift_needed(c, ti_current)) return;
+    if (!e->drift_all && !cell_is_drift_needed(c, e)) return;
   } else {
 
-    /* Not drifting, but may still need to recurse for task skipping. */
+    /* Not drifting, but may still need to recurse for task un-skipping. */
     if (c->split) {
       for (int k = 0; k < 8; k++) {
         if (c->progeny[k] != NULL) {
@@ -782,8 +777,12 @@ static void runner_do_drift(struct cell *c, struct engine *e, int drift) {
     return;
   }
 
+  /* Now, we can drift */
+
+  /* Get some information first */
   const double timeBase = e->timeBase;
   const int ti_old = c->ti_old;
+  const int ti_current = e->ti_current;
   struct part *const parts = c->parts;
   struct xpart *const xparts = c->xparts;
   struct gpart *const gparts = c->gparts;
@@ -796,7 +795,7 @@ static void runner_do_drift(struct cell *c, struct engine *e, int drift) {
   if (!c->split) {
 
     /* Check that we are actually going to move forward. */
-    if (ti_current >= ti_old) {
+    if (ti_current > ti_old) {
 
       /* Loop over all the g-particles in the cell */
       const size_t nr_gparts = c->gcount;
@@ -840,6 +839,12 @@ static void runner_do_drift(struct cell *c, struct engine *e, int drift) {
       dx_max = sqrtf(dx2_max);
 
     } /* Check that we are actually going to move forward. */
+
+    else {
+      /* ti_old == ti_current, just keep the current cell values. */
+      h_max = c->h_max;
+      dx_max = c->dx_max;
+    }
   }
 
   /* Otherwise, aggregate data from children. */
@@ -889,118 +894,6 @@ void runner_do_drift_mapper(void *map_data, int num_elements,
   }
 }
 
-/**
- * @brief Kick particles in momentum space and collect statistics (fixed
- * time-step case)
- *
- * @param r The runner thread.
- * @param c The cell.
- * @param timer Are we timing this ?
- */
-void runner_do_kick_fixdt(struct runner *r, struct cell *c, int timer) {
-
-  const double global_dt = r->e->dt_max;
-  const double timeBase = r->e->timeBase;
-  const int count = c->count;
-  const int gcount = c->gcount;
-  struct part *restrict parts = c->parts;
-  struct xpart *restrict xparts = c->xparts;
-  struct gpart *restrict gparts = c->gparts;
-  const double const_G = r->e->physical_constants->const_newton_G;
-
-  int updated = 0, g_updated = 0;
-  int ti_end_min = max_nr_timesteps, ti_end_max = 0;
-
-  TIMER_TIC
-
-#ifdef TASK_VERBOSE
-  OUT;
-#endif
-
-  /* The new time-step */
-  const int new_dti = global_dt / timeBase;
-
-  /* No children? */
-  if (!c->split) {
-
-    /* Loop over the g-particles and kick everyone. */
-    for (int k = 0; k < gcount; k++) {
-
-      /* Get a handle on the part. */
-      struct gpart *restrict gp = &gparts[k];
-
-      /* If the g-particle has no counterpart */
-      if (gp->id_or_neg_offset > 0) {
-
-        /* First, finish the force calculation */
-        gravity_end_force(gp, const_G);
-
-        /* Kick the g-particle forward */
-        kick_gpart(gp, new_dti, timeBase);
-
-        /* Number of updated g-particles */
-        g_updated++;
-
-        /* Minimal time for next end of time-step */
-        ti_end_min = min(gp->ti_end, ti_end_min);
-        ti_end_max = max(gp->ti_end, ti_end_max);
-      }
-    }
-
-    /* Now do the hydro ones... */
-
-    /* Loop over the particles and kick everyone. */
-    for (int k = 0; k < count; k++) {
-
-      /* Get a handle on the part. */
-      struct part *restrict p = &parts[k];
-      struct xpart *restrict xp = &xparts[k];
-
-      /* First, finish the force loop */
-      hydro_end_force(p);
-      if (p->gpart != NULL) gravity_end_force(p->gpart, const_G);
-
-      /* Kick the particle forward */
-      kick_part(p, xp, new_dti, timeBase);
-
-      /* Number of updated particles */
-      updated++;
-      if (p->gpart != NULL) g_updated++;
-
-      /* Minimal time for next end of time-step */
-      ti_end_min = min(p->ti_end, ti_end_min);
-      ti_end_max = max(p->ti_end, ti_end_max);
-    }
-  }
-
-  /* Otherwise, aggregate data from children. */
-  else {
-
-    /* Loop over the progeny. */
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL) {
-        struct cell *restrict cp = c->progeny[k];
-
-        /* Recurse */
-        runner_do_kick_fixdt(r, cp, 0);
-
-        /* And aggregate */
-        updated += cp->updated;
-        g_updated += cp->g_updated;
-        ti_end_min = min(cp->ti_end_min, ti_end_min);
-        ti_end_max = max(cp->ti_end_max, ti_end_max);
-      }
-  }
-
-  /* Store the values. */
-  c->updated = updated;
-  c->g_updated = g_updated;
-  c->ti_end_min = ti_end_min;
-  c->ti_end_max = ti_end_max;
-
-  if (timer) TIMER_TOC(timer_kick);
-}
-
 /**
  * @brief Kick particles in momentum space and collect statistics (floating
  * time-step case)
@@ -1013,27 +906,22 @@ void runner_do_kick(struct runner *r, struct cell *c, int timer) {
 
   const struct engine *e = r->e;
   const double timeBase = e->timeBase;
-  const int ti_current = r->e->ti_current;
   const int count = c->count;
   const int gcount = c->gcount;
   struct part *restrict parts = c->parts;
   struct xpart *restrict xparts = c->xparts;
   struct gpart *restrict gparts = c->gparts;
-  const double const_G = r->e->physical_constants->const_newton_G;
+  const double const_G = e->physical_constants->const_newton_G;
 
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (c->ti_end_min > ti_current) {
+  if (!cell_is_active(c, e)) {
     c->updated = 0;
     c->g_updated = 0;
     return;
   }
 
-#ifdef TASK_VERBOSE
-  OUT;
-#endif
-
   int updated = 0, g_updated = 0;
   int ti_end_min = max_nr_timesteps, ti_end_max = 0;
 
@@ -1049,7 +937,7 @@ void runner_do_kick(struct runner *r, struct cell *c, int timer) {
       /* If the g-particle has no counterpart and needs to be kicked */
       if (gp->id_or_neg_offset > 0) {
 
-        if (gp->ti_end <= ti_current) {
+        if (gpart_is_active(gp, e)) {
 
           /* First, finish the force calculation */
           gravity_end_force(gp, const_G);
@@ -1080,7 +968,7 @@ void runner_do_kick(struct runner *r, struct cell *c, int timer) {
       struct xpart *restrict xp = &xparts[k];
 
       /* If particle needs to be kicked */
-      if (p->ti_end <= ti_current) {
+      if (part_is_active(p, e)) {
 
         /* First, finish the force loop */
         hydro_end_force(p);
@@ -1231,12 +1119,14 @@ void *runner_main(void *data) {
       /* Get the cells. */
       struct cell *ci = t->ci;
       struct cell *cj = t->cj;
+#ifdef SWIFT_DEBUG_TASKS
       t->rid = r->cpuid;
+#endif
 
 /* Check that we haven't scheduled an inactive task */
 #ifdef SWIFT_DEBUG_CHECKS
       if (cj == NULL) { /* self */
-        if (ci->ti_end_min > e->ti_current && t->type != task_type_sort)
+        if (!cell_is_active(ci, e) && t->type != task_type_sort)
           error(
               "Task (type='%s/%s') should have been skipped ti_current=%d "
               "c->ti_end_min=%d",
@@ -1244,7 +1134,7 @@ void *runner_main(void *data) {
               ci->ti_end_min);
 
         /* Special case for sorts */
-        if (ci->ti_end_min > e->ti_current && t->type == task_type_sort &&
+        if (!cell_is_active(ci, e) && t->type == task_type_sort &&
             t->flags == 0)
           error(
               "Task (type='%s/%s') should have been skipped ti_current=%d "
@@ -1253,7 +1143,7 @@ void *runner_main(void *data) {
               ci->ti_end_min, t->flags);
 
       } else { /* pair */
-        if (ci->ti_end_min > e->ti_current && cj->ti_end_min > e->ti_current)
+        if (!cell_is_active(ci, e) && !cell_is_active(cj, e))
           error(
               "Task (type='%s/%s') should have been skipped ti_current=%d "
               "ci->ti_end_min=%d cj->ti_end_min=%d",
@@ -1277,7 +1167,7 @@ void *runner_main(void *data) {
           else if (t->subtype == task_subtype_external_grav)
             runner_do_grav_external(r, ci, 1);
           else
-            error("Unknown task subtype.");
+            error("Unknown/invalid task subtype (%d).", t->subtype);
           break;
 
         case task_type_pair:
@@ -1292,7 +1182,7 @@ void *runner_main(void *data) {
           else if (t->subtype == task_subtype_grav)
             runner_dopair_grav(r, ci, cj, 1);
           else
-            error("Unknown task subtype.");
+            error("Unknown/invalid task subtype (%d).", t->subtype);
           break;
 
         case task_type_sub_self:
@@ -1309,7 +1199,7 @@ void *runner_main(void *data) {
           else if (t->subtype == task_subtype_external_grav)
             runner_do_grav_external(r, ci, 1);
           else
-            error("Unknown task subtype.");
+            error("Unknown/invalid task subtype (%d).", t->subtype);
           break;
 
         case task_type_sub_pair:
@@ -1324,7 +1214,7 @@ void *runner_main(void *data) {
           else if (t->subtype == task_subtype_grav)
             runner_dosub_grav(r, ci, cj, 1);
           else
-            error("Unknown task subtype.");
+            error("Unknown/invalid task subtype (%d).", t->subtype);
           break;
 
         case task_type_sort:
@@ -1334,19 +1224,16 @@ void *runner_main(void *data) {
           runner_do_init(r, ci, 1);
           break;
         case task_type_ghost:
-          runner_do_ghost(r, ci);
+          runner_do_ghost(r, ci, 1);
           break;
 #ifdef EXTRA_HYDRO_LOOP
         case task_type_extra_ghost:
-          runner_do_extra_ghost(r, ci);
+          runner_do_extra_ghost(r, ci, 1);
           break;
 #endif
         case task_type_kick:
           runner_do_kick(r, ci, 1);
           break;
-        case task_type_kick_fixdt:
-          runner_do_kick_fixdt(r, ci, 1);
-          break;
 #ifdef WITH_MPI
         case task_type_send:
           if (t->subtype == task_subtype_tend) {
@@ -1380,7 +1267,7 @@ void *runner_main(void *data) {
           runner_do_sourceterms(r, t->ci, 1);
           break;
         default:
-          error("Unknown task type.");
+          error("Unknown/invalid task type (%d).", t->type);
       }
 
       /* We're done with this task, see if we get a next one. */
diff --git a/src/runner.h b/src/runner.h
index be19ab61b997f5730a04fa8c01c0787e8b99a8b2..a8caf24248c99438f16729e2cac3e1031535f62b 100644
--- a/src/runner.h
+++ b/src/runner.h
@@ -48,12 +48,13 @@ struct runner {
 };
 
 /* Function prototypes. */
-void runner_do_ghost(struct runner *r, struct cell *c);
+void runner_do_ghost(struct runner *r, struct cell *c, int timer);
+void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer);
 void runner_do_sort(struct runner *r, struct cell *c, int flag, int clock);
 void runner_do_kick(struct runner *r, struct cell *c, int timer);
-void runner_do_kick_fixdt(struct runner *r, struct cell *c, int timer);
 void runner_do_init(struct runner *r, struct cell *c, int timer);
 void runner_do_cooling(struct runner *r, struct cell *c, int timer);
+void runner_do_grav_external(struct runner *r, struct cell *c, int timer);
 void *runner_main(void *data);
 void runner_do_drift_mapper(void *map_data, int num_elements, void *extra_data);
 
diff --git a/src/runner_doiact.h b/src/runner_doiact.h
index 3c968cbf7d955198ad6bb44ab70e93af17735e99..c067d3bc9a576ee9c7dfb6e910eb1aa01012f755 100644
--- a/src/runner_doiact.h
+++ b/src/runner_doiact.h
@@ -109,7 +109,6 @@ void DOPAIR_NAIVE(struct runner *r, struct cell *restrict ci,
                   struct cell *restrict cj) {
 
   const struct engine *e = r->e;
-  const int ti_current = e->ti_current;
 
   error("Don't use in actual runs ! Slow code !");
 
@@ -124,7 +123,7 @@ void DOPAIR_NAIVE(struct runner *r, struct cell *restrict ci,
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return;
 
   const int count_i = ci->count;
   const int count_j = cj->count;
@@ -210,7 +209,7 @@ void DOPAIR_NAIVE(struct runner *r, struct cell *restrict ci,
 
 void DOSELF_NAIVE(struct runner *r, struct cell *restrict c) {
 
-  const int ti_current = r->e->ti_current;
+  const struct engine *e = r->e;
 
   error("Don't use in actual runs ! Slow code !");
 
@@ -226,7 +225,7 @@ void DOSELF_NAIVE(struct runner *r, struct cell *restrict c) {
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (c->ti_end_min > ti_current) return;
+  if (!cell_is_active(c, e)) return;
 
   const int count = c->count;
   struct part *restrict parts = c->parts;
@@ -706,8 +705,7 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci,
  */
 void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) {
 
-  struct engine *restrict e = r->e;
-  const int ti_current = e->ti_current;
+  const struct engine *restrict e = r->e;
 
 #ifdef WITH_VECTORIZATION
   int icount = 0;
@@ -721,7 +719,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) {
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return;
 
   /* Get the sort ID. */
   double shift[3] = {0.0, 0.0, 0.0};
@@ -756,7 +754,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) {
 
     /* Get a hold of the ith part in ci. */
     struct part *restrict pi = &parts_i[sort_i[pid].i];
-    if (pi->ti_end > ti_current) continue;
+    if (!part_is_active(pi, e)) continue;
     const float hi = pi->h;
     const double di = sort_i[pid].d + hi * kernel_gamma + dx_max - rshift;
     if (di < dj_min) continue;
@@ -818,7 +816,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) {
 
     /* Get a hold of the jth part in cj. */
     struct part *restrict pj = &parts_j[sort_j[pjd].i];
-    if (pj->ti_end > ti_current) continue;
+    if (!part_is_active(pj, e)) continue;
     const float hj = pj->h;
     const double dj = sort_j[pjd].d - hj * kernel_gamma - dx_max - rshift;
     if (dj > di_max) continue;
@@ -894,7 +892,6 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj) {
 void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
 
   struct engine *restrict e = r->e;
-  const int ti_current = e->ti_current;
 
 #ifdef WITH_VECTORIZATION
   int icount1 = 0;
@@ -914,7 +911,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return;
 
   /* Get the shift ID. */
   double shift[3] = {0.0, 0.0, 0.0};
@@ -946,28 +943,28 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
   /* Collect the number of parts left and right below dt. */
   int countdt_i = 0, countdt_j = 0;
   struct entry *restrict sortdt_i = NULL, *restrict sortdt_j = NULL;
-  if (ci->ti_end_max <= ti_current) {
+  if (cell_is_all_active(ci, e)) {
     sortdt_i = sort_i;
     countdt_i = count_i;
-  } else if (ci->ti_end_min <= ti_current) {
+  } else if (cell_is_active(ci, e)) {
     if (posix_memalign((void *)&sortdt_i, VEC_SIZE * sizeof(float),
                        sizeof(struct entry) * count_i) != 0)
       error("Failed to allocate dt sortlists.");
     for (int k = 0; k < count_i; k++)
-      if (parts_i[sort_i[k].i].ti_end <= ti_current) {
+      if (part_is_active(&parts_i[sort_i[k].i], e)) {
         sortdt_i[countdt_i] = sort_i[k];
         countdt_i += 1;
       }
   }
-  if (cj->ti_end_max <= ti_current) {
+  if (cell_is_all_active(cj, e)) {
     sortdt_j = sort_j;
     countdt_j = count_j;
-  } else if (cj->ti_end_min <= ti_current) {
+  } else if (cell_is_active(cj, e)) {
     if (posix_memalign((void *)&sortdt_j, VEC_SIZE * sizeof(float),
                        sizeof(struct entry) * count_j) != 0)
       error("Failed to allocate dt sortlists.");
     for (int k = 0; k < count_j; k++)
-      if (parts_j[sort_j[k].i].ti_end <= ti_current) {
+      if (part_is_active(&parts_j[sort_j[k].i], e)) {
         sortdt_j[countdt_j] = sort_j[k];
         countdt_j += 1;
       }
@@ -988,7 +985,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
     const float hig2 = hi * hi * kernel_gamma2;
 
     /* Look at valid dt parts only? */
-    if (pi->ti_end > ti_current) {
+    if (!part_is_active(pi, e)) {
 
       /* Loop over the parts in cj within dt. */
       for (int pjd = 0; pjd < countdt_j && sortdt_j[pjd].d < di; pjd++) {
@@ -1062,7 +1059,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
 #ifndef WITH_VECTORIZATION
 
           /* Does pj need to be updated too? */
-          if (pj->ti_end <= ti_current)
+          if (part_is_active(pj, e))
             IACT(r2, dx, hi, hj, pi, pj);
           else
             IACT_NONSYM(r2, dx, hi, hj, pi, pj);
@@ -1070,7 +1067,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
 #else
 
           /* Does pj need to be updated too? */
-          if (pj->ti_end <= ti_current) {
+          if (part_is_active(pj, e)) {
 
             /* Add this interaction to the symmetric queue. */
             r2q2[icount2] = r2;
@@ -1132,7 +1129,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
     const float hjg2 = hj * hj * kernel_gamma2;
 
     /* Is this particle outside the dt? */
-    if (pj->ti_end > ti_current) {
+    if (!part_is_active(pj, e)) {
 
       /* Loop over the parts in ci. */
       for (int pid = countdt_i - 1; pid >= 0 && sortdt_i[pid].d > dj; pid--) {
@@ -1205,7 +1202,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
 #ifndef WITH_VECTORIZATION
 
           /* Does pi need to be updated too? */
-          if (pi->ti_end <= ti_current)
+          if (part_is_active(pi, e))
             IACT(r2, dx, hj, hi, pj, pi);
           else
             IACT_NONSYM(r2, dx, hj, hi, pj, pi);
@@ -1213,7 +1210,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
 #else
 
           /* Does pi need to be updated too? */
-          if (pi->ti_end <= ti_current) {
+          if (part_is_active(pi, e)) {
 
             /* Add this interaction to the symmetric queue. */
             r2q2[icount2] = r2;
@@ -1270,10 +1267,9 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
       IACT(r2q2[k], &dxq2[3 * k], hiq2[k], hjq2[k], piq2[k], pjq2[k]);
 #endif
 
-  if (ci->ti_end_max > ti_current && ci->ti_end_min <= ti_current)
-    free(sortdt_i);
-  if (cj->ti_end_max > ti_current && cj->ti_end_min <= ti_current)
-    free(sortdt_j);
+  /* Clean-up if necessary */
+  if (cell_is_active(ci, e) && !cell_is_all_active(ci, e)) free(sortdt_i);
+  if (cell_is_active(cj, e) && !cell_is_all_active(cj, e)) free(sortdt_j);
 
   TIMER_TOC(TIMER_DOPAIR);
 }
@@ -1286,7 +1282,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj) {
  */
 void DOSELF1(struct runner *r, struct cell *restrict c) {
 
-  const int ti_current = r->e->ti_current;
+  const struct engine *e = r->e;
 
 #ifdef WITH_VECTORIZATION
   int icount1 = 0;
@@ -1305,8 +1301,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
 
   TIMER_TIC;
 
-  if (c->ti_end_min > ti_current) return;
-  if (c->ti_end_max < ti_current) error("Cell in an impossible time-zone");
+  if (!cell_is_active(c, e)) return;
 
   struct part *restrict parts = c->parts;
   const int count = c->count;
@@ -1318,7 +1313,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
                      count * sizeof(int)) != 0)
     error("Failed to allocate indt.");
   for (int k = 0; k < count; k++)
-    if (parts[k].ti_end <= ti_current) {
+    if (part_is_active(&parts[k], e)) {
       indt[countdt] = k;
       countdt += 1;
     }
@@ -1336,7 +1331,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
     const float hig2 = hi * hi * kernel_gamma2;
 
     /* Is the ith particle inactive? */
-    if (pi->ti_end > ti_current) {
+    if (!part_is_active(pi, e)) {
 
       /* Loop over the other particles .*/
       for (int pjd = firstdt; pjd < countdt; pjd++) {
@@ -1407,7 +1402,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
           r2 += dx[k] * dx[k];
         }
         const int doj =
-            (pj->ti_end <= ti_current) && (r2 < hj * hj * kernel_gamma2);
+            (part_is_active(pj, e)) && (r2 < hj * hj * kernel_gamma2);
 
         /* Hit or miss? */
         if (r2 < hig2 || doj) {
@@ -1518,7 +1513,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
  */
 void DOSELF2(struct runner *r, struct cell *restrict c) {
 
-  const int ti_current = r->e->ti_current;
+  const struct engine *e = r->e;
 
 #ifdef WITH_VECTORIZATION
   int icount1 = 0;
@@ -1537,8 +1532,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
 
   TIMER_TIC;
 
-  if (c->ti_end_min > ti_current) return;
-  if (c->ti_end_max < ti_current) error("Cell in an impossible time-zone");
+  if (!cell_is_active(c, e)) return;
 
   struct part *restrict parts = c->parts;
   const int count = c->count;
@@ -1550,7 +1544,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
                      count * sizeof(int)) != 0)
     error("Failed to allocate indt.");
   for (int k = 0; k < count; k++)
-    if (parts[k].ti_end <= ti_current) {
+    if (part_is_active(&parts[k], e)) {
       indt[countdt] = k;
       countdt += 1;
     }
@@ -1568,7 +1562,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
     const float hig2 = hi * hi * kernel_gamma2;
 
     /* Is the ith particle not active? */
-    if (pi->ti_end > ti_current) {
+    if (!part_is_active(pi, e)) {
 
       /* Loop over the other particles .*/
       for (int pjd = firstdt; pjd < countdt; pjd++) {
@@ -1645,7 +1639,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
 #ifndef WITH_VECTORIZATION
 
           /* Does pj need to be updated too? */
-          if (pj->ti_end <= ti_current)
+          if (part_is_active(pj, e))
             IACT(r2, dx, hi, hj, pi, pj);
           else
             IACT_NONSYM(r2, dx, hi, hj, pi, pj);
@@ -1653,7 +1647,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
 #else
 
           /* Does pj need to be updated too? */
-          if (pj->ti_end <= ti_current) {
+          if (part_is_active(pj, e)) {
 
             /* Add this interaction to the symmetric queue. */
             r2q2[icount2] = r2;
@@ -1731,12 +1725,12 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid,
                  int gettimer) {
 
   struct space *s = r->e->s;
-  const int ti_current = r->e->ti_current;
+  const struct engine *e = r->e;
 
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return;
 
   /* Get the cell dimensions. */
   const float h = min(ci->width[0], min(ci->width[1], ci->width[2]));
@@ -1950,7 +1944,7 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid,
   }
 
   /* Otherwise, compute the pair directly. */
-  else if (ci->ti_end_min <= ti_current || cj->ti_end_min <= ti_current) {
+  else if (cell_is_active(ci, e) || cell_is_active(cj, e)) {
 
     /* Do any of the cells need to be sorted first? */
     if (!(ci->sorted & (1 << sid))) runner_do_sort(r, ci, (1 << sid), 1);
@@ -1972,12 +1966,10 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj, int sid,
  */
 void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer) {
 
-  const int ti_current = r->e->ti_current;
-
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (ci->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, r->e)) return;
 
   /* Recurse? */
   if (ci->split) {
@@ -2014,13 +2006,13 @@ void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer) {
 void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid,
                  int gettimer) {
 
-  struct space *s = r->e->s;
-  const int ti_current = r->e->ti_current;
+  const struct engine *e = r->e;
+  struct space *s = e->s;
 
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return;
 
   /* Get the cell dimensions. */
   const float h = min(ci->width[0], min(ci->width[1], ci->width[2]));
@@ -2234,7 +2226,7 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid,
   }
 
   /* Otherwise, compute the pair directly. */
-  else if (ci->ti_end_min <= ti_current || cj->ti_end_min <= ti_current) {
+  else if (cell_is_active(ci, e) || cell_is_active(cj, e)) {
 
     /* Do any of the cells need to be sorted first? */
     if (!(ci->sorted & (1 << sid))) runner_do_sort(r, ci, (1 << sid), 1);
@@ -2256,12 +2248,10 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj, int sid,
  */
 void DOSUB_SELF2(struct runner *r, struct cell *ci, int gettimer) {
 
-  const int ti_current = r->e->ti_current;
-
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (ci->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, r->e)) return;
 
   /* Recurse? */
   if (ci->split) {
@@ -2287,8 +2277,8 @@ void DOSUB_SELF2(struct runner *r, struct cell *ci, int gettimer) {
 void DOSUB_SUBSET(struct runner *r, struct cell *ci, struct part *parts,
                   int *ind, int count, struct cell *cj, int sid, int gettimer) {
 
-  struct space *s = r->e->s;
-  const int ti_current = r->e->ti_current;
+  const struct engine *e = r->e;
+  struct space *s = e->s;
 
   TIMER_TIC;
 
@@ -2850,7 +2840,7 @@ void DOSUB_SUBSET(struct runner *r, struct cell *ci, struct part *parts,
     }
 
     /* Otherwise, compute the pair directly. */
-    else if (ci->ti_end_min <= ti_current || cj->ti_end_min <= ti_current) {
+    else if (cell_is_active(ci, e) || cell_is_active(cj, e)) {
 
       /* Get the relative distance between the pairs, wrapping. */
       double shift[3] = {0.0, 0.0, 0.0};
diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h
index 0fcd2d2e80a72b92588acd5b8275b9dafc68df45..59a5ae496680390c23458bde65b4bba321ffe7a1 100644
--- a/src/runner_doiact_grav.h
+++ b/src/runner_doiact_grav.h
@@ -71,7 +71,6 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pm(
   const int gcount = ci->gcount;
   struct gpart *restrict gparts = ci->gparts;
   const struct multipole multi = cj->multipole;
-  const int ti_current = e->ti_current;
   const float rlr_inv = 1. / (const_gravity_a_smooth * ci->super->width[0]);
 
   TIMER_TIC;
@@ -84,7 +83,7 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pm(
 #endif
 
   /* Anything to do here? */
-  if (ci->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, e)) return;
 
 #if ICHECK > 0
   for (int pid = 0; pid < gcount; pid++) {
@@ -105,7 +104,7 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pm(
     /* Get a hold of the ith part in ci. */
     struct gpart *restrict gp = &gparts[pid];
 
-    if (gp->ti_end > ti_current) continue;
+    if (!gpart_is_active(gp, e)) continue;
 
     /* Compute the pairwise distance. */
     const float dx[3] = {multi.CoM[0] - gp->x[0],   // x
@@ -138,7 +137,6 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pp(
   const int gcount_j = cj->gcount;
   struct gpart *restrict gparts_i = ci->gparts;
   struct gpart *restrict gparts_j = cj->gparts;
-  const int ti_current = e->ti_current;
   const float rlr_inv = 1. / (const_gravity_a_smooth * ci->super->width[0]);
 
   TIMER_TIC;
@@ -150,7 +148,7 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pp(
 #endif
 
   /* Anything to do here? */
-  if (ci->ti_end_min > ti_current && cj->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, e) && !cell_is_active(cj, e)) return;
 
 #if ICHECK > 0
   for (int pid = 0; pid < gcount_i; pid++) {
@@ -195,17 +193,17 @@ __attribute__((always_inline)) INLINE static void runner_dopair_grav_pp(
       const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
       /* Interact ! */
-      if (gpi->ti_end <= ti_current && gpj->ti_end <= ti_current) {
+      if (gpart_is_active(gpi, e) && gpart_is_active(gpj, e)) {
 
         runner_iact_grav_pp(rlr_inv, r2, dx, gpi, gpj);
 
       } else {
 
-        if (gpi->ti_end <= ti_current) {
+        if (gpart_is_active(gpi, e)) {
 
           runner_iact_grav_pp_nonsym(rlr_inv, r2, dx, gpi, gpj);
 
-        } else if (gpj->ti_end <= ti_current) {
+        } else if (gpart_is_active(gpj, e)) {
 
           dx[0] = -dx[0];
           dx[1] = -dx[1];
@@ -233,7 +231,6 @@ __attribute__((always_inline)) INLINE static void runner_doself_grav_pp(
   const struct engine *e = r->e;
   const int gcount = c->gcount;
   struct gpart *restrict gparts = c->gparts;
-  const int ti_current = e->ti_current;
   const float rlr_inv = 1. / (const_gravity_a_smooth * c->super->width[0]);
 
   TIMER_TIC;
@@ -244,7 +241,7 @@ __attribute__((always_inline)) INLINE static void runner_doself_grav_pp(
 #endif
 
   /* Anything to do here? */
-  if (c->ti_end_min > ti_current) return;
+  if (!cell_is_active(c, e)) return;
 
 #if ICHECK > 0
   for (int pid = 0; pid < gcount; pid++) {
@@ -278,17 +275,17 @@ __attribute__((always_inline)) INLINE static void runner_doself_grav_pp(
       const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
       /* Interact ! */
-      if (gpi->ti_end <= ti_current && gpj->ti_end <= ti_current) {
+      if (gpart_is_active(gpi, e) && gpart_is_active(gpj, e)) {
 
         runner_iact_grav_pp(rlr_inv, r2, dx, gpi, gpj);
 
       } else {
 
-        if (gpi->ti_end <= ti_current) {
+        if (gpart_is_active(gpi, e)) {
 
           runner_iact_grav_pp_nonsym(rlr_inv, r2, dx, gpi, gpj);
 
-        } else if (gpj->ti_end <= ti_current) {
+        } else if (gpart_is_active(gpj, e)) {
 
           dx[0] = -dx[0];
           dx[1] = -dx[1];
@@ -490,14 +487,13 @@ static void runner_do_grav_mm(struct runner *r, struct cell *ci, int timer) {
   const struct engine *e = r->e;
   struct cell *cells = e->s->cells_top;
   const int nr_cells = e->s->nr_cells;
-  const int ti_current = e->ti_current;
   const double max_d =
       const_gravity_a_smooth * const_gravity_r_cut * ci->width[0];
   const double max_d2 = max_d * max_d;
   const double pos_i[3] = {ci->loc[0], ci->loc[1], ci->loc[2]};
 
   /* Anything to do here? */
-  if (ci->ti_end_min > ti_current) return;
+  if (!cell_is_active(ci, e)) return;
 
   /* Loop over all the cells and go for a p-m interaction if far enough but not
    * too far */
diff --git a/src/scheduler.c b/src/scheduler.c
index 44790fcd2fa5f67e6f325ba5849da19e35ab285a..13527d97599b9cdcff18a0143d6238559a3af450 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -42,6 +42,7 @@
 #include "atomic.h"
 #include "const.h"
 #include "cycle.h"
+#include "engine.h"
 #include "error.h"
 #include "intrinsics.h"
 #include "kernel_hydro.h"
@@ -53,7 +54,6 @@
 /**
  * @brief Re-set the list of active tasks.
  */
-
 void scheduler_clear_active(struct scheduler *s) { s->active_count = 0; }
 
 /**
@@ -63,7 +63,6 @@ void scheduler_clear_active(struct scheduler *s) { s->active_count = 0; }
  * @param ta The unlocking #task.
  * @param tb The #task that will be unlocked.
  */
-
 void scheduler_addunlock(struct scheduler *s, struct task *ta,
                          struct task *tb) {
   /* Get an index at which to store this unlock. */
@@ -113,7 +112,6 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta,
  * @param t The #task
  * @param s The #scheduler we are working in.
  */
-
 static void scheduler_splittask(struct task *t, struct scheduler *s) {
 
   /* Static constants. */
@@ -183,16 +181,19 @@ static void scheduler_splittask(struct task *t, struct scheduler *s) {
                                     ci->progeny[k], NULL, 0),
                   s);
 
-          /* Make a task for each pair of progeny. */
-          for (int j = 0; j < 8; j++)
-            if (ci->progeny[j] != NULL)
-              for (int k = j + 1; k < 8; k++)
-                if (ci->progeny[k] != NULL)
-                  scheduler_splittask(
-                      scheduler_addtask(s, task_type_pair, t->subtype,
-                                        pts[j][k], 0, ci->progeny[j],
-                                        ci->progeny[k], 0),
-                      s);
+          /* Make a task for each pair of progeny unless it's ext. gravity. */
+          if (t->subtype != task_subtype_external_grav) {
+
+            for (int j = 0; j < 8; j++)
+              if (ci->progeny[j] != NULL)
+                for (int k = j + 1; k < 8; k++)
+                  if (ci->progeny[k] != NULL)
+                    scheduler_splittask(
+                        scheduler_addtask(s, task_type_pair, t->subtype,
+                                          pts[j][k], 0, ci->progeny[j],
+                                          ci->progeny[k], 0),
+                        s);
+          }
         }
       }
 
@@ -649,7 +650,6 @@ static void scheduler_splittask(struct task *t, struct scheduler *s) {
  * @param num_elements the number of tasks.
  * @param extra_data The #scheduler we are working in.
  */
-
 void scheduler_splittasks_mapper(void *map_data, int num_elements,
                                  void *extra_data) {
 
@@ -663,6 +663,11 @@ void scheduler_splittasks_mapper(void *map_data, int num_elements,
   }
 }
 
+/**
+ * @brief Splits all the tasks in the scheduler that are too large.
+ *
+ * @param s The #scheduler.
+ */
 void scheduler_splittasks(struct scheduler *s) {
 
   /* Call the mapper on each current task. */
@@ -677,12 +682,11 @@ void scheduler_splittasks(struct scheduler *s) {
  * @param type The type of the task.
  * @param subtype The sub-type of the task.
  * @param flags The flags of the task.
- * @param wait
+ * @param wait The number of unsatisfied dependencies of this task.
  * @param ci The first cell to interact.
  * @param cj The second cell to interact.
  * @param tight
  */
-
 struct task *scheduler_addtask(struct scheduler *s, enum task_types type,
                                enum task_subtypes subtype, int flags, int wait,
                                struct cell *ci, struct cell *cj, int tight) {
@@ -708,10 +712,12 @@ struct task *scheduler_addtask(struct scheduler *s, enum task_types type,
   t->implicit = 0;
   t->weight = 0;
   t->rank = 0;
-  t->tic = 0;
-  t->toc = 0;
   t->nr_unlock_tasks = 0;
+#ifdef SWIFT_DEBUG_TASKS
   t->rid = -1;
+  t->tic = 0;
+  t->toc = 0;
+#endif
 
   /* Add an index for it. */
   // lock_lock( &s->lock );
@@ -727,7 +733,6 @@ struct task *scheduler_addtask(struct scheduler *s, enum task_types type,
  *
  * @param s The #scheduler.
  */
-
 void scheduler_set_unlocks(struct scheduler *s) {
 
   /* Store the counts for each task. */
@@ -794,7 +799,6 @@ void scheduler_set_unlocks(struct scheduler *s) {
  *
  * @param s The #scheduler.
  */
-
 void scheduler_ranktasks(struct scheduler *s) {
 
   struct task *tasks = s->tasks;
@@ -860,7 +864,6 @@ void scheduler_ranktasks(struct scheduler *s) {
  * @param s The #scheduler.
  * @param size The maximum number of tasks in the #scheduler.
  */
-
 void scheduler_reset(struct scheduler *s, int size) {
 
   /* Do we need to re-allocate? */
@@ -888,8 +891,6 @@ void scheduler_reset(struct scheduler *s, int size) {
   s->nr_tasks = 0;
   s->tasks_next = 0;
   s->waiting = 0;
-  s->mask = 0;
-  s->submask = 0;
   s->nr_unlocks = 0;
   s->completed_unlock_writes = 0;
   s->active_count = 0;
@@ -904,7 +905,6 @@ void scheduler_reset(struct scheduler *s, int size) {
  * @param s The #scheduler.
  * @param verbose Are we talkative?
  */
-
 void scheduler_reweight(struct scheduler *s, int verbose) {
 
   const int nr_tasks = s->nr_tasks;
@@ -924,55 +924,56 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
     for (int j = 0; j < t->nr_unlock_tasks; j++)
       if (t->unlock_tasks[j]->weight > t->weight)
         t->weight = t->unlock_tasks[j]->weight;
-    if (!t->implicit && t->tic > 0)
-      t->weight += wscale * (t->toc - t->tic);
-    else
-      switch (t->type) {
-        case task_type_sort:
-          t->weight += wscale * intrinsics_popcount(t->flags) * t->ci->count *
-                       (sizeof(int) * 8 - intrinsics_clz(t->ci->count));
-          break;
-        case task_type_self:
-          t->weight += 1 * wscale * t->ci->count * t->ci->count;
-          break;
-        case task_type_pair:
-          if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID)
-            t->weight +=
+    int cost = 0;
+    switch (t->type) {
+      case task_type_sort:
+        cost = wscale * intrinsics_popcount(t->flags) * t->ci->count *
+               (sizeof(int) * 8 - intrinsics_clz(t->ci->count));
+        break;
+      case task_type_self:
+        cost = 1 * wscale * t->ci->count * t->ci->count;
+        break;
+      case task_type_pair:
+        if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID)
+          cost = 3 * wscale * t->ci->count * t->cj->count * sid_scale[t->flags];
+        else
+          cost = 2 * wscale * t->ci->count * t->cj->count * sid_scale[t->flags];
+        break;
+      case task_type_sub_pair:
+        if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) {
+          if (t->flags < 0)
+            cost = 3 * wscale * t->ci->count * t->cj->count;
+          else
+            cost =
                 3 * wscale * t->ci->count * t->cj->count * sid_scale[t->flags];
+        } else {
+          if (t->flags < 0)
+            cost = 2 * wscale * t->ci->count * t->cj->count;
           else
-            t->weight +=
+            cost =
                 2 * wscale * t->ci->count * t->cj->count * sid_scale[t->flags];
-          break;
-        case task_type_sub_pair:
-          if (t->ci->nodeID != nodeID || t->cj->nodeID != nodeID) {
-            if (t->flags < 0)
-              t->weight += 3 * wscale * t->ci->count * t->cj->count;
-            else
-              t->weight += 3 * wscale * t->ci->count * t->cj->count *
-                           sid_scale[t->flags];
-          } else {
-            if (t->flags < 0)
-              t->weight += 2 * wscale * t->ci->count * t->cj->count;
-            else
-              t->weight += 2 * wscale * t->ci->count * t->cj->count *
-                           sid_scale[t->flags];
-          }
-          break;
-        case task_type_sub_self:
-          t->weight += 1 * wscale * t->ci->count * t->ci->count;
-          break;
-        case task_type_ghost:
-          if (t->ci == t->ci->super) t->weight += wscale * t->ci->count;
-          break;
-        case task_type_kick:
-          t->weight += wscale * t->ci->count;
-          break;
-        case task_type_init:
-          t->weight += wscale * t->ci->count;
-          break;
-        default:
-          break;
-      }
+        }
+        break;
+      case task_type_sub_self:
+        cost = 1 * wscale * t->ci->count * t->ci->count;
+        break;
+      case task_type_ghost:
+        if (t->ci == t->ci->super) cost = wscale * t->ci->count;
+        break;
+      case task_type_kick:
+        cost = wscale * t->ci->count;
+        break;
+      case task_type_init:
+        cost = wscale * t->ci->count;
+        break;
+      default:
+        cost = 0;
+        break;
+    }
+#if defined(WITH_MPI) && defined(HAVE_METIS)
+    t->cost = cost;
+#endif
+    t->weight += cost;
   }
 
   if (verbose)
@@ -992,19 +993,15 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
  * @brief #threadpool_map function which runs through the task
  *        graph and re-computes the task wait counters.
  */
-
 void scheduler_rewait_mapper(void *map_data, int num_elements,
                              void *extra_data) {
 
-  struct scheduler *s = (struct scheduler *)extra_data;
   struct task *tasks = (struct task *)map_data;
 
   for (int ind = 0; ind < num_elements; ind++) {
     struct task *t = &tasks[ind];
 
-    if (t->skip || !((1 << t->type) & s->mask) ||
-        !((1 << t->subtype) & s->submask))
-      continue;
+    if (t->skip) continue;
 
     /* Skip sort tasks that have already been performed */
     if (t->type == task_type_sort && t->flags == 0) {
@@ -1026,8 +1023,7 @@ void scheduler_enqueue_mapper(void *map_data, int num_elements,
   struct task *tasks = s->tasks;
   for (int ind = 0; ind < num_elements; ind++) {
     struct task *t = &tasks[tid[ind]];
-    if (atomic_dec(&t->wait) == 1 && !t->skip && ((1 << t->type) & s->mask) &&
-        ((1 << t->subtype) & s->submask)) {
+    if (atomic_dec(&t->wait) == 1 && !t->skip) {
       scheduler_enqueue(s, t);
     }
   }
@@ -1038,79 +1034,66 @@ void scheduler_enqueue_mapper(void *map_data, int num_elements,
  * @brief Start the scheduler, i.e. fill the queues with ready tasks.
  *
  * @param s The #scheduler.
- * @param mask The task types to enqueue.
- * @param submask The sub-task types to enqueue.
  */
+void scheduler_start(struct scheduler *s) {
 
-void scheduler_start(struct scheduler *s, unsigned int mask,
-                     unsigned int submask) {
-
-  /* Store the masks */
-  s->mask = mask;
-  s->submask = submask | (1 << task_subtype_none);
-
-  /* Clear all the waits, rids, and times. */
-  for (int k = 0; k < s->nr_tasks; k++) {
-    s->tasks[k].wait = 1;
-    s->tasks[k].rid = -1;
-    s->tasks[k].tic = 0;
-    s->tasks[k].toc = 0;
-    if (((1 << s->tasks[k].type) & mask) == 0 ||
-        ((1 << s->tasks[k].subtype) & s->submask) == 0)
-      s->tasks[k].skip = 1;
-  }
+  /* Clear all the waits. */
+  for (int k = 0; k < s->nr_tasks; k++) s->tasks[k].wait = 1;
 
   /* Re-wait the tasks. */
   threadpool_map(s->threadpool, scheduler_rewait_mapper, s->tasks, s->nr_tasks,
-                 sizeof(struct task), 1000, s);
+                 sizeof(struct task), 1000, NULL);
 
 /* Check we have not missed an active task */
 #ifdef SWIFT_DEBUG_CHECKS
 
   const int ti_current = s->space->e->ti_current;
-  for (int k = 0; k < s->nr_tasks; k++) {
 
-    struct task *t = &s->tasks[k];
-    struct cell *ci = t->ci;
-    struct cell *cj = t->cj;
-
-    if (cj == NULL) { /* self */
-
-      if (ci->ti_end_min == ti_current && t->skip && t->type != task_type_sort)
-        error(
-            "Task (type='%s/%s') should not have been skipped ti_current=%d "
-            "c->ti_end_min=%d",
-            taskID_names[t->type], subtaskID_names[t->subtype], ti_current,
-            ci->ti_end_min);
-
-      /* Special treatment for sort tasks */
-      if (ci->ti_end_min == ti_current && t->skip &&
-          t->type == task_type_sort && t->flags == 0)
-        error(
-            "Task (type='%s/%s') should not have been skipped ti_current=%d "
-            "c->ti_end_min=%d t->flags=%d",
-            taskID_names[t->type], subtaskID_names[t->subtype], ti_current,
-            ci->ti_end_min, t->flags);
-
-    } else { /* pair */
-
-      if ((ci->ti_end_min == ti_current || cj->ti_end_min == ti_current) &&
-          t->skip)
-        error(
-            "Task (type='%s/%s') should not have been skipped ti_current=%d "
-            "ci->ti_end_min=%d cj->ti_end_min=%d",
-            taskID_names[t->type], subtaskID_names[t->subtype], ti_current,
-            ci->ti_end_min, cj->ti_end_min);
+  if (ti_current > 0) {
+
+    for (int k = 0; k < s->nr_tasks; k++) {
+
+      struct task *t = &s->tasks[k];
+      struct cell *ci = t->ci;
+      struct cell *cj = t->cj;
+
+      if (cj == NULL) { /* self */
+
+        if (ci->ti_end_min == ti_current && t->skip &&
+            t->type != task_type_sort)
+          error(
+              "Task (type='%s/%s') should not have been skipped ti_current=%d "
+              "c->ti_end_min=%d",
+              taskID_names[t->type], subtaskID_names[t->subtype], ti_current,
+              ci->ti_end_min);
+
+        /* Special treatment for sort tasks */
+        if (ci->ti_end_min == ti_current && t->skip &&
+            t->type == task_type_sort && t->flags == 0)
+          error(
+              "Task (type='%s/%s') should not have been skipped ti_current=%d "
+              "c->ti_end_min=%d t->flags=%d",
+              taskID_names[t->type], subtaskID_names[t->subtype], ti_current,
+              ci->ti_end_min, t->flags);
+
+      } else { /* pair */
+
+        if ((ci->ti_end_min == ti_current || cj->ti_end_min == ti_current) &&
+            t->skip)
+          error(
+              "Task (type='%s/%s') should not have been skipped ti_current=%d "
+              "ci->ti_end_min=%d cj->ti_end_min=%d",
+              taskID_names[t->type], subtaskID_names[t->subtype], ti_current,
+              ci->ti_end_min, cj->ti_end_min);
+      }
     }
   }
-
 #endif
 
   /* Loop over the tasks and enqueue whoever is ready. */
   for (int k = 0; k < s->active_count; k++) {
     struct task *t = &s->tasks[s->tid_active[k]];
-    if (atomic_dec(&t->wait) == 1 && !t->skip && ((1 << t->type) & s->mask) &&
-        ((1 << t->subtype) & s->submask)) {
+    if (atomic_dec(&t->wait) == 1 && !t->skip) {
       scheduler_enqueue(s, t);
       pthread_cond_signal(&s->sleep_cond);
     }
@@ -1131,20 +1114,13 @@ void scheduler_start(struct scheduler *s, unsigned int mask,
  * @param s The #scheduler.
  * @param t The #task.
  */
-
 void scheduler_enqueue(struct scheduler *s, struct task *t) {
 
   /* The target queue for this task. */
   int qid = -1;
 
-  /* Fail if this task has already been enqueued before. */
-  if (t->rid >= 0) error("Task has already been enqueued.");
-
-  /* Ignore skipped tasks and tasks not in the masks. */
-  if (t->skip || (1 << t->type) & ~(s->mask) ||
-      (1 << t->subtype) & ~(s->submask)) {
-    return;
-  }
+  /* Ignore skipped tasks */
+  if (t->skip) return;
 
   /* If this is an implicit task, just pretend it's done. */
   if (t->implicit) {
@@ -1247,7 +1223,6 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
  * @return A pointer to the next task, if a suitable one has
  *         been identified.
  */
-
 struct task *scheduler_done(struct scheduler *s, struct task *t) {
 
   /* Release whatever locks this task held. */
@@ -1268,7 +1243,9 @@ struct task *scheduler_done(struct scheduler *s, struct task *t) {
 
   /* Task definitely done, signal any sleeping runners. */
   if (!t->implicit) {
+#ifdef SWIFT_DEBUG_TASKS
     t->toc = getticks();
+#endif
     pthread_mutex_lock(&s->sleep_mutex);
     atomic_dec(&s->waiting);
     pthread_cond_broadcast(&s->sleep_cond);
@@ -1293,7 +1270,6 @@ struct task *scheduler_done(struct scheduler *s, struct task *t) {
  * @return A pointer to the next task, if a suitable one has
  *         been identified.
  */
-
 struct task *scheduler_unlock(struct scheduler *s, struct task *t) {
 
   /* Loop through the dependencies and add them to a queue if
@@ -1310,7 +1286,9 @@ struct task *scheduler_unlock(struct scheduler *s, struct task *t) {
 
   /* Task definitely done. */
   if (!t->implicit) {
+#ifdef SWIFT_DEBUG_TASKS
     t->toc = getticks();
+#endif
     pthread_mutex_lock(&s->sleep_mutex);
     atomic_dec(&s->waiting);
     pthread_cond_broadcast(&s->sleep_cond);
@@ -1332,7 +1310,6 @@ struct task *scheduler_unlock(struct scheduler *s, struct task *t) {
  *
  * @return A pointer to a #task or @c NULL if there are no available tasks.
  */
-
 struct task *scheduler_gettask(struct scheduler *s, int qid,
                                const struct task *prev) {
 
@@ -1381,10 +1358,11 @@ struct task *scheduler_gettask(struct scheduler *s, int qid,
 
 /* If we failed, take a short nap. */
 #ifdef WITH_MPI
-    if (res == NULL && qid > 1) {
+    if (res == NULL && qid > 1)
 #else
-    if (res == NULL) {
+    if (res == NULL)
 #endif
+    {
       pthread_mutex_lock(&s->sleep_mutex);
       res = queue_gettask(&s->queues[qid], prev, 1);
       if (res == NULL && s->waiting > 0) {
@@ -1394,11 +1372,13 @@ struct task *scheduler_gettask(struct scheduler *s, int qid,
     }
   }
 
+#ifdef SWIFT_DEBUG_TASKS
   /* Start the timer on this task, if we got one. */
   if (res != NULL) {
     res->tic = getticks();
     res->rid = qid;
   }
+#endif
 
   /* No milk today. */
   return res;
@@ -1415,7 +1395,6 @@ struct task *scheduler_gettask(struct scheduler *s, int qid,
  * @param nodeID The MPI rank
  * @param tp Parallel processing threadpool.
  */
-
 void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks,
                     int nr_queues, unsigned int flags, int nodeID,
                     struct threadpool *tp) {
diff --git a/src/scheduler.h b/src/scheduler.h
index 22c83ae3c551d4dc7e601e23f7a5301241bd1fa4..8631d22cce6cc5925f154a8f3c875dc8d38d5c8b 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -60,12 +60,6 @@ struct scheduler {
   /* Scheduler flags. */
   unsigned int flags;
 
-  /* Scheduler task mask */
-  unsigned int mask;
-
-  /* Scheduler sub-task mask */
-  unsigned int submask;
-
   /* Number of queues in this scheduler. */
   int nr_queues;
 
@@ -117,7 +111,6 @@ struct scheduler {
  * @param s The #scheduler.
  * @param t The task to be added.
  */
-
 __attribute__((always_inline)) INLINE static void scheduler_activate(
     struct scheduler *s, struct task *t) {
   if (atomic_cas(&t->skip, 1, 0)) {
@@ -134,8 +127,7 @@ void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks,
 struct task *scheduler_gettask(struct scheduler *s, int qid,
                                const struct task *prev);
 void scheduler_enqueue(struct scheduler *s, struct task *t);
-void scheduler_start(struct scheduler *s, unsigned int mask,
-                     unsigned int submask);
+void scheduler_start(struct scheduler *s);
 void scheduler_reset(struct scheduler *s, int nr_tasks);
 void scheduler_ranktasks(struct scheduler *s);
 void scheduler_reweight(struct scheduler *s, int verbose);
diff --git a/src/sourceterms/sn_feedback/sn_feedback_struct.h b/src/sourceterms/sn_feedback/sn_feedback_struct.h
index 2b4dbe97b0a687cc48d0ba7e12b105f306cb7e96..dd1842a6717c6c5a20352324cbe6b018c73e7b3e 100644
--- a/src/sourceterms/sn_feedback/sn_feedback_struct.h
+++ b/src/sourceterms/sn_feedback/sn_feedback_struct.h
@@ -42,4 +42,4 @@ struct supernova_struct {
   double x, y, z;
   enum supernova_status status;
 };
-#endif SWIFT_SN_FEEDBACK_STRUCT_H
+#endif /* SWIFT_SN_FEEDBACK_STRUCT_H */
diff --git a/src/space.c b/src/space.c
index 86c8cc36eacd58c89181831b3c5472026281043c..e04aad2207e736ac8fe4da28b7d22b7c7999765b 100644
--- a/src/space.c
+++ b/src/space.c
@@ -188,10 +188,9 @@ void space_rebuild_recycle(struct space *s, struct cell *c) {
  * @brief Re-build the top-level cell grid.
  *
  * @param s The #space.
- * @param cell_max Maximum cell edge length.
  * @param verbose Print messages to stdout or not.
  */
-void space_regrid(struct space *s, double cell_max, int verbose) {
+void space_regrid(struct space *s, int verbose) {
 
   const size_t nr_parts = s->nr_parts;
   const ticks tic = getticks();
@@ -226,13 +225,16 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
     h_max = buff;
   }
 #endif
-  if (verbose) message("h_max is %.3e (cell_max=%.3e).", h_max, cell_max);
+  if (verbose) message("h_max is %.3e (cell_min=%.3e).", h_max, s->cell_min);
 
   /* Get the new putative cell dimensions. */
   const int cdim[3] = {
-      floor(s->dim[0] / fmax(h_max * kernel_gamma * space_stretch, cell_max)),
-      floor(s->dim[1] / fmax(h_max * kernel_gamma * space_stretch, cell_max)),
-      floor(s->dim[2] / fmax(h_max * kernel_gamma * space_stretch, cell_max))};
+      floor(s->dim[0] /
+            fmax(h_max * kernel_gamma * space_stretch, s->cell_min)),
+      floor(s->dim[1] /
+            fmax(h_max * kernel_gamma * space_stretch, s->cell_min)),
+      floor(s->dim[2] /
+            fmax(h_max * kernel_gamma * space_stretch, s->cell_min))};
 
   /* Check if we have enough cells for periodicity. */
   if (s->periodic && (cdim[0] < 3 || cdim[1] < 3 || cdim[2] < 3))
@@ -241,7 +243,8 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
         "is switched on.\nThis error is often caused by any of the "
         "followings:\n"
         " - too few particles to generate a sensible grid,\n"
-        " - the initial value of 'SPH:max_smoothing_length' is too large,\n"
+        " - the initial value of 'Scheduler:max_top_level_cells' is too "
+        "small,\n"
         " - the (minimal) time-step is too large leading to particles with "
         "predicted smoothing lengths too large for the box size,\n"
         " - particle with velocities so large that they move by more than two "
@@ -315,6 +318,8 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
                        s->nr_cells * sizeof(struct cell)) != 0)
       error("Failed to allocate cells.");
     bzero(s->cells_top, s->nr_cells * sizeof(struct cell));
+
+    /* Set the cells' locks */
     for (int k = 0; k < s->nr_cells; k++)
       if (lock_init(&s->cells_top[k].lock) != 0)
         error("Failed to init spinlock.");
@@ -343,7 +348,6 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
     if (verbose)
       message("set cell dimensions to [ %i %i %i ].", cdim[0], cdim[1],
               cdim[2]);
-    fflush(stdout);
 
 #ifdef WITH_MPI
     if (oldnodeIDs != NULL) {
@@ -392,10 +396,6 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
       space_rebuild_recycle(s, &s->cells_top[k]);
       s->cells_top[k].sorts = NULL;
       s->cells_top[k].nr_tasks = 0;
-      s->cells_top[k].nr_density = 0;
-      s->cells_top[k].nr_gradient = 0;
-      s->cells_top[k].nr_force = 0;
-      s->cells_top[k].nr_grav = 0;
       s->cells_top[k].density = NULL;
       s->cells_top[k].gradient = NULL;
       s->cells_top[k].force = NULL;
@@ -435,11 +435,10 @@ void space_regrid(struct space *s, double cell_max, int verbose) {
  * @brief Re-build the cells as well as the tasks.
  *
  * @param s The #space in which to update the cells.
- * @param cell_max Maximal cell size.
  * @param verbose Print messages to stdout or not
  *
  */
-void space_rebuild(struct space *s, double cell_max, int verbose) {
+void space_rebuild(struct space *s, int verbose) {
 
   const ticks tic = getticks();
 
@@ -447,7 +446,7 @@ void space_rebuild(struct space *s, double cell_max, int verbose) {
   // message("re)building space..."); fflush(stdout);
 
   /* Re-grid if necessary, or just re-set the cell data. */
-  space_regrid(s, cell_max, verbose);
+  space_regrid(s, verbose);
 
   size_t nr_parts = s->nr_parts;
   size_t nr_gparts = s->nr_gparts;
@@ -734,6 +733,8 @@ void space_split(struct space *s, struct cell *cells, int nr_cells,
  */
 void space_sanitize(struct space *s) {
 
+  s->sanitized = 1;
+
   for (int k = 0; k < s->nr_cells; k++) {
 
     struct cell *c = &s->cells_top[k];
@@ -1444,6 +1445,7 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) {
 
     const int count = c->count;
     const int gcount = c->gcount;
+    const int depth = c->depth;
     int maxdepth = 0;
     float h_max = 0.0f;
     int ti_end_min = max_nr_timesteps, ti_end_max = 0;
@@ -1453,8 +1455,8 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) {
     struct xpart *xparts = c->xparts;
 
     /* Check the depth. */
-    while (c->depth > (maxdepth = s->maxdepth)) {
-      atomic_cas(&s->maxdepth, maxdepth, c->depth);
+    while (depth > (maxdepth = s->maxdepth)) {
+      atomic_cas(&s->maxdepth, maxdepth, depth);
     }
 
     /* If the depth is too large, we have a problem and should stop. */
@@ -1562,6 +1564,15 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) {
     else
       c->owner = 0; /* Ok, there is really nothing on this rank... */
   }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* All cells and particles should have consistent h_max values. */
+  for (int ind = 0; ind < num_cells; ind++) {
+    int depth = 0;
+    if (!checkCellhdxmax(&cells_top[ind], &depth))
+      message("    at cell depth %d", depth);
+  }
+#endif
 }
 
 /**
@@ -1740,6 +1751,7 @@ void space_init(struct space *s, const struct swift_params *params,
   s->dim[0] = dim[0];
   s->dim[1] = dim[1];
   s->dim[2] = dim[2];
+  s->sanitized = 0;
   s->periodic = periodic;
   s->gravity = gravity;
   s->nr_parts = Npart;
@@ -1748,9 +1760,24 @@ void space_init(struct space *s, const struct swift_params *params,
   s->nr_gparts = Ngpart;
   s->size_gparts = Ngpart;
   s->gparts = gparts;
-  s->cell_min = parser_get_param_double(params, "SPH:max_smoothing_length");
   s->nr_queues = 1; /* Temporary value until engine construction */
 
+  /* Decide on the minimal top-level cell size */
+  const double dmax = max(max(dim[0], dim[1]), dim[2]);
+  int maxtcells =
+      parser_get_opt_param_int(params, "Scheduler:max_top_level_cells",
+                               space_max_top_level_cells_default);
+  s->cell_min = 0.99 * dmax / maxtcells;
+
+  /* Check that it is big enough. */
+  const double dmin = min(min(dim[0], dim[1]), dim[2]);
+  int needtcells = 3 * dmax / dmin;
+  if (maxtcells < needtcells)
+    error(
+        "Scheduler:max_top_level_cells is too small %d, needs to be at "
+        "least %d",
+        maxtcells, needtcells);
+
   /* Get the constants for the scheduler */
   space_maxsize = parser_get_opt_param_int(params, "Scheduler:cell_max_size",
                                            space_maxsize_default);
@@ -1764,14 +1791,6 @@ void space_init(struct space *s, const struct swift_params *params,
     message("max_size set to %d, sub_size set to %d, split_size set to %d",
             space_maxsize, space_subsize, space_splitsize);
 
-  /* Check that we have enough cells */
-  if (s->cell_min * 3 > dim[0] || s->cell_min * 3 > dim[1] ||
-      s->cell_min * 3 > dim[2])
-    error(
-        "Maximal smoothing length (%e) too large. Needs to be "
-        "smaller than 1/3 the simulation box size [%e %e %e]",
-        s->cell_min, dim[0], dim[1], dim[2]);
-
   /* Apply h scaling */
   const double scaling =
       parser_get_opt_param_double(params, "InitialConditions:h_scaling", 1.0);
@@ -1850,13 +1869,15 @@ void space_init(struct space *s, const struct swift_params *params,
   if (lock_init(&s->lock) != 0) error("Failed to create space spin-lock.");
 
   /* Build the cells and the tasks. */
-  if (!dry_run) space_regrid(s, s->cell_min, verbose);
+  if (!dry_run) space_regrid(s, verbose);
 }
 
 /**
  * @brief Cleans-up all the cell links in the space
  *
  * Expensive funtion. Should only be used for debugging purposes.
+ *
+ * @param s The #space to clean.
  */
 void space_link_cleanup(struct space *s) {
 
@@ -1864,6 +1885,20 @@ void space_link_cleanup(struct space *s) {
   space_map_cells_pre(s, 1, cell_clean_links, NULL);
 }
 
+/**
+ * @brief Checks that all cells have been drifted to the current point in time
+ *
+ * Expensive function. Should only be used for debugging purposes.
+ *
+ * @param s The #space to check.
+ * @param ti_current The (integer) time.
+ */
+void space_check_drift_point(struct space *s, int ti_current) {
+
+  /* Recursively check all cells */
+  space_map_cells_pre(s, 1, cell_check_drift_point, &ti_current);
+}
+
 /**
  * @brief Frees up the memory allocated for this #space
  */
diff --git a/src/space.h b/src/space.h
index 011dfb71a6c3ac2b51093ce83bc6b65ceecc2821..53cf2d0c8fa548ae19aa7452abb38c3e3e028165 100644
--- a/src/space.h
+++ b/src/space.h
@@ -42,6 +42,7 @@
 #define space_maxsize_default 8000000
 #define space_subsize_default 64000000
 #define space_maxcount_default 10000
+#define space_max_top_level_cells_default 12
 #define space_stretch 1.10f
 #define space_maxreldx 0.25f
 
@@ -122,6 +123,9 @@ struct space {
   /*! Number of queues in the system. */
   int nr_queues;
 
+  /*! Has this space already been sanitized ? */
+  int sanitized;
+
   /*! The associated engine. */
   struct engine *e;
 
@@ -165,7 +169,7 @@ void space_parts_sort_mapper(void *map_data, int num_elements,
                              void *extra_data);
 void space_gparts_sort_mapper(void *map_data, int num_elements,
                               void *extra_data);
-void space_rebuild(struct space *s, double h_max, int verbose);
+void space_rebuild(struct space *s, int verbose);
 void space_recycle(struct space *s, struct cell *c);
 void space_split(struct space *s, struct cell *cells, int nr_cells,
                  int verbose);
@@ -179,6 +183,7 @@ void space_do_gparts_sort();
 void space_init_parts(struct space *s);
 void space_init_gparts(struct space *s);
 void space_link_cleanup(struct space *s);
+void space_check_drift_point(struct space *s, int ti_current);
 void space_clean(struct space *s);
 
 #endif /* SWIFT_SPACE_H */
diff --git a/src/statistics.c b/src/statistics.c
index 0afca84f517ca201fc184de2abff40b9ce9d5711..7a567a447a7514634435823e03bec5e4ac157d4e 100644
--- a/src/statistics.c
+++ b/src/statistics.c
@@ -62,7 +62,8 @@ void stats_add(struct statistics *a, const struct statistics *b) {
   /* Add everything */
   a->E_kin += b->E_kin;
   a->E_int += b->E_int;
-  a->E_pot += b->E_pot;
+  a->E_pot_self += b->E_pot_self;
+  a->E_pot_ext += b->E_pot_ext;
   a->E_rad += b->E_rad;
   a->entropy += b->entropy;
   a->mass += b->mass;
@@ -107,6 +108,10 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) {
   const double timeBase = s->e->timeBase;
   struct statistics *const global_stats = data->stats;
 
+  /* Required for external potential energy */
+  const struct external_potential *potential = s->e->external_potential;
+  const struct phys_const *phys_const = s->e->physical_constants;
+
   /* Local accumulator */
   struct statistics stats;
   stats_init(&stats);
@@ -117,15 +122,16 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) {
     /* Get the particle */
     const struct part *p = &parts[k];
     const struct xpart *xp = &xparts[k];
+    const struct gpart *gp = (p->gpart != NULL) ? gp = p->gpart : NULL;
 
     /* Get useful variables */
     const float dt = (ti_current - (p->ti_begin + p->ti_end) / 2) * timeBase;
     const double x[3] = {p->x[0], p->x[1], p->x[2]};
     float a_tot[3] = {p->a_hydro[0], p->a_hydro[1], p->a_hydro[2]};
-    if (p->gpart != NULL) {
-      a_tot[0] += p->gpart->a_grav[0];
-      a_tot[1] += p->gpart->a_grav[1];
-      a_tot[2] += p->gpart->a_grav[2];
+    if (gp != NULL) {
+      a_tot[0] += gp->a_grav[0];
+      a_tot[1] += gp->a_grav[1];
+      a_tot[2] += gp->a_grav[2];
     }
     const float v[3] = {xp->v_full[0] + a_tot[0] * dt,
                         xp->v_full[1] + a_tot[1] * dt,
@@ -148,7 +154,10 @@ void stats_collect_part_mapper(void *map_data, int nr_parts, void *extra_data) {
 
     /* Collect energies. */
     stats.E_kin += 0.5f * m * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
-    stats.E_pot += 0.;
+    stats.E_pot_self += 0.f;
+    if (gp != NULL)
+      stats.E_pot_ext +=
+          m * external_gravity_get_potential_energy(potential, phys_const, gp);
     stats.E_int += m * hydro_get_internal_energy(p, dt);
     stats.E_rad += cooling_get_radiated_energy(xp);
 
@@ -179,6 +188,10 @@ void stats_collect_gpart_mapper(void *map_data, int nr_gparts,
   const double timeBase = s->e->timeBase;
   struct statistics *const global_stats = data->stats;
 
+  /* Required for external potential energy */
+  const struct external_potential *potential = s->e->external_potential;
+  const struct phys_const *phys_const = s->e->physical_constants;
+
   /* Local accumulator */
   struct statistics stats;
   stats_init(&stats);
@@ -216,7 +229,9 @@ 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]);
-    stats.E_pot += 0.;
+    stats.E_pot_self += 0.f;
+    stats.E_pot_ext +=
+        m * external_gravity_get_potential_energy(potential, phys_const, gp);
   }
 
   /* Now write back to memory */
@@ -258,15 +273,16 @@ void stats_collect(const struct space *s, struct statistics *stats) {
 void stats_print_to_file(FILE *file, const struct statistics *stats,
                          double time) {
 
-  const double E_tot = stats->E_kin + stats->E_int + stats->E_pot;
+  const double E_pot = stats->E_pot_self + stats->E_pot_ext;
+  const double E_tot = stats->E_kin + stats->E_int + E_pot;
 
   fprintf(file,
           " %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e %14e "
-          "%14e\n",
-          time, stats->mass, E_tot, stats->E_kin, stats->E_int, stats->E_pot,
-          stats->E_rad, stats->entropy, stats->mom[0], stats->mom[1],
-          stats->mom[2], stats->ang_mom[0], stats->ang_mom[1],
-          stats->ang_mom[2]);
+          "%14e %14e %14e\n",
+          time, stats->mass, E_tot, stats->E_kin, stats->E_int, E_pot,
+          stats->E_pot_self, stats->E_pot_ext, stats->E_rad, stats->entropy,
+          stats->mom[0], stats->mom[1], stats->mom[2], stats->ang_mom[0],
+          stats->ang_mom[1], stats->ang_mom[2]);
   fflush(file);
 }
 
diff --git a/src/statistics.h b/src/statistics.h
index ece1f813bb8aff2b5402753bafc031696cfa562d..b007d1314c458254986862f99b7cb1669d995545 100644
--- a/src/statistics.h
+++ b/src/statistics.h
@@ -37,8 +37,11 @@ struct statistics {
   /*! Internal energy */
   double E_int;
 
-  /*! Potential energy */
-  double E_pot;
+  /*! Self potential energy */
+  double E_pot_self;
+
+  /*! External potential energy */
+  double E_pot_ext;
 
   /*! Radiative energy */
   double E_rad;
diff --git a/src/task.c b/src/task.c
index 54b9363b7ac3d5c372b591c00b0b03cc274f66b5..ea97fdd1bb930d005889fa7c73a3f2cb7b5f054a 100644
--- a/src/task.c
+++ b/src/task.c
@@ -48,10 +48,10 @@
 
 /* Task type names. */
 const char *taskID_names[task_type_count] = {
-    "none",       "sort",    "self",    "pair",          "sub_self",
-    "sub_pair",   "init",    "ghost",   "extra_ghost",   "kick",
-    "kick_fixdt", "send",    "recv",    "grav_gather_m", "grav_fft",
-    "grav_mm",    "grav_up", "cooling", "sourceterms"};
+    "none",     "sort",    "self",          "pair",        "sub_self",
+    "sub_pair", "init",    "ghost",         "extra_ghost", "kick",
+    "send",     "recv",    "grav_gather_m", "grav_fft",    "grav_mm",
+    "grav_up",  "cooling", "sourceterms"};
 
 const char *subtaskID_names[task_subtype_count] = {
     "none", "density", "gradient", "force", "grav", "external_grav", "tend"};
@@ -148,7 +148,6 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
 
     case task_type_init:
     case task_type_kick:
-    case task_type_kick_fixdt:
     case task_type_send:
     case task_type_recv:
       if (t->ci->count > 0 && t->ci->gcount > 0)
@@ -372,32 +371,6 @@ int task_lock(struct task *t) {
   return 1;
 }
 
-/**
- * @brief Prints the list of tasks contained in a given mask
- *
- * @param mask The mask to analyse
- */
-void task_print_mask(unsigned int mask) {
-
-  printf("task_print_mask: The tasks to run are [");
-  for (int k = 1; k < task_type_count; k++)
-    printf(" %s=%s", taskID_names[k], (mask & (1 << k)) ? "yes" : "no");
-  printf(" ]\n");
-}
-
-/**
- * @brief Prints the list of subtasks contained in a given submask
- *
- * @param submask The submask to analyse
- */
-void task_print_submask(unsigned int submask) {
-
-  printf("task_print_submask: The subtasks to run are [");
-  for (int k = 1; k < task_subtype_count; k++)
-    printf(" %s=%s", subtaskID_names[k], (submask & (1 << k)) ? "yes" : "no");
-  printf(" ]\n");
-}
-
 /**
  * @brief Print basic information about a task.
  *
diff --git a/src/task.h b/src/task.h
index f840c0b4b8e807dce28f6f13479dbdf4995ab66d..c9425fdd137e2c1708dbd05436d1db685bdd3bfd 100644
--- a/src/task.h
+++ b/src/task.h
@@ -46,7 +46,6 @@ enum task_types {
   task_type_ghost,
   task_type_extra_ghost,
   task_type_kick,
-  task_type_kick_fixdt,
   task_type_send,
   task_type_recv,
   task_type_grav_gather_m,
@@ -105,9 +104,6 @@ struct task {
   /*! List of tasks unlocked by this one */
   struct task **unlock_tasks;
 
-  /*! Start and end time of this task */
-  ticks tic, toc;
-
 #ifdef WITH_MPI
 
   /*! Buffer for this task's communications */
@@ -127,8 +123,10 @@ struct task {
   /*! Weight of the task */
   int weight;
 
-  /*! ID of the queue or runner owning this task */
-  short int rid;
+#if defined(WITH_MPI) && defined(HAVE_METIS)
+  /*! Individual cost estimate for this task. */
+  int cost;
+#endif
 
   /*! Number of tasks unlocked by this one */
   short int nr_unlock_tasks;
@@ -151,14 +149,20 @@ struct task {
   /*! Is this task implicit (i.e. does not do anything) ? */
   char implicit;
 
+#ifdef SWIFT_DEBUG_TASKS
+  /*! ID of the queue or runner owning this task */
+  short int rid;
+
+  /*! Start and end time of this task */
+  ticks tic, toc;
+#endif
+
 } SWIFT_STRUCT_ALIGN;
 
 /* Function prototypes. */
 void task_unlock(struct task *t);
 float task_overlap(const struct task *ta, const struct task *tb);
 int task_lock(struct task *t);
-void task_print_mask(unsigned int mask);
-void task_print_submask(unsigned int submask);
 void task_do_rewait(struct task *t);
 void task_print(const struct task *t);
 
diff --git a/src/timers.h b/src/timers.h
index d75508afd88cf2fd57d06f9530bff8607d79d127..bc877d4094425a4948290d2c7c099f49cbd44280 100644
--- a/src/timers.h
+++ b/src/timers.h
@@ -56,6 +56,7 @@ enum {
   timer_dosub_pair_grav,
   timer_dopair_subset,
   timer_do_ghost,
+  timer_do_extra_ghost,
   timer_dorecv_cell,
   timer_gettask,
   timer_qget,
diff --git a/src/tools.c b/src/tools.c
index 060bf1439f30dc6237938c060bc4ddc8d9be822b..e526bb1b838f6d97b72eadb4070f3f2a94938c04 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -481,7 +481,7 @@ void engine_single_density(double *dim, long long int pid,
   }
 
   /* Dump the result. */
-  hydro_end_density(&p, 0);
+  hydro_end_density(&p);
   message("part %lli (h=%e) has wcount=%e, rho=%e.", p.id, p.h,
           p.density.wcount, hydro_get_density(&p));
   fflush(stdout);
diff --git a/src/units.c b/src/units.c
index 2241d441ec9af9b6d5083191e8f61010ebaccb20..05b83a3427cc40efe8a9cc3a79aa48fec4af05c1 100644
--- a/src/units.c
+++ b/src/units.c
@@ -320,9 +320,15 @@ void units_get_base_unit_exponants_array(float baseUnitsExp[5],
 
     case UNIT_CONV_VOLUME:
       baseUnitsExp[UNIT_LENGTH] = 3.f;
+      break;
 
     case UNIT_CONV_INV_VOLUME:
       baseUnitsExp[UNIT_LENGTH] = -3.f;
+      break;
+
+    default:
+      error("Invalid choice of pre-defined units");
+      break;
   }
 }
 
diff --git a/tests/test125cells.c b/tests/test125cells.c
index d423efba3fda764310721586e14192afdee18a85..3ae80d952f78e8f50235cf38af493501c1c97634 100644
--- a/tests/test125cells.c
+++ b/tests/test125cells.c
@@ -269,12 +269,12 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
         set_velocity(part, vel, size);
         set_energy_state(part, press, size, density);
 
+        hydro_first_init_part(part, xpart);
+
         part->id = ++(*partId);
         part->ti_begin = 0;
         part->ti_end = 1;
 
-        hydro_first_init_part(part, xpart);
-
 #if defined(GIZMO_SPH)
         part->geometry.volume = part->conserved.mass / density;
         part->primitives.rho = density;
@@ -311,6 +311,7 @@ struct cell *make_cell(size_t n, const double offset[3], double size, double h,
   cell->loc[1] = offset[1];
   cell->loc[2] = offset[2];
 
+  cell->ti_old = 1;
   cell->ti_end_min = 1;
   cell->ti_end_max = 1;
 
@@ -617,7 +618,7 @@ int main(int argc, char *argv[]) {
 #endif
 
     /* Ghost to finish everything on the central cells */
-    for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j]);
+    for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0);
 
 /* Do the force calculation */
 #if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
@@ -704,7 +705,7 @@ int main(int argc, char *argv[]) {
 #endif
 
   /* Ghost to finish everything on the central cells */
-  for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j]);
+  for (int j = 0; j < 27; ++j) runner_do_ghost(&runner, inner_cells[j], 0);
 
 /* Do the force calculation */
 #if !(defined(MINIMAL_SPH) && defined(WITH_VECTORIZATION))
diff --git a/tests/test27cells.c b/tests/test27cells.c
index 22619af53c39218da34d771fab6ed2d10993689c..f58b4dc410637f3d91369dab1b442de0b7044c08 100644
--- a/tests/test27cells.c
+++ b/tests/test27cells.c
@@ -167,7 +167,7 @@ void zero_particle_fields(struct cell *c) {
  */
 void end_calculation(struct cell *c) {
   for (int pid = 0; pid < c->count; pid++) {
-    hydro_end_density(&c->parts[pid], 1);
+    hydro_end_density(&c->parts[pid]);
   }
 }